4
votes

When using a tile overlay, the only method I've seen thus far is to listen to Google when it grabs tiles, take the X,Y,Z coordinates from the call, and compare that to the set of tiles.

var tileOptions = {
    getTileUrl: function(coord, zoom) {
        return "/tiles/" + zoom + "_" + coord.x + "_" + coord.y + ".png";
    }
}

If the tile at that X,Y,Z exists, it is found and called normally. If it doesn't, there is a 404 error. In my case, I have six sets of tiles which could potentially all be active at the same time, and the script which creates the tiles ignores and does not create blank tiles, meaning that every time the map changes zoom or position, there is the possibility of having upwards of a hundred 404 errors as all the tiles attempt to load.

To try and help counter this in some part, I've limited the tile search to the region which the tiles cover:

var tileOptions = {
    if (zoom>=16 && zoom<=20) {
        if (zoom=16) { if (coord.x>=16000 && coord.x<=16004) { if (coord.y>=24200 && coord.y<=24203) { 
            getTileUrl: function(coord, zoom) {
                //return the tiles
            }}}
        }
        if (zoom=17) //...
        }
        if (zoom=18) //...
        }
        //...
    }
}

However, the region spans the area of 8x10 blocks, and is not completely covered by the overlays. This method, in addition to only preventing errors when the user tries to stray away from the map, is also overly complicated and is likely slowing down the tile calling process.

Thus, I'm looking for a method of "placing" the tiles as apposed to "calling" them: instead of the map asking "do you have this tile" and risking 404 errors, a script or function would say "I have these tiles, place them" ignoring the tiles which are missing (blank), ideally only loading the tiles which are visible on the page.

Alternatively, I'm also looking for a method of having the function know what tiles exist so it only attempts to call those titles and thus avoiding 404 errors.

In short, how I can avoid 404 errors in calling tiles?


Thoughts after writing the question: I assume that the first ideal solution would be to take the stacks of if statements and convert them into a more dynamic check for the tiles and, if a tile exists, use the "return" to actually call it; however, this still seems to result in the same 404 errors but from a different function.


Using Indexes: As proposed below by meetamit (if I understand it right), create an index to check the exist/non-exist of the tiles first rather than calling the tile and using the 404 as the check. However, with a set of tiles in "Z_X_Y.png" format, how could I easily create these indexes?

1

1 Answers

6
votes

Sounds like you're extending an ImageMapType, which requires you to implement the getTileUrl() method. To "place" as opposed to "call" the tiles, as you say, you'd need to create an Overlay Map Type, which is inherently more involved, and, will get even more involved when you attempt to do what you're describe using 404s.

Really though, no matter how you implement this, if you rely on 404's as the "test" for existence of the image, you're likely severely slowing down your map's performance by tying up unneeded connections to the server. Ideally (as you've hinted) the client would have some index representing which tiles are available. If the index indicates that a tile is not available, your getTileUrl() would return null. That is, assuming the Google Maps API knows not to try and load null urls; if that's not the case, the method could return the URL of a transparent image, which gets cached once and re-used for each missing tile.

How to represent this index is a matter of choice. It could be a simple JSON, which might get quite large (but even if so, every client would cache it so it loads once ever). Or, you can get creative and come up with a more concise format to represent the index. You could even pre-generate an image with, say, black and white pixels representing existence or lack of tiles. You'd then draw this image into a <canvas> and read the appropriate pixel out of the canvas to determine whether the tile exists.

Adding to answer, after refinement of question

A JSON index could look like this:

var index = {
  "4": {
    xMin: 10,
    xMax: 31,
    yMin: 4,
    yMax: 11,
    tiles: [1,1,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1 ...]
  },
  "5": {
    xMin: 5,
    xMax: 16,
    yMin: 2,
    yMax: 6,
    tiles: [1,1,1,1,0,0,1,1,1,0,0,1,1 ...]
  }
}

Each top-level object corresponds to zoom level. This way, index["5"].tiles gives you all the the tiles for zoom-level 5. 1 means you have the tile, 0 means no tile. The min/max values xMin, xMax, yMin, yMax are for defining the region you're concerned with -- since you've indicated that your tiles don't cover the entire world.

To find out if a tile exists:

function tileExists(z, x, y) {
  var obj = index[String(z)],
      tilesPerRow = obj.xMax - obj.xMin,
      // Calculate the index of tile within the sequential array
      iTile = (x - obj.xMin) + ((y - obj.yMin) * tilesPerRow);

  return Boolean(obj.tiles[iTile]);
}

Again, using JSON, this index could get really big. It depends on how much of the world you're covering, and for what zoom levels. At zoom level 10, if you covered the whole world, you'd have roughly 1 million tiles, which means a REALLY BIG array. Then you'll likely need to adopt a more compressed format. It's up to you how to define it. You could define it such that the array [14,2,120,1,201,3] gets interpreted as "14 1's, followed by 2 0's, followed by 120 1's, followed by 1 zero, etc..." It really depends on what your data looks like, and how much work you're willing/able to do. Hope this helps enough.