/* INFINITY CODE */ /* https://infinity-code.com */ using System; using System.Collections.Generic; using InfinityCode.RealWorldTerrain.XML; using UnityEngine; namespace InfinityCode.RealWorldTerrain.Utils { /// /// Class for working with GPX. /// public class RealWorldTerrainGPXObject { /// /// GPX document version. /// public string version = "1.1"; /// /// Name or URL of the software that created your GPX document. \n /// This allows others to inform the creator of a GPX instance document that fails to validate. /// public string creator = "RealWorldTerrain"; /// /// Metadata about the gpx. /// public Meta metadata; /// /// A list of waypoints. /// public List waypoints; /// /// A list of routes. /// public List routes; /// /// A list of tracks. /// public List tracks; /// /// You can add extend GPX by adding your own elements from another schema here. /// public RealWorldTerrainXML extensions; private RealWorldTerrainGPXObject() { waypoints = new List(); routes = new List(); tracks = new List(); } /// /// Constructor /// /// Name or URL of the software that created your GPX document. /// GPX document version. public RealWorldTerrainGPXObject(string creator, string version = "1.1") : this() { this.creator = creator; this.version = version; } /// /// Load GPX Object from string. /// /// A string containing GPX content. /// Instance of GPX Object public static RealWorldTerrainGPXObject Load(string content) { RealWorldTerrainGPXObject instance = new RealWorldTerrainGPXObject(); try { RealWorldTerrainXML xml = RealWorldTerrainXML.Load(content); instance.version = xml.A("version"); instance.creator = xml.A("creator"); foreach (RealWorldTerrainXML n in xml) { if (n.name == "wpt") instance.waypoints.Add(new Waypoint(n)); else if (n.name == "rte") instance.routes.Add(new Route(n)); else if (n.name == "trk") instance.tracks.Add(new Track(n)); else if (n.name == "metadata") instance.metadata = new Meta(n); else if (n.name == "extensions") instance.extensions = n; else Debug.Log(n.name); } } catch (Exception exception) { Debug.Log(exception.Message + "\n" + exception.StackTrace); } return instance; } /// /// Returns RealWorldTerrainXML, contains full information about GPX Object. /// /// Instance of RealWorldTerrainXML. public RealWorldTerrainXML ToXML() { RealWorldTerrainXML xml = new RealWorldTerrainXML("gpx"); xml.A("version", version); xml.A("creator", creator); if (metadata != null) metadata.AppendToNode(xml.Create("metadata")); if (waypoints != null) foreach (Waypoint i in waypoints) i.AppendToNode(xml.Create("wpt")); if (routes != null) foreach (Route i in routes) i.AppendToNode(xml.Create("rte")); if (tracks != null) foreach (Track i in tracks) i.AppendToNode(xml.Create("trk")); if (extensions != null) xml.AppendChild(extensions); return xml; } public override string ToString() { return ToXML().outerXml; } /// /// Information about the copyright holder and any license governing use of this file. /// By linking to an appropriate license, you may place your data into the public domain or grant additional usage rights. /// public class Copyright { /// /// Copyright holder /// public string author; /// /// Year of copyright. /// public int? year; /// /// Link to external file containing license text. /// public string license; /// /// Constructor /// /// Copyright holder public Copyright(string author) { this.author = author; } /// /// Creates instance and loads the data from the node. /// /// Copyright node public Copyright(RealWorldTerrainXML node) { author = node.A("author"); foreach (RealWorldTerrainXML n in node) { if (n.name == "year") year = n.Value(); else if (n.name == "license") license = n.Value(); else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { node.A("author", author); if (year.HasValue) node.Create("year", year.Value); if (!string.IsNullOrEmpty(license)) node.Create("license", license); } } /// /// Two lat/lon pairs defining the extent of an element. /// public class Bounds { /// /// The minimum latitude. /// public double minlat { get { return _minlat; } set { _minlat = RealWorldTerrainMath.Clip(value, -90, 90); } } /// /// The minimum longitude. /// public double minlon { get { return _minlon; } set { _minlon = RealWorldTerrainMath.Repeat(value, -180, 180); } } /// /// The maximum latitude. /// public double maxlat { get { return _maxlat; } set { _maxlat = RealWorldTerrainMath.Clip(value, -90, 90); } } /// /// The maximum longitude. /// public double maxlon { get { return _maxlon; } set { _maxlon = RealWorldTerrainMath.Repeat(value, -180, 180); } } private double _minlat; private double _minlon; private double _maxlat; private double _maxlon; /// /// Creates instance and loads the data from the node. /// /// Bounds node public Bounds(RealWorldTerrainXML node) { minlat = node.A("minlat"); minlon = node.A("minlon"); maxlat = node.A("maxlat"); maxlon = node.A("maxlon"); } /// /// Constructor /// /// The minimum longitude. /// The minimum latitude. /// The maximum longitude. /// The maximum latitude. public Bounds(double minlon, double minlat, double maxlon, double maxlat) { this.minlat = minlat; this.minlon = minlon; this.maxlat = maxlat; this.maxlon = maxlon; } public void AppendToNode(RealWorldTerrainXML node) { node.A("minlat", minlat); node.A("minlon", minlon); node.A("maxlat", maxlat); node.A("maxlon", maxlon); } } /// /// An email address. Broken into two parts (id and domain) to help prevent email harvesting. /// public class EMail { /// /// ID half of email address /// public string id; /// /// Domain half of email address /// public string domain; /// /// Constructor /// /// ID half of email address /// Domain half of email address public EMail(string id, string domain) { this.id = id; this.domain = domain; } /// /// Creates instance and loads the data from the node. /// /// EMail node public EMail(RealWorldTerrainXML node) { id = node.A("id"); domain = node.A("domain"); } public void AppendToNode(RealWorldTerrainXML node) { node.A("id", id); node.A("domain", domain); } } /// /// A link to an external resource (Web page, digital photo, video clip, etc) with additional information. /// public class Link { /// /// URL of hyperlink. /// public string href; /// /// Text of hyperlink. /// public string text; /// /// Mime type of content (image/jpeg) /// public string type; /// /// Constructor /// /// URL of hyperlink. public Link(string href) { this.href = href; } /// /// Creates instance and loads the data from the node. /// /// Link node public Link(RealWorldTerrainXML node) { href = node.A("href"); foreach (RealWorldTerrainXML n in node) { if (n.name == "text") text = n.Value(); else if (n.name == "type") type = n.Value(); else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { node.A("href", href); if (!string.IsNullOrEmpty(text)) node.Create("text", text); if (!string.IsNullOrEmpty(type)) node.Create("type", type); } } /// /// Information about the GPX file, author, and copyright restrictions goes in the metadata section. \n /// Providing rich, meaningful information about your GPX files allows others to search for and use your GPS data. /// public class Meta { /// /// The name of the GPX file. /// public string name; /// /// A description of the contents of the GPX file. /// public string description; /// /// The person or organization who created the GPX file. /// public Person author; /// /// Copyright and license information governing use of the file. /// public Copyright copyright; /// /// URLs associated with the location described in the file. /// public List links; /// /// The creation date of the file. /// public DateTime? time; /// /// Keywords associated with the file. Search engines or databases can use this information to classify the data. /// public string keywords; /// /// Minimum and maximum coordinates which describe the extent of the coordinates in the file. /// public Bounds bounds; /// /// You can add extend GPX by adding your own elements from another schema here. /// public RealWorldTerrainXML extensions; /// /// Constructor /// public Meta() { links = new List(); } /// /// Creates instance and loads the data from the node. /// /// Meta node public Meta(RealWorldTerrainXML node) : this() { foreach (RealWorldTerrainXML n in node) { if (n.name == "name") name = n.Value(); else if (n.name == "desc") description = n.Value(); else if (n.name == "author") author = new Person(n); else if (n.name == "copyright") copyright = new Copyright(n); else if (n.name == "link") links.Add(new Link(n)); else if (n.name == "time") time = DateTime.Parse(n.Value()); else if (n.name == "keywords") keywords = n.Value(); else if (n.name == "bounds") bounds = new Bounds(n); else if (n.name == "extensions") extensions = n; else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { if (!string.IsNullOrEmpty(name)) node.Create("name", name); if (!string.IsNullOrEmpty(description)) node.Create("desc", description); if (author != null) author.AppendToNode(node); if (copyright != null) copyright.AppendToNode(node.Create("copyright")); if (links != null && links.Count > 0) foreach (Link l in links) l.AppendToNode(node.Create("link")); if (time.HasValue) node.Create("time", time.Value.ToUniversalTime().ToString("s") + "Z"); if (!string.IsNullOrEmpty(keywords)) node.Create("keywords", keywords); if (bounds != null) bounds.AppendToNode(node.Create("bounds")); if (extensions != null) node.AppendChild(extensions); } } /// /// A person or organization. /// public class Person { /// /// Name of person or organization. /// public string name; /// /// Email address. /// public EMail email; /// /// Link to Web site or other external information about person. /// public Link link; /// /// Constructor /// public Person() { } /// /// Creates instance and loads the data from the node. /// /// Person node public Person(RealWorldTerrainXML node) { foreach (RealWorldTerrainXML n in node) { if (n.name == "name") name = n.Value(); else if (n.name == "email") email = new EMail(n); else if (n.name == "link") link = new Link(n); else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { if (!string.IsNullOrEmpty(name)) node.Create("name", name); if (email != null) email.AppendToNode(node.Create("email")); if (link != null) link.AppendToNode(node.Create("link")); } } /// /// Route - an ordered list of waypoints representing a series of turn points leading to a destination. /// public class Route { /// /// GPS name of route. /// public string name; /// /// GPS comment for route. /// public string comment; /// /// Text description of route for user. Not sent to GPS. /// public string description; /// /// Source of data. Included to give user some idea of reliability and accuracy of data. /// public string source; /// /// Links to external information about the route. /// public List links; /// /// GPS route number. /// public uint? number; /// /// Type (classification) of route. /// public string type; /// /// A list of route points. /// public List points; /// /// You can add extend GPX by adding your own elements from another schema here. /// public RealWorldTerrainXML extensions; /// /// Constructor /// public Route() { links = new List(); points = new List(); } /// /// Creates instance and loads the data from the node. /// /// Route node public Route(RealWorldTerrainXML node) : this() { foreach (RealWorldTerrainXML n in node) { if (n.name == "name") name = n.Value(); else if (n.name == "cmt") comment = n.Value(); else if (n.name == "desc") description = n.Value(); else if (n.name == "src") source = n.Value(); else if (n.name == "link") links.Add(new Link(n)); else if (n.name == "number") number = n.Value(); else if (n.name == "type") type = n.Value(); else if (n.name == "rtept") points.Add(new Waypoint(n)); else if (n.name == "extensions") extensions = n; else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { if (!string.IsNullOrEmpty(name)) node.Create("name", name); if (!string.IsNullOrEmpty(comment)) node.Create("cmt", comment); if (!string.IsNullOrEmpty(description)) node.Create("desc", description); if (!string.IsNullOrEmpty(source)) node.Create("src", source); if (links != null) foreach (Link l in links) l.AppendToNode(node.Create("link")); if (number.HasValue) node.Create("number", number.Value); if (!string.IsNullOrEmpty(type)) node.Create("type", type); foreach (Waypoint p in points) p.AppendToNode(node.Create("rtept")); if (extensions != null) node.AppendChild(extensions); } } /// /// Track - an ordered list of points describing a path. /// public class Track { /// /// GPS name of track. /// public string name; /// /// GPS comment for track. /// public string comment; /// /// User description of track. /// public string description; /// /// Source of data. Included to give user some idea of reliability and accuracy of data. /// public string source; /// /// Links to external information about track. /// public List links; /// /// GPS track number. /// public uint? number; /// /// Type (classification) of track. /// public string type; /// /// A Track Segment holds a list of Track Points which are logically connected in order. \n /// To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data. /// public List segments; /// /// You can add extend GPX by adding your own elements from another schema here. /// public RealWorldTerrainXML extensions; /// /// Constructor /// public Track() { links = new List(); segments = new List(); } /// /// Creates instance and loads the data from the node. /// /// Track node public Track(RealWorldTerrainXML node) : this() { foreach (RealWorldTerrainXML n in node) { if (n.name == "name") name = n.Value(); else if (n.name == "cmt") comment = n.Value(); else if (n.name == "desc") description = n.Value(); else if (n.name == "src") source = n.Value(); else if (n.name == "link") links.Add(new Link(n)); else if (n.name == "number") number = n.Value(); else if (n.name == "type") type = n.Value(); else if (n.name == "trkseg") segments.Add(new TrackSegment(n)); else if (n.name == "extensions") extensions = n; else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { if (!string.IsNullOrEmpty(name)) node.Create("name", name); if (!string.IsNullOrEmpty(comment)) node.Create("cmt", comment); if (!string.IsNullOrEmpty(description)) node.Create("desc", description); if (!string.IsNullOrEmpty(source)) node.Create("src", source); if (links != null) foreach (Link l in links) l.AppendToNode(node.Create("link")); if (number.HasValue) node.Create("number", number.Value); if (!string.IsNullOrEmpty(type)) node.Create("type", type); foreach (TrackSegment p in segments) p.AppendToNode(node.Create("trkseg")); if (extensions != null) node.AppendChild(extensions); } } /// /// A Track Segment holds a list of Track Points which are logically connected in order. \n /// To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data. /// public class TrackSegment { /// /// A Track Point holds the coordinates, elevation, timestamp, and metadata for a single point in a track. /// public List points; /// /// You can add extend GPX by adding your own elements from another schema here. /// public RealWorldTerrainXML extensions; /// /// Constructor /// public TrackSegment() { points = new List(); } /// /// Creates instance and loads the data from the node. /// /// TrackSegment node public TrackSegment(RealWorldTerrainXML node) : this() { foreach (RealWorldTerrainXML n in node) { if (n.name == "trkpt") points.Add(new Waypoint(n)); else if (n.name == "extensions") extensions = n; else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { foreach (Waypoint p in points) p.AppendToNode(node.Create("trkpt")); if (extensions != null) node.AppendChild(extensions); } } /// /// Waypoint, point of interest, or named feature on a map. /// public class Waypoint { /// /// Elevation (in meters) of the point. /// public double? elevation; /// /// Creation/modification timestamp for element. \n /// Date and time in are in Univeral Coordinated Time (UTC), not local time! \n /// Conforms to ISO 8601 specification for date/time representation. \n /// Fractional seconds are allowed for millisecond timing in tracklogs. /// public DateTime? time; /// /// Height (in meters) of geoid (mean sea level) above WGS84 earth ellipsoid. As defined in NMEA GGA message. /// public double? geoidheight; /// /// The GPS name of the waypoint. This field will be transferred to and from the GPS. \n /// GPX does not place restrictions on the length of this field or the characters contained in it. \n /// It is up to the receiving application to validate the field before sending it to the GPS. /// public string name; /// /// GPS waypoint comment. Sent to GPS as comment. /// public string comment; /// /// A text description of the element. Holds additional information about the element intended for the user, not the GPS. /// public string description; /// /// Source of data. Included to give user some idea of reliability and accuracy of data. "Garmin eTrex", "USGS quad Boston North", e.g. /// public string source; /// /// Link to additional information about the waypoint. /// public List links; /// /// Text of GPS symbol name. For interchange with other programs, use the exact spelling of the symbol as displayed on the GPS. If the GPS abbreviates words, spell them out. /// public string symbol; /// /// Type (classification) of the waypoint. /// public string type; /// /// Type of GPX fix. /// public string fix; /// /// Number of satellites used to calculate the GPX fix. /// public uint? sat; /// /// Horizontal dilution of precision. /// public double? hdop; /// /// Vertical dilution of precision. /// public double? vdop; /// /// Position dilution of precision. /// public double? pdop; /// /// Number of seconds since last DGPS update. /// public double? ageofdgpsdata; /// /// You can add extend GPX by adding your own elements from another schema here. /// public RealWorldTerrainXML extensions; private double _lat; private double _lon; private double? _magvar; private short? _dgpsid; /// /// The latitude of the point. Decimal degrees, WGS84 datum. /// public double lat { get { return _lat; } set { _lat = RealWorldTerrainMath.Clip(value, -90, 90); } } /// /// The longitude of the point. Decimal degrees, WGS84 datum. /// public double lon { get { return _lon; } set { _lon = RealWorldTerrainMath.Repeat(value, -180, 180); } } /// /// Magnetic variation (in degrees) at the point /// public double? magvar { get { return _magvar; } set { if (value.HasValue) _magvar = RealWorldTerrainMath.Clip(value.Value, 0, 360); else _magvar = null; } } /// /// ID of DGPS station used in differential correction. /// public short? dgpsid { get { return _dgpsid; } set { if (value.HasValue) { if (value.Value < 0) _dgpsid = 0; else if (value.Value > 1023) _dgpsid = 1023; else _dgpsid = value.Value; } else _dgpsid = null; } } /// /// Constructor /// /// The longitude of the point. /// The latitude of the point. public Waypoint(double lon, double lat) { links = new List(); this.lat = lat; this.lon = lon; } /// /// Creates instance and loads the data from the node. /// /// Waypoint node public Waypoint(RealWorldTerrainXML node) { links = new List(); lat = node.A("lat"); lon = node.A("lon"); foreach (RealWorldTerrainXML n in node) { if (n.name == "ele") elevation = n.Value(); else if (n.name == "time") time = DateTime.Parse(n.Value()); else if (n.name == "magvar") magvar = n.Value(); else if (n.name == "geoidheight") geoidheight = n.Value(); else if (n.name == "name") name = n.Value(); else if (n.name == "cmt") comment = n.Value(); else if (n.name == "desc") description = n.Value(); else if (n.name == "src") source = n.Value(); else if (n.name == "link") links.Add(new Link(n)); else if (n.name == "sym") symbol = n.Value(); else if (n.name == "type") type = n.Value(); else if (n.name == "fix") fix = n.Value(); else if (n.name == "sat") sat = n.Value(); else if (n.name == "hdop") hdop = n.Value(); else if (n.name == "vdop") vdop = n.Value(); else if (n.name == "pdop") pdop = n.Value(); else if (n.name == "ageofdgpsdata") ageofdgpsdata = n.Value(); else if (n.name == "dgpsid") dgpsid = n.Value(); else if (n.name == "extensions") extensions = n; else Debug.Log(n.name); } } public void AppendToNode(RealWorldTerrainXML node) { node.A("lat", lat); node.A("lon", lon); if (elevation.HasValue) node.Create("ele", elevation.Value); if (time.HasValue) node.Create("time", time.Value.ToUniversalTime().ToString("s") + "Z"); if (magvar.HasValue) node.Create("magvar", magvar.Value); if (geoidheight.HasValue) node.Create("geoidheight", geoidheight.Value); if (!string.IsNullOrEmpty(name)) node.Create("name", name); if (!string.IsNullOrEmpty(comment)) node.Create("cmt", comment); if (!string.IsNullOrEmpty(description)) node.Create("desc", description); if (!string.IsNullOrEmpty(source)) node.Create("src", source); if (links != null) foreach (Link l in links) l.AppendToNode(node.Create("link")); if (!string.IsNullOrEmpty(symbol)) node.Create("sym", symbol); if (!string.IsNullOrEmpty(type)) node.Create("type", type); if (!string.IsNullOrEmpty(fix)) node.Create("fix", fix); if (sat.HasValue) node.Create("sat", sat.Value); if (hdop.HasValue) node.Create("hdop", hdop.Value); if (vdop.HasValue) node.Create("vdop", vdop.Value); if (pdop.HasValue) node.Create("pdop", pdop.Value); if (ageofdgpsdata.HasValue) node.Create("ageofdgpsdata", ageofdgpsdata.Value); if (dgpsid.HasValue) node.Create("dgpsid", dgpsid.Value); if (extensions != null) node.AppendChild(extensions); } } } }