public void TiledBarrierGraph_RemoveEdge_2Edges_RemoveFirst_ShouldLeave1() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var v3 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341432); var e1 = graphs.AddEdge(v1, v2, Enumerable.Empty <(double longitude, double latitude)>(), new TagsCollection());; var e2 = graphs.AddEdge(v1, v3, Enumerable.Empty <(double longitude, double latitude)>(), new TagsCollection()); graphs.DeleteEdge(e1); var enumerator = graphs.GetEnumerator(); Assert.True(enumerator.MoveTo(v1)); Assert.True(enumerator.MoveNext()); Assert.Equal(v1, enumerator.Vertex1); Assert.Equal(v3, enumerator.Vertex2); Assert.Equal(e2, enumerator.Edge); Assert.False(enumerator.MoveNext()); Assert.True(enumerator.MoveTo(v2)); Assert.False(enumerator.MoveNext()); Assert.True(enumerator.MoveTo(v3)); Assert.True(enumerator.MoveNext()); Assert.Equal(v1, enumerator.Vertex2); Assert.Equal(v3, enumerator.Vertex1); Assert.Equal(e2, enumerator.Edge); Assert.False(enumerator.MoveNext()); }
/// <summary> /// Builds a guid for the given face. /// </summary> /// <param name="graph">The graph.</param> /// <param name="face">The face.</param> public static Guid?GetFaceGuid(this TiledBarrierGraph graph, int face) { var locations = graph.FaceToClockwiseCoordinates(face).Select(x => TileStatic.ToLocalTileCoordinates(14, x, 16384)).ToArray(); return(GetFaceGuidFor(locations)); }
public void TiledBarrierGraphExtensions_NextClockwise_TwoOtherEdges_ShouldBeClockWise() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex( 4.801325798034667, 51.268153126307524); var v2 = graphs.AddVertex( 4.801331162452698, 51.267829233580834); var v3 = graphs.AddVertex( 4.801816642284393, 51.26783426820025); var v4 = graphs.AddVertex( 4.801317751407623, 51.26754225936128); var e1 = graphs.AddEdge(v1, v2); var e2 = graphs.AddEdge(v2, v3); var e3 = graphs.AddEdge(v2, v4); var enumerator = graphs.GetEnumerator(); enumerator.MoveTo(v1); enumerator.MoveNext(); using var clockwise = enumerator.NextClockwise().GetEnumerator(); Assert.True(clockwise.MoveNext()); Assert.Equal(v4, clockwise.Current.Vertex2); Assert.True(clockwise.MoveNext()); Assert.Equal(v3, clockwise.Current.Vertex2); Assert.False(clockwise.MoveNext()); }
internal static void AddTileFor(this TiledBarrierGraph graph, int vertex, Func <uint, IEnumerable <OsmGeo> > getTile, Func <TagsCollectionBase, bool> isBarrier) { var vLocation = graph.GetVertex(vertex); var t = TileStatic.WorldTileLocalId(vLocation.longitude, vLocation.latitude, graph.Zoom); graph.AddTiles(new[] { t }, getTile, isBarrier); }
public void TiledBarrierGraph_RemoveEdge_2Edges_Add4_Remove2_ShouldLeave2() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var v3 = graphs.AddVertex(4.752981662750244, 50.978996666362015, 564341432); var v4 = graphs.AddVertex(4.7518390417099, 50.97881090537897, 564341433); var e1 = graphs.AddEdge(v1, v2); var e2 = graphs.AddEdge(v3, v4); var v5 = graphs.AddVertex(4.752375483512878, 50.97889534228157); var e3 = graphs.AddEdge(v1, v5); var e4 = graphs.AddEdge(v5, v2); var e5 = graphs.AddEdge(v3, v5); var e6 = graphs.AddEdge(v5, v4); graphs.DeleteEdge(e1); graphs.DeleteEdge(e2); var enumerator = graphs.GetEnumerator(); Assert.True(enumerator.MoveTo(v1)); Assert.True(enumerator.MoveNext()); Assert.Equal(e3, enumerator.Edge); Assert.False(enumerator.MoveNext()); Assert.True(enumerator.MoveTo(v2)); Assert.True(enumerator.MoveNext()); Assert.Equal(e4, enumerator.Edge); Assert.False(enumerator.MoveNext()); Assert.True(enumerator.MoveTo(v3)); Assert.True(enumerator.MoveNext()); Assert.Equal(e5, enumerator.Edge); Assert.False(enumerator.MoveNext()); Assert.True(enumerator.MoveTo(v4)); Assert.True(enumerator.MoveNext()); Assert.Equal(e6, enumerator.Edge); Assert.False(enumerator.MoveNext()); var expected = new HashSet <int>(new [] { e3, e4, e5, e6 }); Assert.True(enumerator.MoveTo(v5)); Assert.True(enumerator.MoveNext()); Assert.True(expected.Remove(enumerator.Edge)); Assert.True(enumerator.MoveNext()); Assert.True(expected.Remove(enumerator.Edge)); Assert.True(enumerator.MoveNext()); Assert.True(expected.Remove(enumerator.Edge)); Assert.True(enumerator.MoveNext()); Assert.True(expected.Remove(enumerator.Edge)); Assert.False(enumerator.MoveNext()); Assert.Empty(expected); }
/// <summary> /// Builds a guid for the given vertex. /// </summary> /// <param name="graph">The graph.</param> /// <param name="vertex">The vertex.</param> public static Guid GetVertexGuid(this TiledBarrierGraph graph, int vertex) { // we have a planar graph so location <-> guid. // we generate an id based on the vertex location relative in a tile. var location = graph.GetVertex(vertex); var tileLocation = TileStatic.ToLocalTileCoordinates(graph.Zoom, location, 16384); return(GuidUtility.Create(Namespace, tileLocation.GetBytes())); }
public void TiledBarrierGraph_Enumerator_1Vertex_ShouldEnumerate1Vertex() { var graphs = new TiledBarrierGraph(); var v = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var enumerator = graphs.GetEnumerator(); Assert.True(enumerator.MoveTo(v)); Assert.False(enumerator.MoveNext()); }
public void TiledBarrierGraph_AddVertex_1Vertex_ShouldAddVertex0() { var graphs = new TiledBarrierGraph(); var v = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); Assert.Equal(1, graphs.VertexCount); Assert.Equal(0, v); var vLocation = graphs.GetVertex(v); Assert.Equal((4.7522735595703125, 50.97918242660188), vLocation); }
internal static void LoadForTile(this TiledBarrierGraph graph, uint tile, Func <uint, IEnumerable <OsmGeo> > getTile, Func <TagsCollectionBase, bool> isBarrier) { // mark tile as loaded. graph.SetTileLoaded(tile); // first load the tile in question. var tileData = getTile(tile); var extraTiles = graph.Add(tileData, isBarrier); // add all the tiles. graph.AddTiles(extraTiles, getTile, isBarrier); }
public void Faces_AssignFaces_OneLoop2_ShouldAssign2() { var graphs = new TiledBarrierGraph(); // 0 // / \ // 1---2 var v1 = graphs.AddVertex( 4.788075685501099, 51.26676188180721, 564341430); var v2 = graphs.AddVertex( 4.786123037338257, 51.26496276736555, 564341431); var v3 = graphs.AddVertex( 4.790832996368408, 51.265137311403734, 564341432); var e1 = graphs.AddEdge(v1, v2); var e2 = graphs.AddEdge(v3, v2); var e3 = graphs.AddEdge(v3, v1); graphs.SetTileLoaded(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v1), 14)); graphs.SetTileLoaded(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v2), 14)); graphs.SetTileLoaded(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v3), 14)); graphs.AssignFaces(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v1), 14)); Assert.Equal(3, graphs.FaceCount); var enumerator = graphs.GetEnumerator(); enumerator.MoveTo(v1); enumerator.MoveNextUntil(e1); Assert.NotEqual(int.MaxValue, enumerator.FaceLeft); var left = enumerator.FaceLeft; Assert.NotEqual(int.MaxValue, enumerator.FaceRight); var right = enumerator.FaceRight; enumerator.MoveTo(v2); enumerator.MoveNextUntil(e2); Assert.False(enumerator.Forward); Assert.Equal(left, enumerator.FaceRight); Assert.Equal(right, enumerator.FaceLeft); enumerator.MoveTo(v3); enumerator.MoveNextUntil(e3); Assert.Equal(left, enumerator.FaceLeft); Assert.Equal(right, enumerator.FaceRight); }
public void Faces_AssignFaces_OneEdge_ShouldAssign1() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var e = graphs.AddEdge(v1, v2); var tile = Tiles.TileStatic.WorldTileLocalId(4.7522735595703125, 50.97918242660188, 14); graphs.SetTileLoaded(tile); graphs.AssignFaces(tile); Assert.Equal(1, graphs.FaceCount); }
EnumerateFaceClockwise( this TiledBarrierGraph graph, int face, int maxFaceCount = ushort.MaxValue) { var enumerator = graph.GetFaceEnumerator(); if (!enumerator.MoveTo(face)) { yield break; } if (face == 0) { yield break; } var edges = new List <(int vertex1, int edge, bool forward, int vertex2, (double longitude, double latitude)[] shape)>();
public void TiledBarrierGraphExtensions_NextClockwise_NoOtherEdge_ShouldBeEmpty() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var e = graphs.AddEdge(v1, v2); var enumerator = graphs.GetEnumerator(); enumerator.MoveTo(v1); enumerator.MoveNext(); var clockwise = enumerator.NextClockwise(); Assert.Empty(clockwise); }
public void TiledBarrierGraph_SetFace_Right_1Edge_ShouldSetFaceRight() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var e = graphs.AddEdge(v1, v2); var f = graphs.AddFace(); graphs.SetFace(e, false, f); var enumerator = graphs.GetEnumerator(); enumerator.MoveTo(v1); enumerator.MoveNext(); Assert.Equal(int.MaxValue, enumerator.FaceLeft); Assert.Equal(f, enumerator.FaceRight); }
public void TiledBarrierGraph_FaceEnumerator_1Edge_Left_ShouldEnumerateEdge() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var e = graphs.AddEdge(v1, v2); var f = graphs.AddFace(); graphs.SetFace(e, true, f); var enumerator = graphs.GetFaceEnumerator(); Assert.True(enumerator.MoveTo(f)); Assert.True(enumerator.MoveNext()); Assert.Equal(e, enumerator.Edge); Assert.True(enumerator.IsLeft); Assert.False(enumerator.MoveNext()); }
internal static void AddTiles(this TiledBarrierGraph graph, IEnumerable <uint> tiles, Func <uint, IEnumerable <OsmGeo> > getTile, Func <TagsCollectionBase, bool> isBarrier) { foreach (var tile in tiles) { // mark tile as loaded. graph.SetTileLoaded(tile); // get the data and load it. var tileData = getTile(tile); graph.Add(tileData, isBarrier); } // prune graph. graph.PruneDeadEnds(); graph.PruneShapePoints(); // standardize edges // loading them in a different order could lead to different start and end points graph.StandardizeEdges(); }
public void TiledBarrierGraph_AddEdge_1Edge_ShouldAddEdge0() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex(4.7522735595703125, 50.97918242660188, 564341430); var v2 = graphs.AddVertex(4.7525310516357420, 50.97851368626033, 564341431); var e = graphs.AddEdge(v1, v2); Assert.Equal(2, graphs.VertexCount); Assert.Equal(0, e); var enumerator = graphs.GetEnumerator(); Assert.True(enumerator.MoveTo(v1)); Assert.True(enumerator.MoveNext()); Assert.Equal(v1, enumerator.Vertex1); Assert.Equal(v2, enumerator.Vertex2); Assert.Equal(0, enumerator.Edge); Assert.NotNull(enumerator.Shape); Assert.Empty(enumerator.Shape); Assert.Equal(0, enumerator.Tags.Count); Assert.False(enumerator.MoveNext()); }
public void Faces_RightTurnLoop_3EdgeLoop_Backward_ShouldReturnCounterClockwiseLoop() { var graphs = new TiledBarrierGraph(); // 0 // / \ // 1---2 var v1 = graphs.AddVertex( 4.788075685501099, 51.26676188180721, 564341430); var v2 = graphs.AddVertex( 4.786123037338257, 51.26496276736555, 564341431); var v3 = graphs.AddVertex( 4.790832996368408, 51.265137311403734, 564341432); var e1 = graphs.AddEdge(v1, v2); var e2 = graphs.AddEdge(v2, v3); var e3 = graphs.AddEdge(v3, v1); graphs.SetTileLoaded(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v1), 14)); graphs.SetTileLoaded(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v2), 14)); graphs.SetTileLoaded(Tiles.TileStatic.WorldTileLocalId(graphs.GetVertex(v3), 14)); // calculate right turn loop starting at e1. var enumerator = graphs.GetEnumerator(); enumerator.MoveTo(v2); enumerator.MoveNextUntil(e1); var(loop, _) = enumerator.RightTurnLoop(); Assert.NotNull(loop); Assert.Equal(3, loop.Count); Assert.Equal((v2, e1, false, v1), loop[0]); Assert.Equal((v1, e3, false, v3), loop[1]); Assert.Equal((v3, e2, false, v2), loop[2]); }
public void TiledBarrierGraphExtensions_NextClockwise_OneOtherEdge_ShouldBeSingle() { var graphs = new TiledBarrierGraph(); var v1 = graphs.AddVertex( 4.801331162452698, 51.267829233580834); var v2 = graphs.AddVertex( 4.801325798034667, 51.268153126307524); var v3 = graphs.AddVertex( 4.801816642284393, 51.26783426820025); var e1 = graphs.AddEdge(v2, v1); var e2 = graphs.AddEdge(v1, v3); var enumerator = graphs.GetEnumerator(); enumerator.MoveTo(v2); enumerator.MoveNext(); var clockwise = enumerator.NextClockwise(); Assert.Single(clockwise); }
public static void AssignLanduse( this TiledBarrierGraph tiledBarrierGraph, uint tile, Func <((double longitude, double latitude) topLeft, (double longitude, double latitude) bottomRight), IEnumerable <(Polygon polygon, string type)> > getLanduse)
public static void WriteTileTo(this TiledBarrierGraph graph, Stream stream, uint tile) { var enumerator = graph.GetEnumerator(); // write vertices. for (var v = 0; v < graph.VertexCount; v++) { if (!enumerator.MoveTo(v)) { continue; } if (!enumerator.MoveNext()) { continue; } // write vertex details. var vertexGuid = graph.GetVertexGuid(v); stream.Write(vertexGuid.ToByteArray()); stream.Write(graph.GetVertexLocationBytes(v)); } stream.Write(EmptyGuid); // write edges. for (var v = 0; v < graph.VertexCount; v++) { if (!enumerator.MoveTo(v)) { continue; } // check (and count) edges. var edges = 0; while (enumerator.MoveNext()) { if (enumerator.Forward) { edges++; } } if (edges == 0) { continue; } // write vertex1 details. var vertex1Guid = graph.GetVertexGuid(v); stream.Write(vertex1Guid.ToByteArray()); // write edges details. stream.Write(BitConverter.GetBytes(edges)); // the number of edges. enumerator.MoveTo(v); while (enumerator.MoveNext()) { if (!enumerator.Forward) { continue; } enumerator.WriteToStream(stream); } } stream.Write(EmptyGuid); // write faces. for (var f = 1; f < graph.FaceCount; f++) { var faceGuid = graph.GetFaceGuid(f); if (faceGuid == null) { continue; } stream.Write(faceGuid.Value.ToByteArray()); // write edges. var edges = graph.EnumerateFaceClockwise(f).ToList(); stream.Write(BitConverter.GetBytes(edges.Count)); foreach (var edge in edges) { enumerator.MoveTo(edge.vertex1); enumerator.MoveNextUntil(edge.edge); stream.Write(enumerator.GetEdgeGuid().ToByteArray()); stream.WriteByte(edge.forward ? (byte)1 : (byte)0); } // write attributes. stream.WriteAttributes(graph.GetFaceData(f)); } stream.Write(EmptyGuid); }
public static (bool success, IEnumerable <uint> missingTiles) AssignFaces(this TiledBarrierGraph graph, uint tile) { if (!graph.HasTile(tile)) { return(false, new[] { tile }); } var tileBox = TileStatic.Box(graph.Zoom, tile); var tilesMissing = new HashSet <uint>(); graph.ResetFaces(); // the default face for the case where a loop cannot be found. var unAssignableFace = graph.AddFace(); // check each edges for faces and if missing assign them. var enumerator = graph.GetEnumerator(); for (var v = 0; v < graph.VertexCount; v++) { if (!enumerator.MoveTo(v)) { continue; } if (!enumerator.MoveNext()) { continue; } var vBox = graph.GetVertexBox(v); if (vBox == null || !vBox.Value.Overlaps(tileBox)) { continue; } // var vTile = TileStatic.WorldTileLocalId(vLocation.longitude, vLocation.latitude, graph.Zoom); // if (vTile != tile) continue; enumerator.MoveTo(v); while (enumerator.MoveNext()) { if (enumerator.Forward && enumerator.FaceRight != int.MaxValue) { continue; } if (!enumerator.Forward && enumerator.FaceLeft != int.MaxValue) { continue; } // check if the edge bbox overlaps the tiles. var eBox = enumerator.Box; if (!eBox.Overlaps(tileBox)) { continue; } // ok this edge has an undetermined face. var result = enumerator.AssignFace(unAssignableFace); if (!result.success) { tilesMissing.UnionWith(result.missingTiles); } } } if (tilesMissing.Count > 0) { return(false, tilesMissing); } return(true, Enumerable.Empty <uint>()); }
public void Faces_AssignFaces_NoEdges_ShouldDoNothing() { var graphs = new TiledBarrierGraph(); graphs.AssignFaces(41525); }
private static byte[] GetVertexLocationBytes(this TiledBarrierGraph graph, int v) { return(TileStatic.ToLocalTileCoordinates(graph.Zoom, graph.GetVertex(v), 16384).GetBytes()); }
private static IEnumerable <uint> Add(this TiledBarrierGraph graph, IEnumerable <OsmGeo> osmGeos, Func <TagsCollectionBase, bool> isBarrier) { var uncoveredTiles = new HashSet <uint>(); // collect all nodes with more than one barrier way. var nodes = new Dictionary <long, (double longitude, double latitude)?>(); var vertexNodes = new Dictionary <long, int>(); foreach (var osmGeo in osmGeos) { if (!(osmGeo is Way way)) { continue; } if (way.Nodes == null) { continue; } if (!isBarrier(way.Tags)) { continue; } for (var n = 0; n < way.Nodes.Length; n++) { var nodeId = way.Nodes[n]; if (graph.TryGetVertex(nodeId, out var vertex)) { // node already there as a vertex. vertexNodes[nodeId] = vertex; } else { // not yet a vertex. // keep first, last and reused nodes are intersections. if (n == 0 || n == way.Nodes.Length - 1 || nodes.ContainsKey(nodeId)) { vertexNodes[nodeId] = int.MaxValue; } } nodes[nodeId] = null; } } // add all vertices new vertices and store node locations. using var enumerator = osmGeos.GetEnumerator(); var hasNext = true; while (hasNext) { hasNext = enumerator.MoveNext(); if (!hasNext) { break; } if (!(enumerator.Current is Node node)) { break; } if (node.Id == null || node.Latitude == null || node.Longitude == null) { continue; } if (graph.TryGetVertex(node.Id.Value, out _)) { continue; } if (!nodes.ContainsKey(node.Id.Value)) { continue; // not part of a barrier way. } nodes[node.Id.Value] = (node.Longitude.Value, node.Latitude.Value); var tile = TileStatic.WorldTileLocalId(node.Longitude.Value, node.Latitude.Value, graph.Zoom); if (!vertexNodes.ContainsKey(node.Id.Value) && graph.HasTile(tile)) { continue; // node is not a vertex and inside a loaded tile. } var vertex = graph.AddVertex(node.Longitude.Value, node.Latitude.Value, node.Id.Value); vertexNodes[node.Id.Value] = vertex; if (!graph.HasTile(tile)) { uncoveredTiles.Add(tile); } } // add all edges. var shape = new List <(double longitude, double latitude)>(); while (hasNext) { if (!hasNext) { break; } if (!(enumerator.Current is Way way)) { break; } if (way.Nodes == null || way.Tags == null || way.Id == null) { hasNext = enumerator.MoveNext(); continue; } if (!isBarrier(way.Tags)) { hasNext = enumerator.MoveNext(); continue; } if (graph.HasWay(way.Id.Value)) { hasNext = enumerator.MoveNext(); continue; } // way is a barrier, add it as one or more edges. shape.Clear(); var vertex1 = int.MaxValue; foreach (var node in way.Nodes) { if (!vertexNodes.TryGetValue(node, out var vertex)) { if (!nodes.TryGetValue(node, out var nodeLocation)) { throw new InvalidDataException( $"Node {node} in way {way.Id} not found!"); } if (nodeLocation == null) { OsmSharp.Logging.Logger.Log(nameof(TiledBarrierGraphBuilder), TraceEventType.Warning, $"Node location for node {node} in way {way.Id} not found!"); } else { shape.Add(nodeLocation.Value); } continue; } else if (vertex == int.MaxValue) { OsmSharp.Logging.Logger.Log(nameof(TiledBarrierGraphBuilder), TraceEventType.Warning, $"Node {node} in way {way.Id} not found in tile!"); continue; } if (vertex1 == int.MaxValue) { vertex1 = vertex; continue; } graph.AddEdgeFlattened(vertex1, vertex, shape, way.Tags, way.Id.Value); vertex1 = vertex; shape.Clear(); } hasNext = enumerator.MoveNext(); } return(uncoveredTiles); }
public static async Task BuildForTile(uint tile, string folder, Func <uint, IEnumerable <OsmGeo> > getTile, Func <TagsCollectionBase, bool> isBarrier) { // wait until tile is removed from queue. while (true) { if (_tiles.ContainsKey(tile)) { await Task.Delay(200); } else { _tiles[tile] = tile; break; } } try { var file = Path.Combine(folder, $"{tile}.tile.graph.zip"); if (File.Exists(file)) { return; } // load data for tile. var graph = new TiledBarrierGraph(); graph.LoadForTile(tile, getTile, isBarrier); // run face assignment for the tile. var result = graph.AssignFaces(tile); while (!result.success) { // extra tiles need loading.- graph.AddTiles(result.missingTiles, getTile, isBarrier); // try again. result = graph.AssignFaces(tile); } // assign landuse. IEnumerable <(Polygon polygon, string type)> GetLanduse( ((double longitude, double latitude)topLeft, (double longitude, double latitude)bottomRight) box) { return(LandusePolygons.GetLandusePolygons(box, graph.Zoom, getTile, t => { if (DefaultMergeFactorCalculator.Landuses.TryCalculateValue(t, out var type)) { return type; } return null; })); } graph.AssignLanduse(tile, GetLanduse); await using var stream = File.Open(file, FileMode.Create); await using var compressedStream = new GZipStream(stream, CompressionLevel.Fastest); graph.WriteTileTo(compressedStream, tile); } finally { _tiles.Remove(tile, out _); } }