By Craig Pelkie
Copyright © 2001, Craig Pelkie
ALL RIGHTS RESERVED
You can visualize data many different ways. For example, the Web page shown in Figure 1 is a serviceable display of a customer file. If you need to know how much a particular customer owes, you can look at the Balance Due column. What is not so easy to see is the relative value of one balance due to another. For that, the bar graph shown in Figure 2 is more useful.

Figure 1: The QCUSTCDT data as displayed in a traditional Web page.

Figure 2: The QCUSTCDT Balance Due data displayed as an SVG.
Using a relatively new XML-based technology called Scalable Vector Graphics (SVG), you can dynamically create charts like the one shown in Figure 2 from any Web application. In this example, the bar graph is generated from a Net.Data macro that queries the database, then generates the XML code. The XML is displayed in the browser using an SVG rendering agent. In this sample, I use the SVG Viewer that is freely available from Adobe.
SVG is not limited to simple bar graphs. For example, you can create complex images like the map shown in Figure 3. Right-clicking on the map brings up the SVG Viewer pop-up menu (Figure 4). Selecting the Zoom In option enlarges the section of the map, as shown in Figure 5.

Figure 3: A sample map, created with SVG (from Adobe's SVG Web site, http://www.adobe.com).

Figure 4: The pop-up menu for the Adobe SVG Viewer.

Figure 5: A zoom-in view of the map shown in Figure 3 (from Adobe's SVG web site, http://www.adobe.com).
Now why is this important or interesting, and why is SVG any different from standard Web graphics, such as GIF or JPG files?
SVG is a radical departure from traditional Web graphics, primarily because it is a text based language used to describe how vector graphics should be rendered. This is in contrast to GIF, JPG or even Macromedia Flash files, which are binary files. Because SVG uses XML, you can readily generate SVG code from any programming language. For example, to create the bar graph shown in Figure 2, you simply read data from your database file and write SVG XML statements as output. Those statements are then passed back from the Web server to the browser, where they are rendered using the SVG viewer. If you create the bar graph as a GIF or JPG, you need to use a drawing program to create the file, save it, then send it to the Web page. When the underlying data changes, you need to recreate the file again.
Another difference is that GIF and JPG files are raster files, as opposed to SVG’s vector format. Raster files use a binary format to describe how individual pixels should appear. Raster files are set at creation time to be rendered at a certain size and using certain colors. If you enlarge a raster file, you see “pixelation”; the graphic starts to look rougher.
SVG can be readily and proportionally enlarged or reduced. Because SVG uses vectors, all of the elements are mathematically enlarged or reduced by the same amount. If you look again at Figures 3 and 5, you can see that the detail view (Figure 5) looks as good as the initial view (Figure 3).
Because an SVG is made up of a series of objects described with XML statements, you can attach behaviors to any of the objects. For example, when you move the mouse over the different bars in Figure 2, the customer detail data is displayed in the rectangle. You can also attach HTTP links to an object, so that by clicking the object, you are taken to a different Web page.
An SVG can also use scripting code such as JavaScript to control its behavior when rendered. For example, you can animate an object, change its location or colors, or use a number of other effects.
In addition to SVG, there are other vector based techniques available for rendering graphics. For example, IBM has Graphical Data Display Manager (GDDM), Macromedia has Flash, and there are other XML-based markup languages (VML, Vector Markup Language and PGML, Precision Graphics Markup Language). SVG incorporates the features of VML and PGML.
The main benefit of SVG as compared to proprietary techniques such as GDDM and Flash is that SVG is an open W3C standard (see http://www.w3.org/Graphics/SVG/Overview.html). That means that the specification for how SVG works is available to all interested parties. Because SVG uses XML, you can use a variety of tools to create SVG, from Notepad to WYSIWYG programs such as Adobe Illustrator. You can also choose from a number of SVG viewers, some of which run as browser plug-ins, while others are stand-alone programs.
From my research into the available options for working with dynamic graphics, it seems that the only other viable and similar approach to SVG is the Java 2D API. Both Java 2D and SVG provide similar capabilities. Sun Microsystems, the creators of Java, are enthusiastic supporters of SVG, and even provide Java classes to convert Java 2D code to SVG. If you program in Java, you can use Java 2D or SVG; if you use another programming language, you can use SVG.
To get to the point where I could generate the bar chart shown in Figure 2, I had to become familiar with the basics of SVG. The best starting point I found is Adobe’s Web site at http://www.adobe.com/svg/viewer/install/main.html. You can download Adobe’s SVG viewer and then view many samples of SVG in your browser. Adobe also has a tutorial and links to other SVG related Web sites. You will probably want to go to the W3C site and download the SVG Specification document (approximately 500 pages). The Specification is almost impossible to read, since its intended audience seems to be people who will be implementing SVG viewers, but there are some sections of the specification that are indispensable for reference.
SVG itself supports only three basic object types: vector graphic shapes, images and text. The vector graphic shapes include rectangle, circle, ellipse, line, polyline (multiple line segments, unclosed) and polygon. Image provides support for rendering other image types within SVG, for example, you can embed a GIF or JPG file in SVG. Text provides support for specifying text, with complete support for fonts, attributes, and text placement within the SVG.
After viewing some samples, I determined that I could create the bar graph with the rectangle shape and text. I used Notepad to create the initial SVG file, with my browser open in another window. This was a very productive technique to experiment with SVG, since I could immediately view the SVG in the browser as soon as I saved my changes in Notepad.
Figure 6 shows the sample code to display the bar graph and text data for customer M T Abraham. The <g> tag is used to indicate where a new SVG graphic block begins. You can include as many tags inside the <g> tag as required, including additional <g> blocks. Each customer has their own <g> block, which includes several SVG objects.
<g
id="bar1">
/***************************************************/
/* M T Abraham */
/***************************************************/
<text id="t1s"
x="1" y="91"
style="&font;&l;&hid;">M T
Abraham</text>
<text id="t1" x="0" y="90"
style="&font;&b;">M T Abraham</text>
<rect id="r1s"
x="71" y="81"
width="162.8400" height="15"
style="&l;"/>
<rect id="r1" x="70" y="80"
width="162.8400" height="15"
style="&u;"
onmouseover="setStyles(evt,
'1', true)"
onmouseout="setStyles(evt, '1',
false)"/>
<text id="t1x"
style="&font;&hid;&b;">
<tspan x="310"
y="120"
style="&re;">$525.00 27.14%</tspan>
<tspan x="310"
dy="1em">392 Mill St</tspan>
<tspan x="310" dy="1em">Isle,
MN 56342</tspan>
<tspan x="310"
dy="1em">15:34:28</tspan>
</text>
</g>
Figure 6: The SVG XML code used to
display customer data and the bar graph.
Two <text> blocks are used to display the customer name. The name is initially rendered in the viewer using the second <text> block. When you move the mouse over the bar for a customer, the text in the first <text> block is made visible through scripting code. The first <text> block provides a drop-shadow effect, which can be seen in Figure 7 (F L Lee). The drop shadow is achieved by offsetting the “x” and “y” coordinates by one.
The bar graphs are rendered using the two <rect> blocks. Like the <text> blocks, the first <rect> block is used to render the drop shadow. For the bar graphs, the drop shadow is always visible. When the mouse moves over a bar, a border is drawn around the rectangle to highlight it (see Figure 7).

Figure 7: A zoom-in view of the bar graph, showing text and bar shadowing.
The final <text> block is used to display the customer detail data in the rectangle at the right in the SVG (see Figure 2). The bounding rectangle itself is drawn separately at another location in the XML, since it is used by all of the customer <g> blocks.
The basic pattern of code for each customer is shown in Figure 6. The variables that need to be substituted for each customer from database records are the name, balance due, percentage of total balance due, street, city, state and ZIP code. In addition, the width of the <rect> blocks needs to be calculated to render the bar graph. The “x” positions for each element in the <g> block need to be calculated and incremented when a new <g> block is started so that the customer names and bar graphs appear vertically in the SVG.
For this example, I used Net.Data to access the database file of customer data and create the XML statements for the SVG. My primary reason for using Net.Data is because most of you will be able to run Net.Data on your AS/400 systems and see the results quite quickly; there is no requirement to compile an RPG-CGI program. That being said, I believe that for future SVG projects I would prefer to use Java or RPG, since there tends to be a lot of calculation and character string work required to create the XML statements. Although Net.Data provides the required functionality, it does so at the expense of relatively verbose (and somewhat obscure) code.
%{*********************************************************%}
%{
svg_ndm.ndm - Generate SVG using Net.Data %}
%{ %}
%{
Craig Pelkie %}
%{
Bits & Bytes Programming, Inc. %}
%{
craig@web400.com %}
%{*********************************************************%}
%{*********************************************************%}
%{
A: Defines for macro %}
%{*********************************************************%}
%define
{
DATABASE = "*LOCAL"
DTW_DEFAULT_REPORT = "NO"
DTW_PRINT_HEADER = "NO"
DTW_REMOVE_WS = "NO"
tblBaldue = %table
%}
%{*********************************************************%}
%{
B: Get balance due table %}
%{*********************************************************%}
%function(DTW_SQL)
GetBaldue(out tblBaldue) {
select * from qiws.qcustcdt
where
(baldue > 0) and (baldue < 1000.00)
order by baldue desc
%}
%{*********************************************************%}
%{
C: Make XML to display the SVG bar graph %}
%{*********************************************************%}
%macro_function
MakeXML (in tblBaldue) {
@dtw_tb_rows(tblBaldue, numRows)
%{*****************************************************%}
%{ D: work variables %}
%{
r - row counter %}
%{
y - y-axis position %}
%{
total - total balance due %}
%{*****************************************************%}
@dtw_assign(r, "1")
@dtw_assign(y, "90")
@dtw_assign(total, "0")
%{*****************************************************%}
%{ E: colors for bar graphs %}
%{
colors - array of colors (see
<! ENTITY values %}
%{
colorN - current color
number %}
%{
colorMax - maximum number of colors %}
%{*****************************************************%}
@dtw_assign(colors, "ungpry")
@dtw_assign(colorN, "1")
@dtw_assign(colorMax, "6")
%{*****************************************************%}
%{ F: Calculate, display total balance
due %}
%{*****************************************************%}
%while (r <= numRows) {
@dtw_add(total, @dtw_tb_rgetv(tblBaldue,
r, "10"), total)
@dtw_add(r, "1", r)
%}
<text id="baldue"
x="1" y="50"
style="&font;&bl;">
Total balance due:
@dtw_rconcat("$", total)</text>
%{*****************************************************%}
%{ G: Format/display bar, info box for
each customer %}
%{*****************************************************%}
@dtw_assign(r, "1")
%while (r <= numRows) {
<g id="bar$(r)">
%{*************************************************%}
%{ H: Initialize y-axis values: %}
%{
yts - y-axis for text (name) shadow %}
%{
yrs - y-axis for rectangle (bar) shadow %}
%{
yr - y-axis for rectangle
(bar) %}
%{*************************************************%}
@dtw_add(y, "1", yts)
@dtw_subtract(y, "9", yrs)
@dtw_subtract(y, "10", yr)
%{*************************************************%}
%{ I: Extract values from row to
simple variables %}
%{*************************************************%}
@dtw_assign(cusnum,
@dtw_tb_rgetv(tblBaldue, r, "1"))
@dtw_assign(lstnam,
@dtw_tb_rgetv(tblBaldue, r, "2"))
@dtw_assign(init, @dtw_tb_rgetv(tblBaldue, r, "3"))
@dtw_assign(street,
@dtw_tb_rgetv(tblBaldue, r, "4"))
@dtw_assign(city, @dtw_tb_rgetv(tblBaldue, r, "5"))
@dtw_assign(state, @dtw_tb_rgetv(tblBaldue, r, "6"))
@dtw_assign(zipcod,
@dtw_tb_rgetv(tblBaldue, r, "7"))
@dtw_assign(cdtlmt,
@dtw_tb_rgetv(tblBaldue, r, "8"))
@dtw_assign(chgcod,
@dtw_tb_rgetv(tblBaldue, r, "9"))
@dtw_assign(baldue,
@dtw_tb_rgetv(tblBaldue, r, "10"))
@dtw_assign(cdtdue, @dtw_tb_rgetv(tblBaldue, r, "11"))
%{*************************************************%}
%{ J: Make customer name as INIT
LSTNAM %}
%{*************************************************%}
@dtw_assign(name, init)
@dtw_assign(name, @dtw_rconcat(name,
" "))
@dtw_assign(name, @dtw_rconcat(name,
lstnam))
%{*************************************************%}
%{ K: Set width of bar to display
percentage of %}
%{ total balance due. %}
%{ Bar graphs are displayed as their
percentage of %}
%{ a maximum width of 300. To better
show scale, %}
%{ the calculated percentage is
multipled by 2. %}
%{*************************************************%}
@dtw_divide(baldue, total,
"4", percent)
@dtw_multiply(percent,
"300", width)
@dtw_multiply(width, "2", width)
@dtw_multiply(percent,
"100", "4", percent)
%{*************************************************%}
%{ L: Extract color value from colors
array. %}
%{*************************************************%}
@dtw_substr(colors, colorN,
"1", color)
@dtw_add(colorN, "1", colorN)
%if (colorN > colorMax) {
@dtw_assign(colorN, "1")
%endif
%{*************************************************%}
%{ M: Output SVG tags for
customer. %}
%{*************************************************%}
/***************************************************/
/* $(name) */
/***************************************************/
<text id="t$(r)s"
x="1" y="$(yts)"
style="&font;&l;&hid;">$(name)</text>
<text id="t$(r)" x="0" y="$(y)"
style="&font;&b;">$(name)</text>
<rect id="r$(r)s"
x="71" y="$(yrs)"
width="$(width)" height="15"
style="&l;"/>
<rect id="r$(r)" x="70" y="$(yr)"
width="$(width)" height="15"
style="&$(color);"
onmouseover="setStyles(evt,
'$(r)', true)"
onmouseout="setStyles(evt,
'$(r)', false)"/>
<text id="t$(r)x"
style="&font;&hid;&b;">
<tspan x="310"
y="120"
style="&re;">@dtw_rconcat("$", baldue)
$(percent)%</tspan>
<tspan x="310"
dy="1em">$(street)</tspan>
<tspan x="310"
dy="1em">$(city), $(state)
$(zipcod)</tspan>
<tspan x="310"
dy="1em">@dtw_rtime()</tspan>
</text>
</g>
@dtw_add(r, "1", r)
@dtw_add(y, "20", y)
%}
%}
%{*********************************************************%}
%{
N: %html input block - macro starts executing here %}
%{*********************************************************%}
%html(input)
{
Content-Type:
image/svg-xml
Content-encoding:
ebcdic
<?xml
version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE
svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN"
"http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd"
[
<!ENTITY b "fill:black;">
<!ENTITY u "fill:blue;">
<!ENTITY n "fill:brown;">
<!ENTITY g "fill:green;">
<!ENTITY l "fill:lightgray;">
<!ENTITY p "fill:purple;">
<!ENTITY r "fill:red;">
<!ENTITY y "fill:yellow;">
<!ENTITY font
"font-size:9;">
<!ENTITY hid "visibility:hidden;">
]>
<svg
width="500pt" height="280pt" viewBox="0 0 450
280" xml:space="preserve">
<script>
<![CDATA[
//**********************************************************
// O: return style object for
document element
//**********************************************************
function getStyle(evt, element) {
var SVGDoc =
evt.getTarget().getOwnerDocument();
return
SVGDoc.getElementById(element).getStyle();
}
//**********************************************************
// P: set visibility property for
a style
//**********************************************************
function setVisibility(style, set)
{
style.SetProperty("visibility", "hidden");
if (set == true)
style.SetProperty("visibility", "visible");
}
//**********************************************************
// Q: set style properties
//**********************************************************
function setStyles(evt, id, set) {
//******************************************************
// Set properties for text
//******************************************************
var style = getStyle(evt, 't'
+ id);
style.SetProperty("fill", "black");
style.SetProperty("stroke-width", "0");
if (set == true)
style.SetProperty("fill", "navy");
//******************************************************
// Set properties for text
drop shadow
//******************************************************
style = getStyle(evt, 't' + id
+ 's');
setVisibility(style, set);
//******************************************************
// Set properties for extra
text
//******************************************************
style = getStyle(evt, 't' + id
+ 'x');
setVisibility(style, set);
//******************************************************
// Set properties for extra
text bounding rectangles
//******************************************************
style = getStyle(evt,
'moreTexts');
setVisibility(style, set);
style = getStyle(evt,
'moreText');
setVisibility(style, set);
//******************************************************
// Set properties for chart
row
//******************************************************
style = getStyle(evt, 'r' +
id);
style.SetProperty("stroke", "black");
style.SetProperty("stroke-width", "0");
if (set == true)
style.SetProperty("stroke-width", "1");
}
]]>
</script>
/***************************************************************/
/* R: SVG for customer detail bounding
rectangle */
/***************************************************************/
<g id="txtRect">
<rect id="moreTexts"
x="301" y="111" width="100" height="50"
style="stroke-width:1;stroke:lightgray;fill:none;&hid;"/>
<rect id="moreText" x="300" y="110"
width="100" height="50"
style="stroke-width:1;stroke:navy;fill:none;&hid;"/>
</g>
@GetBaldue(tblBaldue)
@MakeXML(tblBaldue)
</svg>
%}
Figure 8: The Net.Data macro that produces
the QCUSTCDT Balance Due SVG.
The Net.Data macro shown in Figure 8 is entirely conventional, with one significant difference. The difference is that I am writing customized HTTP headers to the browser to indicate that I am sending back SVG, rather than the default Net.Data content type of text/html. To run the macro, load the code to a directory in your IFS, then invoke the macro, starting at the input section (see Block N in the code listing).
The Net.Data, SVG and JavaScript code is explained as follows, using letters that are keyed to sections of the macro.
The %define block includes the conventional Net.Data definitions, and also the DTW_PRINT_HEADER and DTW_REMOVE_WS definitions. Those two definitions are required so that I can emit my customized HTTP header from the macro.
This is a conventional Net.Data SQL block. It gets the balance due records from file QIWS/QCUSTCDT. Note that I limited the selection with a “where” clause to exclude customers with no balance due and to exclude the one customer in QCUSTCDT with an excessive balance due (the excessive balance due would skew the bar graphs so that the remaining customers would have very narrow bars).
The GetBaldue function is called near the very end of the macro, in the input block.
This starts the MakeXML macro function block, which is where all of the calculations are performed for each row returned from the SQL statement. Because I will be using explicit Net.Data table access code in the block, I retrieve the number of rows in the table returned from the SQL statement.
The MakeXML block is called near the end of the macro in the input block.
These variables are used to track the current row being processed, the current “y” (vertical) position in the SVG and the total balance due.
If you look back at Figure 2, you will see that there are six bar graph colors that repeat after all six are used. The variables in this section are used to assign a color code, the current color index, and the maximum number of colors.
The color codes (letters “ungpry”) correspond to the XML ENTITY elements defined in Section N. When generating the XML code, you can assign the color by using the entity value rather than spelling out the value: &p instead of “fill:purple”. This is similar to using macros in other languages, and is used simply to provide a shortcut way of specifying the color code. The SVG Specification suggests that you may want to code attribute specifications with entity values rather than spelled out values, mainly to keep the size of the generated XML code as short as possible. Although the size of the generated XML is not a big issue in this sample, when you generate XML for a complicated SVG (such as is shown in Figure 3), you may have to set attributes for hundreds or thousands of elements.
The code in this section reads the result set from the SQL statement and accumulates the total balance due. After accumulating the total, an SVG <text> object is written to the output stream to display the text “Total balance due:” and the amount (see Figure 2).
This section starts by assigning the value 1 to the row counter variable again. The %while statement starts a block to process all of the rows in the result set.
The <g> block tag is emitted, which includes the text “bar1” (for example) to uniquely identify each SVG block. The Net.Data math operations are used to set the y-axis values for the text shadow, bar shadow and the bar itself.
For each customer, you need to calculate the y-axis values used to place the text and rectangles on the SVG. This code calculates the y-axis values; as the values increase, the elements are located further down in the SVG.
Strictly speaking, the code in this section is not required, since I could have referred to the table values using the @dtw_tb_rgetv function at the point where the value is used. However, since the values will be used in XML statements that are complicated enough by themselves, I chose to extract the values from the result set table into simple named variables. I also extracted some values that aren’t currently used in the SVG, but by including them now, I’ve made it easier to modify the macro in the future.
This code simply concatenates the customer initials with a blank space and the customer last name.
The code in this section calculates the width of the bars. I determine how wide each customer’s bar should be, within a total width of 300. Each customer’s bar is in proportion to their total of the balance due. The formula I used is:
Width = (customer_baldue / total_baldue) * 300
That yielded an initial width value. Looking at the results, I saw that the bars were quite short, so I simply double the resulting widths to draw the bars as shown in Figure 2.
The final @dtw_multiply is used to format the percentage of the total balance due for each customer. That percentage is displayed in the data rectangle when the mouse moves over the bar (see Figure 2).
This code simply extracts the next color value from the colors array (see Section E), then increments the current color value. It “rolls over” to color 1 after all of the colors are used.
The code in this section outputs the XML block as shown in Figure 6. When this section is reached, all of the calculations have been done. At this point, the XML code is simply emitted with Net.Data character string substitutions providing the values for each customer.
The Net.Data variables are identified using the $(varname) syntax. My feeling is that it would be very tricky (bug-prone) to try to code this block of code without having a working example to refer to. That is why I created the simple test (Figure 6) so that I had a model I could copy.
The final two @dtw_add statements at the end of this section increment the row counter and the y-axis value, to prepare for the next customer.
This is where the Net.Data macro starts processing. Because I am emitting raw XML code back to the browser, I need to explicitly specify the Content-Type and Content-encoding HTTP headers, as shown. Those two lines must be the first two lines in this section, and the second line (Content-encoding) must be immediately followed by one completely blank line. The penalty for violating these rules is rather severe: you get a nasty server error displayed in your browser.
Those two header lines actually took most of the time I spent to get the macro working. For some reason, the knowledge of HTTP headers is considered to be a state secret. Finding the actual content type value to use (image/svg-xml) involved no more than a pleasant half hour looking through the SVG Specification; that line is needed to invoke the SVG Viewer when the code finally arrives at the browser.
I am completely baffled by the ebcdic value in the Content-encoding line; I simply know that without it, nothing works. I had intuitively and incorrectly reasoned that since I was developing all of this code on my PC using an ASCII based editor, and saving it to the AS/400 IFS, that the code would be served out in ASCII format. All I can surmise is that Net.Data reads the code from the IFS, converts it to EBCDIC, then leaves it up to you to figure out how to get it back to the browser. In any event, it only took a few hours to find the few scattered references to this value. By the way, don’t look in the Net.Data manuals; the answer, such as it is, is provided in the Net.Data Forum by one of the Rochester Net.Data developers (I get to the forum at http://www.as400.ibm.com/netdata then click the link for Forum. I know they’ve changed the URL to some silly thing, but my way still works.) Hopefully you can use your time more usefully to explore SVG, now that I have warned you.
Rather than walk you through the XML statements and DOCTYPE and lots of special punctuation required to jump-start an XML file, I will simply relate that I copied the code from one of the working examples at the Adobe site.
This section and Sections P and Q are sections that I also will not describe in detail. The code in these sections is the JavaScript code that provides the event handling when you move the mouse over or off one of the bar graphs (see Section M, the second <rect> object, for the onmouseover and onmouseout events that invoke the setStyles JavaScript code). Like most programmers, I found some sample SVG event handling code, copied it, then modified it to get the effects I wanted.
I will point out that when you create event handlers, you will want the unreadable SVG Specification manual near at hand. That manual does list all of the events that you can respond to and all of the attributes that you can set for each SVG object.
Another tip you can use is to grab the SVG source when you see an SVG that you like. One of the options in the pop-up menu for the SVG Viewer (see Figure 4) is View Source. You can use the Save SVG As to save the complete SVG source, including any scripting code, to a text file.
The code in this section describes the <g> element for the rectangle that surrounds customer detail data. This rectangle also uses a drop-shadow. The rectangle is hidden until you move the mouse over a customer bar, at which point scripting code changes its visibility property to visible. When you move the mouse off the graph, the scripting code sets the property back to hidden.
The map shown in Figure 3 is SVG, meaning that you can view its source and see the hundreds of lines of XML code required to describe it. Because the map uses free-form shapes rather than relatively easy to work with rectangles or circles, you will need to use some sort of graphics program to create the SVG. One program that I am aware of to help produce SVG is Adobe Illustrator version 9, which includes a Save As SVG option. If you are computer graphically gifted or know someone who is, you can start creating complicated SVG files.
As for me, if I needed to create a graphic such as a map, I would start by scanning in a suitable map, importing it into Illustrator, then “tracing” over it with the mouse, saving the tracing to the SVG file.
After generating the SVG file, you can open it in a code editor and start identifying the XML code that is used to render an object. For example, if I want to create a clickable object for the Central Business District shown in Figure 5, I need to identify the <g> block in the XML code that renders that object. I can then use the identifier (id) of that element and attach scripting or linking code to it. Once I have the element ID, it is relatively easy to link to another Web page which can display another SVG or a text page with additional data about the element.
It is important to realize that SVG is not a “flash in the pan”. Virtually all of the major software vendors, including Adobe, IBM, Sun, Microsoft, Macromedia and many others, participated in creating the SVG specification. SVG is moving towards the W3C “recommendation” status, meaning that it will be regarded as a tool with the same standing as HTML and XML. There are currently several viewers available, along with many samples. As I have shown in this article, you can use SVG to create dynamic database driven graphics.
Craig Pelkie is a programmer based in Southern California. Between electrical blackouts, he writes articles, prepares seminars and training materials, travels a lot, and occasionally gets to write code. Craig is currently working with WebSphere on the AS/400 and Windows 2000. He was quite surprised to discover SVG and worked very hard to prepare this article. As a consultant, he is open to short or long term interesting projects. You can contact him at craig@web400.com.