如何将 WGS84 纬度经度转换为 UWP MapControl 的 X Y Tile 坐标
How to convert WGS84 Latitude Longitude into X Y Tile's coordinates for UWP MapControl
我正在实施 CustomMapTileDataSource
功能。
到目前为止,我可以用一些形状动态绘制自定义 Tiles
(见下图)。
现在我需要在 UWP MapControl
提供的每个 Tile 上绘制 行 。
我所有的线都是预先计算的 WGS82 坐标和经过良好测试。
我使用这个 MSDN 手册https://docs.microsoft.com/en-us/windows/uwp/maps-and-location/overlay-tiled-images
在这里我可以得到 WGS83 矩形来过滤我必须画的线。
private async void customDataSource_BitmapRequestedAsync(CustomMapTileDataSource sender, MapTileBitmapRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
const int tileSide = 256;
TileSystem.TileXYToPixelXY(args.X, args.Y, out int pixelX, out int pixelY);
var rect = new List<BasicGeoposition>();
// Add Top Left Geo Point
TileSystem.PixelXYToLatLong(pixelX, pixelY, args.ZoomLevel, out double latTopLeft, out double lngTopLeft);
rect.Add(new BasicGeoposition() { Latitude = latTopLeft, Longitude = lngTopLeft });
// Add Top Right Geo Point
TileSystem.PixelXYToLatLong(pixelX + tileSide, pixelY, args.ZoomLevel, out double latTopRight, out double lngTopRight);
rect.Add(new BasicGeoposition() { Latitude = latTopRight, Longitude = lngTopRight });
// Add Bottom Right Geo Point
TileSystem.PixelXYToLatLong(pixelX + tileSide, pixelY + tileSide, args.ZoomLevel, out double latBottomRight, out double lngBottomRight);
rect.Add(new BasicGeoposition() { Latitude = latBottomRight, Longitude = lngBottomRight });
// Add Bottom Left Geo Point
TileSystem.PixelXYToLatLong(pixelX, pixelY + tileSide, args.ZoomLevel, out double latBottomLeft, out double lngBottomLeft);
rect.Add(new BasicGeoposition() { Latitude = latBottomLeft, Longitude = lngBottomLeft });
var filterdLines = GetLinesByGeoRect(rect);
args.Request.PixelData = await CreateBitmapAsStreamAsync(filterdLines, args.ZoomLevel);
deferral.Complete();
}
而且这个事件特别适合画一个具体的瓷砖,你可以在上图中看到(我已经用 ds.DrawCircle 做了测试绘图)。
private async Task<RandomAccessStreamReference> CreateBitmapAsStreamAsync(List<MyLine> lines, int zoom)
{
int pixelHeight = 256;
int pixelWidth = 256;
var randomAccessStream = new InMemoryRandomAccessStream();
var outputStream = randomAccessStream.GetOutputStreamAt(0);
var softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, pixelHeight, pixelWidth, BitmapAlphaMode.Premultiplied);
var resourceCreator = CanvasDevice.GetSharedDevice();
var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(resourceCreator, softwareBitmap);
var canvasRenderTarget = new CanvasRenderTarget(resourceCreator, softwareBitmap.PixelWidth, softwareBitmap.PixelHeight, 96);
using (var ds = canvasRenderTarget.CreateDrawingSession())
{
ds.Antialiasing = CanvasAntialiasing.Antialiased;
foreach (var line in lines)
{
// Try to convert WGS84 Lat Long to Screen coordinates for this Tile
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int startX, out int startY);
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int endX, out int endY);
/*
50.016952, 8.772860 zoom 1 X: 268 Y: 174
50.016952, 8.772860 zoom 11 X: 274920 Y: 177771
*/
ds.DrawLine(startX, startY, endX, endY, Colors.Black, 1);
}
// ds.DrawCircle(new Vector2(100, 100), 100, Colors.Blue); // just to test drawing in Tile
// ds.DrawRectangle(1, 1, 255, 255, Colors.Black, 2); // just to test drawing in Tile
}
await canvasRenderTarget.SaveAsync(randomAccessStream, CanvasBitmapFileFormat.Png);
await randomAccessStream.FlushAsync();
return RandomAccessStreamReference.CreateFromStream(randomAccessStream);
}
问题从这里开始TileSystem.LatLongToPixelXY
正如您在我的示例中看到的相同坐标和不同级别的细节 (1-20)
它给出了奇怪的 X 和 Y
50.016952, 8.772860 zoom 1 X: 268 Y: 174
50.016952, 8.772860 zoom 11 X: 274920 Y: 177771
无法为 256 x 256 像素的 Tile 应用值 177771 绘制一些东西!
所以我不知道如何将它应用到当前的 Tile 上?
看来微软应该给出更明确的解释:
如何转换当前Tile的经纬度正在绘制中?
谢谢!
感谢 Duncan 更新 #1 解决方案!
我们使用
TileSystem.TileXYToPixelXY(args.X, args.Y, out int pixelX, out int pixelY);
在
内部传递
private async Task<RandomAccessStreamReference> CreateBitmapAsStreamAsync(List<MyLine> lines, int zoom, int pixelX, int pixelY)
{
ds.Antialiasing = CanvasAntialiasing.Antialiased;
foreach (var line in lines)
{
// Try to convert WGS84 Lat Long to Screen coordinates for this Tile
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int startX, out int startY);
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int endX, out int endY);
float rX0 = startX - pixelX; // <--- Here is Duncan's solution THNK YOU!
float rY0 = startY - pixelY;
float rX1 = endX - pixelX;
float rY1 = endY - pixelY;
ds.DrawLine(rX0, rY0, rX1, rY1, Colors.Black, 1);
}
}
给定细节级别的整个墨卡托 space 的 TileSystem.LatLongToPixelXY returns 像素值 - 即级别 1 有 4 个图块,因此像素 space 为 512x512。在级别 2 它将是 1024x1024,等等。
在你的例子中 50.016952, 8.772860 zoom 1 X: 268 Y: 174
将落在第一级的右上角,像素偏移量 X=12,Y=174 在该块中。
您可以使用 TileXYToPixelXY 方法获取给定图块左上角像素的像素 X、Y,然后从全局 XY 中减去该像素,或者仅将整数除以 256 以获得图块 x、y 和整数 mod乘以 256 以获得图块内的像素。
我正在实施 CustomMapTileDataSource
功能。
到目前为止,我可以用一些形状动态绘制自定义 Tiles
(见下图)。
现在我需要在 UWP MapControl
提供的每个 Tile 上绘制 行 。
我所有的线都是预先计算的 WGS82 坐标和经过良好测试。
我使用这个 MSDN 手册https://docs.microsoft.com/en-us/windows/uwp/maps-and-location/overlay-tiled-images
在这里我可以得到 WGS83 矩形来过滤我必须画的线。
private async void customDataSource_BitmapRequestedAsync(CustomMapTileDataSource sender, MapTileBitmapRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
const int tileSide = 256;
TileSystem.TileXYToPixelXY(args.X, args.Y, out int pixelX, out int pixelY);
var rect = new List<BasicGeoposition>();
// Add Top Left Geo Point
TileSystem.PixelXYToLatLong(pixelX, pixelY, args.ZoomLevel, out double latTopLeft, out double lngTopLeft);
rect.Add(new BasicGeoposition() { Latitude = latTopLeft, Longitude = lngTopLeft });
// Add Top Right Geo Point
TileSystem.PixelXYToLatLong(pixelX + tileSide, pixelY, args.ZoomLevel, out double latTopRight, out double lngTopRight);
rect.Add(new BasicGeoposition() { Latitude = latTopRight, Longitude = lngTopRight });
// Add Bottom Right Geo Point
TileSystem.PixelXYToLatLong(pixelX + tileSide, pixelY + tileSide, args.ZoomLevel, out double latBottomRight, out double lngBottomRight);
rect.Add(new BasicGeoposition() { Latitude = latBottomRight, Longitude = lngBottomRight });
// Add Bottom Left Geo Point
TileSystem.PixelXYToLatLong(pixelX, pixelY + tileSide, args.ZoomLevel, out double latBottomLeft, out double lngBottomLeft);
rect.Add(new BasicGeoposition() { Latitude = latBottomLeft, Longitude = lngBottomLeft });
var filterdLines = GetLinesByGeoRect(rect);
args.Request.PixelData = await CreateBitmapAsStreamAsync(filterdLines, args.ZoomLevel);
deferral.Complete();
}
而且这个事件特别适合画一个具体的瓷砖,你可以在上图中看到(我已经用 ds.DrawCircle 做了测试绘图)。
private async Task<RandomAccessStreamReference> CreateBitmapAsStreamAsync(List<MyLine> lines, int zoom)
{
int pixelHeight = 256;
int pixelWidth = 256;
var randomAccessStream = new InMemoryRandomAccessStream();
var outputStream = randomAccessStream.GetOutputStreamAt(0);
var softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, pixelHeight, pixelWidth, BitmapAlphaMode.Premultiplied);
var resourceCreator = CanvasDevice.GetSharedDevice();
var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(resourceCreator, softwareBitmap);
var canvasRenderTarget = new CanvasRenderTarget(resourceCreator, softwareBitmap.PixelWidth, softwareBitmap.PixelHeight, 96);
using (var ds = canvasRenderTarget.CreateDrawingSession())
{
ds.Antialiasing = CanvasAntialiasing.Antialiased;
foreach (var line in lines)
{
// Try to convert WGS84 Lat Long to Screen coordinates for this Tile
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int startX, out int startY);
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int endX, out int endY);
/*
50.016952, 8.772860 zoom 1 X: 268 Y: 174
50.016952, 8.772860 zoom 11 X: 274920 Y: 177771
*/
ds.DrawLine(startX, startY, endX, endY, Colors.Black, 1);
}
// ds.DrawCircle(new Vector2(100, 100), 100, Colors.Blue); // just to test drawing in Tile
// ds.DrawRectangle(1, 1, 255, 255, Colors.Black, 2); // just to test drawing in Tile
}
await canvasRenderTarget.SaveAsync(randomAccessStream, CanvasBitmapFileFormat.Png);
await randomAccessStream.FlushAsync();
return RandomAccessStreamReference.CreateFromStream(randomAccessStream);
}
问题从这里开始TileSystem.LatLongToPixelXY 正如您在我的示例中看到的相同坐标和不同级别的细节 (1-20) 它给出了奇怪的 X 和 Y
50.016952, 8.772860 zoom 1 X: 268 Y: 174
50.016952, 8.772860 zoom 11 X: 274920 Y: 177771
无法为 256 x 256 像素的 Tile 应用值 177771 绘制一些东西!
所以我不知道如何将它应用到当前的 Tile 上?
看来微软应该给出更明确的解释:
如何转换当前Tile的经纬度正在绘制中?
谢谢!
感谢 Duncan 更新 #1 解决方案!
我们使用
TileSystem.TileXYToPixelXY(args.X, args.Y, out int pixelX, out int pixelY);
在
内部传递private async Task<RandomAccessStreamReference> CreateBitmapAsStreamAsync(List<MyLine> lines, int zoom, int pixelX, int pixelY)
{
ds.Antialiasing = CanvasAntialiasing.Antialiased;
foreach (var line in lines)
{
// Try to convert WGS84 Lat Long to Screen coordinates for this Tile
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int startX, out int startY);
TileSystem.LatLongToPixelXY(line.latt, line.longt, zoom, out int endX, out int endY);
float rX0 = startX - pixelX; // <--- Here is Duncan's solution THNK YOU!
float rY0 = startY - pixelY;
float rX1 = endX - pixelX;
float rY1 = endY - pixelY;
ds.DrawLine(rX0, rY0, rX1, rY1, Colors.Black, 1);
}
}
给定细节级别的整个墨卡托 space 的 TileSystem.LatLongToPixelXY returns 像素值 - 即级别 1 有 4 个图块,因此像素 space 为 512x512。在级别 2 它将是 1024x1024,等等。 在你的例子中 50.016952, 8.772860 zoom 1 X: 268 Y: 174 将落在第一级的右上角,像素偏移量 X=12,Y=174 在该块中。 您可以使用 TileXYToPixelXY 方法获取给定图块左上角像素的像素 X、Y,然后从全局 XY 中减去该像素,或者仅将整数除以 256 以获得图块 x、y 和整数 mod乘以 256 以获得图块内的像素。