Functioning renderer
This commit is contained in:
@@ -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<long> nodeRefs = new List<long>();
|
||||
public Dictionary<string, string> tags = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
private static readonly HttpClient client = new HttpClient();
|
||||
|
||||
List<Way> parsedWays = new List<Way>();
|
||||
// Internal storage
|
||||
Dictionary<long, Vector2> nodes = new Dictionary<long, Vector2>(); // 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<string> 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<long> nodeRefs = new List<long>();
|
||||
public Dictionary<string, string> tags = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
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<string, string> values = new Dictionary<string, string>()
|
||||
{
|
||||
{"data", query}
|
||||
};
|
||||
Debug.Log("Overpass API query built.");
|
||||
List<Way> parsedWays = new List<Way>();
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user