} // End Function IsInside public static void Test() { //GeoApis.LatLng[] polygon1 = {new GeoApis.LatLng(0, 0), // new GeoApis.LatLng(10, 0), // new GeoApis.LatLng(10, 10), // new GeoApis.LatLng(0, 10)}; GeoApis.LatLng[] polygon1 = new GeoApis.LatLng[] { new GeoApis.LatLng(47.03687500000000000000M, 8.29712870000000000000M) , new GeoApis.LatLng(47.03681430000000000000M, 8.29710690000000000000M) , new GeoApis.LatLng(47.03659660000000000000M, 8.29702870000000000000M) , new GeoApis.LatLng(47.03666160000000000000M, 8.29665210000000000000M) , new GeoApis.LatLng(47.03651580000000000000M, 8.29659780000000000000M) , new GeoApis.LatLng(47.03657590000000000000M, 8.29625910000000000000M) , new GeoApis.LatLng(47.03700750000000000000M, 8.29641350000000000000M) , new GeoApis.LatLng(47.03696060000000000000M, 8.29669370000000000000M) , new GeoApis.LatLng(47.03691760000000000000M, 8.29667890000000000000M) , new GeoApis.LatLng(47.03688220000000000000M, 8.29688350000000000000M) , new GeoApis.LatLng(47.03691530000000000000M, 8.29689480000000000000M) , new GeoApis.LatLng(47.03687500000000000000M, 8.29712870000000000000M) }; // GeoApis.LatLng p = new GeoApis.LatLng(20, 20); GeoApis.LatLng p = new GeoApis.LatLng(47.03677485655230000000M, 8.29674839973449900000M); // GeoApis.LatLng p = new GeoApis.LatLng(46.03687500000000000000M, 8.29712870000000000000M); bool inside = IsInside(polygon1, p); System.Console.WriteLine(inside); } // End Sub Test
} // End Function OnSegment // To find orientation of ordered triplet (p, q, r). // The function returns following values // 0 --> p, q and r are colinear // 1 --> Clockwise // 2 --> Counterclockwise private static int Orientation(GeoApis.LatLng p, GeoApis.LatLng q, GeoApis.LatLng r) { decimal val = (q.lat - p.lat) * (r.lng - q.lng) - (q.lng - p.lng) * (r.lat - q.lat); if (val == 0) { return(0); // colinear } return((val > 0) ? 1 : 2); // clock or counterclock wise } // End Function Orientation
// Given three colinear points p, q, r, // the function checks if point q lies // on line segment 'pr' private static bool OnSegment(GeoApis.LatLng p, GeoApis.LatLng q, GeoApis.LatLng r) { if (q.lng <= System.Math.Max(p.lng, r.lng) && q.lng >= System.Math.Min(p.lng, r.lng) && q.lat <= System.Math.Max(p.lat, r.lat) && q.lat >= System.Math.Min(p.lat, r.lat)) { return(true); } return(false); } // End Function OnSegment
static bool isClockwise(GeoApis.LatLng[] poly) { decimal sum = 0; for (int i = 0; i < poly.Length - 1; i++) { GeoApis.LatLng cur = poly[i], next = poly[i + 1]; sum += (next.lat - cur.lat) * (next.lng + cur.lng); } // Next i return(sum > 0); } // End Function isClockwise
} // End Function Orientation // The function that returns true if // line segment 'p1q1' and 'p2q2' intersect. private static bool DoIntersect(GeoApis.LatLng p1, GeoApis.LatLng q1, GeoApis.LatLng p2, GeoApis.LatLng q2) { // Find the four orientations needed for // general and special cases int o1 = Orientation(p1, q1, p2); int o2 = Orientation(p1, q1, q2); int o3 = Orientation(p2, q2, p1); int o4 = Orientation(p2, q2, q1); // General case if (o1 != o2 && o3 != o4) { return(true); } // Special Cases // p1, q1 and p2 are colinear and // p2 lies on segment p1q1 if (o1 == 0 && OnSegment(p1, p2, q1)) { return(true); } // p1, q1 and p2 are colinear and // q2 lies on segment p1q1 if (o2 == 0 && OnSegment(p1, q2, q1)) { return(true); } // p2, q2 and p1 are colinear and // p1 lies on segment p2q2 if (o3 == 0 && OnSegment(p2, p1, q2)) { return(true); } // p2, q2 and q1 are colinear and // q1 lies on segment p2q2 if (o4 == 0 && OnSegment(p2, q1, q2)) { return(true); } // Doesn't fall in any of the above cases return(false); } // End Function DoIntersect
} // End Function DoIntersect // Returns true if the point p lies // inside the polygon[] with n vertices public static bool IsInside(GeoApis.LatLng[] polygon, GeoApis.LatLng p) { int n = polygon.Length; // There must be at least 3 vertices in polygon[] if (n < 3) { return(false); } // Create a point for line segment from p to infinite GeoApis.LatLng extreme = new GeoApis.LatLng(INF, p.lat); // Count intersections of the above line // with sides of polygon int count = 0, i = 0; do { int next = (i + 1) % n; // Check if the line segment from 'p' to // 'extreme' intersects with the line // segment from 'polygon[i]' to 'polygon[next]' if (DoIntersect(polygon[i], polygon[next], p, extreme)) { // If the point 'p' is colinear with line // segment 'i-next', then check if it lies // on segment. If it lies, return true, otherwise false if (Orientation(polygon[i], p, polygon[next]) == 0) { return(OnSegment(polygon[i], p, polygon[next])); } count++; } i = next; } while (i != 0); // Return true if count is odd, false otherwise return(count % 2 == 1); // Same as (count%2 == 1) } // End Function IsInside
public static GeoApis.Polygon GetNearestBuildingPolygon(decimal latitide, decimal longitude) { OpenToolkit.Mathematics.DecimalVector2 geoPoint = new OpenToolkit.Mathematics.DecimalVector2(latitide, longitude); GeoApis.LatLngBounds bounds = GeoApis.LatLngBounds.FromPoint( new GeoApis.LatLng(latitide, longitude) , 1000 ); // this is, radius = 500m decimal area = bounds.BoundsArea; if (area > 0.25m) { System.Console.WriteLine("The maximum bbox size is 0.25, and your request was too large.\nEither request a smaller area, or use planet.osm."); return(null); } // End if (area > 0.25m) string xml = null; #if fromFile xml = System.IO.File.ReadAllText(@"D:\Stefan.Steiger\Desktop\map.osm.xml", System.Text.Encoding.UTF8); #else const string OSM_API_VERSION = "0.6"; // string url = "https://www.openstreetmap.org/api/0.6/map?bbox=8.626273870468141,47.69679769756054,8.636573553085329,47.700530864557194&no_cache=1562588642802"; string url = "https://www.openstreetmap.org/api/" + OSM_API_VERSION + "/map?bbox=" + bounds.ToBBoxString(); string[] proxyList = ProxyHelper.GetProxyArray(); string proxy = null; REPEAT_UNTIL_SUCCESS: proxy = proxyList[s_rnd.Next(0, proxyList.Length)]; // proxy = "139.162.38.191:80"; // proxy = "178.128.51.105"; // proxy = "105.27.237.27:80"; proxy = "161.117.251.194:80"; proxy = "42.3.51.114:80"; proxy = "173.212.202.65:80"; proxy = "47.91.105.34:80"; try { using (System.Net.WebClient wc = new WebClientWithCustomTimeout(15)) { // wc.Proxy = new System.Net.WebProxy(proxy); xml = wc.DownloadString(url); } // End Using wc } catch (System.Net.WebException webEx) { System.Console.WriteLine(webEx.Message); System.Console.WriteLine(webEx.Status); if (webEx.Status == System.Net.WebExceptionStatus.Timeout) { System.Console.WriteLine("Timeout ! "); } else if (webEx.Status == System.Net.WebExceptionStatus.ProtocolError) { System.Console.WriteLine("ProtocolError"); } System.Net.HttpWebResponse response = webEx.Response as System.Net.HttpWebResponse; if (response != null) { System.Console.WriteLine("HTTP Status Code: " + response.StatusCode.ToString() + ", " + response.StatusDescription); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { System.Console.WriteLine("401"); } string responseHtml = null; using (System.IO.Stream responseStream = response.GetResponseStream()) { using (System.IO.TextReader tr = new System.IO.StreamReader(responseStream)) { responseHtml = tr.ReadToEnd(); } } System.Console.WriteLine(responseHtml); } else { // no http status code available } // goto REPEAT_UNTIL_SUCCESS; } catch (System.Exception ex) { System.Console.WriteLine(ex.Message); // goto REPEAT_UNTIL_SUCCESS; } #endif System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); System.Xml.XmlNodeList nodes = doc.SelectNodes("//node"); System.Collections.Generic.Dictionary <string, GeoApis.LatLng> nodeDictionary = new System.Collections.Generic.Dictionary <string, GeoApis.LatLng>( System.StringComparer.InvariantCultureIgnoreCase ); System.Collections.Generic.Dictionary <string, GeoApis.Polygon> buildingPolygonDictionary = new System.Collections.Generic.Dictionary <string, GeoApis.Polygon>( System.StringComparer.InvariantCultureIgnoreCase ); if (nodes.Count > 1000) { System.Console.WriteLine(nodes.Count); throw new System.InvalidOperationException("Densely populated area. Too many nodes to process"); return(null); } foreach (System.Xml.XmlElement node in nodes) { string id = node.GetAttribute("id"); string nodeLat = node.GetAttribute("lat"); string nodeLong = node.GetAttribute("lon"); decimal dlat = 0; decimal dlong = 0; decimal.TryParse(nodeLat, out dlat); decimal.TryParse(nodeLong, out dlong); nodeDictionary[id] = new GeoApis.LatLng(dlat, dlong); } // Next node // https://stackoverflow.com/questions/1457638/xpath-get-nodes-where-child-node-contains-an-attribute // querySelectorAll('way tag[k="building"]') System.Xml.XmlNodeList buildings = doc.SelectNodes("//way[tag/@k=\"building\"]"); foreach (System.Xml.XmlElement building in buildings) { System.Collections.Generic.List <GeoApis.LatLng> lsPolygonPoints = new System.Collections.Generic.List <GeoApis.LatLng>(); System.Xml.XmlNodeList buildingNodes = building.SelectNodes("./nd"); foreach (System.Xml.XmlElement buildingNode in buildingNodes) { string reff = buildingNode.GetAttribute("ref"); lsPolygonPoints.Add(nodeDictionary[reff]); } // Next buildingNode GeoApis.LatLng[] polygonPoints = toCounterClockWise(lsPolygonPoints.ToArray()); string id = building.GetAttribute("id"); GeoApis.Polygon poly = new GeoApis.Polygon(polygonPoints); poly.OsmId = id; buildingPolygonDictionary[id] = poly; } // Next building // System.Console.WriteLine(buildingPolygonDictionary); decimal?min = null; string uid = null; foreach (System.Collections.Generic.KeyValuePair <string, GeoApis.Polygon> kvp in buildingPolygonDictionary) { if (OsmPolygonHelper.IsInside(kvp.Value.Points, geoPoint)) { uid = kvp.Key; min = 0; break; } decimal minDist = kvp.Value.GetMinimumDistance(geoPoint); if (min.HasValue) { if (minDist < min.Value) { min = minDist; uid = kvp.Key; } // End if (minDist < min.Value) } else { uid = kvp.Key; min = minDist; } } // Next kvp if (uid == null || !buildingPolygonDictionary.ContainsKey(uid)) { return(null); } return(buildingPolygonDictionary[uid]); } // End Sub