private void LoadDetailsFromSource(ISector sector) { BlobCollection blobs = OSMReader.GetAllBlobs(sector); foreach (var way in blobs.EnumerateWays(false)) { for (int i = 0; i < way.refs.Count; i++) { long ref1 = way.refs[i]; long ref2 = way.refs[(i + 1) % way.refs.Count]; if (ref1 == ref2) { continue; } Vector2d v1 = blobs.nodes[ref1]; Vector2d v2 = blobs.nodes[ref2]; LongLat longLat1 = new SphereVector(sector.ProjectToSphereCoordinates(v1)).ToLongLat(); LongLat longLat2 = new SphereVector(sector.ProjectToSphereCoordinates(v2)).ToLongLat(); // hmm, this logic will ignore edges that brush up exactly against the top or left of their sector (ex: nodes 6151473219, 6151473220 in way 146849673) // I have to compensate for this elswhere ISector sector1 = GetContainingSector(longLat1, 8); ISector sector2 = GetContainingSector(longLat2, 8); if (!sector1.Equals(sector2)) { var e = new EdgeInfo() { wayID = way.id, node1 = ref1, node2 = ref2, longLat1 = longLat1, longLat2 = longLat2 }; if (!edgeInfo.Contains(e)) { edgeInfo.Add(e); } List <int> wKeys = new List <int>(); List <int> wVals = new List <int>(); foreach (var pair in way.keyValues) { wKeys.Add(LoadIntoStringTable(pair.Key)); wVals.Add(LoadIntoStringTable(pair.Value)); } var w = new WayInfo() { id = way.id, keys = wKeys, values = wVals, startNode = way.refs.First(), endNode = way.refs.Last(), relations = new List <long>() }; if (!wayInfo.ContainsKey(w.id)) { wayInfo[w.id] = w; } } } } HashSet <long> extraWays = new HashSet <long>(); foreach (var relation in blobs.EnumerateRelations()) { foreach (var way in relation.memids) { extraWays.Add(way); if (wayInfo.ContainsKey(way) && !relationInfo.ContainsKey(relation.id)) { List <int> rKeys = new List <int>(); List <int> rVals = new List <int>(); List <int> rRoles = new List <int>(); foreach (var pair in relation.keyValues) { rKeys.Add(LoadIntoStringTable(pair.Key)); rVals.Add(LoadIntoStringTable(pair.Value)); } foreach (var role in relation.roleValues) { rRoles.Add(LoadIntoStringTable(role)); } var r = new RelationInfo() { id = relation.id, keys = rKeys, values = rVals, roleValues = rRoles, memids = relation.memids, types = relation.types }; relationInfo[r.id] = r; } } } foreach (var way in blobs.EnumerateWays(false)) { if (!extraWays.Contains(way.id)) { continue; } List <int> wKeys = new List <int>(); List <int> wVals = new List <int>(); foreach (var pair in way.keyValues) { wKeys.Add(LoadIntoStringTable(pair.Key)); wVals.Add(LoadIntoStringTable(pair.Value)); } var w = new WayInfo() { id = way.id, keys = wKeys, values = wVals, startNode = way.refs.First(), endNode = way.refs.Last() }; if (!wayInfo.ContainsKey(w.id)) { wayInfo[w.id] = w; } } }
// 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; }
private static IEnumerable<long> TempGetWays(string key, string value, BlobCollection blobs) { return blobs.EnumerateWays(false).Where(x => x.keyValues.ContainsKey(key) && x.keyValues[key] == value).Select(x => x.id); }
private static IEnumerable<long> TempGetRelationWays(string key, string value, BlobCollection blobs) { HashSet<long> ways = new HashSet<long>(); foreach (var blob in blobs.blobs) { if (blob.type != "OSMData") continue; int typeIndex = blob.pBlock.stringtable.vals.IndexOf("type"); int multipolygonIndex = blob.pBlock.stringtable.vals.IndexOf("multipolygon"); int outerIndex = blob.pBlock.stringtable.vals.IndexOf("outer"); int innerIndex = blob.pBlock.stringtable.vals.IndexOf("inner"); int keyIndex = blob.pBlock.stringtable.vals.IndexOf(key); int valueIndex = blob.pBlock.stringtable.vals.IndexOf(value); if (new[] { typeIndex, multipolygonIndex, outerIndex, innerIndex, keyIndex, valueIndex }.Contains(-1)) continue; foreach (var pGroup in blob.pBlock.primitivegroup) { foreach (var relation in pGroup.relations) { bool isKeyValue = false; bool isTypeMultipolygon = false; for (int i = 0; i < relation.keys.Count; i++) { if (relation.keys[i] == keyIndex && relation.vals[i] == valueIndex) isKeyValue = true; if (relation.keys[i] == typeIndex && relation.vals[i] == multipolygonIndex) isTypeMultipolygon = true; } if (isKeyValue && isTypeMultipolygon) { for (int i = 0; i < relation.roles_sid.Count; i++) { // just outer for now if (relation.types[i] == 1) { if (relation.roles_sid[i] == 0 && innerIndex != 0 && outerIndex != 0) { // some ways are in a relation without any inner/outer tag // ex: 359181377 in relation 304768 ways.Add(relation.memids[i]); } else { if (relation.roles_sid[i] == innerIndex) ways.Add(relation.memids[i]); if (relation.roles_sid[i] == outerIndex) ways.Add(relation.memids[i]); } } } } } } } return ways; }
// TODO: somehow merge this with DoIntersections all in one pass elegantly // this method is called before all of these maps get added together // it will detect and remove shapes found inside each other public static void FixLoops(List<SectorConstrainedOSMAreaGraph> addingMaps, BlobCollection blobs) { // TODO: I think we still need to exclude intersecting loops from our thing STRtree<LoopRef> rtree = new STRtree<LoopRef>(); List<MainLoopRef> mainLoopRefs = new List<MainLoopRef>(); HashSet<AreaNode> explored = new HashSet<AreaNode>(); foreach (var addingMap in addingMaps) { foreach (var nodeList in addingMap.nodes.Values) { foreach (var node in nodeList) { if (explored.Contains(node)) continue; List<AreaNode> newLoop = new List<AreaNode>(); AreaNode curr = node; while (true) { newLoop.Add(curr); explored.Add(curr); if (curr.next == node) { mainLoopRefs.Add(new MainLoopRef() { nodes = newLoop, graph = addingMap }); break; } curr = curr.next; } } } } foreach (var loopRef in mainLoopRefs) { loopRef.isCW = GetArea(loopRef.nodes, blobs) < 0; } foreach (var loopRef in mainLoopRefs) { for (int i = 0; i < loopRef.nodes.Count; i++) { Vector2d pos1 = blobs.nodes[loopRef.nodes[i].id]; Vector2d pos2 = blobs.nodes[loopRef.nodes[(i + 1) % loopRef.nodes.Count].id]; 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, new LoopRef() { nodes = loopRef.nodes, graph = loopRef.graph, v1 = pos1, v2 = pos2, n1 = loopRef.nodes[i].id, n2 = loopRef.nodes[(i + 1) % loopRef.nodes.Count].id, isCW = loopRef.isCW }); } } rtree.Build(); List<MainLoopRef> remove = new List<MainLoopRef>(); foreach (var loopRef in mainLoopRefs) { HashSet<long> nodesLookup = new HashSet<long>(); foreach (var n in loopRef.nodes) nodesLookup.Add(n.id); var node = blobs.nodes[loopRef.nodes.First().id]; Vector2d v1 = new Vector2d(node.X, 10); Vector2d v2 = new Vector2d(node.X, -10); var env = new Envelope(node.X, node.X, -10, 10); var intersections = rtree.Query(env); bool doRemove = false; foreach (var group in intersections.GroupBy(x => x.graph)) { bool isOwnGraph = group.First().graph == loopRef.graph; if (!isOwnGraph && group.Any(x => x.nodes.Any(y => nodesLookup.Contains(y.id)))) continue; // let's ignore WHOLE GRAPHS that intersect with us (might need to revise this thinking) var lessfiltered = group.Where(x => x.v1.X != x.v2.X).ToList(); // skip vertical intersections var sorted = lessfiltered.Where(x => Math.Min(x.v1.X, x.v2.X) != node.X).ToList(); // on perfect-overlap of adjoining lines, this will count things appropriately sorted = sorted.OrderBy(x => -Intersect(v1, v2, x.v1, x.v2).Y).ToList(); // order from bottom to top sorted = sorted.Where(x => x.nodes != loopRef.nodes).ToList(); // ignore our own loop List<int> prevSwaps = new List<int>(); for (int i = 1; i < sorted.Count; i++) { // swap perfectly adjacent if necessary if ((sorted[i - 1].v2 == sorted[i].v1 && sorted[i - 1].v2.X == node.X && sorted[i - 1].V1Y() < sorted[i].V2Y()) || (sorted[i - 1].v1 == sorted[i].v2 && sorted[i - 1].v1.X == node.X && sorted[i - 1].V2Y() < sorted[i].V1Y())) { var temp = sorted[i - 1]; sorted[i - 1] = sorted[i]; sorted[i] = temp; prevSwaps.Add(i - 1); } } var validateStack = new List<LoopRef>(); var contains = new List<LoopRef>(); if (sorted.Count % 2 != 0) throw new NotImplementedException(); // malformed shape for (int i = 0; i < sorted.Count; i++) { bool outer = !sorted[i].isCW; // we will no longer be checking for outers immediately within outers, etc. because sometimes people mess up bool opens = outer == sorted[i].IsLeftToRight(); if (opens) { validateStack.Add(sorted[i]); } else { if (validateStack.Last().nodes != sorted[i].nodes) throw new NotImplementedException(); // malformed shape if (Intersect(v1, v2, validateStack.Last().v1, validateStack.Last().v2).Y > node.Y && Intersect(v1, v2, sorted[i].v1, sorted[i].v2).Y < node.Y) { contains.Add(sorted[i]); // add the one that's above it, sure } validateStack.RemoveAt(validateStack.Count - 1); } } if (validateStack.Count > 0) throw new NotImplementedException(); // malformed shape // remove duplicates that overlap perfectly for (int i = sorted.Count - 1; i > 0; i--) { if (sorted[i].ContainsNode(sorted[i - 1].v1) || sorted[i].ContainsNode(sorted[i - 1].v2)) { Vector2d inCommon = sorted[i].ContainsNode(sorted[i - 1].v1) ? sorted[i - 1].v1 : sorted[i - 1].v2; if (inCommon.X == node.X) { sorted.RemoveRange(i - 1, 2); i -= 2; } } } if (contains.Count == 0 && isOwnGraph && loopRef.isCW) doRemove = true; if (contains.Count == 0) continue; var immediate = contains.First(); if (isOwnGraph && immediate.isCW == loopRef.isCW) doRemove = true; // remove the outermost invalid loop if (!isOwnGraph && !immediate.isCW) doRemove = true; // remove anything immediately contained by an outer } if (doRemove) remove.Add(loopRef); } foreach (var r in remove) { foreach (var node in r.nodes) { r.graph.nodes[node.id].Remove(node); if (r.graph.nodes[node.id].Count == 0) r.graph.nodes.Remove(node.id); } } }
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 }