Hack 67. Serve Custom Map Imagery

You can serve your own custom tiles to Google Maps with a simple script.

If you've created a custom map layer [Hack #66], you need a way to serve requests for tiles. This hack describes a PHP script to serve Google Maps compatible imagery.

Every time you drag the map around in the window, the Google Map application requests whatever tiles it needs in order to show more of the map. It does this by passing three values to the server for each tile it needs: an X coordinate value, a Y coordinate value, and a Zoom value. For each tile it needs, the map application sends a set of these three values.

Our image serving script uses the three values sent by the Google Map application to dynamically create paths to the appropriate image required to fill in a space in the map window. Our script is kept in a file named index.php. It assumes that tiles have been created and named by the naming standards described in "Automatically Cut and Name Custom Map Tiles" [Hack #68].

With a little fancy footwork we can also use our script to send generic filler tiles to the map when we don't have custom tiles for a requested area. In the most common case, you will probably want to create a custom map that covers a small area, and not the whole globe. Therefore you will probably need to use some filler tiles where your map does not cover an area or zoom level. We can use our image serving script to determine when we don't have an appropriate custom tile and need to send a filler tile.

If you decide that you do need to make tiles for the whole planet, be sure to read "How Big Is the World?" [Hack #16] first!

To understand this script, we'll start with the whole thing and then break it down. Here is the full script that goes in our file index.php:

5 ){ $content = file_get_contents( ZOOM_IN ); }else if ( is_numeric($x) && is_numeric($y) && is_numeric($z) && file_exists( $filename ) ){ $content = file_get_contents( $filename ); }else{ $content = file_get_contents( NO_DATA ); } header("Content-type: image/gif"); echo $content; ?>

OK, there it is. Now let's break it down.

The first three lines define our filler tiles. We have one for each of three conditions. If we are in an area for which we have data, but not at the current zoom level, we use the zoom_in.gif and zoom_out.gif images. If we have no data, we send the no_data.gif image.

You'll notice that above we also set the path to these tiles to a directory called maptiles/. This is where we will put all our image tiles. This directory sits inside the images/ folder, where this script also resides, as established in "Add Your Own Custom Map" [Hack #66].

Next we get the x, y, and zoom values from the map and store them in similarly named variables.

$x = $_GET["x"]; $y = $_GET["y"]; $z = $_GET["zoom"];

Using these three values, we create a path to a tile file, here a GIF, and we store it in the variable $filename. You'll note that the x, y, and zoom (z) values are part of the tile name. This is the basic structure we use to name all our tiles. This is how we differentiate the hundreds or thousands of custom tiles we likely end up with when creating a custom map. Now that's a lot of custom names, but don't worry about it too much, because later we'll automate the cutting and naming of your tiles.

$filename = "./maptiles/${x}_${y}_${z}.gif";

Now we have a path appropriate to the data sent, but that does not mean the tile actually exists. $filename, whatever its value, is just a fourth image option, in addition to NO_DATA, ZOOM_IN, and ZOOM_OUT. Before we send any image back to the browser, we want to check if the request is within our zoom range and map area. This next chunk of the script uses several if/else statements to filter though the possibilities and determine the appropriate image. The winner will be stored in variable $content. The specific checks we make are explained by the in-line comments:

// if the zoom level requested is too close, we send images that // say zoom out if ( $z < 2 ) { $content = file_get_contents( ZOOM_OUT ); // if the zoom level requested is too far out, we send images that // say zoom in } else if ( $z > 5 ){ $content = file_get_contents( ZOOM_IN ); // here we make sure values were sent for x, y, and zoom, and that // the image tile actually exists. if it all checks out we send // the file we defined in $filename } else if ( is_numeric($x) && is_numeric($y) && is_numeric($z) && file_exists( $filename ) ){ $content = file_get_contents( $filename ); // otherwise, if one of the values was not sent, or the tile does // not exist we send a NO_DATA image } else { $content = file_get_contents( NO_DATA ); }

Now that we know which image we are sending back, we are ready to do so. The reason we didn't just send it previously is that we first need to tell the browser that we are sending an image. We tell it this by sending it some header information:

header("Content-type: image/gif");

Now we can pass it the image path with a simple echo of $content

echo $content; ?>

 

7.7.1. Conclusion

That's all there is to it! Save it as index.php and upload it to your image folder.

There are two things I really like about this script that make it a lifesaver. The first is the NO_DATA condition. For the subway map, I made only tiles that covered the greater NYC area. Outside that, there are no custom tiles. Without the NO_DATA image, in these places where I don't have tiles to match Google's, we would normally see broken image links or the grey background of the map window. But thanks to the NO_DATA image, I can create one blue tile that matches my border and extends infinitely anywhere I don't have custom tiles for a location. Figures 7-18, 7-19, and 7-20 show what my zoom-in, zoom-out, and no-data files look like.

Figure 7-18. Zoom-in image

Figure 7-19. Zoom-out image

Figure 7-20. No-data image

Alternatively for those more advanced, you could extend this script to relay the x, y, and zoom values back to Google; then you could request their tiles for places where you do not have any.

The other thing I like about this script is that you can quickly adjust which zoom levels are available. You just set min and max thresholds in those if/else statements and you get alternative tiles sent to the map for anything outside your zoom range. In creating the NYC Subway Map, I tweaked the map art at each level. This is a bit time consuming, but gives me better-looking maps. So I incrementally add new zoom levels as I complete the art. Every time I add a new one all I have to do is go into this script and change the min or max value to make it available.

7.7.2. See Also

Will James

Категории