PostGIS Spatial Database Engine UMN Mapserver Boston Geographic Information Systems    Checkout our PostGIS in Action book.  First chapter is a free download   PostGreSQL Object Relational Database Management System
GIS Books  Home   Consulting Services  About Boston GIS   Boston GIS Blog  Postgres OnLine Journal
PostGIS in Action is out in hard-copy,
download the first chapter
and SQL Primer for free. Tips and Tricks for PostGIS
  GIS Article comments Comments Rss
Part 3: Using your own custom built OSM tiles in OpenLayers

In this third Part of our OpenStreetMap series we will demonstrate how to use the osm tiles we built in Part 2: Building Tiles with PostGIS OpenStreetMap data.

Creating your own Tile Layer Class

  1. This first part is optional: Note that when you built the tiles, as you got deeper and deeper levels, therw were a lot more tiles and even worse, a lot of those damn tiles were empty. I personally hate carrying around thousands of files of nothingness. It takes a long time to upload nothing. So what I do first is delete those nothing tiles which there seem to be more of than real tiles before I upload them to my server. If you are on Windows, the easiest way to do that is with a command line VBscript. Here is a little script to get rid of nothing. Before you do this, set aside one blank tile to use when you get a 404 request. This will only work on Windows. I haven't figured out the complimentary command for Linux yet. Simply save the contents of this code to a file called delete_osm_blank_tiles.vbs and edit the last line to point to your osm maptiles cache. Then run the script at a command line with cscript delete_osm_blank_tiles.vbs.

    Sub RecursiveDeleteBlankOSMImages(ByVal strDirectory)
      Dim objFolder, objSubFolder, objFile
      Dim strExt
      Dim strExtensionsToDelete
      Dim lngSize
      lngSize = 104 ' The threshold in bytes (this is 104bytes - anything less seems to be a blank image)
      strExtensionsToDelete = "png"
      Set objFolder = objFSO.GetFolder(strDirectory)
      For Each objFile in objFolder.Files   
        For Each strExt in Split(Ucase(strExtensionsToDelete),",")
          If Right(Ucase(objFile.Path), Len(strExt)+1) = "." & strExt AND objFile.Size < lngSize Then
              wscript.echo "Deleting:" & objFile.Path
          End If
      For Each objSubFolder in objFolder.SubFolders
        RecursiveDeleteBlankOSMImages objSubFolder.Path
    End Sub
    Dim objFSO
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    RecursiveDeleteBlankOSMImages "C:\osm_matiles\" 
  2. Copy the tiles to your web server or one of those cloud storage web accessible file distributions like S3.
  3. Next create a new layer class and save it in it's own JS file. Replace http://localhost/osm_matiles, http://localhost/images/404.png with your own server details. NOTE: We have an array of 3 tile urls. For our case, this is pretty useless, but for load balancing purposes and to also get rid of javascripts limitations of calling a server x amount of times in any period, you may want to setup more than one tile server or mask your single tile server with multiple dns names (to fool javscript). So my OpenStreetMapLocal.js file looks something like below and I place it in the same folder as my mapping application:
    OpenLayers.Util.OSMLocal = {};
     * Constant: MISSING_TILE_URL
     * {String} URL of image to display for missing tiles
    OpenLayers.Util.OSMLocal.MISSING_TILE_URL = "http://localhost/images/404.png";
     * Function: onImageLoadError
    OpenLayers.Util.onImageLoadError = function() {
        this.src = OpenLayers.Util.OSMLocal.MISSING_TILE_URL;
     * Class: OpenLayers.Layer.OSM.LocalMassachusettsMapnik
     * Inherits from:
     *  - <OpenLayers.Layer.OSM>
    OpenLayers.Layer.OSM.LocalMassachusettsMapnik = OpenLayers.Class(OpenLayers.Layer.OSM, {
         * Constructor: OpenLayers.Layer.OSM.LocalMassachuettsMapnik
         * Parameters:
         * name - {String}
         * options - {Object} Hashtable of extra options to tag onto the layer
        initialize: function(name, options) {
            var url = [
            options = OpenLayers.Util.extend({
                numZoomLevels: 19,
                buffer: 0,
                transitionEffect: "resize"
            }, options);
            var newArguments = [name, url, options];
            OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
        CLASS_NAME: "OpenLayers.Layer.OSM.LocalMassachusettsMapnik"

Using your custom tile layer class

Now you link in this file as you would OpenStreetMap file and use this new class as you would any other OpenStreetMap OpenLayer layer class and you can even create ones with different themes if you want by creating a different set of tiles and theming them differently. That's it folks. Here is a complete version just containing Massachusetts and neighboring State tiles with a complimentary Town boundary layer dished out by our local MassGIS government agency.

<html><title>OpenStreetMap with Local Tile Cache</title>
<script src=""></script>
<script src="js/OpenStreetMapLocal.js"></script>
    var lat= 42.06651;  //POINT(-71.7169041387889 42.0665181226572)
    var lon=-71.71690;
    var zoom=5;
    var map; //complex object of type OpenLayers.Map
    var ls = new OpenLayers.Control.LayerSwitcher();
    var mgisurls = [""];
    var prjwgs84 = new OpenLayers.Projection("EPSG:4326");

    function init() {
        //var boundsms = new OpenLayers.Bounds(33869, 772032, 335919, 959719) //EPSG:26986
        var boundswm = new OpenLayers.Bounds(-8182919, 5035362.5,-7784059.5, 5304535) //EPSG:900913 Web Mercator (native projection of tiles)
        map = new OpenLayers.Map ("map", {
                controls:[ new OpenLayers.Control.Navigation({'mouseWheelOptions': {interval: 100}}),
                new OpenLayers.Control.PanZoomBar(),ls ,
                  new OpenLayers.Control.Attribution(), 
                  new OpenLayers.Control.MousePosition()
            maxExtent: boundswm,
            maxResolution: 700,
            numZoomLevels: 18,
            units: 'm',
            projection: new OpenLayers.Projection("EPSG:900913"),
            displayProjection: new OpenLayers.Projection("EPSG:4326")
        } );

        var lyrMapnik = new OpenLayers.Layer.OSM.LocalMassachusettsMapnik("OSM Mapnik Massachusetts", {'isBaseLayer': true});   
        var lyrTowns = new OpenLayers.Layer.WMS("Towns",  mgisurls, 
            { 'layers': "massgis:GISDATA.TOWNS_POLYM", 
                'styles': "",
                'transparent': "true", 'FORMAT': "image/png"}, 
                { 'isBaseLayer': false, 'attribution': '<br /><a href="">MASSGIS (MA ITD)</a>',
                'visibility': false, 'buffer': 1, 'singleTile':false, 'tileSize': new OpenLayers.Size(240,150) })
        map.addLayers([lyrMapnik, lyrTowns]);
        if( ! map.getCenter() ){
            var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
<div style="width:880; height:550" id="map"></div>
<script type="text/javascript" defer="true">

To see it in action go to

Post Comments About Part 3: Using your own custom built OSM tiles in OpenLayers

This Document is available under the GNU Free Documentation License 1.2 & for download at the BostonGIS site

Boston GIS      Copyright 2024      Paragon Corporation