Skip to content

Commit

Permalink
New tool: Fix custom OSM file
Browse files Browse the repository at this point in the history
* New tool: Fix custom OSM file.

* README update.
  • Loading branch information
iwatkot authored Jan 12, 2025
1 parent cd1cb6d commit 7274071
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"console": "integratedTerminal",
"justMyCode": true,
"env": {
"PYTHONPATH": "${workspaceFolder}:${PYTHONPATH}"
"PYTHONPATH": "${workspaceFolder}"
}
}
]
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ Tools are divided into categories, which are listed below.
- **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.

#### For Textures and DEM

- **Fix custom OSM file** - this tool fixes the most common errors in the custom OSM file, but it can not guarantee that the file will be fixed completely if some non-common errors are present.

- **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.

#### For Background terrain
Expand Down
67 changes: 67 additions & 0 deletions maps4fs/toolbox/custom_osm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""This module contains functions to work with custom OSM files."""

import json
from xml.etree import ElementTree as ET

import osmnx as ox
from osmnx._errors import InsufficientResponseError

from maps4fs.generator.game import FS25


def check_osm_file(file_path: str) -> bool:
"""Tries to read the OSM file using OSMnx and returns True if the file is valid,
False otherwise.
Arguments:
file_path (str): Path to the OSM file.
Returns:
bool: True if the file is valid, False otherwise.
"""
with open(FS25().texture_schema, encoding="utf-8") as f:
schema = json.load(f)

tags = []
for element in schema:
element_tags = element.get("tags")
if element_tags:
tags.append(element_tags)

for tag in tags:
try:
ox.features_from_xml(file_path, tags=tag)
except InsufficientResponseError:
continue
except Exception: # pylint: disable=W0718
return False
return True


def fix_osm_file(input_file_path: str, output_file_path: str) -> tuple[bool, int]:
"""Fixes the OSM file by removing all the <relation> nodes and all the nodes with
action='delete'.
Arguments:
input_file_path (str): Path to the input OSM file.
output_file_path (str): Path to the output OSM file.
Returns:
tuple[bool, int]: A tuple containing the result of the check_osm_file function
and the number of fixed errors.
"""
broken_entries = ["relation", ".//*[@action='delete']"]

tree = ET.parse(input_file_path)
root = tree.getroot()

fixed_errors = 0
for entry in broken_entries:
for element in root.findall(entry):
root.remove(element)
fixed_errors += 1

tree.write(output_file_path)
result = check_osm_file(output_file_path)

return result, fixed_errors
76 changes: 76 additions & 0 deletions webui/tools/custom_osm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import os

import streamlit as st
from config import INPUT_DIRECTORY
from tools.tool import Tool

from maps4fs.toolbox.custom_osm import fix_osm_file


class FixCustomOsmFile(Tool):
title = "Fix a custom OSM file"
description = (
"This tool tries to fix a custom OSM file by removing all incorrect entries. "
"It does not guarantee that the file will be fixed, if some specific errors are "
"present in the file, since the tool works with a common set of errors."
)
icon = "🛠️"

save_path = None
download_path = None

def content(self):
if "fixed_osm" not in st.session_state:
st.session_state.fixed_osm = False
uploaded_file = st.file_uploader(
"Upload a custom OSM file", type=["osm"], key="osm_uploader"
)

if uploaded_file is not None:
self.save_path = self.get_save_path(uploaded_file.name)
with open(self.save_path, "wb") as f:
f.write(uploaded_file.read())

base_name = os.path.basename(self.save_path).split(".")[0]
output_name = f"{base_name}_fixed.osm"
self.download_path = self.get_save_path(output_name)

if st.button("Fix the file", icon="▶️"):
try:
result, number_of_errors = fix_osm_file(self.save_path, self.download_path)
except Exception as e:
st.error(
f"The file is completely broken it's even impossible to read it. Error: {e}"
)
return

st.success(f"Fixed the file with {number_of_errors} errors.")
if result:
st.success("The file was read successfully.")
else:
st.error("Even after fixing, the file could not be read.")

st.session_state.fixed_osm = True

if st.session_state.fixed_osm:
with open(self.download_path, "rb") as f:
st.download_button(
label="Download",
data=f,
file_name=f"{self.download_path.split('/')[-1]}",
mime="application/zip",
icon="📥",
)

st.session_state.fixed_osm = False

def get_save_path(self, file_name: str) -> str:
"""Get the path to save the file in the input directory.
Arguments:
file_name {str} -- The name of the file.
Returns:
str -- The path to save the file in the input directory.
"""
return os.path.join(INPUT_DIRECTORY, file_name)
3 changes: 2 additions & 1 deletion webui/tools/section.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Type

from tools.background import ConvertImageToObj
from tools.custom_osm import FixCustomOsmFile
from tools.dem import GeoTIFFWindowingTool
from tools.textures import TextureSchemaEditorTool
from tools.tool import Tool
Expand Down Expand Up @@ -31,7 +32,7 @@ class Shemas(Section):
class TexturesAndDEM(Section):
title = "🖼️ Textures and DEM"
description = "Tools to work with textures and digital elevation models."
tools = [GeoTIFFWindowingTool]
tools = [FixCustomOsmFile, GeoTIFFWindowingTool]


class Background(Section):
Expand Down

0 comments on commit 7274071

Please sign in to comment.