public VectorTileObj GetData(Vector3 <double> lonLatZoom) // throws WebException { if (memoryMapCache.TryGetValue(lonLatZoom.ToString(), out VectorTileObj data)) { return(data); } else if (diskMapCache.TryGetValue(lonLatZoom.ToString(), out string path)) { data = new VectorTileObj(File.ReadAllBytes(path)); } else { byte[] byteData = GetDataFromSource(lonLatZoom); // Save to disk path = $"{pathToDataFolder}\\tiles\\{lonLatZoom.EncodeToString()}{MapFileExtension}"; File.WriteAllBytes(path, byteData); // Add to disk cache diskMapCache.TryAdd(lonLatZoom.ToString(), path); data = new VectorTileObj(byteData); } CacheMapToMemory(lonLatZoom.ToString(), data); CacheRoads(lonLatZoom, data); return(data); }
private MapTile CreateTile(int x, int y, Vector3 <double> lonLatZoom) { VectorTileObj vectorTileObj = dataController.GetData(lonLatZoom); ReadOnlyCollection <string> layers = vectorTileObj.LayerNames(); Bitmap drawingObjects = new Bitmap(256, 256); Graph graph = dataController.GetRoads(lonLatZoom); using (Graphics graphics = Graphics.FromImage(drawingObjects)) { foreach (string layerName in drawingLayersPallete.Keys) { if (layers.Contains(layerName)) { VectorTileLayer layer = vectorTileObj.GetLayer(layerName); MVTDrawer.DrawLayer(layer, drawingLayersPallete[layerName], graphics, 4096 / 128); } } MVTDrawer.DrawLinks(graph.nodes, graphics, 32); } return(Dispatcher.Invoke(() => { Model3DGroup model3DGroup = new Model3DGroup(); model3DGroup.Children.Add(CreateTileModel(x, y, ImageUtils.GetImageStream(drawingObjects))); return new MapTile(model3DGroup); })); }
private void CacheMapToMemory(string lonLatZoom, VectorTileObj data) { if (memoryMapCache.Count == Settings.memoryDataCacheOpacity) { memoryMapCache.TryRemove(memoryMapCache.Keys.First(), out data); } memoryMapCache.TryAdd(lonLatZoom, data); }
// Need check memory and disk existing because we can load road graph for path finding private void CacheRoads(Vector3 <double> lonLatZoom, VectorTileObj data) { if (memoryRoadsCache.ContainsKey(lonLatZoom.ToString())) { return; } if (diskRoadCache.TryGetValue(lonLatZoom.ToString(), out string path)) { memoryRoadsCache.TryAdd(lonLatZoom.ToString(), JsonConvert.DeserializeObject <Graph>(File.ReadAllText(path), settings)); return; } Graph graph; if (data.LayerNames().Contains("roads")) { VectorTileLayer layer = data.GetLayer("roads"); MaskGraphCreator maskDrawer = new MaskGraphCreator(GetTileSize(ConvertToMapZoom(Settings.zoom))); for (int i = 0; i < layer.FeatureCount(); i++) { VectorTileFeature feature = layer.GetFeature(i); foreach (List <Vector2 <int> > geometry in feature.Geometry <int>()) { for (int index = 1; index < geometry.Count; index++) { maskDrawer.DrawMaskLine(geometry[index - 1], geometry[index]); } } } graph = maskDrawer.GetGraph(); } else { graph = new Graph(); } memoryRoadsCache.TryAdd(lonLatZoom.ToString(), graph); // Save to disk path = $"{pathToDataFolder}\\roads\\{lonLatZoom.EncodeToString()}{RoadFileExtension}"; File.WriteAllText(path, JsonConvert.SerializeObject(graph, settings)); // Add to disk cache diskRoadCache.TryAdd(lonLatZoom.ToString(), path); }
/// <summary> /// Return the whole vector tile as GeoJSON /// </summary> /// <param name="tile"></param> /// <param name="zoom">Zoom level of the tile</param> /// <param name="tileColumn">Column of the tile (OSM tile schema)</param> /// <param name="tileRow">Row of the tile (OSM tile schema)</param> /// <param name="clipBuffer"> /// <para>Vector tiles may contain data reaching beyond the actual tile border.</para> /// <para>Define if/how this data should be included.</para> /// <para>'null': data from the vector tile as is</para> /// <para>'>=0': clip distance in internal tile units</para> /// </param> /// <returns>GeoJSON of the vector tile contents</returns> public static string ToGeoJson( this VectorTileObj tile , ulong zoom , ulong tileColumn , ulong tileRow , uint?clipBuffer = null ) { //to get '.' instead of ',' when using "string.format" with double/float and non-US system number format settings //CultureInfo en_US = new CultureInfo("en-US"); // escaping '{' '}' -> @"{{" "}}" //escaping '"' -> @"""" string templateFeatureCollection = @"{{""type"":""FeatureCollection"",""features"":[{0}]}}"; string templateFeature = @"{{""type"":""Feature"",""geometry"":{{""type"":""{0}"",""coordinates"":[{1}]}},""properties"":{2}}}"; var geojsonFeatures = new List <string>(); foreach (string layerName in tile.LayerNames()) { VectorTileLayer layer = tile.GetLayer(layerName); for (int i = 0; i < layer.FeatureCount(); i++) { VectorTileFeature feat = layer.GetFeature(i, clipBuffer); if (feat.GeometryType == GeomType.UNKNOWN) { continue; } //resolve properties var keyValue = new List <string>(); int tagCnt = feat.Tags.Count; for (int j = 0; j < tagCnt; j += 2) { string key = layer.Keys[feat.Tags[j]]; object val = layer.Values[feat.Tags[j + 1]]; keyValue.Add(string.Format(NumberFormatInfo.InvariantInfo, @"""{0}"":""{1}""", key, val)); } //build geojson properties object from resolved properties string geojsonProps = string.Format( NumberFormatInfo.InvariantInfo , @"{{""id"":{0},""lyr"":""{1}""{2}{3}}}" , feat.Id , layer.Name , keyValue.Count > 0 ? "," : "" , string.Join(",", keyValue.ToArray()) ); //work through geometries string geojsonCoords = ""; string geomType = feat.GeometryType.Description(); //multipart List <List <LatLng> > geomWgs84 = feat.GeometryAsWgs84(zoom, tileColumn, tileRow); if (geomWgs84.Count > 1) { switch (feat.GeometryType) { case GeomType.POINT: geomType = "MultiPoint"; geojsonCoords = string.Join( "," , geomWgs84 .SelectMany(g => g) .Select(g => string.Format(NumberFormatInfo.InvariantInfo, "[{0},{1}]", g.Lng, g.Lat)) .ToArray() ); break; case GeomType.LINESTRING: geomType = "MultiLineString"; var parts = new List <string>(); foreach (List <LatLng> part in geomWgs84) { parts.Add("[" + string.Join( "," , part.Select(g => string.Format(NumberFormatInfo.InvariantInfo, "[{0},{1}]", g.Lng, g.Lat)).ToArray() ) + "]"); } geojsonCoords = string.Join(",", parts.ToArray()); break; case GeomType.POLYGON: geomType = "MultiPolygon"; var partsMP = new List <string>(); foreach (List <LatLng> part in geomWgs84) { partsMP.Add("[" + string.Join( ",", part.Select(g => string.Format(NumberFormatInfo.InvariantInfo, "[{0},{1}]", g.Lng, g.Lat)).ToArray() ) + "]"); } geojsonCoords = "[" + string.Join(",", partsMP.ToArray()) + "]"; break; } } else if (geomWgs84.Count == 1) { //singlepart switch (feat.GeometryType) { case GeomType.POINT: geojsonCoords = string.Format(NumberFormatInfo.InvariantInfo, "{0},{1}", geomWgs84[0][0].Lng, geomWgs84[0][0].Lat); break; case GeomType.LINESTRING: geojsonCoords = string.Join( ",", geomWgs84[0].Select(g => string.Format(NumberFormatInfo.InvariantInfo, "[{0},{1}]", g.Lng, g.Lat)) .ToArray() ); break; case GeomType.POLYGON: geojsonCoords = "[" + string.Join( ",", geomWgs84[0].Select(g => string.Format(NumberFormatInfo.InvariantInfo, "[{0},{1}]", g.Lng, g.Lat)).ToArray() ) + "]"; break; } } else { //no geometry continue; } geojsonFeatures.Add( string.Format( NumberFormatInfo.InvariantInfo , templateFeature , geomType , geojsonCoords , geojsonProps ) ); } } string geoJsonFeatColl = string.Format( NumberFormatInfo.InvariantInfo , templateFeatureCollection , string.Join(",", geojsonFeatures.ToArray()) ); return(geoJsonFeatColl); }