I’m skeptical, very skeptical that a <mapml> will increase interoperability.

I stand by the belief that interoperability is not a technical issue, but rather it’s a political issue.

I think it’s hard to become aware of the issue, since we’re all biased. I, as a FLOSS hacker, expect to see raw data, datasheets, and examples of technical minor details (but usually hit a wall when asking for the specs), since I provide all that expecting nothing but the same in return. Since I haven’t been looking at things from the eyes of a CEO, in the past it’s been hard for me to appreciate the work of entities like the OGC: they take a bunch of companies a bit too eager to call a tactical lawyer strike, sit them on the same table, and somehow manage to achieve a lets-not-sue-each-other-asses-off-over-patents truce.

I don’t think that compromise approaches are the best from a technical standpoint, but hey, at least there’s this feeling of willingness to try and play together.

But on the specific realm of maps on the web, I’ve personally seen a few instances of entities that, through their acts, display unwillingness to play together. And then I have to come by and do horrible, beautiful, beautifully horrible technical hacks to overcome this unwillingness.

I’m gonna name names, and I’m gonna painfully point out pain points. My hope is that we can all agree that these hacks should never have happened in the first place, and to come up with something that allows us all to never repeat this.

Follow me in this story of…

Part 2: The Mutant Hacks of the Terms Of Service

As a Leaflet maintainer, I’ve been overlooking the influx of Leaflet bugs and questions for a long while. And I’ll share this fact: People want to display basemaps from Google Maps when they use Leaflet.

Now, from a technical standpoint, this is as easy as declaring a L.TileLayer like so:

L.TileLayer("https://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga").addTo(map);

But there’s a “but”. Google’s own ToS disallow this, because that’s an undocumented interface, and accessing Google Maps assets (i.e. map tiles) must be done through a documented API. The current ToS are somewhat vague about this issue, but the old 2018 ToS redaction (which is the one that prompted the hack) was quite explicit about this:

10.1 Administrative Restrictions.

No access to APIs or Content except through the Service. You will not access the Maps API(s) or the Content except through the Service. For example, you must not access map tiles or imagery through interfaces or channels (including undocumented Google interfaces) other than the Maps API(s).

So while a web developer can do that, it possibly will get them in trouble for not complying with the ToS.

What to do? Is there a way to request Google Maps map tiles through a documented API, and show them in a Leaflet map? Sure there is! And it’s only a <sarcasm>tiny</sarcasm> hack.

First, get an instance of the Google Maps JS API next to your Leaflet instance:

Static mutant

Then, sync the pan/zoom movements of the Leaflet instance to the GMaps instance:

Then, apply CSS hacks as needed to put the GMaps instance below the Leaflet DOM elements, et voilà:

It works. It’s a hack, but it works. It uses documented APIs, so it’s ToS-compliant, so Google shouldn’t have any reason to look down on this technique.

But this hack was… hackish, and it started to show some problems a few years ago; namely, stuff got off-sync when zooming, because slight differences in the tweening functions for the zoom transitions, impossibility to leverage Leaflet’s fractional zoom, or jankiness when using flyTo(). These started to eat some of my maintenance time.

Then I had the idea for (IMHO) my greatest hack ever: Leverage DOM mutation observers.

DOM mutation observers are a W3C standard completely unrelated to maps, supposedly helpful to reactive JS frameworks which rely on heavy, complex DOM manipulation.

The hack is beautiful. Get one Leaflet instance and one GMaps instance synced up, just as before. But instead of CSS’ing stuff into place, listen for mutations on the DOM container for the GMaps instance, and wait.

Tiles will appear inside the GMaps instance (or rather, DOM nodes will mutate into the GMaps container element). So whenever a <img> tile is loaded inside GMaps…

Mutant loading a tile

…the code can react to that: it can rip the tile off the GMaps instance, and move it into a Leaflet GridLayer scaffold:

Mutant having moved a tile

Since the tiles are now in the Leaflet container, all the previous problems are just gone. Now there’s a new set of problems (impossibility of calculating maxNativeZoom, race conditions with the mind-boggling tile pruning algorithm, and handling multiple copies of the same tile at low zoom levels). I’m not surprised of the new set of problems, given the monumental proportions of the hack.

The whole thing is published at a gitlab repo, under the name of GoogleMutant, with an npm package and a bug tracker and the usual shenanigans.

Of all my 20+ Leaflet plugins, it’s the one with the most downloads. I think that’s a sad thing.


Apple’s MapkitJS has similar problems: not only there’s (1) the now-usual ToS, (2) the need of Apple-specific hardware and (3) the need to shell out a minimum of 100US$ a year for a developer account to use it, but on top of that there’s (4) vector tiles with a totally undocumented format.

But it’s possible to apply some tricks from GoogleMutant to create something that I called MapkitMutant: grab the <canvas> element from the MapkitJS instance, put it in a Leaflet ImageOverlay, and sync bounding boxes.

It’s a bit weird since MapkitJS won’t let users zoom out to display more than 180° of longitude (fear of displaying Mercator projection artifacts? hand-holding developers that get confused when stuff gets repeated horizontally?), but on the plus side the one-frame delay that MapkitJS takes to redraw and report its new BBox cancels out with a synthetic one-frame delay before the setBounds() call on the ImageOverlay, so… stuff actually syncs up pretty perfectly.

Git repo under the name of MapkitMutant. npm package. Usual shenanigans.


These hacks are horribly beautiful and beautifully horrible, and in an ideal world they should never have happened. They should be obsoleted, given a funeral, and buried under salt. But right now, they serve as a monument to the lack of a OGC-ish tile interface (WMTS, TMS, I’m even fine with Bing’s quadkey approach right now).

At the time I’m writing this, I’m not holding much hope that a <mapml> spec will achieve that: it’s about actually willing to make web dev’s life easier (since, right now, it feels like wanting web devs to use your API and only your API). I’m not seeing that will now. I can only hope to see it later.


Next up is Part 3: Three Vertical Elephants