Hello, I am attempting to load a local XYZ tiles to my map. I feel like the relevant example is this so I have followed this is https://developers.arcgis.com/net/wpf/sample-code/web-tiled-layer/ but instead of a Url I am pointing to a local folder on my PC.
private readonly string _templateUri = "file:///C:\\Users/Zach/Desktop/DEM/XYZ2/{z}/{x}/{y}.png";
// List of subdomains for use when constructing the web tiled layer
private readonly List<string> _tiledLayerSubdomains = new List<string> { "12" };
public mapviewTest()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
// Create the layer from the URL and the subdomain list
WebTiledLayer myBaseLayer = new WebTiledLayer(_templateUri, _tiledLayerSubdomains);
// Create a basemap from the layer
Basemap layerBasemap = new Basemap(myBaseLayer);
// Create a map to hold the basemap
Map myMap = new Map(layerBasemap);
// Add the map to the map view
MyMapView.Map = myMap;
}
The main folder has a Subfolder named "12" then in that folder several other folders with numbers which is where the actual PNG images live.
Am I going about this the correct way?
Did you mean to add the backslash in the tile template? It should just be forward slashes. Also the doc states you need to use the level row and column parameter instead of x y z. That might be why. (If you load the layer, you should get a load error telling you exactly this) Also you don't need the subdomain overload, since you don't have a subdomain in the template uri.
So you'd have something like:
private readonly string _templateUri = "file:///C:/Users/Zach/Desktop/DEM/XYZ2/{level}/{col}/{row}.png";
This made it work for me. You can also instead implement a custom ImageTiledLayer, which gives you full control of how the image bytes are loaded. It is more work, but gives a lot better control. Example:
using Esri.ArcGISRuntime.ArcGISServices;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace CustomTileLayerSample
{
public class LocalTiledLayer : ImageTiledLayer
{
private readonly string _templateUri = "C:\\Users\\Zach\\Desktop\\DEM\\XYZ2\\{0}\\{1}\\{2}.png";
public LocalTiledLayer() : base(CreateTileInfo(), new Envelope(-2.003750722959434E7, -1.997186888040859E7, 2.003750722959434E7, 1.9971868880408563E7, SpatialReferences.WebMercator))
{
}
private static TileInfo CreateTileInfo()
{
List<LevelOfDetail> levels = new List<LevelOfDetail>();
var resolution = 156543.03392800014;
var scale = 5.91657527591555E8;
int levelCount = 24;
for (int i = 0; i < levelCount; i++)
{
levels.Add(new LevelOfDetail(i, resolution, scale));
resolution /= 2; scale/=2;
}
return new TileInfo(96, TileImageFormat.Png32, levels, new MapPoint(-2.0037508342787E7, 2.0037508342787E7, SpatialReferences.WebMercator), SpatialReferences.WebMercator, 256, 256);
}
protected override async Task<ImageTileData> GetTileDataAsync(int level, int row, int column, CancellationToken cancellationToken)
{
var file = string.Format(_templateUri, level, row, column);
if (File.Exists(file))
{
var bytes = await File.ReadAllBytesAsync(file).ConfigureAwait(false);
return new ImageTileData(level, row, column, bytes, "image/png");
}
return new ImageTileData(level, row, column, new byte[] { }, "image/png");
}
}
}
You could even take this a step further and generate tiles on the fly:
protected override Task<ImageTileData> GetTileDataAsync(int level, int row, int column, CancellationToken cancellationToken)
{
return Task.Run(() =>
{
using var pen = new System.Drawing.Pen(System.Drawing.Color.Black);
using var font = new System.Drawing.Font("Times New Roman", 12);
using var image = new System.Drawing.Bitmap(256, 256);
using var g = Graphics.FromImage(image);
g.DrawString($"{level}/{row}/{column}", font, System.Drawing.Brushes.Black, new PointF(100, 122));
g.DrawLine(pen, 0, 0, 255, 0);
g.DrawLine(pen, 0, 0, 0, 255);
using var filestream = new MemoryStream();
image.Save(filestream, System.Drawing.Imaging.ImageFormat.Png);
return new ImageTileData(level, row, column, filestream.ToArray(), "image/png");
});
}