166 lines
4.2 KiB
Python
166 lines
4.2 KiB
Python
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)
|
|
|
|
|
|
|
|
|