from fastapi import APIRouter, status, UploadFile, HTTPException from fastapi.responses import StreamingResponse from io import BytesIO, StringIO import simplekml import gpxpy import re import xml.etree.ElementTree as ET router = APIRouter(prefix="/coords", tags=["Координаты для жижи"]) @router.post( "/coords.txt", status_code=status.HTTP_200_OK, ) async def create_txt(file: UploadFile): try: file = await file.read() file = StringIO(file.decode("utf-8")) txt = generate_txt(file) txt.seek(0) return StreamingResponse( content=txt, media_type="text/plain", headers={"Content-Disposition": "attachment; filename=dildo.txt", "Content-Type": "application/octet-stream"} ) except: raise HTTPException(status_code=409, detail="Ты передал какую-то хуйню") def generate_txt(file: StringIO): tree = ET.parse(file) root = tree.getroot() # Пространства имён — нужно указать, иначе findall ничего не найдёт ns = { 'kml': 'http://www.opengis.net/kml/2.2', 'gx': 'http://www.google.com/kml/ext/2.2' } placemarks = root.findall('.//kml:Placemark', ns) points = [] for placemark in placemarks: # Получаем имя точки name_elem = placemark.find('kml:name', ns) name = name_elem.text.strip() if name_elem is not None else "(без названия)" # Получаем координаты coord_elem = placemark.find('.//kml:Point/kml:coordinates', ns) if coord_elem is not None and coord_elem.text: lon, lat, *_ = map(float, coord_elem.text.strip().split(',')) points.append((name, lat, lon)) buffer = StringIO() for name, lat, lon in points: point = (lat, lon) buffer.write(f"{convert_point_decimal_to_dms(point)}:{name}\n") return buffer def decimal_to_dms_str(deg, is_lat=True): direction = '' if is_lat: direction = 'N' if deg >= 0 else 'S' else: direction = 'E' if deg >= 0 else 'W' deg = abs(deg) d = int(deg) m_float = (deg - d) * 60 m = int(m_float) s = round((m_float - m) * 60) return f"{d}°{m:02d}'{s:02d}\"{direction}" def convert_point_decimal_to_dms(point): lat, lon = point return f"{decimal_to_dms_str(lat, True)} {decimal_to_dms_str(lon, False)}" @router.post( "/map.kmz", status_code=status.HTTP_200_OK, ) async def create_kmz(track_name: str, file: UploadFile): try: file = await file.read() file = StringIO(file.decode("utf-8")) kml = generate_kml(file, track_name) kml.seek(0) return StreamingResponse( content=kml, media_type="application/vnd.google-earth.kmz", headers={"Content-Disposition": "attachment; filename=generated.kmz"} ) except: raise HTTPException(status_code=409, detail="Ты передал какую-то хуйню") def generate_kml(file: StringIO, track_name: str): buffer = BytesIO() waypoints = [] kml = simplekml.Kml() points = [] for line in file.readlines(): if len(line.strip()) == 0: continue coords = line.split(":") coord = coords[0].strip() current_label = int(coords[1].strip()) lat, lon = parse_dms_string(coord) name = f"Опора №{current_label}" wpt = gpxpy.gpx.GPXWaypoint(latitude=lat, longitude=lon, name=name) waypoints.append(wpt) points.append((lon,lat)) linestring = kml.newlinestring(name=track_name) linestring.coords = points linestring.style.linestyle.width = 3 linestring.style.linestyle.color = simplekml.Color.red for wpt in waypoints: lat = wpt.latitude lon = wpt.longitude name = wpt.name pnt = kml.newpoint(name=name, coords=[(lon, lat)]) # KML ожидает (lon, lat) pnt.style.iconstyle.icon.href = 'http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png' kml.savekmz(buffer) return buffer def parse_dms_string(dms_str): pattern = r"(\d{1,3})°(\d{1,2})'(\d{1,2})\"([NSEW])" matches = re.findall(pattern, dms_str) if len(matches) != 2: raise ValueError("Ожидалось две координаты в формате DMS") def to_decimal(d, m, s, direction): decimal = int(d) + int(m) / 60 + int(s) / 3600 if direction in ['S', 'W']: decimal = -decimal return decimal lat = to_decimal(*matches[0]) lon = to_decimal(*matches[1]) return round(lat, 6), round(lon, 6)