Skip to content

Commit

Permalink
New tool: Fix custom OSM file.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwatkot committed Jan 12, 2025
1 parent cd1cb6d commit cf57bb5
Show file tree
Hide file tree
Showing 4 changed files with 146 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
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 cf57bb5

Please sign in to comment.