[mapguide-users] EXAMPLE: Serving MapTiles directly

Pietro Ianniello pietro.ianniello at gmail.com
Wed Feb 24 11:00:01 EST 2010


Dear list,
here is a simple ASP.NET ashx to server MapGuide Tiles Directly.

It' different from the one found on "
http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp"
because it doesn't require any web server configuration.
The only point is to use my ashx as a proxy to MapGuide.
Testing would be nice. If it's all OK, and someone wants to post it on
wiki... or translate in PHP...
BUT don't test it with a non server OS. For example in WindowsXP I get lot
of empty tiles. This is not the case with Windows Server 2003.
REMINDER FOR ME: don't loose time! Test on real server OSs!
NOTE: *call somewhere "
MapGuideApi.MgInitializeWebTier(MgMapHelper.WebconfigIniPhysicalPath);"
before using this handler!*
           [*App_Start is a good choise*]


Code:

<%@ WebHandler Language="C#" Class="GetMGTiles" %>
//#define LOG_WITH_LOG4NET
using System;
using System.Web;

using System.Text;
using System.Net;
using System.Collections.Specialized;
using MapGuideHelper;
using OSGeo.MapGuide;
using System.IO;

//This is different from [
http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp ]
//where the web server must be configured.
//Here you use GetMGTiles.ashx as OpenLayers mapguide tiled layer address
[not HttpTile!].
//NOTE: if you try this with a non server OS [such as WindowsXP] you will
certainly get missed tiles
public class GetMGTiles : IHttpHandler
{
    #region Compile Time Configuration

    private readonly int BUFF_SIZE = 20480;//4096

    #endregion

    #region Properties to be configured [if needed] on application start

    private static string _tileDirectory =
"C:/OSGeo/MapGuide/Server/Repositories/TileCache/";
    public static string TileDirectory
    {
        get { return _tileDirectory; }
        set { _tileDirectory = value; }
    }

    private static string _imageFileFormat = ".png"; //depends on
"serverconfig.ini"
    private static string _contentType = "image/png"; //depends on
"serverconfig.ini"
    public static string ImageFileFormat
    {
        get { return _imageFileFormat; }
        set
        {
            _contentType = String.Concat("image/", value);
            _imageFileFormat = String.Concat(".", value);
        }
    }

    private static double _tileRowsPerFolder = 30.0; //depends on
"serverconfig.ini"
    private static int _tileRowsPerFolderInt = 30; //depends on
"serverconfig.ini"
    public static int TileRowsPerFolder
    {
        get { return _tileRowsPerFolderInt; }
        set { _tileRowsPerFolder = _tileRowsPerFolderInt = value; }
    }
    private static double _tileColumnsPerFolder = 30.0; //depends on
"serverconfig.ini"
    private static int _tileColumnsPerFolderInt = 30; //depends on
"serverconfig.ini"
    public static int TileColumnsPerFolder
    {
        get { return _tileColumnsPerFolderInt; }
        set { _tileColumnsPerFolder = _tileColumnsPerFolderInt = value; }
    }

    #endregion

    #region input request params

    int _tileRow, _tileCol, _scaleIndex;
    string _baseLayerGroupname;
    string _mapDefinition;

    #endregion

    protected void ServeMgTile(HttpResponse response)
    {
        MgUserInformation userInfo = null;
        MgSiteConnection siteConnection = null;
        MgTileService tileService = null;
        MgResourceIdentifier resIdent = null;
        MgByteReader mgReader = null;
        try
        {

///////MapGuideApi.MgInitializeWebTier(MgMapHelper.WebconfigIniPhysicalPath);
            userInfo = new MgUserInformation("Anonymous", "");
            siteConnection = new MgSiteConnection();
            siteConnection.Open(userInfo);

            tileService =
siteConnection.CreateService(MgServiceType.TileService) as MgTileService;
            resIdent = new MgResourceIdentifier(_mapDefinition);
            mgReader = tileService.GetTile(resIdent, _baseLayerGroupname,
_tileCol, _tileRow, _scaleIndex);

            //--------------------------------------------------------------
            //Debugging helper:
            //  context.Response.Cache.SetNoStore();
            //  context.Response.AppendHeader("Pragma", "no-cache");
            //--------------------------------------------------------------

            int readBytes;
            byte[] buffer = new byte[BUFF_SIZE];
            while ((readBytes = mgReader.Read(buffer, BUFF_SIZE)) > 0)
            {
                response.OutputStream.Write(buffer, 0, readBytes);
            }
        }
        finally
        {
            if (null != mgReader) mgReader.Dispose();
            if (null != resIdent) resIdent.Dispose();
            if (null != tileService) tileService.Dispose();
            if (null != siteConnection) siteConnection.Dispose();
            if (null != userInfo) userInfo.Dispose();
        }
    }

    protected void StreamFile(string filePath, HttpResponse response)
    {
        //response.AddHeader("content-disposition",
String.Concat("attachment; filename=",Path.GetFileName(filePath)));
        using (FileStream fileStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            long fileSize = fileStream.Length;
#if false
            //all at once:
            byte[] buffer = new byte[(int)fileSize];
            fileStream.Read(buffer, 0, (int)fileStream.Length);
            fileStream.Close();
            response.BinaryWrite(buffer);
#else
            int readBytes;
            byte[] buffer = new byte[BUFF_SIZE];
            while ((readBytes = fileStream.Read(buffer, 0, BUFF_SIZE)) > 0)
            {
                response.OutputStream.Write(buffer, 0, readBytes);
            }
            fileStream.Close();
#endif
        }
    }

    public void ProcessRequest(HttpContext context)
    {
        try
        {

//---------------------------------------------------------------------------------------------------------------------
            //Startup

//---------------------------------------------------------------------------------------------------------------------
            //It's always GET, but this costs nothing:
            NameValueCollection nvInput = context.Request.HttpMethod ==
"POST" ?
                                                      context.Request.Form :
nvInput = context.Request.QueryString;
#if false
            //string requestedUrl = context.Request.Path.ToLower();
            if (nvInput == null) //it's never null
            {
                context.Response.Clear();
                context.Response.ContentType = "text/plain";
                context.Response.Write("Hello World");
                return;
            }
            if (nvInput.Count == 0)//don't mind if is 0 => we will just get
null strings when trying to read them
            {
                context.Response.Clear();
                context.Response.ContentType = "text/plain";
                context.Response.Write("Hello World");
                return;
            }
#endif

//---------------------------------------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------------------------------------
            //Input params reading

//---------------------------------------------------------------------------------------------------------------------
            _mapDefinition =
HttpUtility.UrlDecode(nvInput["mapdefinition"]);
            if (String.IsNullOrEmpty(_mapDefinition))
            {
                context.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
                return;
            }
            if (nvInput["operation"].ToUpper() != "GETTILEIMAGE")
            {
                context.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
                return;
            }
            //---------------------------------
            //we don't read nvInput["version"]
            //---------------------------------
            _baseLayerGroupname =
HttpUtility.UrlDecode(nvInput["basemaplayergroupname"]);
            if (String.IsNullOrEmpty(_baseLayerGroupname))
            {
                context.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
                return;
            }
            if (!Int32.TryParse(nvInput["tilecol"], out _tileCol))
            {
                context.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
                return;
            }
            if (!Int32.TryParse(nvInput["tilerow"], out _tileRow))
            {
                context.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
                return;
            }
            if (!Int32.TryParse(nvInput["scaleindex"], out _scaleIndex))
            {
                context.Response.StatusCode =
(int)HttpStatusCode.BadRequest;
                return;
            }


//---------------------------------------------------------------------------------------------------------------------
            //Tile file name

//---------------------------------------------------------------------------------------------------------------------
            //The tiles base dir, if for example we want
            //
"Library://Samples/Sheboygan/MapsTiled/Sheboygan.MapDefinition"
            // , is
            //          "Samples_Sheboygan_MapsTiled_Sheboygan/"
            string mapDefDir = _mapDefinition.Replace("Library://",
"").Replace("/", "_").Replace(".MapDefinition", "/");


            //---------------------------------
            //Taken from Openlayers:
            int tileRowGroup = (int)(Math.Floor(Math.Abs(_tileRow /
_tileRowsPerFolder))) * TileRowsPerFolder;
            int tileColGroup = (int)(Math.Floor(Math.Abs(_tileCol /
_tileColumnsPerFolder))) * TileColumnsPerFolder;
            int imgPartA = _tileRow % TileRowsPerFolder;
            int imgPartB = _tileCol % TileColumnsPerFolder;
            //---------------------------------
            StringBuilder sb = new StringBuilder(256);
            sb.Append(_tileDirectory);
            sb.Append(mapDefDir);
            sb.Append("S");
            sb.Append(this._scaleIndex);
            sb.Append("/");
            sb.Append(this._baseLayerGroupname);
            sb.Append("/R");
            sb.Append(tileRowGroup);
            sb.Append("/C");
            sb.Append(tileColGroup);
            sb.Append("/");
            sb.Append(imgPartA);
            sb.Append("_");
            sb.Append(imgPartB);
            sb.Append(_imageFileFormat);

//---------------------------------------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------------------------------------
            //We give file if possible, let mapGuide build the tile
otherwise

//---------------------------------------------------------------------------------------------------------------------
            context.Response.ContentType = _contentType;
            string file = sb.ToString();
            if (System.IO.File.Exists(file))
            {
                try
                {
#if false
                    context.Response.WriteFile(file);
#else
                    StreamFile(file, context.Response);
#endif
                }
                catch (System.IO.IOException ex)
                {
                    //FileExists is not enough:
                    //MapGuide could possibly be writing the file while we
where trying to get access!!!!
#if LOG_WITH_LOG4NET
                    log4net.ILog logger =
log4net.LogManager.GetLogger("exceptions");
                    logger.Error("GetMGTiles.ashx::ProcessRequest - Tried
StreamFile [System.IO.IOException] - serve file via MapGuide", ex);
#endif
                    ServeMgTile(context.Response);
                }

                context.Response.Flush();
                context.Response.Close();
            }
            else
            {
                context.Response.ContentType = _contentType;
                ServeMgTile(context.Response);
                context.Response.Flush();
                context.Response.Close();
            }
        }
        catch (ArgumentNullException ex)
        {
            log4net.ILog logger =
log4net.LogManager.GetLogger("exceptions");
            logger.Error("GetMGTiles.ashx::ProcessRequest
[ArgumentNullException]", ex);

            context.Response.StatusCode = (int)HttpStatusCode.NotFound;
            System.Diagnostics.Trace.WriteLine(ex.ToString());
        }
        catch (System.Security.SecurityException ex)
        {
#if LOG_WITH_LOG4NET
            log4net.ILog logger =
log4net.LogManager.GetLogger("exceptions");
            logger.Error("GetMGTiles.ashx::ProcessRequest
[SecurityException]", ex);
#endif
            context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            System.Diagnostics.Trace.WriteLine(ex.ToString());
        }
        catch (UriFormatException ex)
        {
#if LOG_WITH_LOG4NET
            log4net.ILog logger =
log4net.LogManager.GetLogger("exceptions");
            logger.Error("GetMGTiles.ashx::ProcessRequest
[UriFormatException]", ex);
#endif
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            System.Diagnostics.Trace.WriteLine(ex.ToString());
        }
        catch (TimeoutException ex)
        {
#if LOG_WITH_LOG4NET
            log4net.ILog logger =
log4net.LogManager.GetLogger("exceptions");
            logger.Error("GetMGTiles.ashx::ProcessRequest
[TimeoutException]", ex);
#endif
            context.Response.StatusCode =
(int)HttpStatusCode.RequestTimeout;
            System.Diagnostics.Trace.WriteLine(ex.ToString());
        }
        catch (Exception ex)
        {
#if LOG_WITH_LOG4NET
            log4net.ILog logger =
log4net.LogManager.GetLogger("exceptions");
            logger.Error("GetMGTiles.ashx::ProcessRequest [Exception]", ex);
#endif
            context.Response.StatusCode =
(int)HttpStatusCode.InternalServerError;
            System.Diagnostics.Trace.WriteLine(ex.ToString());
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

}

------------
Test page [slightly modified from the wiki]
Assumes GetMGTiles.ashx is accessible in http://localhost/ash/
GetMGTiles.ashx
-----------

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>Mapguide Http Tile Cache Demo</title>

    <script type="text/javascript" src="
http://www.openlayers.org/api/OpenLayers.js"></script>

</head>
<body onload="initTiled()">

    <script type="text/javascript">
        var windowSize = 550; //200;//
        var extent;

        var addrs = "http://localhost/";
        //var addrs = "http://192.168.1.247/";

        var mg_url = addrs +
"mapguide/mapagent/mapagent.fcgi?USERNAME=Anonymous&";
        var http_url = addrs + "sheboyganTiles";
        var my_proxy_url = addrs + "ash/GetMGTiles.ashx";
        var mg_map, http_or_tileproxy_map;
        var eventsLog = "";
        var c = 0;
        //var bUseHttpUrl = false; //http_url || my_proxy_url

        function initTiled() {

            document.getElementById('map_mg').style.width = windowSize;
            document.getElementById('map_http').style.width = windowSize;

            document.getElementById('map_mg').style.height = windowSize;
            document.getElementById('map_http').style.height = windowSize;

            OpenLayers.DOTS_PER_INCH = 96;

            //extent = new
OpenLayers.Bounds(-87.865114442365922,43.665065564837931,-87.595394059497067,43.823852564430069);

            var extent = new OpenLayers.Bounds(-87.764986990963,
43.691398128788, -87.6955215109, 43.79752000048);
            var tempScales = [1000, 1930.6977300000001, 3727.5937199999998,
7196.8567300000004, 13894.95494, 26826.95795, 51794.746789999997, 100000];

            var mapOptions = {
                maxExtent: extent,
                restrictedExtent: extent,
                scales: tempScales
            };

            var params = {
                mapdefinition:
'Library://Samples/Sheboygan/MapsTiled/Sheboygan.MapDefinition',
                basemaplayergroupname: "Base Layer Group"
            }

            mg_map = new OpenLayers.Map('map_mg', mapOptions);
            http_or_tileproxy_map = new OpenLayers.Map('map_http',
mapOptions);

            var mgOptions = {
                singleTile: false,
                useHttpTile: false,
                buffer: 1
            }

            /*var httpOptions = {
                singleTile: false,
                useHttpTile: true,
                buffer: 1
            }*/

            var myProxyOptions = {
                singleTile: false,
                useHttpTile: false,
                buffer: 1
            }

            mg_layer = new OpenLayers.Layer.MapGuide("MapGuide OS tiled
layer", mg_url, params, mgOptions);
            //if (bUseHttpUrl) http_layer = new
OpenLayers.Layer.MapGuide("MapGuide OS tiled layer", http_url, params,
httpOptions);
            //else
            http_layer = new OpenLayers.Layer.MapGuide("MG OS proxy - tiled
layer", my_proxy_url, params, myProxyOptions);

            mg_map.addLayer(mg_layer);

            mg_map.addControl(new OpenLayers.Control.MousePosition());
            http_or_tileproxy_map.addControl(new
OpenLayers.Control.MousePosition());

            http_or_tileproxy_map.addLayer(http_layer);

            mg_map.setCenter(extent.getCenterLonLat());
            http_or_tileproxy_map.setCenter(extent.getCenterLonLat());

            mg_map.events.register("zoomend", mg_map, setZoomHttp);
            //http_or_tileproxy_map.events.register("zoomend",
http_or_tileproxy_map, setZoomMg);

            mg_map.events.register("moveend", mg_map, moveHttp);
            //http_or_tileproxy_map.events.register("moveend",
http_or_tileproxy_map, moveMg);

        }

        function moveHttp() {
            if (mg_map.getCenter() != http_or_tileproxy_map.getCenter())
                http_or_tileproxy_map.setCenter(mg_map.getCenter());
        }

        function moveMg() {
            if (mg_map.getCenter() != http_or_tileproxy_map.getCenter())
                mg_map.setCenter(http_or_tileproxy_map.getCenter());
        }



        function setZoomMg() {
            if (mg_map.getZoom() != http_or_tileproxy_map.getZoom())
                mg_map.zoomTo(http_or_tileproxy_map.getZoom());
        }
        function setZoomHttp() {
            if (mg_map.getZoom() != http_or_tileproxy_map.getZoom())
                http_or_tileproxy_map.zoomTo(mg_map.getZoom());

        }

    </script>

    <div style="float: left">
        <h1>Served by Mapguide</h1>
        <div id="map_mg" style="border: medium inset Maroon; float: left;
background-color: #F5DEB3;">
        </div>
        <br />This cannot be proxied, it lacks cache headers
    </div>
    <div style="float: left">
        <h1>Served by Http</h1>
        <div id="map_http" style="border: medium inset Maroon; float: left;
background-color: #F5DEB3;">
        </div>
        <br />This is can be proxied by proxy servers
    </div>
</body>
</html>




------------
[OFFTOPIC] A suggestion about MapGuide Installer -

it would be nice that besides the installer, there was a zipped archive with
the installed files, to give the possibility of manually installing MapGuide
without running an installer. This could be clearly set to UNSUPPORTED, and
that no help would be given in the list to who desires to use the zipped
archive.

This is because, in my opinion, one cannot understand the system, if not by
using a manual install. It's the path I followed when I for the first time
used MapGuide.

To provide such a thing is very simple: install in C:\OsGeo, zip folder and
publish in "http://mapguide.osgeo.org/download". Only for windows, linux
always needs compilation because of different shared objects between
distributions.

At this point one can extract the zip wherever she wants, change ini files,
set virtual directories, enable PHP, change folder permissions, and the game
is done!
------------

Pietro Ianniello
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.osgeo.org/pipermail/mapguide-users/attachments/20100224/54e24aeb/attachment.html


More information about the mapguide-users mailing list