Thursday, 22 May 2014

Solving the XYZ problem in MapGuide

I think I've finally cracked the XYZ problem in MapGuide.

The XYZ problem for those wondering, was how to consume tiled maps in MapGuide as XYZ layers (ala. Google Maps, OSM, etc). For the longest time, my line of thinking was that there must've been some kind of black magic mathematics involved that allowed us to translate X, Y and Z into Row, Column and ScaleIndex parameters that the MapGuide Tile APIs use.

Well unfortunately that went nowhere. The two tile schemes are not inherently translatable, so I went back to the drawing board. After reading this page about XYZ tiles for the umpteenth time the concept finally clicked.

  • Formulas exist to translate X, Y and Z to longitude and latitude
  • Translating X, Y, Z and (X+1), (Y+1), Z gives us two pairs of longitude/latitudes forming a bounding box for a given XYZ cell.
  • Given a bounding box, we now have the means to render whatever lies within that bounding box with the MapGuide API. If necessary, we can also transform this bounding box into map coordinate space using the MgCoordinateSystem APIs.
Now unfortunately, we cannot use the existing Tile APIs in MapGuide to render XYZ tiles because they only accept row, column and scaleindex and not a bounding box. In addition, parameters such as tile size and image formats are locked to whatever's globally defined in serverconfig.ini. So to produce XYZ tiles we use a different primitive for tile rendering: The RenderMap API. 

The RenderMap API can accept bounding boxes so the XYZ tile rendering process is simply:
  • Convert X, Y, Z to a lat/lon bounding box
  • If the map is not WGS84, transform this bounding box to the map's coordinate system
  • Invoke the RenderMap API with the computed bounds to render our 256x256 XYZ "tile"
To verify this process works, I cooked up an implementation in mapguide-rest and used this wonderful page on maptiler.org and the Sheboygan dataset as a frame of reference.

For our test, we made a tiled version of the Sheboygan Map using the web mercator (WGS84.PseudoMercator) coordinate system.

Our XYZ route in mapguide-rest is as follows:

http://servername/mapguide/rest/library/{resourcePath}.MapDefinition/xyz/{groupName}/{z}/{x}/{y}/tile.{format}

So let's zoom in on the Sheboygan area in maptiler


Based on the above image, our tiles of interest (to verify if our XYZ tiles are correct) are:
  • X: 2099, Y: 2985, Z: 13 (Top Left)
  • X: 2100, Y: 2985, Z: 13 (Top Right)
  • X: 2099, Y: 2986, Z: 13 (Bottom Left)
  • X: 2100, Y: 2986, Z: 13 (Bottom Right)
Here's our top left tile in mapguide-rest


Here's the top right tile


Here's bottom left tile


And finally, the bottom right tile


If you cross reference these tiles against the above grid on maptiler, you'll find that the MapGuide-rendered XYZ tiles do indeed line up with the Google ones, which tells us that our XYZ tile rendering implementation is working. Yay!

So what does this mean now that we have the means to serve XYZ tiles? It means your maps can be consumed by a greater range of clients. 

Not just OpenLayers


But also Leaflet


And much more.

Do you know what else happens to use a XYZ tiling scheme? Vector Tiles!

How can we produce vector tiles? We simply take that same bounding box computed from our XYZ parameters and instead of rendering an image, we run a series of FDO queries with that bounding box and write out the appropriate GeoJSON for all features into a GeoJSON "tile" file and serve that file out. For optimal performance and size, we should actually be using TopoJSON, but:
  • This is a first cut implementation
  • If there was a PHP library that can write TopoJSON so I don't have to write one myself, that would be great!
So lo and behold, vector tiles served from mapguide-rest consumed by an OpenLayers 3 client viewer in their full monochromatic glory!


UPDATE: Or how about ... one in full color glory with the bells and whistles that ol3 provides?



So my line of thinking about the mythical HTML5 map viewer with client-side vector rendering is now simply: Let OpenLayers 3 solve that problem. Let's just focus on making MapGuide deliver the required data to ol3 in the desired format in the most efficient manner.

This is hopefully a small step in that direction.

1 comment:

mapNinja said...

Heck!
That slightly blows my mind... accessible FDO, data agnostic map server with MapGuide stylisation delivering to a range of clients and vector layers too!

One of the issues we always had (and hack around with an HTTP tile mod in the .TEMPL files) is that creating a session map always created session tiles - even if Tiled and hence removed the benefits of tiled optimisations.