RealWorldTerrainOSMUtils.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /* INFINITY CODE 2013-2019 */
  2. /* http://www.infinity-code.com */
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Xml;
  8. using InfinityCode.RealWorldTerrain.OSM;
  9. using UnityEngine;
  10. namespace InfinityCode.RealWorldTerrain
  11. {
  12. public static class RealWorldTerrainOSMUtils
  13. {
  14. public static object _OSMLocker;
  15. public static object OSMLocker
  16. {
  17. get
  18. {
  19. if (_OSMLocker == null) _OSMLocker = new object();
  20. return _OSMLocker;
  21. }
  22. }
  23. public static string osmURL = "https://overpass-api.de/api/interpreter?data=";
  24. public static string[] projectMaterials;
  25. public static MeshFilter AppendMesh(GameObject gameObject, Mesh mesh, Material material, string assetName)
  26. {
  27. MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
  28. meshFilter.sharedMesh = mesh;
  29. meshFilter.mesh = mesh;
  30. MeshRenderer renderer = gameObject.AddComponent<MeshRenderer>();
  31. renderer.materials = new[] { material };
  32. renderer.sharedMaterials = new[] { material };
  33. return meshFilter;
  34. }
  35. public static void GenerateCompressedFile(byte[] data, ref Dictionary<string, RealWorldTerrainOSMNode> nodes, ref Dictionary<string, RealWorldTerrainOSMWay> ways, ref List<RealWorldTerrainOSMRelation> relations, string compressedFilename)
  36. {
  37. XmlDocument document = new XmlDocument();
  38. document.LoadXml(System.Text.Encoding.UTF8.GetString(data));
  39. if (nodes == null) nodes = new Dictionary<string, RealWorldTerrainOSMNode>();
  40. if (ways == null) ways = new Dictionary<string, RealWorldTerrainOSMWay>();
  41. if (relations == null) relations = new List<RealWorldTerrainOSMRelation>();
  42. if (document.DocumentElement == null) return;
  43. foreach (XmlNode node in document.DocumentElement.ChildNodes)
  44. {
  45. if (node.Name == "node")
  46. {
  47. RealWorldTerrainOSMNode n = new RealWorldTerrainOSMNode(node);
  48. if (!nodes.ContainsKey(n.id)) nodes.Add(n.id, n);
  49. }
  50. else if (node.Name == "way")
  51. {
  52. RealWorldTerrainOSMWay way = new RealWorldTerrainOSMWay(node);
  53. if (!ways.ContainsKey(way.id)) ways.Add(way.id, way);
  54. }
  55. else if (node.Name == "relation") relations.Add(new RealWorldTerrainOSMRelation(node));
  56. }
  57. SaveOSM(compressedFilename, nodes, ways, relations);
  58. GC.Collect();
  59. }
  60. public static List<Vector3> GetGlobalPointsFromWay(RealWorldTerrainOSMWay way, Dictionary<string, RealWorldTerrainOSMNode> _nodes)
  61. {
  62. List<Vector3> points = new List<Vector3>();
  63. if (way.nodeRefs.Count == 0) return points;
  64. foreach (string nodeRef in way.nodeRefs)
  65. {
  66. RealWorldTerrainOSMNode node;
  67. if (_nodes.TryGetValue(nodeRef, out node)) points.Add(new Vector3(node.lng, 0, node.lat));
  68. }
  69. return points;
  70. }
  71. public static List<Vector3> GetGlobalPointsFromWay(RealWorldTerrainOSMWay way, Dictionary<string, RealWorldTerrainOSMNode> _nodes, out float minLng, out float minLat, out float maxLng, out float maxLat)
  72. {
  73. minLng = minLat = float.MaxValue;
  74. maxLng = maxLat = float.MinValue;
  75. List<Vector3> points = new List<Vector3>();
  76. if (way.nodeRefs.Count == 0) return points;
  77. foreach (string nodeRef in way.nodeRefs)
  78. {
  79. RealWorldTerrainOSMNode node;
  80. if (_nodes.TryGetValue(nodeRef, out node))
  81. {
  82. if (minLng > node.lng) minLng = node.lng;
  83. if (minLat > node.lat) minLat = node.lat;
  84. if (maxLng < node.lng) maxLng = node.lng;
  85. if (maxLat < node.lat) maxLat = node.lat;
  86. points.Add(new Vector3(node.lng, 0, node.lat));
  87. }
  88. }
  89. return points;
  90. }
  91. public static float GetTriangleDirectionOffset(Vector2 rp, ref Vector2 vp1, ref Vector2 vp2, float angle)
  92. {
  93. rp *= 100;
  94. if (vp1 == Vector2.zero)
  95. {
  96. vp1 = rp;
  97. return 0;
  98. }
  99. if (vp2 == Vector2.zero)
  100. {
  101. vp2 = vp1;
  102. vp1 = rp;
  103. return RealWorldTerrainUtils.Angle2D(vp2, vp1);
  104. }
  105. vp2 = vp1;
  106. vp1 = rp;
  107. float a = RealWorldTerrainUtils.Angle2D(vp2, vp1);
  108. if (a - angle > 180) a -= 360;
  109. else if (angle - a > 180) a += 360;
  110. return a - angle;
  111. }
  112. public static List<Vector3> GetWorldPointsFromWay(RealWorldTerrainOSMWay way, Dictionary<string, RealWorldTerrainOSMNode> _nodes, RealWorldTerrainContainer container)
  113. {
  114. List<Vector3> points = new List<Vector3>();
  115. if (way.nodeRefs.Count == 0) return points;
  116. foreach (string nodeRef in way.nodeRefs)
  117. {
  118. RealWorldTerrainOSMNode node;
  119. if (_nodes.TryGetValue(nodeRef, out node)) points.Add(RealWorldTerrainEditorUtils.CoordsToWorldWithElevation(new Vector3(node.lng, 0, node.lat), container));
  120. }
  121. return points;
  122. }
  123. public static void InitOSMServer()
  124. {
  125. RealWorldTerrainOSMOverpassServer osmServer = (RealWorldTerrainOSMOverpassServer)RealWorldTerrainPrefs.LoadPref("OSMServer", 0);
  126. if (osmServer == RealWorldTerrainOSMOverpassServer.main) osmURL = "https://overpass-api.de/api/interpreter?data=";
  127. else if (osmServer == RealWorldTerrainOSMOverpassServer.main2) osmURL = "https://z.overpass-api.de/api/interpreter?data=";
  128. else if (osmServer == RealWorldTerrainOSMOverpassServer.french) osmURL = "https://overpass.openstreetmap.fr/api/interpreter?data=";
  129. else if (osmServer == RealWorldTerrainOSMOverpassServer.taiwan) osmURL = "https://overpass.nchc.org.tw/api/interpreter?data=";
  130. else if (osmServer == RealWorldTerrainOSMOverpassServer.kumiSystems) osmURL = "https://overpass.kumi.systems/api/interpreter?data=";
  131. }
  132. public static void LoadOSM(string _filename, out Dictionary<string, RealWorldTerrainOSMNode> _nodes, out Dictionary<string, RealWorldTerrainOSMWay> _ways, out List<RealWorldTerrainOSMRelation> _relations, bool moveRelationsToWays = true)
  133. {
  134. _nodes = new Dictionary<string, RealWorldTerrainOSMNode>();
  135. _relations = new List<RealWorldTerrainOSMRelation>();
  136. _ways = new Dictionary<string, RealWorldTerrainOSMWay>();
  137. if (!File.Exists(_filename)) return;
  138. FileStream fs = File.OpenRead(_filename);
  139. BinaryReader br = new BinaryReader(fs);
  140. int nodesCount = br.ReadInt32();
  141. for (int i = 0; i < nodesCount; i++)
  142. {
  143. RealWorldTerrainOSMNode node = new RealWorldTerrainOSMNode(br);
  144. _nodes.Add(node.id, node);
  145. }
  146. int wayCount = br.ReadInt32();
  147. for (int i = 0; i < wayCount; i++)
  148. {
  149. RealWorldTerrainOSMWay way = new RealWorldTerrainOSMWay(br);
  150. if (!_ways.ContainsKey(way.id)) _ways.Add(way.id, way);
  151. }
  152. int relationCount = br.ReadInt32();
  153. for (int i = 0; i < relationCount; i++) _relations.Add(new RealWorldTerrainOSMRelation(br));
  154. if (moveRelationsToWays) MoveRelationsToWays(_relations, _ways, _nodes);
  155. }
  156. private static void MoveRelationToWay(RealWorldTerrainOSMRelation relation, Dictionary<string, RealWorldTerrainOSMWay> ways, List<string> waysInRelation, Dictionary<string, RealWorldTerrainOSMNode> nodes)
  157. {
  158. if (relation.members.Count == 0) return;
  159. List<string> nodeRefs = new List<string>();
  160. List<RealWorldTerrainOSMRelationMember> members = relation.members.Where(m => m.type == "way" && m.role == "outer").ToList();
  161. if (members.Count == 0) return;
  162. RealWorldTerrainOSMWay relationWay;
  163. if (!ways.TryGetValue(members[0].reference, out relationWay) || relationWay == null) return;
  164. nodeRefs.AddRange(relationWay.nodeRefs);
  165. members.RemoveAt(0);
  166. while (members.Count > 0)
  167. {
  168. if (!MoveRelationMemberToWay(nodeRefs, members, ways)) break;
  169. }
  170. RealWorldTerrainOSMWay way = new RealWorldTerrainOSMWay();
  171. members = relation.members.Where(m => m.type == "way" && m.role == "inner").ToList();
  172. if (members.Count > 0)
  173. {
  174. way.holes = new List<RealWorldTerrainOSMWay>();
  175. foreach (RealWorldTerrainOSMRelationMember member in members)
  176. {
  177. RealWorldTerrainOSMWay holeWay;
  178. if (ways.TryGetValue(member.reference, out holeWay)) way.holes.Add(holeWay);
  179. }
  180. }
  181. waysInRelation.AddRange(relation.members.Select(m => m.reference));
  182. way.nodeRefs = nodeRefs;
  183. way.id = relation.id;
  184. way.tags = relation.tags;
  185. ways.Add(way.id, way);
  186. }
  187. private static bool MoveRelationMemberToWay(List<string> nodeRefs, List<RealWorldTerrainOSMRelationMember> members, Dictionary<string, RealWorldTerrainOSMWay> ways)
  188. {
  189. string lastRef = nodeRefs[nodeRefs.Count - 1];
  190. int memberIndex = -1;
  191. for (int i = 0; i < members.Count; i++)
  192. {
  193. RealWorldTerrainOSMRelationMember member = members[i];
  194. RealWorldTerrainOSMWay w = ways[member.reference];
  195. if (w.nodeRefs[0] == lastRef)
  196. {
  197. nodeRefs.AddRange(w.nodeRefs.Skip(1));
  198. memberIndex = i;
  199. break;
  200. }
  201. if (w.nodeRefs[w.nodeRefs.Count - 1] == lastRef)
  202. {
  203. List<string> refs = w.nodeRefs;
  204. refs.Reverse();
  205. nodeRefs.AddRange(refs.Skip(1));
  206. memberIndex = i;
  207. break;
  208. }
  209. }
  210. if (memberIndex != -1) members.RemoveAt(memberIndex);
  211. else return false;
  212. return true;
  213. }
  214. private static void MoveRelationsToWays(List<RealWorldTerrainOSMRelation> relations, Dictionary<string, RealWorldTerrainOSMWay> ways, Dictionary<string, RealWorldTerrainOSMNode> nodes)
  215. {
  216. List<string> waysInRelation = new List<string>();
  217. foreach (RealWorldTerrainOSMRelation relation in relations) MoveRelationToWay(relation, ways, waysInRelation, nodes);
  218. foreach (string id in waysInRelation)
  219. {
  220. if (!ways.ContainsKey(id)) continue;
  221. ways.Remove(id);
  222. }
  223. }
  224. public static void SaveOSM(string _filename, Dictionary<string, RealWorldTerrainOSMNode> _nodes, Dictionary<string, RealWorldTerrainOSMWay> _ways, List<RealWorldTerrainOSMRelation> _relations)
  225. {
  226. FileStream fs = File.OpenWrite(_filename);
  227. BinaryWriter bw = new BinaryWriter(fs);
  228. if (_nodes != null)
  229. {
  230. bw.Write(_nodes.Count);
  231. foreach (KeyValuePair<string, RealWorldTerrainOSMNode> pair in _nodes) pair.Value.Write(bw);
  232. }
  233. else bw.Write(0);
  234. if (_ways != null)
  235. {
  236. bw.Write(_ways.Count);
  237. foreach (KeyValuePair<string, RealWorldTerrainOSMWay> pair in _ways) pair.Value.Write(bw);
  238. }
  239. else bw.Write(0);
  240. if (_relations != null)
  241. {
  242. _relations = new List<RealWorldTerrainOSMRelation>(_relations.Distinct());
  243. bw.Write(_relations.Count);
  244. foreach (RealWorldTerrainOSMRelation relation in _relations) relation.Write(bw);
  245. }
  246. else bw.Write(0);
  247. bw.Close();
  248. }
  249. }
  250. }