diff --git a/README.md b/README.md index f973ef7..366c922 100644 --- a/README.md +++ b/README.md @@ -583,6 +583,8 @@ You can also apply some advanced settings to the map generation process.
- Farmlands margin - this value (in meters) will be applied to each farmland, making it bigger. You can use the value to adjust how much the farmland should be bigger than the actual field. By default, it's set to 3. +- Add grass - if enabled, the tool will add grass to all the empty areas (without roads, fields, buildings, etc.). By default, it's set to True. + - Random plants - when adding decorative foliage, enabling this option will add different species of plants to the map. If unchecked only basic grass (smallDenseMix) will be added. Defaults to True. - Add Farmyards - if enabled, the tool will create farmlands from the regions that are marked as farmyards in the OSM data. Those farmlands will not have fields and also will not be drawn on textures. By default, it's turned off. @@ -607,6 +609,8 @@ You can also apply some advanced settings to the map generation process.
### I3D Advanced settings +- Add trees - if enabled, the tool will add trees to the map on the areas, that defined as forests in the OSM data. By default, it's set to True. + - Forest density - the density of the forest in meters. The lower the value, the lower the distance between the trees, which makes the forest denser. Note, that low values will lead to enormous number of trees, which may cause the Giants Editor to crash or lead to performance issues. By default, it's set to 10. - Trees relative shift - represents the maximum possible shift of the tree from it's original position in percents of the forest density value. The higher the value, the more the trees will be shifted from their original positions. Warning: higher values can lead to overlapping trees. diff --git a/maps4fs/generator/component/background.py b/maps4fs/generator/component/background.py index 7498a6a..d567fbe 100644 --- a/maps4fs/generator/component/background.py +++ b/maps4fs/generator/component/background.py @@ -144,7 +144,7 @@ def create_foundations(self, dem_image: np.ndarray) -> np.ndarray: mean_value = cv2.mean(dem_image, mask=mask)[0] mean_value = np.round(mean_value).astype(dem_image.dtype) - self.logger.info("Mean value of the building area: %s", mean_value) + self.logger.debug("Mean value of the building area: %s", mean_value) # Set the pixel values in the DEM to the average pixel value. dem_image[mask == 255] = mean_value diff --git a/maps4fs/generator/component/grle.py b/maps4fs/generator/component/grle.py index 99765d7..0ccc425 100644 --- a/maps4fs/generator/component/grle.py +++ b/maps4fs/generator/component/grle.py @@ -104,10 +104,8 @@ def process(self) -> None: self.logger.warning("Invalid InfoLayer schema: %s.", info_layer) self._add_farmlands() - if self.game.plants_processing: + if self.game.plants_processing and self.map.grle_settings.add_grass: self._add_plants() - else: - self.logger.warning("Adding plants it's not supported for the %s.", self.game.code) def previews(self) -> list[str]: """Returns a list of paths to the preview images (empty list). diff --git a/maps4fs/generator/component/i3d.py b/maps4fs/generator/component/i3d.py index c946ef0..17f7534 100644 --- a/maps4fs/generator/component/i3d.py +++ b/maps4fs/generator/component/i3d.py @@ -56,7 +56,9 @@ def process(self) -> None: if self.game.i3d_processing: self._add_fields() - self._add_forests() + + if self.map.i3d_settings.add_trees: + self._add_forests() self._add_splines() def update_height_scale(self, value: int | None = None) -> None: diff --git a/maps4fs/generator/settings.py b/maps4fs/generator/settings.py index b051f59..03f0148 100644 --- a/maps4fs/generator/settings.py +++ b/maps4fs/generator/settings.py @@ -167,11 +167,12 @@ class GRLESettings(SettingsModel): """ farmland_margin: int = 0 - random_plants: bool = True add_farmyards: bool = False base_price: int = 60000 price_scale: int = 100 + add_grass: bool = True base_grass: tuple | str = ("smallDenseMix", "meadow") + random_plants: bool = True plants_island_minimum_size: int = 10 plants_island_maximum_size: int = 200 plants_island_vertex_count: int = 30 @@ -187,6 +188,7 @@ class I3DSettings(SettingsModel): forest_density (int): density of the forest (distance between trees). """ + add_trees: bool = True forest_density: int = 10 trees_relative_shift: int = 20 diff --git a/webui/generator/advanced_settings.py b/webui/generator/advanced_settings.py index 3b0d8d4..ba14203 100644 --- a/webui/generator/advanced_settings.py +++ b/webui/generator/advanced_settings.py @@ -22,17 +22,24 @@ def get_settings(self): category = {} with st.expander(category_name, expanded=False): - for raw_field_name, field_value in model.__dict__.items(): + for idx, (raw_field_name, field_value) in enumerate(model.__dict__.items()): default_value = default_category_settings.get(raw_field_name) if default_value is not None: field_value = default_value field_name = self.snake_to_human(raw_field_name) disabled = self.is_disabled_on_public(raw_field_name) - st.write(getattr(Settings, raw_field_name.upper())) + with st.empty(): widget = self._create_widget( "main", field_name, raw_field_name, field_value, disabled ) + st.write(getattr(Settings, raw_field_name.upper())) + example = getattr(Settings, f"{raw_field_name.upper()}_EXAMPLE", None) + if example: + with st.popover("How it works"): + st.markdown(example) + if not idx == len(model.__dict__) - 1: + st.markdown("---") category[raw_field_name] = widget @@ -52,5 +59,5 @@ def is_disabled_on_public(self, raw_field_name: str) -> bool: if not self.public: return False - disabled_fields = ["resize_factor", "dissolve", "zoom_level"] # , "download_images"] + disabled_fields = ["resize_factor", "zoom_level"] # , "download_images"] return raw_field_name in disabled_fields diff --git a/webui/generator/generator.py b/webui/generator/generator.py index 8aaee96..ec4f9f0 100644 --- a/webui/generator/generator.py +++ b/webui/generator/generator.py @@ -189,8 +189,8 @@ def limit_on_public(self, settings_json: dict) -> dict: limited_settings = settings_json.copy() limited_settings["BackgroundSettings"]["resize_factor"] = 8 - limited_settings["TextureSettings"]["dissolve"] = False - limited_settings["SatelliteSettings"]["zoom_level"] = 14 + # limited_settings["TextureSettings"]["dissolve"] = False + limited_settings["SatelliteSettings"]["zoom_level"] = 16 # limited_settings["SatelliteSettings"]["download_images"] = False return limited_settings diff --git a/webui/generator/main_settings.py b/webui/generator/main_settings.py index d1a4632..7474ee3 100644 --- a/webui/generator/main_settings.py +++ b/webui/generator/main_settings.py @@ -40,7 +40,7 @@ def __init__(self, public: bool, **kwargs): size_options = [2048, 4096, 8192, 16384, "Custom"] if self.public: - size_options = size_options[:3] + size_options = size_options[:2] st.write("Select size of the map:") self.map_size_input = st.selectbox( @@ -50,6 +50,9 @@ def __init__(self, public: bool, **kwargs): on_change=self.map_preview, ) + if self.public: + st.warning(Messages.PUBLIC_MAP_SIZE, icon="💡") + if self.map_size_input == "Custom": st.write("Enter map size (meters):") custom_map_size_input = st.number_input( diff --git a/webui/templates.py b/webui/templates.py index fd9e1ac..b46f6d4 100644 --- a/webui/templates.py +++ b/webui/templates.py @@ -38,6 +38,8 @@ class Messages: "In this section you can generate a global preview of all the generated maps. \n" ) + PUBLIC_MAP_SIZE = "If you run the app locally, you can generate maps of any size." + MOVED = "The app has moved to ➡️➡️➡️ [maps4fs.xyz](https://maps4fs.xyz)" MAIN_PAGE_COMMUNITY_WARNING = ( "🚜 Hey, farmer! \n" @@ -162,6 +164,13 @@ class Settings: "Follow the recommendations of the DTM provider you selected for the best result. \n" "ℹ️ **Units:** integer value." ) + BLUR_RADIUS_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/78e18ceb-b5b1-4a9d-b51b-4834effdaf9b) \n" + "⚠️ **Note:** This image represents the difference when using low quality DEM data " + "with resolution of 30 meters per pixel. If you're using high quality DEM data, " + "do not use high blur radius values, because it will destroy the details of the terrain." + ) + PLATEAU = ( "DEM plateau value is used to make the whole map higher or lower. " "This value will be added to each pixel of the DEM image, making it higher. " @@ -188,10 +197,19 @@ class Settings: "for your map. \n" "ℹ️ **Units:** meters." ) + WATER_DEPTH_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/22b99071-3169-4c02-9425-1e9fec0e27ec) \n" + "⚠️ **Note:** This image represents the difference when using low quality DEM data " + "where there's no data about the water depth. If you're using high quality DEM data, " + "you don't need to use this setting, or it will break the terrain." + ) ADD_FOUNDATIONS = ( "If add foundations is enabled, the terrain under the buildings will be flattened." ) + ADD_FOUNDATIONS_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/51cd6005-6971-49e5-a649-4ea31abac95d)" + ) # Background Settings @@ -214,6 +232,10 @@ class Settings: "If remove center is enabled, the region of playable map terrain will be removed " "from the background terrain 3D model." ) + REMOVE_CENTER_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/912864b7-c790-47a9-a001-dd1936d21c17)" + ) + APPLY_DECIMATION = ( "If apply decimation is enabled, the background terrain will be decimated to the " "specified value. It can be useful if you want to reduce the size of the 3D model. " @@ -239,10 +261,16 @@ class Settings: "position as the field ends. This can cause some issues with gameplay. \n" "ℹ️ **Units:** meters." ) + FARMLAND_MARGIN_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/c160bf6d-9217-455b-9655-462dc09b943c)" + ) RANDOM_PLANTS = ( "If random plants are enabled the different species of plants will be generated. " "If unchecked, only basic smallDenseMix will be applied." ) + RANDOM_PLANTS_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/0728b31a-c2a3-4cc5-ab25-09d8832f0e3a)" + ) ADD_FARMYARDS = ( "If add farmyards is enabled and info_layer: farmyards is present in the texture schema, " @@ -250,33 +278,56 @@ class Settings: "corresponding field. It can be useful if you want to add some farmland in the " "regions without fields." ) + ADD_GRASS = ( + "If add grass is enabled, all the empty areas (without fields, roads, etc.) will " + "be filled with grass." + ) + ADD_GRASS_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/49c0376a-b83b-46f0-9e25-2f11e03e16c0)" + ) + BASE_GRASS = "Select the plant that will be used as a base grass." PLANTS_ISLAND_MINIMUM_SIZE = ( "Plants island minimum size value is used to set the minimum size of the plants islands " "when random size of the island will be selected, it will be the lowest possible size. \n" "ℹ️ **Units:** meters." ) + PLANTS_ISLAND_MINIMUM_SIZE_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/1cac092f-62c7-4da3-82d1-a72cb64109cb)" + ) PLANTS_ISLAND_MAXIMUM_SIZE = ( "Plants island maximum size value is used to set the maximum size of the plants islands " "when random size of the island will be selected, it will be the highest possible size. \n" "ℹ️ **Units:** meters." ) + PLANTS_ISLAND_MAXIMUM_SIZE_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/1cac092f-62c7-4da3-82d1-a72cb64109cb)" + ) PLANTS_ISLAND_VERTEX_COUNT = ( "Plants island vertex count value is used to set the number of vertices of the plants. " "The higher the value, the more complex shapes of the island will be. \n" "ℹ️ **Units:** number of vertices." ) + PLANTS_ISLAND_VERTEX_COUNT_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/b8c7f1d5-9446-4d4c-9a51-5357f7984c60)" + ) PLANTS_ISLAND_ROUNDING_RADIUS = ( "Plants island rounding radius value is used to set the rounding radius of the plants. " "The higher the value, the more rounded the vertices will be. \n" "ℹ️ **Units:** meters." ) + PLANTS_ISLAND_ROUNDING_RADIUS_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/1b488107-9e85-479b-a59d-d3778404db8d)" + ) PLANTS_ISLAND_PERCENT = ( "Plants island percent value is used to set the relation between the map size and the " "number of islands of plants. For example, if set to 100% for map size of 2048, the number" " of islands will be 2048. \n" "ℹ️ **Units:** percents of the map size." ) + PLANTS_ISLAND_PERCENT_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/aa10bfbc-1cea-4002-b8ef-bfe0a902d1d5)" + ) BASE_PRICE = ( "Base price value is used to set the base price of the farmland. It will be used to " "calculate the final price of the farmland. \n" @@ -295,6 +346,14 @@ class Settings: # I3D Settings + ADD_TREES = ( + "If add trees is enabled, the trees will be added on the areas that defined as forests " + "on the OSM data." + ) + ADD_TREES_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/50dd8f82-f4f9-411e-a17a-ea10a0b95c20)" + ) + FOREST_DENSITY = ( "Forest density value represents the distance between trees in the forest. " "The higher the value, the more sparse the forest will be and less trees will be " @@ -303,6 +362,9 @@ class Settings: "performance issues. \n" "ℹ️ **Units:** meters between trees." ) + FOREST_DENSITY_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/bf353ed6-f25c-4226-b0d6-105ada0f097b)" + ) TREES_RELATIVE_SHIFT = ( "Represents the maximum possible shift of the tree from it's original position in percents" @@ -318,12 +380,18 @@ class Settings: "It makes them look better in game, but it will require some time. " "It's recommended to keep this option enabled." ) + DISSOLVE_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/b7da059b-eb35-4a4e-a656-168c31257b15)" + ) FIELDS_PADDING = ( "Field padding value is used to add some padding around the fields. " "It will make the fields smaller, can be useful if they are too close to each other. \n" "ℹ️ **Units:** meters." ) + FIELDS_PADDING_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/b88ebfb3-7afb-4012-a845-42a04fefa7d2)" + ) SKIP_DRAINS = ( "If skip drains is enabled, the drains and ditches will be ignored while generating " @@ -345,6 +413,9 @@ class Settings: "more smooth. Be careful with high values, because it may make your spline too complex. \n" "ℹ️ **Units:** number of additional points between each pair of existing points." ) + SPLINE_DENSITY_EXAMPLE = ( + "![](https://github.com/user-attachments/assets/8acd2910-4b86-42d1-a509-e8ccc7c8169d)" + ) ADD_REVERSED_SPLINES = ( "If add reversed splines is enabled, the splines will be generated in both directions. " "Otherwise, only one direction will be generated (as in the OSM data)." @@ -358,8 +429,9 @@ class Settings: SATELLITE_MARGIN = ( "Satellite margin value (in meters) is used to add some margin around the satellite images. " - "It will result satellite images to be bigger than the map size, which can be useful " - "for adjusting the images. \n" + "It's not recommended to change this value in most cases, because as a result, the " + "area of satellite images won't match the area of the map and it will lead to " + "challenges when trying to align the images with the map. \n" "ℹ️ **Units:** meters." ) diff --git a/webui/webui.py b/webui/webui.py index d24604e..c01d18f 100644 --- a/webui/webui.py +++ b/webui/webui.py @@ -1,5 +1,7 @@ import os +from datetime import datetime +import requests import streamlit as st import streamlit.components.v1 as components from config import DOCS_DIRECTORY, FAQ_MD, get_mds @@ -24,6 +26,7 @@ def __init__(self): toolbox_tab, knowledge_tab, faq_tab, + changelog_tab, ) = st.tabs( [ "🗺️ Map Generator", @@ -34,6 +37,7 @@ def __init__(self): "🧰 Modder Toolbox", "📖 Knowledge base", "📝 FAQ", + "📄 Changelog", ] ) @@ -44,7 +48,7 @@ def __init__(self): components.iframe( "https://stats.maps4fs.xyz/public/dashboard/" "f8defe6a-09db-4db1-911f-b6b02075d4b2#refresh=60", - height=2500, + height=2000, scrolling=False, ) @@ -124,5 +128,37 @@ def __init__(self): with faq_tab: st.write(open(FAQ_MD, "r", encoding="utf-8").read()) + with changelog_tab: + st.markdown("## Changelog") + st.markdown( + "This page provides information about the latest changes to the maps4fs project." + ) + try: + url = "https://api.github.com/repos/iwatkot/maps4fs/releases" + response = requests.get(url) + if response.status_code == 200: + releases = response.json() + for release in releases: + version = release.get("tag_name") + release_name = release.get("name") + published_at = release.get("published_at") + date_time = datetime.strptime(published_at, "%Y-%m-%dT%H:%M:%SZ") + html_url = release.get("html_url") + + st.markdown( + f"![{version}](https://img.shields.io/badge/version-{version}-blue) \n" + f"Date: **{date_time.strftime('%d.%m.%Y')}** \n" + f"**{release_name}** \n" + f"[More info]({html_url})" + ) + + st.markdown("---") + st.markdown( + "Older releases available on [GitHub]" + "(https://github.com/iwatkot/maps4fs/releases)." + ) + except Exception as e: + st.error(f"An error occurred while fetching the changelog: {e}") + WebUI()