private static double Sorter(NewIntersection intersection, BlobCollection blobs) { // return ex: 2.5 means halfway between ref[2] and ref[3] Vector2d A = blobs.nodes[intersection.wayRef.refs[intersection.nodePos - 1]]; Vector2d B = blobs.nodes[intersection.nodeID]; Vector2d C = blobs.nodes[intersection.wayRef.refs[intersection.nodePos]]; double partial = (B - A).Length() / (C - A).Length(); if (partial < -0.1 || partial > 1.1) throw new NotImplementedException(); return intersection.nodePos - 1 + partial; }
// also setup the collinearness private static bool CheckCollinear(long v, int aPos, int bPos, WayRef wayRefAB, Dictionary<Way, List<NewIntersection>> intersections, BlobCollection blobs, bool doCollinearness, bool isBorderIntersection) { bool isCollinear = false; if (isBorderIntersection) { long a = wayRefAB.wayRef.refs[aPos]; long b = wayRefAB.wayRef.refs[bPos]; if (v == a || v == b) return false; // points are already shared, so we'll ignore it Vector2d aV = blobs.nodes[a]; Vector2d bV = blobs.nodes[b]; Vector2d vV = blobs.nodes[v]; if (vV.X == aV.X && vV.X == bV.X && vV.Y < 1 && vV.Y > 0) isCollinear = true; if (vV.Y == aV.Y && vV.Y == bV.Y && vV.X < 1 && vV.X > 0) isCollinear = true; } else { long a = wayRefAB.wayRef.refs[aPos]; long b = wayRefAB.wayRef.refs[bPos]; if (v == a || v == b) return false; // points are already shared, so we'll ignore it double angle1 = CalcAngleDiff(a, b, a, v, blobs); double angle2 = CalcAngleDiff(b, a, b, v, blobs); double angleDiff = 0.01; if (angle1 < angleDiff && angle2 < angleDiff) { isCollinear = true; } } if (isCollinear) { if (doCollinearness) { NewIntersection newNode = new NewIntersection() { wayRef = wayRefAB.wayRef, nodeID = v, nodePos = aPos + 1 }; if (!intersections.ContainsKey(wayRefAB.wayRef)) intersections.Add(wayRefAB.wayRef, new List<NewIntersection>()); intersections[wayRefAB.wayRef].Add(newNode); } return true; } return false; }
internal static void DoIntersections(BlobCollection blobs) { Dictionary<string, long> uids = new Dictionary<string, long>(); // finally decided I needed something to guarantee uniqueness like this TODO: can probably eliminate this long uidCounter = -1000; List<Way> ways = TempGetWays(blobs); ways.Add(blobs.borderWay); List<WayRef> wayRefs = new List<WayRef>(); STRtree<WayRef> rtree = new STRtree<WayRef>(); foreach (var way in ways) { for (int i = 1; i < way.refs.Count; i++) { WayRef wayRef = new WayRef() { wayRef = way, nodePos = i - 1 }; wayRefs.Add(wayRef); Vector2d pos1 = blobs.nodes[way.refs[i - 1]]; Vector2d pos2 = blobs.nodes[way.refs[i]]; var env = new Envelope(Math.Min(pos1.X, pos2.X), Math.Max(pos1.X, pos2.X), Math.Min(pos1.Y, pos2.Y), Math.Max(pos1.Y, pos2.Y)); rtree.Insert(env, wayRef); } } rtree.Build(); Dictionary<Way, List<NewIntersection>> intersections = new Dictionary<Way, List<NewIntersection>>(); foreach (var n1 in wayRefs) { Vector2d pos1 = blobs.nodes[n1.wayRef.refs[n1.nodePos]]; Vector2d pos2 = blobs.nodes[n1.wayRef.refs[n1.nodePos + 1]]; var env = new Envelope(Math.Min(pos1.X, pos2.X), Math.Max(pos1.X, pos2.X), Math.Min(pos1.Y, pos2.Y), Math.Max(pos1.Y, pos2.Y)); foreach (var n2 in rtree.Query(env)) { if (n1.GetHashCode() <= n2.GetHashCode()) continue; // as a way of preventing the same query twice long Aid = n1.wayRef.refs[n1.nodePos]; long Bid = n1.wayRef.refs[n1.nodePos + 1]; long Cid = n2.wayRef.refs[n2.nodePos]; long Did = n2.wayRef.refs[n2.nodePos + 1]; Vector2d A = blobs.nodes[Aid]; Vector2d B = blobs.nodes[Bid]; Vector2d C = blobs.nodes[Cid]; Vector2d D = blobs.nodes[Did]; if (Math.Min(A.X, B.X) > Math.Max(C.X, D.X)) continue; if (Math.Max(A.X, B.X) < Math.Min(C.X, D.X)) continue; if (Math.Min(A.Y, B.Y) > Math.Max(C.Y, D.Y)) continue; if (Math.Max(A.Y, B.Y) < Math.Min(C.Y, D.Y)) continue; string intersectionKey = Aid + "," + Cid; // TODO: we're going to treat -1 as always matching for now bool ACSame = Aid == Cid; bool ADSame = Aid == Did; bool BCSame = Bid == Cid; bool BDSame = Bid == Did; // a subset of possible tiny angles that can cause rounding errors // only thing that changes between these is the condition, line direction, and the newpoint id // TODO: is this really what fixed the nonsense at 240202043? the angleDiff was only 0.009, which seems too big to cause an issue bool someCollinear = false; bool isBorderIntersection = Aid < 0 || Bid < 0 || Cid < 0 || Did < 0; someCollinear |= CheckCollinear(Aid, n2.nodePos, n2.nodePos + 1, n2, intersections, blobs, true, isBorderIntersection); someCollinear |= CheckCollinear(Bid, n2.nodePos, n2.nodePos + 1, n2, intersections, blobs, true, isBorderIntersection); someCollinear |= CheckCollinear(Cid, n1.nodePos, n1.nodePos + 1, n1, intersections, blobs, true, isBorderIntersection); someCollinear |= CheckCollinear(Did, n1.nodePos, n1.nodePos + 1, n1, intersections, blobs, true, isBorderIntersection); if (!ACSame && !ADSame && !BCSame && !BDSame) // proper intersection { if (someCollinear) { if (n1.wayRef.id == n2.wayRef.id) { n1.wayRef.selfIntersects = true; // mark for destruction, probably } } else { // let's sort these lines to guarantee duplicates by value long[] ns = new long[] { Aid, Bid, Cid, Did }; if (ns[0] >= ns[2]) ns = ns.Reverse().ToArray(); if (ns[0] >= ns[1]) { var t = ns[1]; ns[1] = ns[0]; ns[0] = t; } if (ns[2] >= ns[3]) { var t = ns[3]; ns[3] = ns[2]; ns[2] = t; } Vector2d intersection = Intersect(blobs.nodes[ns[0]], blobs.nodes[ns[1]], blobs.nodes[ns[2]], blobs.nodes[ns[3]]); if (intersection != null) { long intersectionID; if (uids.ContainsKey(intersectionKey)) { intersectionID = uids[intersectionKey]; } else { intersectionID = uidCounter--; uids[intersectionKey] = intersectionID; } NewIntersection newNode1 = new NewIntersection() { nodeID = intersectionID, wayRef = n1.wayRef, nodePos = n1.nodePos + 1 }; NewIntersection newNode2 = new NewIntersection() { nodeID = intersectionID, wayRef = n2.wayRef, nodePos = n2.nodePos + 1 }; blobs.nodes[intersectionID] = intersection; if (!intersections.ContainsKey(n1.wayRef)) intersections.Add(n1.wayRef, new List<NewIntersection>()); intersections[n1.wayRef].Add(newNode1); if (!intersections.ContainsKey(n2.wayRef)) intersections.Add(n2.wayRef, new List<NewIntersection>()); intersections[n2.wayRef].Add(newNode2); if (n1.wayRef.id == n2.wayRef.id) { n1.wayRef.selfIntersects = true; // mark for destruction, probably } } } } } } List<long> wayids = new List<long>(); foreach (var pair in intersections) { if (wayids.Contains(pair.Key.id)) throw new NotImplementedException(); wayids.Add(pair.Key.id); } foreach (var pair in intersections) { foreach (var intersection in pair.Value) { intersection.sortRank = Sorter(intersection, blobs); } } foreach (var pair in intersections) { var sorted = pair.Value.OrderBy(x => x.sortRank).ToList(); // get rid of duplicates for (int i = sorted.Count - 1; i > 0; i--) { if (sorted[i].nodeID == sorted[i - 1].nodeID) sorted.RemoveAt(i); } // now insert them for (int i = sorted.Count - 1; i >= 0; i--) { pair.Key.refs.Insert(sorted[i].nodePos, sorted[i].nodeID); } } RemoveDuplicates(blobs); // remove duplicates at the end to also remove duplicate intersections }