How to convert WGS84 Latitude Longitude into X Y Tile's coordinates for UWP MapControl

Dmitry Boyko 21 Reputation points
2021-08-28T13:40:26.407+00:00

Hi!

I am implementing CustomMapTileDataSource feature. So far I can draw custom Tiles on fly with some shapes (see image below).
127303-777.png

Now I need to draw lines on each Tile which UWP MapControl provides.

All my lines are precalculated with WGS82 coordinates and well tested.

I use this MSDN manual https://learn.microsoft.com/en-us/windows/uwp/maps-and-location/overlay-tiled-images

And here I can get WGS83 rect to filter lines I have to draw.

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();  
}  

And this event is particular fit to draw a concrete Tile that you can see in image up (I have used ds.DrawCircle to do test drawing).

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);  
}  

The problem starts here TileSystem.LatLongToPixelXY As you see in my example for the same coordinates and different level of details (1-20) it gives weird X and Y

50.016952, 8.772860 zoom 1 X: 268 Y: 174

50.016952, 8.772860 zoom 11 X: 274920 Y: 177771

It is impossible to apply the value 177771 for the Tile 256 x 256 pixels do draw something!

So I have no clue how to apply it for the current Tile?

It seems like Microsoft should provide more clear explanation:

How to convert Latitude Longitude for current Tile is under drawing?

Thank you!

Universal Windows Platform (UWP)
Windows Maps
Windows Maps
A Microsoft app that provides voice navigation and turn-by-turn driving, transit, and walking directions.
253 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,454 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Dmitry Boyko 21 Reputation points
    2021-08-29T01:47:21.833+00:00

    Thanks to Duncan Lawler here is the solution https://stackoverflow.com/questions/68964868/how-to-convert-wgs84-latitude-longitude-into-x-y-tiles-coordinates-for-uwp-mapc

    The TileSystem.LatLongToPixelXY returns pixel values for the entire Mercator space of a given level of detail - i.e. There are 4 tiles at level 1, so the pixel space is 512x512. At level 2 it will be 1024x1024, etc. In your example 50.016952, 8.772860 zoom 1 X: 268 Y: 174 Would fall in the upper right tile of level one and pixel offset X=12, Y=174 in that tile. You can use the TileXYToPixelXY method to get the pixel X,Y for the upper left pixel of a given tile and subtract that from the global XY, or just integer divide by 256 to get the tile x,y and integer mod by 256 to get the pixel within the tile.

    0 comments No comments