Use Cases > PTV xMap > How to Use Server-Side Layers With Add-On AJAX Maps

How to use server-side layers with add-on AJAX Maps

The PTV AJAX Maps servlet enables you to include map widgets in your web applications and offer fine-grained control via a powerful JavaScript interface. How to use the functionalities is described in the PTV AJAX Maps tutorial which is available after deployment of the servlet. Otherwise, use the following AJAX Maps API documentation.

The AJAX Map component lets you use JavaScript on the client side (browser) to add information to the map. You can draw lines and shapes and add POIs to the map using JavaScript functions. But there is another way you can add layers of data to your map. This tutorial shows you how to tell the AJAX component to fetch layer data from a remote server via HTTP. The layers can then be generated on the server side the same as you would do it for a xMap request.

How it works

The PTV xServer add-on AJAX Maps uses a PTV xMap Server to render the maps. As you know from other PTV xMap Server code samples you can add custom layers to maps. Custom layers can be used to add custom text, vector shapes like lines and polygons, bitmaps, etc.. The PTV AJAX Maps JavaScript API lets you add you own layers to the PTV xMap Server request. This is done by providing an URL from which the servlet retrieves the XML description of the layer (or multiple layers) via HTTP. The retrieved layers are then added to the PTV xMap Server request, which is used to render the map for the PTV AJAX Maps JavaScript component.

The URL can either point to a static XML file, a CGI script or to a web application which generates custom layers on demand. Below, you will learn how to generate valid XML layer data, how to configure your servlet properly and what you have to do on the client side (JavaScript) to make it work.

Creating a layer provider

The key to providing the servlet with custom layers is to generate valid XML. Below, you learn how to easily define custom layers and generate XML for them.

C# sample

The simplest way to define custom layers and generate XML is to use the same mechanism you would use with SOAP. This means we use classes that were generated from the WSDL file to define layers and we use the XML serialisation support of the .NET framework to produce XML code.

If you start a new APS.NET website, the first step would be to add PTV xMap Server as Web Reference (see Consuming Web Services with C# for details). After that you add a new Handler (.ashx file) to you website. You could also use a Page (.aspx) for this example, but as we do not want to generate HTML the templating capabilities of a Page only introduce unnecessary overhead.

Below we assume you have an IIS running on the same machine as the PTV AJAX Maps servlet deployed in a current Apache Tomcat container. If this is not the case and your server runs on a different host and/or port, you will have to adapt the URLs from this example accordingly.

<%@ WebHandler Language="C#" Class="sampleLayer" %>
  // URL: http://localhost/AjaxLayerProvider/sampleLayer.ashx
  using System;
  using System.Web;
  using System.IO;
  using System.Xml;
  using System.Xml.Serialization;

  using xserver;

public class sampleLayer : IHttpHandler {

   public void ProcessRequest(HttpContext context) {
    // we are polite and set the proper content type
    context.Response.ContentType = "text/xml";

    // 1.
    // create a layer like shown in previous xMap tutorials
    Text text = new Text();
    text.text = "Time: " + DateTime.Now.ToLongTimeString();
    text.position = new Point(934955, 6268523);

    Texts texts = new Texts();
    texts.options = new TextOptions();
    texts.options.bgColor = new Color(255, 255, 255);
    texts.options.alignment = TextAlignment.CENTER;
    texts.options.fillBg = true;
    texts.options.showFrame = true;
    texts.options.frameColor = new Color(0, 0, 0);
    texts.options.textColor = new Color(255, 0, 0);
    texts.options.font = new Font();
    texts.options.font.size = 12;
    texts.options.font.name = "Arial";
    texts.wrappedTexts = new Text[] { text };

    CustomLayer layer = new CustomLayer();
    layer.centerObjects = false;
    layer.drawPriority = 2500;
    layer.name = "KarlsruheTime";
    layer.objectInfos = ObjectInfoType.REFERENCEPOINT;
    layer.visible = true;
    layer.wrappedTexts = new Texts[] { texts };

    // 2.
    // serialise the layer to XML and write it to the response stream
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.OmitXmlDeclaration = true;
    settings.Indent = true;

    // create an XmlWriter that writes to the response stream
    XmlWriter xmlWriter = XmlWriter.Create(context.Response.OutputStream, settings);

    // create an XmlSerializer with the right type and namespace
    // note: typeof(Layer) is right for all kinds of layers [typeof(CustomLayer)
    // will NOT WORK]
    XmlSerializer serializer = new XmlSerializer(typeof(Layer),
    "http://xmap.xserver.ptvag.com");

    // write the layer to the response
    serializer.Serialize(xmlWriter, layer);

    // DONE
  }
}

 

Now, when you open the URL of the just created Handler (e.g. http://localhost/AjaxLayerProvider/sampleLayer.ashx) in your web browser, you should get the XML fragment below.

<Layer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xsi:type="CustomLayer" name="KarlsruheTime" visible="true" centerObjects="false"
drawPriority="2500" objectInfos="REFERENCEPOINT" xmlns="http://xmap.xserver.ptvag.com">
 <wrappedBitmaps xsi:nil="true" />
 <wrappedLines xsi:nil="true" />
 <wrappedTexts>
    <Texts>
     <options alignment="CENTER" fillBg="true" pixelX="0" pixelY="0" relX="0" relY="0" showFrame="true">
      <bgColor blue="255" green="255" red="255" />
      <font bold="false" frame="false" italic="false"
      name="Arial" size="12" underline="false" />
      <frameColor blue="0" green="0" red="0" />
      <textColor blue="0" green="0" red="255" />
     </options>

     <wrappedTexts>
      <Text text="Time: 11:42:41">
       <position>
       <point x="934955" y="6268523" xmlns="http://common.xserver.ptvag.com" />
       </position>
     </Text>
    </wrappedTexts>
    </Texts>
  </wrappedTexts>
</Layer>

You can also write more than one layer to the output, but you will have to create a new XmlWriter for each Serialize statement. Otherwise the XmlWriter will complain about invalid XML, because we generate only a fragment of an XML file. Our output will not be a well-formed XML file by itself.

Configuring PTV xServer add-on AJAX Maps

Before you can use the remote XML layer data, you have to make sure the PTV AJAX Maps servlet is properly configured. In general two configuration entries are required: one to enable the RemoteLayerStringProvider and a second to allow access to a remote HTTP server.

 

Step 1: In order to use the RemoteLayerStringProvider which is responsible for handling the remote XML data, you have to make sure the ajaxmaps.properties file (e.g. <tomcat basic directory>/conf/ajaxmaps.properties ) contains the following line:

customLayerStrings=xml,com.ptvag.webcomponent.map.RemoteLayerStringProvider

This line assigns the provider name 'xml' to the RemoteLayerStringProvider. We will later use this name to refer to the RemoteLayerStringProvider on the client side (JavaScript).

 

Step 2: By default the servlet doesn't allow connections to arbitrary remote servers. The allowedRemoteURLPrefixes parameter which has to be added into the ajaxmaps.properties file allows you to specify a comma separated list of prefixes which determines the allowed URLs. All prefixes should end with as slash (/). If the do not end with a slash one will be appended.

Enforcing a trailing '/' makes sure a prefix like http://localhost' doesn't allow a connection to http://localhost.foobar.com/xyz . But should remember:

The prefixes should be as long as possible to narrow down the allowed connections. For example if all your XML providing scripts will reside in http://myserver/cgi-bin/layers/ , you should probably use http://myserver/cgi-bin/layers/ as prefix and not http://myserver/cgi-bin .

# Add this line to you conf/ajaxmaps.properties file

allowedRemoteURLPrefixes=http://localhost/AjaxLayerProvider/,http://127.0.0.1/AjaxLayerProvider/

Preparing the client side

Now you are ready to create an HTML page that includes the PTV AJAX Maps component with the server generated layer data. Create a file serverSideLayers.html in the directory <basic directory>/webapps/ajaxmaps/ with the following content:

<html>
 <head>
 <!--[if IE]><script type="text/javascript" src="webcomponent/script/excanvas.js"></script><![endif]-->
 <script type="text/javascript" src="webcomponent/script/qooxdoo/script/qx-transport.js"></script>
 <script type="text/javascript" src=".qxrpc"></script>
 <script type="text/javascript" src="webcomponent/script/map.js"></script>

 <script type="text/javascript">
 var map;

 function init() {
 var container = document.getElementById("mapContainer");
 var rect = { left:4350664, right:4360664, top:5469867, bottom:5459867 };
 map = new com.ptvag.webcomponent.map.Map(container, rect);
 }

 function addExternalLayer() {
  var manager = map.getServerDrawnObjectManager();
  var url = "http://localhost/AjaxLayerProvider/sampleLayer.ashx";
  url += "?bbox=COORDWIN@@@size=IMGSIZE";
  /* COORDWIN will be replaced with current
  bounding box coordinates (top,left,right,bottom)
  @@@ will be replaced with &
  IMGSIZE will be replaced by image width & height */
  manager.addPOICategory("xml", url);
  }
 qxp.dev.log.Logger.ROOT_LOGGER.setMinLevel(qxp.dev.log.Logger.LEVEL_INFO);
 qxp.dev.log.Logger.getClassLogger(qxp.core.Init).setMinLevel
 (qxp.dev.log.Logger.LEVEL_ERROR);
 </script>
 </head>

 <body onload="init()">
 <div id="map" style="width:640px;height:480px">

   <div id="mapContainer"
   style="width:100%;height:100%">
   </div>
  </div>
 <form>
  <input type="button" value="Add external layer" onclick="addExternalLayer()" />
 </form>
 </body>
</html>

As you can read in the JavaScript comments above, we provide the XML generating 'script' (here: sampleLayer.ashx) with the bounding box and size of the currently viewed map. You can use this information when generating layers to e.g. scale a layer objects to the map size, center objects on the map.

Now open the page in your web browser (using HTTP, not locally) and click the button. A layer with the current time should be visible in center of the map. The time is updated every time the map refreshes and a request to your layer provider is issued.