如何将 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 以获得图块内的像素。