question

DmitryBoyko-8706 avatar image
0 Votes"
DmitryBoyko-8706 asked DmitryBoyko-8706 edited

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

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://docs.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!



dotnet-csharpwindows-uwpwindows-maps
777.png (1.9 MiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

DmitryBoyko-8706 avatar image
0 Votes"
DmitryBoyko-8706 answered DmitryBoyko-8706 edited

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.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.