[Python] Starting server to get Cesium terrain height only works one time

Discussão em 'Python' iniciado por Stack, Agosto 16, 2025.

  1. Stack

    Stack Membro Participativo

    I have multiple datasets with coordinates in them and I want to plot these points in Cesium for Unreal. For me to do that, I need to correct terrain heights and I want to have them in Python (since I'm doing some other stuff as well). Here I'm a bit stuck.

    There is no API for Cesium that seems to be available for this task, so I used Python to create a HTML file, put the coordinates in there and retreive the terrain heights:

    import json
    import threading
    import time
    from flask import Flask, request, send_file
    import webbrowser
    import tempfile
    import os

    def get_terrain_heights(points, access_token):
    results = []

    app = Flask(__name__)
    temp_html_file = tempfile.NamedTemporaryFile(delete=False, suffix=".html")
    temp_html_file.close()

    html_content = f"""
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Cesium Terrain Fetch</title>
    <link href="https://cdn.jsdelivr.net/npm/cesium@1.105.1/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/cesium@1.105.1/Build/Cesium/Cesium.js"></script>
    <style>
    html, body, #cesiumContainer {{width:100%; height:100%; margin:0; padding:0; overflow:hidden;}}
    </style>
    </head>
    <body>
    <div id="cesiumContainer"></div>
    <script>
    Cesium.Ion.defaultAccessToken = '{access_token}';
    const viewer = new Cesium.Viewer('cesiumContainer', {{
    terrainProvider: Cesium.createWorldTerrain()
    }});

    const points = {json.dumps(points)};
    let results = [];

    function sendToPython(data) {{
    fetch('/submit', {{
    method: 'POST',
    headers: {{'Content-Type': 'application/json'}},
    body: JSON.stringify(data)
    }});
    }}

    points.forEach(p => {{
    const position = Cesium.Cartographic.fromDegrees(p.lon, p.lat);
    Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [position]).then(updatedPositions => {{
    const terrainHeight = updatedPositions[0].height;
    const ellipsoidHeight = terrainHeight + p.alt_above_ground;
    results.push({{
    lat: p.lat,
    lon: p.lon,
    alt_above_ground: p.alt_above_ground,
    terrain_height: terrainHeight,
    alt_ellipsoid: ellipsoidHeight
    }});
    if (results.length === points.length) {{
    sendToPython(results);
    alert('Terrain heights fetched. You can close this window.');
    }}
    }});
    }});
    </script>
    </body>
    </html>
    """

    with open(temp_html_file.name, "w") as f:
    f.write(html_content)

    @app.route('/')
    def index():
    return send_file(temp_html_file.name)

    @app.route('/submit', methods=['POST'])
    def submit():
    nonlocal results
    results = request.json
    return {"status": "ok"}

    def run_app():
    app.run(port=5000)

    thread = threading.Thread(target=run_app, daemon=True)
    thread.start()

    webbrowser.open("http://127.0.0.1:5000/")

    while not results:
    time.sleep(0.5)

    # Delete the temporary file only after getting results
    try:
    os.unlink(temp_html_file.name)
    except:
    pass

    return results


    # Usage:
    points = [{"lat": 52.3109, "lon": 4.77028, "alt_above_ground": 0},
    {"lat": 52.3109, "lon": 4.57028, "alt_above_ground": 0}]
    access_token = "access_token"
    terrain_data = get_terrain_heights(points, access_token)


    This works actually really good, it returns a list of dictionaries with the information I need. So this works, but only once. The second time I want to use this code it gives me the error:

    FileNotFoundError: [WinError 2] The system cannot find the file specified: 'C:\Users\username\AppData\Local\Temp\tmp4y1vst8b.html'

    This is logical, since it removes the temporary file after using. When I remove that piece of code however, it still does not work: it keeps running in the Python kernel and nothing happens, no data is retreived.

    Can someone assist in how to handle this? I want to go to a solution that I simply run the function, it retreives the information I need and then it's done. If I want to run it after 1 minute again with other information, then it should be able to do that.

    Continue reading...

Compartilhe esta Página