A Pirate Map with LibGD and Google Maps
Yesterday I thought about visualizing photo geodata. While tinkering with different ideas, I thought about making it look like a real paper photograph placed on a real map.
Of course it had to be automated somehow. So I spent a few hours with PHP's GD library functions. It's really cool what you can do with it.
Let's start with the result:
The image above was created by my MapDecorate class. Download, use and modify it as you like. It's licensed under the Creative Commons Attribution-Share Alike 3.0 license.
Here is how it works:
- fetch a map image from the Google Static Maps API for given coordinates
- convert the map colors to sepia
- place an X on the center
- put the compass on top
- add a paper texture
- do an address lookup on the coordinates and if found write it next to the X
- write a custom text at the bottom
- fetch an image from a given URL
- optionally grayscale it
- apply some grain texture to the photo
- rotate the photo and place it on the map
- apply a mask to cut out the map border
- cache the map image for later reuse
The techniques I used are quite similar to the ones in my MonsterID script. It is basically overlaying different PNG images with alpha transparency. However there are a few things in it that took me a while to get right.
One thing was to rotate the photo but retain a transparent background. The trick is to pass a -1
as background color to imagerotate:
$img = imagerotate($img, 5, -1);
The other question was how to apply the alpha mask for cutting the border. There is no builtin function libGD so I had to do it on my own. Here is what I came up with:
function imagealphamask(&$img, &$mask){ $width = imagesx($img); $height = imagesy($img); imagealphablending($img, false); for ($x = 0; $x < $width; $x++) { for ($y = 0; $y < $height; $y++) { // current color $rgb = imagecolorat($img, $x, $y); $r = ($rgb >> 16) & 0xFF; $g = ($rgb >> 8) & 0xFF; $b = $rgb & 0xFF; // red channel of mask $rgb = imagecolorat($mask, $x, $y); $alpha = ($rgb >> 16) & 0xFF; $alpha = floor($alpha/2); $color = imagecolorallocatealpha($img,$r,$g,$b,$alpha); imagesetpixel($img,$x,$y,$color); } } imageSaveAlpha($img, true); }
Be sure both passed images have the same dimensions! The function surely can be improved. It currently only uses the red part of the given mask image 1) and it's really slow.
Here's another example with a color photo and the reverse geocoding in action: