Moxi Media Inc.
Creating Custom Tools Discussion Forum
IMF Developer's Guide » Creating Custom Tools  

This document describes how to create a custom tool in the IMF. A custom tool is one that can be developed to provide a new map-related function, where the user chooses the tool to become 'active', then clicks on the map to execute the tool's function. It is important to note the distinction between a button and a tool. A button causes something to happen right away when the user clicks the icon, where a tool set a 'state' which defines what happens when the user clicks a map. If you just need something to happen when the user clicks the icon, define a button instead.

Tools are defined with the <TOOL> element. Although some programming is involved, custom tools are easy to add to the IMF interface, and provide the developer with a powerful capability to add new interactive mapping functions to an IMF site.

You will need to create a handler routine that is associated with the custom tool, but the good news is that there is no additional work involved in connecting your script to the tool in the IMF GUI.

How a custom tool works

A custom tool is defined in the site configuration XML file similar to the following tool included with IMF.

<TOOL name="Pushpin"
      url="$IMF/imfPushpin.jsp"
      shape="point"
      popup="false"
      hint="Pushpin"
      status="Add a pushpin where click the map."
      graphic="$IMF/tools/pushpin_1.gif"
      active-graphic="$IMF/tools/pushpin_2.gif"
      width="16" height="16" />

The way a custom tool works is that when the tool is active the script or document that is defined as the handler is called in the data frame or a popup window ( depending on the value of the popup attribute). The name of the tool, the shape type, and the coordinates of the user click point are passed to the script or document as parameters. The process uses a simple HTTP POST process. In the above example, a form is displayed in the data frame for the user to select a graphic and enter some text to add to the map at the location clicked.

The above example uses the shape type is "point". The shape type can also be set to a "box", "line", or "polygon". If the shape type is "box", the script will be called with a pair of coordinates when the user draws the box, or only one coordinate if the user clicks the map once without dragging the mouse to define a box. If the shape type is "line" or "polygon", the script will be called EACH TIME THE USER CLICKS THE MAP, with the list of coordinates that make up the line or the polygon. The measure tools are an example of how to handle a tool that accepts more than one coordinate value, showing the location and distance between the coordinates.

To review what a tool handler script gets passed, an example may help. We will use a script called imfArgs.jsp as the handler for our examples. imfArgs.jsp is a handy script that is included in IMF to help developers to understand the the arguments passed when creating reporting and custom tool handlers. It simply displays a list of the arguments passed to the script. Try the following to see what imfArgs.jsp does:

http://www.moximedia.com/imf/imfArgs.jsp?msg=hello&recipient=world

Now, set up the following custom tool definitions using imfArgs.jsp as the handler. This will add a custom tool to your application for each of the shape types.

<TOOL name="Point Test"
      shape="point"
      url="$IMF/imfArgs.jsp"
      popup="false"
      hint="Point Test"
      status="Custom tool test - point."
      graphic="$IMF/tools/markup_point1.gif"
      active-graphic="$IMF/tools/markup_point2.gif"
      width="16" height="16" />

<TOOL name="Box Test"
      shape="box"
      url="$IMF/imfArgs.jsp"
      popup="false"
      hint="Box Test"
      status="Custom tool test - box."
      graphic="$IMF/tools/markup_rect1.gif"
      active-graphic="$IMF/tools/markup_rect2.gif"
      width="16" height="16" />

<TOOL name="Line Test"
      shape="line"
      url="$IMF/imfArgs.jsp"
      popup="false"
      hint="Line Test"
      status="Custom tool test - line."
      graphic="$IMF/tools/markup_line1.gif"
      active-graphic="$IMF/tools/markup_line2.gif"
      width="16" height="16" />

<TOOL name="Polygon Test"
      shape="polygon"
      url="$IMF/imfArgs.jsp"
      popup="false"
      hint="Polygon Test"
      status="Custom tool test - polygon."
      graphic="$IMF/tools/markup_polygon1.gif"
      active-graphic="$IMF/tools/markup_polygon2.gif"
      width="16" height="16" />

Try out your new tools. It will help you to understand clearly what is happening when the user clicks the map. All that is left to do is to create a real handler that does what you need your tool to do. You can create custom tools to do hyperlinks, create combination spatial / attribute query forms, or anything where you need some spatial user input. For example, you can create a parcel drill down identify tool, where you select a parcel and use it as a filter to display records from other layers that intersect the parcel. The possibilities are endless. The editing tools extension is simply a set of custom tools created to perform editing tasks.

An example

Let's use what we know to build an example. Before we do, there is a distinction between what can be done by developers that will be installing scripts inside the IMF distribution directory and what can be done by developers that don't have direct access to the IMF (site developers that install their code in remote directories or in other directories on the same server). The scripts installed in the IMF directory can access the map object in the user's session, and those in other places cannot. Some API scripts have been developed to allow remote scripts to perform some actions that would otherwise require in-directory access, but handlers that need to access and modify the map settings are much easier to develop if they will be installed in the IMF directory tree. The recommended location for custom scripts that are installed in the IMF directory is in the ext directory or a subdirectory of that.

This example is for an in-directory installation to add a user-drawn line to the acetate layer of the map. We will actually develop two JSP scripts, one that displays each time the user clicks, and the script that adds the line to the map when the user is done. The definition of our tool will be:

<TOOL name="Line Markup"
      shape="line"
      url="$IMF/ext/lineMarkupHandler1.jsp"
      hint="Line Markup"
      status="Adds a line that you draw to the map."
      graphic="$IMF/tools/markup_line1.gif"
      active-graphic="$IMF/tools/markup_line2.gif"
      width="16" height="16" />

The following is a simple document that displays the status as the user clicks. Note that it doesn't really do much except count the points and put the argument string (the coordinates of the line) into a form that it will be passed to the "final" handler script when the user clicks the submit button.

<%@ page import="com.moximedia.wms.geom.Points" %>
<% 

  // The points are contained in the "pts" parameter as a string of 
  // x,y coordinates, with the values separated by commas. The 
  // Points constructor parses this string into a valid object.

  String arg = request.getParameter("pts"); 
  Points pts = new Points(arg);

%>
<html>
<head>
</head>
<body bgcolor="#ffffff">
<form name="frm" method="post" action="imfLineMarkupHandler2.jsp">
<%= pts.getCount() %>
<% if (pts.getCount() <= 1) { %>
point recorded. Click more points to define the line.
<% } else { %>
points recorded. Click more points to define the line, or click the
Add to Map button when you are finished drawing your line.
</p> 
<input type="hidden" name="pts" value="<%= arg %>">
<input type="submit" name="Submit" value="Add to Map">
<% } %>
<form>
</body>
</html>

The second script (called when the user clicks the button) adds the line to the map using the pts parameter passed to the script:

<%@ page import="com.moximedia.wms.*" %>
<%@ page import="com.moximedia.wms.acetate.*" %>
<%@ page import="com.moximedia.wms.geom.Points" %>
<% 

  // Get the map object from the user's session.

  Map map = (Map) session.getAttribute("imf.map");

  // Get or create an acetate layer.

  AcetateLayer lyr = map.getLayers().getAcetateLayerByName("User Markup");
  if (lyr == null) {
    lyr = new AcetateLayer("User Markup");
    map.getLayers().addLayer(lyr);
  }

  // The points are contained in the "pts" parameter as a string of 
  // x,y coordinates, with the values separated by commas. The 
  // Points constructor parses this string into a valid object.

  String arg = request.getParameter("pts"); 
  Points pts = new Points(arg);

  // Add the line to the acetate layer.

  AcetateLine line = new AcetateLine(pts);
  line.setColor(new java.awt.Color(255,0,0,255));
  line.setUnits(line.ACETATE_OBJECT_UNITS_DATABASE);
  lyr.add(line);

%>
<html>
<head>
<script language="JavaScript">
// Call the function in the map window to update the map.
var dMap = top.frames["mapFrame"];
dMap.refreshMap();
</script>
</head>
<body bgcolor="#ffffff">
Line added to map.
</body>
</html>

Valid HTML 4.01!