Deleting Blank Tiles

 

Creating raster tilesets almost invariably leads to the creation of some blank tiles – covering those areas of space where no features were present in the underlying dataset. Depending on the image format you use for your tiles, and the method you used to create them, those “blank” tiles may be pure white, or some other solid colour, or they may have an alpha channel set to be fully transparent.

Here’s an example of a directory of tiles I just created. In this particular z/x directory, more than half the tiles are blank. Windows explorer shows them as black but that’s because it doesn’t process the alpha channel correctly. They are actually all 256px x 256px PNG images, filled with ARGB (0, 0, 0, 0):

image

What to do with these tiles? Well, there’s two schools of thought:

  • The first is that they should be retained. They are, after all, valid image files that can be retrieved and overlaid on the map. Although they aren’t visually perceptible, the very presence of the file demonstrates that the dataset was tested at this location, and confirms that no features exist there. This provides important metadata about the dataset in itself, and confirms the tile generation process was complete. The blank images themselves are generally small, and so storage is not generally an issue.
  • The opposing school of thought is that they should be deleted. It makes no sense to keep multiple copies of exactly the same, blank tile. If a request is received for a tile that is not present in the dataset, the assumption can be made that it contains no data, and a single, generic blank tile can be returned in all such instances – there is no benefit of returning the specific blank tile associated with that tile request. This not only reduces disk space on the tile server itself, but the client needs only cache a single blank tile that can be re-used in all cases where no data is present.

I can see arguments in favour of both sides. But, for my current project, disk and cache space is at a premium, so I decided I wanted to delete any blank tiles from my dataset. To determine which files were blank, I initially thought of testing the filesize of the image. However, even though I knew that every tile was of a fixed dimension (256px x 256px), an empty tile can still vary in filesize according to the compression algorithm used. Then I thought I could loop through each pixel in the image and use GetPixel() to retrieve the data to see whether the entire image was the same colour, but it turns out that GetPixel() is slooooowwwww….

The best solution I’ve found is to use an unsafe method, BitMap.LockBits to provide direct access to the pixel byte data of the image, and then read and compare the byte values directly. In my case, my image tiles are 32bit PNG files, which use 4 bytes per pixel (BGRA), and my “blank” tiles are completely transparent (Alpha channel = 0). Therefore, in my case I used the following function, which returns true if all the pixels in the image are completely transparent, or false otherwise:

public static Boolean IsEmpty(string imageFileName)
{
  using (Bitmap b = ReadFileAsImage(imageFileName))
  {
    System.Drawing.Imaging.BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat);
    unsafe
    {
      int PixelSize = 4; // Assume 4Bytes per pixel ARGB
      for (int y = 0; y < b.Height; y++)
      {
        byte* p = (byte*)bmData.Scan0 + (y * bmData.Stride);
        for (int x = 0; x < b.Width; x++)         {           byte blue = p[x * PixelSize]; // Blue value. Just in case needed later           byte green = p[x * PixelSize + 1]; // Green. Ditto           byte red = p[x * PixelSize + 2]; // Red. Ditto           byte alpha = p[x * PixelSize + 3];           if (alpha > 0) return false;
        }
      }
    }
    b.UnlockBits(bmData);
  }

  return true;
}

 

It needs to be compiled with the /unsafe option (well, it did say in the title that this post was dangerous!). Then, I just walked through the directory structure of my tile images, passing each file into this function and deleting those where IsEmpty() returned true.

 

High resolution in Static Google Maps API

The Static Maps API is a great service for when you need to quickly generate a static, lightweight map. Following the updates to the Maps API/Earth API Terms of Service earlier this year, mobile application developers are now free to use the Static Maps API in native mobile apps, providing the map links to the Google Maps app or website for the same location. However the high pixel density of some mobile screens can make the labels and icons on such a static map unreadably small.

For this reason, we’re introducing a new feature of the Static Maps API that smoothly doubles the size of the whole image including labels, icons, and overlays, to suit high resolution screens. To apply this upscaling to an existing Static Maps API image, simply append &scale=2 to the URL.

The image returned will contain the same level of detail, but be twice as wide and tall without the stepping or blurring that results from upscaling an existing image:

scale=1
(default)
scale=2

Note that when using the scale parameter the pixel size of the returned image is the product of the size and scale values given in the URL. The size parameter therefore now specifies the size of the required image in Density-independent pixels or Points, as they are known in the Android SDK and iOS SDK respectively.

Web based apps can also benefit from higher resolution Static Maps by adding a scale=2 image to the page with a CSS width and height that matches the size parameter. By default, the browser on many mobile devices upscales images in web pages by a device specific factor based on the pixel density of the screen. However these devices will preserve additional detail in the image if the image pixel : CSS pixel ratio matches or exceeds the scaling factor, which can be found by querying window.devicePixelRatio in JavaScript.

For example, below are the above two maps side-by-side with the same CSS size. On a desktop screen with a window.devicePixelRatio of 1.0 these will look almost indistinguishable. However open this blog post on a device with a high resolution screen, such as a Samsung Nexus S or Apple iPhone 4, and the image on the right will look significantly sharper.

scale=1 scale=2

As an added benefit, Maps API Premier developers can also request maps with a scale factor of 4. Although this exceeds the maximum window.devicePixelRatio of current mobile devices it does facilitate generating maps for printing at high quality:

scale=4

Note however that the largest image pixel size that the Static Maps API can generate for Premier customers, regardless of the value of the scale parameter, is 2048×2048. Consequently at scale=4 the largest available value for the size parameter is 512×512. Note also that any use of the Static Maps API for generating maps to print must comply with the Permission Guidelines for Google Maps and Earth, which apply equally to Maps API Premier customers.

For more information on the scale parameter, and the other great Static Maps features, check out the Static Maps API documentation. We hope this feature helps your apps enjoy the full benefit of the high resolution screens that are increasingly popular, and makes them just that little bit easier on the eye!