internal SectorConstrainedOSMAreaGraph GetCoastAreaMap(string key, string value) { // remember: "If you regard this as tracing around an area of land, then the coastline way should be running counterclockwise." // gather ways with matching starts/ends to form a super-way, coast ways should always run the same direction, so this becomes easier SuperWayCollection superWays = GenerateSuperWayCollection(EnumerateWays().Where(x => x.keyValues.ContainsKey(key) && x.keyValues[key] == value), false); SectorConstrainedOSMAreaGraph map = DoMultipolygon(superWays); if (OSMMetaFinal.IsPixelLand(sector) || borderWay.refs.Count > 5) // just return a big ol' square { SectorConstrainedOSMAreaGraph temp = new SectorConstrainedOSMAreaGraph(); for (int i = 1; i < borderWay.refs.Count; i++) { if (!temp.nodes.ContainsKey(borderWay.refs[i])) { temp.nodes[borderWay.refs[i]] = new List <AreaNode>(); temp.nodes[borderWay.refs[i]].Add(new AreaNode() { id = borderWay.refs[i] }); } } for (int i = 1; i < borderWay.refs.Count; i++) { AreaNode prev = temp.nodes[borderWay.refs[i - 1]].Single(); AreaNode next = temp.nodes[borderWay.refs[i]].Single(); if (prev.id != next.id) { prev.next = next; next.prev = prev; } } map = map.Intersect(temp, this); } BlobsIntersector.FixLoops(new List <SectorConstrainedOSMAreaGraph>() { map }, this); if (Constants.DEBUG_MODE) { map.CheckValid(); } return(map); }
internal SectorConstrainedOSMAreaGraph GetAreaMap(string key, string value) { var simpleWays = EnumerateWays().Where(x => x.keyValues.ContainsKey(key) && x.keyValues[key] == value); // we expect all of these to be closed loops // copy this dang multipolygon way id gathering logic Dictionary <long, Way> wayLookup = new Dictionary <long, Way>(); foreach (var way in EnumerateWays()) { wayLookup[way.id] = way; } List <List <long> > inners = new List <List <long> >(); List <List <long> > outers = new List <List <long> >(); List <long> relationIds = new List <long>(); HashSet <long> otherInnerOuters = new HashSet <long>(); foreach (var blob in 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 (isTypeMultipolygon) { if (isKeyValue) { List <long> innerWayIds = new List <long>(); List <long> outerWayIds = new List <long>(); 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 outerWayIds.Add(relation.memids[i]); } else { if (relation.roles_sid[i] == innerIndex) { innerWayIds.Add(relation.memids[i]); } if (relation.roles_sid[i] == outerIndex) { outerWayIds.Add(relation.memids[i]); } } } } inners.Add(innerWayIds); outers.Add(outerWayIds); relationIds.Add(relation.id); } else { for (int i = 0; i < relation.roles_sid.Count; i++) { // just outer for now if (relation.types[i] == 1) { otherInnerOuters.Add(relation.memids[i]); } } } } } } } HashSet <long> innersAndOuters = new HashSet <long>(); foreach (var innerList in inners) { foreach (var inner in innerList) { innersAndOuters.Add(inner); } } foreach (var outerList in outers) { foreach (var outer in outerList) { innersAndOuters.Add(outer); } } // end gathering logic // add each simple way, flipping them where necessary List <SectorConstrainedOSMAreaGraph> addingMaps = new List <SectorConstrainedOSMAreaGraph>(); foreach (var way in simpleWays) { if (innersAndOuters.Contains(way.id)) { continue; // sometimes lake multipolygons also tag several pieces - at best this is redundant, and at worst causes errors } if (way.refs.Count < 3) { continue; // we -usually- only ever see lines with multipolygons, but I found a weird way like 43435045 } if (way.selfIntersects) { continue; // just ignore ways like 43410874 to keep you sane } SectorConstrainedOSMAreaGraph simpleMap = new SectorConstrainedOSMAreaGraph(); var superLoop = new List <Way>() { way }; if (way.refs.Last() != way.refs.First()) { if (otherInnerOuters.Contains(way.id)) { continue; // unsure of how else to ignore bad ways like 43815149 } way.refs.Add(way.refs.First()); // some folks forget to close a simple way, or perhaps the mistake is tagging subcomponents of a relation } if (!IsValid(superLoop)) { continue; // ignore relations like 512080985 which contain duplicate nodes } double wayArea = GetArea(superLoop); if (Math.Abs(wayArea) < SMALLEST_ALLOWED_AREA) { continue; // ignore zero-area ways since it really messes with the tesselator (ex: way 43624681) TODO: maybe check for absolute zero via node duplication? } bool isCW = wayArea < 0; if (isCW) { way.refs.Reverse(); // the simple polygons are always "outers" } bool untouchedLoop = CheckIfUntouchedAndSpin(superLoop); if (untouchedLoop) { AddUntouchedLoop(simpleMap, superLoop); } else { AddConstrainedPaths(simpleMap, superLoop); simpleMap.CloseLines(this); if (Constants.DEBUG_MODE) { simpleMap.CheckValid(); } } simpleMap.RemoveDuplicateLines(); if (Constants.DEBUG_MODE) { simpleMap.CheckValid(); } addingMaps.Add(simpleMap); } // construct each multipolygon to add separately for (int i = 0; i < inners.Count; i++) // foreach multipolygon, basically { if (OSMMetaFinal.GLOBAL_FINAL.badRelations.Contains(relationIds[i])) { continue; } // TODO: with islands inside of ponds inside of islands inside of ponds, etc. we wouldn't expect this to work // however, we're taking advantage of the fact that Add/Subtract doesn't check for that for now (until Finalize) SuperWayCollection superInnerWays = GenerateSuperWayCollection(inners[i].Where(x => wayLookup.ContainsKey(x)).Select(x => Copy(wayLookup[x])), true); SuperWayCollection superOuterWays = GenerateSuperWayCollection(outers[i].Where(x => wayLookup.ContainsKey(x)).Select(x => Copy(wayLookup[x])), true); superInnerWays.loopedWays = superInnerWays.loopedWays.Where(x => Math.Abs(GetArea(x)) >= SMALLEST_ALLOWED_AREA).ToList(); // ignore zero-area ways since it really messes with the tesselator (ex: way 43624681) TODO: maybe check for absolute zero via node duplication? superOuterWays.loopedWays = superOuterWays.loopedWays.Where(x => Math.Abs(GetArea(x)) >= SMALLEST_ALLOWED_AREA).ToList(); // ignore zero-area ways since it really messes with the tesselator (ex: way 43624681) TODO: maybe check for absolute zero via node duplication? if (!IsValid(superInnerWays)) { continue; } if (!IsValid(superOuterWays)) { continue; } OrientSuperWays(superInnerWays, superOuterWays, gridPointInfo.relations.Contains(relationIds[i])); SectorConstrainedOSMAreaGraph innerMap = DoMultipolygon(superInnerWays); SectorConstrainedOSMAreaGraph outerMap = DoMultipolygon(superOuterWays); if (Constants.DEBUG_MODE) { innerMap.CheckValid(); } if (Constants.DEBUG_MODE) { outerMap.CheckValid(); } SectorConstrainedOSMAreaGraph multiPolygon = outerMap.Subtract(innerMap, this); if (Constants.DEBUG_MODE) { multiPolygon.CheckValid(); } addingMaps.Add(multiPolygon); } SectorConstrainedOSMAreaGraph map = new SectorConstrainedOSMAreaGraph(); BlobsIntersector.FixLoops(addingMaps, this); foreach (var addingMap in addingMaps) { map.Add(addingMap, this); } if (Constants.DEBUG_MODE) { map.CheckValid(); } return(map); }