Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Example of creating an interactive map in ASP.NET using MapAround and Leaflet.

0.00/5 (No votes)
18 Sep 2013 1  
This article will present an example of a WEB application with interactive maps, and includes development of WMS server and features for map display on the client side.

MapAround WMS Sample

Introduction

There are many projects where interactive maps are needed to display information. Integration with maps, for example, Google maps, can be used in some cases. However, when it comes to displaying a large amount of information from various map layers – which in turn can physically be based on various data sources – the best solution is to use your own map server.

This article will present an example of a WEB application with interactive maps, and includes development of WMS server and features for map display on the client side. MapAround will be used as a GIS engine to implement WMS. This OpenSource library is written in C#. It is provided under GPLv3 license (can also be obtained through a commercial license) and incorporates all the features necessary to develop own GIS applications. Map display will be based on JavaScript library Leaflet. This library is also provided by OpenSource under its own license similar to GPL and includes very extensive features – supports a set of data access standards, such as WMS, WMTS, and others. This library also has an expanded architecture and works efficiently across all modern browsers.

Using the code

MapAround-based WMS

What is WMS? WMS (Web Map Server) is a communication protocol for data transmission between the server providing map data and a client using these data for display (and for other operations, see the protocol specification). The protocol itself has a very simple set of commands. They are only three types of commands - receiving information about the server, receiving the image of a given map area, and receiving information about objects on the map. We need not go into much detail since MapAround already has this protocol implemented. It is only left to solve how to "raise" our WMS. Using APS.NET, or to be exact, ASP WebHandler, would be the easiest solution.

Loading map status

The first step in processing a WMS request is to initialize the map status, i.e., the list of layers, map coloring styles, and conformity of the layers with data sources. Of course, all these operations can be encoded directly into the body of the handler. However, MapAround has a special tool called MapWorkspace, which is used to save and load map status. It would be unreasonable not to use this tool. Workspace in MapAround is an ordinary XML document that contains descriptions of map layers. The design style of each layer and the data source assigned to the layer are described. By the way, MapAround has no rigid binding to map data sources. Moreover, layers can be loaded both from file formats and from DBMSs. Unfortunately, the free version of the library has no supporting tools to create and edit Workspace. Despite this, however, creating this document will not be a difficult process. An example of Workspace can be found in a demo project

Thus, reading the Workspace will be the first step in processing a request.

public void ProcessRequest(HttpContext context)
  {
     httpContext = context;
     //Receiving the path to the Workspace.
     var WorkspaceFilePath = System.IO.Path.Combine(httpContext.Server.MapPath("~"), "workspace.xml");
     //Receiving the path to the directory with map data.
     MapFolder = System.IO.Path.Combine(httpContext.Server.MapPath("~"), "..", "..", "data");
            ....
     mapWs = new MapWorkspace();
     mapWs.XmlRepresentation = File.ReadAllText(WorkspaceFilePath);      
     //Reading the Workspace and additional configuration of the display style.
     mapWs.Map.RenderingSettings.AntiAliasGeometry = true;
     mapWs.Map.RenderingSettings.AntiAliasText = true;
          

Binding layers to data sources

Inside the Workspace, each layer must be assigned to the appropriate data source with parameters for accessing it. However, the MapAround itself knows nothing about them, so one needs to make the final binding manually.

 
foreach (LayerBase l in mapWs.Map.Layers)
   {
     //In this example, we use only vector layers. But MapAround also supports raster layers.
     FeatureLayer fl = l as FeatureLayer;
     if (fl != null)
        {
          fl.DataSourceNeeded += LayerDataSourceNeeded; 
          //Subscribing to data source request and data source release.
          fl.DataSourceReadyToRelease += LayerDataSourceReadyToRelease;                       
          //If the layer is not automatically loadable (this is indicated in the Workspace), then data is loaded forcibly.
          if (!fl.AreFeaturesAutoLoadable)
              fl.LoadFeatures();
         }
                  
               
   }

Data source request and release methods will be called for the layer whenever the need to render the layer arises.

private void LayerDataSourceNeeded(object sender, MapAround.Mapping.FeatureDataSourceEventArgs e)
 {
    FeatureLayer l = sender as FeatureLayer;
    string featuresFilePath = string.Empty;
    switch (l.DataProviderRegName)
       {
              
         //Determining which data source is required for the layer
         case "MapAround.DataProviders.ShapeFileSpatialDataProvider":
                    l.AreFeaturesAutoLoadable = true;
                    ShapeFileSpatialDataProvider shapeP = new ShapeFileSpatialDataProvider();
                    shapeP.AttributesEncoding = Encoding.UTF8;                 
                    //Initializing all the settings necessary for the current data source.
                    shapeP.FileName = GetFeaturesFilePath(l.DataProviderParameters["file_name"]);
                    shapeP.ProcessAttributes = true;                    
                    e.Provider = shapeP;
                    break;
                default:
                    throw new Exception("Map data provider not found: \"" + l.DataProviderRegName + "\"");
     }
 }
		
private void LayerDataSourceReadyToRelease(object sender, MapAround.Mapping.FeatureDataSourceEventArgs e)
   {
     FeatureLayer l = sender as FeatureLayer;

     switch (l.DataProviderRegName)
     {
               
        //There is no need for special action to Release the current data source.
        case "MapAround.DataProviders.ShapeFileSpatialDataProvider":
                   break;
               
                   default:
                    throw new Exception("Map data provider not found: \"" + l.DataProviderRegName + "\"");
     }
  }

Processing WMS request

The next step is to directly process WMS request. For this purpose, MapAround has the WMSServer class, which implements all the necessary actions.

            //Creating an object with WMS description (will be issued through a GetCapabilities request).
            IMapServer server = new WMSServer(new WmsServiceDescription("MapAround Demo", ""));
	    //Image quality
            server.ImageQuality = 95;        
	    //Map object that was set up earlier.
            server.Map = mapWs.Map;
	    //Size of increased box. Needed to prevent loss of objects at the joint between "tiles".
            server.GutterSize = 180;
	    //Event occurring when rendering the next image.
            server.BeforeRenderNewImage += server_BeforeRenderNewImage;

WMSServer can also be configured to use data caching mechanism. When rendering large maps, this can increase performance considerably.

 FileTileCacheAccessor tileCacheAccessor =
                    new FileTileCacheAccessor(Path.Combine(HttpContext.Current.Server.MapPath("~"), "map\\cache"));
  //Prefix to prevent the cache of different maps from overlapping.
  tileCacheAccessor.Prefix = TileCacheKeyPrefix;
  server.TileCacheAccessor = tileCacheAccessor;
 

The BeforeRenderNewImage event is called each time whenever WMSServer starts rendering a map on request. When it is processed, we should recalculate the scale factor of the map (the number of pixels per unit of the map) and initiate loading of layer data.

private void server_BeforeRenderNewImage(object sender, RenderNewImageEventArgs e)
    {
        BoundingRectangle bbox = QueryStringDataExtractor.GetBBox(httpContext.Request.QueryString["BBOX"]);
        Size displaySize = QueryStringDataExtractor.GetDisplaySize(httpContext.Request.QueryString["WIDTH"], httpContext.Request.QueryString["HEIGHT"]);
         //Scale calculation.
          double mapScale = displaySize.Width / bbox.Width;
         
	 //Loading data for layers.
         mapWs.Map.LoadFeatures(mapScale, mapWs.Map.MapViewBoxFromPresentationViewBox(e.BboxWithGutters));
         mapWs.Map.LoadRasters(mapScale, mapWs.Map.MapViewBoxFromPresentationViewBox(e.BboxWithGutters));
    }
 

The scale factor is used to determine the list of layers that need to be rendered for the current map zoom level. This parameter is specified for each layer separately.

Next, we need to record the output of WMSServer in the handler stream.

   string mime = string.Empty;
   context.Response.Clear();
   //Passing all parameters to WMSServer. Setting the handler stream as an output stream.
   server.GetResponse(context.Request.Params, context.Response.OutputStream, out mime);
   //Setting the mime type of response (Can be both an image and a text)
   context.Response.ContentType = mime;
   //Настройки для кэша
   context.Response.Cache.SetCacheability(HttpCacheability.Public);
   context.Response.Cache.SetMaxAge(new TimeSpan(24, 0, 0));                             
   context.Response.End();
 

Leaflet-based client

After the server side – for formation of maps – has been completed, we must implement a foreground for map display on the user side. JavaScript library Leaflet, already possessing all the necessary features, will be used for this purpose. Thus, all actions aimed at creating a page for display come down to only connecting and setting up the library.

     
<div id="map2" class="leaflet-container leaflet-fade-anim" tabindex="0">
     </div>
     <script type="text/javascript">
                 //Describing our layer taken from WMS
        var nexrad = new L.TileLayer.WMS("/map/WmsHandler.ashx", {
            layers: "layer1,layer2,layer3", //List of layers requested from WMS
            format: 'image/png',           
            transparent: true
        });
        
		//Connecting the map
        var map2 = new L.Map('map2', { 
            center: new L.LatLng(56.30,44), //Specifying the point where the center of the map will be located when opened.
            zoom: 5,  //Initial zoom level.
            layers: [ nexrad],//List of layers. Here we specify our WMS layer previously declared.
            maxZoom: 28, 
            minZoom: 0,
            zoomControl: true
        });
     </script>
   

As you can see from the code above, is a very simple task to connect the library.

That's it! Our application is ready!

History 

  • 18th September, 2013 - initial article version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here