diff --git a/Assets/Scenes/scene.unity b/Assets/Scenes/scene.unity index a509ab3..da36c14 100644 --- a/Assets/Scenes/scene.unity +++ b/Assets/Scenes/scene.unity @@ -242,7 +242,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0c03158bf19bc8c4ab178ebaee653914, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::Map - overpassUrl: https://mapz.hozuvkod.dev/api/interpreter + overpassUrl: https://mapz.honzuvkod.dev/api/interpreter queryRadiusMeters: 200 latitude: 50.772789001464844 longitude: 15.076871871948242 @@ -262,7 +262,7 @@ Transform: m_GameObject: {fileID: 1274200469} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 314.27454, y: 0, z: 616.551} + m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -293,7 +293,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 0} + - {fileID: 2100000, guid: 283cf727b4c3ac64c94d59598e221b10, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 diff --git a/Assets/Scripts/Map.cs b/Assets/Scripts/MapRenderer.cs similarity index 84% rename from Assets/Scripts/Map.cs rename to Assets/Scripts/MapRenderer.cs index bf7ca72..28ed84c 100644 --- a/Assets/Scripts/Map.cs +++ b/Assets/Scripts/MapRenderer.cs @@ -1,26 +1,22 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Net.Http; using System.Text; -using System.Threading; -using System.Threading.Tasks; using System.Xml; using UnityEngine; using UnityEngine.Networking; -using UnityEngine.UIElements; [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] -public class Map : MonoBehaviour, IMapDataCollector +public class MapRenderer : MonoBehaviour { [Header("Overpass settings")] public string overpassUrl = "https://mapz.honzuvkod.dev/api/interpreter"; public float queryRadiusMeters = 200f; // radius around lat/lon to query [Header("Location (lat, lon)")] - public double latitude = 50.7727878f; // example Prague - public double longitude = 15.0768714f; + public double latitude; // example Prague + public double longitude; [Header("Building settings")] public Material buildingMaterial; @@ -34,21 +30,78 @@ public class Map : MonoBehaviour, IMapDataCollector [Header("Misc")] public float metersPerUnit = 1f; // scale: 1 unit = 1 meter public bool autoStart = true; - class Way - { - public long id; - public List nodeRefs = new List(); - public Dictionary tags = new Dictionary(); - } - private static readonly HttpClient client = new HttpClient(); - - List parsedWays = new List(); + // Internal storage Dictionary nodes = new Dictionary(); // id -> latlon - + void Start() { - CallApi(); + if (autoStart) + StartCoroutine(GenerateForLocation(latitude, longitude)); + } + + // Public entry for other scripts + public void StartGenerating(double lat, double lon) + { + latitude = lat; longitude = lon; + StartCoroutine(GenerateForLocation(lat, lon)); + } + + IEnumerator GenerateForLocation(double lat, double lon) + { + ClearChildren(); + + // compute bbox from radius + float degPerMeter = 1f / 111000f; // approximate + double delta = queryRadiusMeters * degPerMeter; + double south = lat - delta; + double north = lat + delta; + double west = lon - delta; + double east = lon + delta; + + string q = $"[out:xml][timeout:25];(way[\"building\"]({south.ToString().Replace(",",".")},{west.ToString().Replace(",", ".")},{north.ToString().Replace(",", ".")},{east.ToString().Replace(",", ".")});way[\"highway\"]({south.ToString().Replace(",", ".")},{west.ToString().Replace(",", ".")},{north.ToString().Replace(",", ".")},{east.ToString().Replace(",", ".")}););(._;>;);out body;"; + + WWWForm form = new WWWForm(); + form.AddField("data", q); + + using (UnityWebRequest www = UnityWebRequest.Post(overpassUrl, form)) + { + www.downloadHandler = new DownloadHandlerBuffer(); + yield return www.SendWebRequest(); + + if (www.result != UnityWebRequest.Result.Success) + { + Debug.LogError("Overpass request failed: " + www.error); + yield break; + } + + string xml = www.downloadHandler.text; + ParseOverpassXml(xml); + + // create separate GameObjects for buildings and roads + GameObject buildingsRoot = new GameObject("Buildings"); + buildingsRoot.transform.parent = this.transform; + + GameObject roadsRoot = new GameObject("Roads"); + roadsRoot.transform.parent = this.transform; + + // iterate parsed ways + foreach (var w in parsedWays) + { + if (w.tags.ContainsKey("building")) + { + GameObject b = BuildBuildingMesh(w); + b.transform.parent = buildingsRoot.transform; + } + else if (w.tags.ContainsKey("highway")) + { + GameObject r = BuildRoadMesh(w); + r.transform.parent = roadsRoot.transform; + } + } + + Debug.Log("Map generation complete: " + parsedWays.Count + " ways, " + nodes.Count + " nodes."); + } } void ClearChildren() @@ -61,64 +114,16 @@ public class Map : MonoBehaviour, IMapDataCollector DestroyImmediate(g); } - public async Task CallOverpassApi(FormUrlEncodedContent query) + #region Overpass XML parsing + class Way { - Debug.Log("Calling Overpass API..."); - - - var response = await client.PostAsync(overpassUrl, query); - Debug.Log("Received response from Overpass API."); - return await response.Content.ReadAsStringAsync(); + public long id; + public List nodeRefs = new List(); + public Dictionary tags = new Dictionary(); } - public FormUrlEncodedContent QueryBuilder(double[] GPS) - { - Debug.Log("Building Overpass API query..."); - string query = @$" - [out:xml] - [timeout:90]; - ( - way(around:{queryRadiusMeters}, {GPS[0].ToString().Replace(",", ".")}, {GPS[1].ToString().Replace(",", ".")}); - ); - out geom;"; - Dictionary values = new Dictionary() - { - {"data", query} - }; - Debug.Log("Overpass API query built."); + List parsedWays = new List(); - return new FormUrlEncodedContent(values); - } - - public async void CallApi() - { - Debug.Log("Testing Overpass API..."); - double[] GPS = new double[] {latitude, longitude}; - string result = await CallOverpassApi(QueryBuilder(GPS)); - Debug.Log("Overpass API response received:"); - ParseOverpassXml(result); - - GameObject buildingsRoot = new GameObject("Buildings"); - buildingsRoot.transform.parent = this.transform; - GameObject roadsRoot = new GameObject("Roads"); - roadsRoot.transform.parent = this.transform; - foreach (var w in parsedWays) - { - if (w.tags.ContainsKey("building")) - { - //Debug.Log("Building"); - GameObject b = BuildBuildingMesh(w); - b.transform.parent = buildingsRoot.transform; - } - else if (w.tags.ContainsKey("highway")) - { - //Debug.Log("Highway"); - GameObject r = BuildRoadMesh(w); - r.transform.parent = roadsRoot.transform; - } - } - - } void ParseOverpassXml(string xmlText) { nodes.Clear(); @@ -161,6 +166,8 @@ public class Map : MonoBehaviour, IMapDataCollector parsedWays.Add(w); } } + #endregion + #region Utilities: latlon to local meters // Convert latitude/longitude to local XY meters relative to center point Vector3 LatLonToLocal(double lat, double lon) @@ -499,4 +506,3 @@ public class Map : MonoBehaviour, IMapDataCollector } #endregion } -