Writing GIS Applications for the WWW
The World Wide Web (WWW) gained fame as a massive, stateless, connectionless,
client/server application. Browsers sent requests to Web servers to get
documents. Originally, the document only contained text and graphics. Browser
clients never "logged on" to the server and the connections only lasted
long enough for the client to retrieve the necessary document. After transferring
the document, neither the client nor the server remembered much about each
other. Connectionless means that only for the duration of the retrieval
of a document (or more generally the information associated with
a Uniform Resource Locator (URL)) are the client and Web server actually
connected or communicating with each other. Stateless means that for normal
HTTP transactions neither the client nor the server remembers much about
the other. After a transaction finishes, the server forgets the client
and the client is free to request any available document anywhere in the
WWW. Client/server applications built on stateless and connectionless protocols
are very new to GIS. For previous client/server models (like ArcView/SDE
or Map Objects/SDE), a session had both state and connection. For client/server
GIS software systems, the client usually maintains the state of the map.
For example, when a map is displayed, it has a bounding box or extent in
map coordinates. This information is critical to reporting the map coordinates
of the cursor and therefore for zooming, panning, identifying, selecting
and measuring. Since the client is effectively disconnected from any server
(and in the future may ask many different servers to assist with spatial
queries), it must continually store and update this information. In an
HTML solution, this is done with hidden variables. With JavaScript or VB
Script, global variables, accessible to any object, are used to share state.
With Java, an object in the client remembers the current extent as a property
or a variable. This paper is an introduction to writing client/server GIS
applications. Our client/server model is the WWW and we focus on a typical
three-tier configuration
Table of Contents
1. Introduction
The World Wide Web (WWW) gained fame as a massive, stateless, connectionless,
client/server application. Browsers sent requests to Web servers to get
documents. Originally, the document only contained text and graphics. Browser
clients never "logged on" to the server and the connections only lasted
long enough for the client to retrieve the necessary document. After transferring
the document, neither the client nor the server remembered much about each
other. Connectionless means that only for the duration of the retrieval
of a document (or more generally the information associated with
a Uniform Resource Locator (URL)) are the client and Web server actually
connected or communicating with each other. Stateless means that for normal
HTTP transactions neither the client nor the server remembers much about
the other. After a transaction finishes, the server forgets the client
and the client is free to request any available document anywhere in the
WWW.
Client/server applications built on stateless and connectionless protocols
are very new to GIS. For previous client/server models (like ArcView/SDE
or Map Objects/SDE), a session had both state and connection. For client/server
GIS software systems, the client usually maintains the state of the map.
For example, when a map is displayed, it has a bounding box or extent in
map coordinates. This information is critical to reporting the map coordinates
of the cursor and therefore for zooming, panning, identifying, selecting
and measuring. Since the client is effectively disconnected from any server
(and in the future may ask many different servers to assist with spatial
queries), it must continually store and update this information. In an
HTML solution, this is done with hidden variables. With JavaScript or VB
Script, global variables, accessible to any object, are used to share state.
With Java, an object in the client remembers the current extent as a property
or a variable.
This paper is an introduction to writing client/server GIS applications.
Our client/server model is the WWW and we focus on a typical three-tier
configuration:
-
A WWW client like Netscape Navigator (Navigator) or Microsoft Internet
Explorer (IE)
-
A Web Server like Netscape Enterprise Server or Microsoft's Internet
Information Server (IIS)
-
A GIS Server like Esri's ArcView Internet Map Server (AvIMS).
In the first sections, WWW URL's are discussed along with how to use them
to invoke GIS Server scripts to DRAW a map. This simple method is the core
of an implementation of zoom-in, zoom-out, pan, zoom-to-extent-of-selected
and other map drawing functions. Other basic commands are also described
for SELECT, IDENTIFY, RETURNEXTENT and LOCATE. The last function returns
a point from an address. A simple scripting technique shows how to group
these basic operations together into new functions. In our example, the
scripts are actually created at the client and delivered to the server
for processing. This gives the client great flexibility in defining new
methods without constantly having to write special purpose functions on
the server. We end with a view of the future: GIS applications as a set
of customized networked objects.
2. Client/Server Applications for the WWW
The WWW is a global, client/server network. Web browsers, the clients,
connect to Web servers to get documents. The host and the document requested
by the browser are encoded in a Uniform Resource Locator (URL).
Example 2.1: Simple URL's
A URL is made up of several parts. To access an HTML document, an absolute
URL completely specifying all information, has the following components:
-
A protocol: http, ftp
-
A host name or an IP address of a host: www.wptc.com, www.Esri.com
or 207.87.124.133
-
A path to a document: /storefaq
-
A document: class_schedule.html
Although this simple URL encoding of a host and a document
enabled the creation of Web documents with text, graphics and hyperlinks,
it wasn't enough for even the simplest data access. To support HTML forms
and Common Gateway Interface (CGI) applications, the URL encoding was extended.
The extension allowed an executable program to received a collection of
name/value variable pairs.
Example 2.2: Looking for Esri on Yahoo
Yahoo (www.yahoo.com) provides a search engine for the Web. In a text window,
search terms are entered. When the Search button is pressed, the
search terms are sent to Yahoo's database query tools. The search terms
are actually encoded in a URL. The following looks for the term "Esri"
in Yahoo's database:
http://search.yahoo.com/bin/search?p=Esri
On Saturday, April 12th, it returned references to 35 sites. The URL for
a document or the URL produced by an HTML form is always visible in the
location window of the browser. For the URL looking for "Esri" on Yahoo:
-
http is the protocol
-
search.yahoo.com is the host
-
/bin is the path
-
search is the name of the executable program
-
? is a special character in this URL
-
p=Esri tells the search program to set its p
variable to Esri.
This URL can be entered in the location window of a browser like any other
URL. This extends URL encoding to making programs with parameters Web addressable
resources -- just like HTML documents and gif images.
The rules for encoding a fully qualified CGI URL are:
-
Protocol: http
-
Host Name: av.yahoo.com
-
Path: /bin
-
Executable: query
-
Separator: ? to separate the executable from its parameters
-
Name/Value pairs: p=Esri&hc=0&hs=35
-
Each pair is separated by an &
-
Most punctuation and non-alphanumeric characters are written replaced
by a "%" and their hex value. For example: %3F replaces the "?".
-
Blanks are converted to "+"'s.
Example 2.3: A silly query with special characters
It's a silly query, but to illustrate the encoding, if you keyed "Mr. Jones:
Did he score 100%?" into the search window at Yahoo, the URL would be:
http://search.yahoo.com/search?p=Mr.+Jones%3A+Did+he+score+100%25%3F
The %3A replaced ":", the %25 replaced the "%"; the "%3F" replaced the
"?". Plus signs fill in for blanks.
Encoding CGI script references and parameters in URL's led to the widespread
deployment of database application. HTML forms were used for data-entry
(site-registration, userid/passwords) or as query front-ends. Database
applications on the WWW are excellent examples of three-tier client/server
applications.
Examples 2.4: Three Tier Client/Server Database Application
-
The Client connects across a network to the Server
-
The Server, acting as a client, connects to the Application Server
-
The Application Server provides services to the Server's client which
are relayed to the Client.
Specific Examples:
-
Most database applications on the Web are three-tier client/server
networks. The Client is a Web browser, the Server is a Web server and the
Application Server is a Database Server like Oracle Workgroup Server or
Microsoft's SQL Server
-
Esri's Real Estate Information Network was a three-tier network using
ArcView as a Client, Esri's GeoServer as the Server and SDE as the Application
Server.
-
Esri's Internet Map Servers (Map Objects and ArcView) are both three-tier
client/server networks. The Client is a Web browser, the Server is a Web
Server and the Application Server is either the ArcView Internet Map Server
or the Map Objects Internet Map Server.
Web servers have no special knowledge of spatial datasets. The simplest
technique for writing a GIS application on the Web is to connect the Web
server to a GIS Server (like the Map Objects Internet Map Server, ArcView
Internet Map Server, possibly SDE or wptc's gifView GIS Server)
and encode requests for spatial processing as URL's. This is much like
encoding a complex database query generated by the HTML form in a URL.
The rest of this paper discusses how to write GIS applications for the
WWW using this strategy. The example GIS Server is Esri's ArcView Internet
Map Server. We start with a simple DRAW command. Other commands to SELECT,
IDENTIFY, RETURNEXTENT and LOCATE are described, but not in detail. As
an example, these commands are used to zoom to the extents of a polygon
containing an address. This might be used in an application where we wanted
to view the service zone associated with an address. The service zone could
be a hospital district, school attendance zone, census tract, electoral
district, wire center or a zip code. This example is implemented using
a very simple scripting language based on our five commands.
3. GIS Servers for the WWW
To provide GIS services to a Web Client in a three-tier client/server
network, the GIS Server must satisfy two basic criteria:
-
A bidirectional connection exists between the Web Server and the GIS
Server.
-
The Web Server must be able to send the name/value pairs contained
in a URL
-
The GIS Server must be able to write the results of the processing
back to the Web Server, who then delivers them to the Web Client.
-
The GIS Server is capable of producing Web compatible output. This
means:
-
Graphics usable by Web Browsers or Java Applets: gif and JPEG
-
HTML documents
-
Structured or tagged text files for use by Java applets
The ArcView Internet Map Server utilizes a socket to connect to the Web
Server. Sockets are a common TCP/IP protocol for passing messages between
two processes on the same or different hosts. One system, the server (in
our example, the ArcView Internet Map Server), listens to a fixed
socket address called a port. Clients (the Web Server is a client requesting
services from ArcView) send messages to this port; the server receives
them and writes responses back. Socket protocols allow the server to block
the port while it is processing a request. This means that other requests
will queue up at the port waiting for services. Sockets provide an efficient
way to pass messages and have the additional advantage of providing first-in-first-out
(FIFO) queuing.
Example 3.1: Registering AvIMS with the Web Server
ArcView IMS registers the socket where it listens for requests by sending
a URL to the Web Server:
http://winnt.wptc.com/.Esrimap
?Reg=project411c8239
&Machine=winnt
&Port=1045
&Timeout=20
&Retry=5
&MaxPending=10
This URL is recognized by the Esrimap.dll as registering project411c8239
for the host winnt on port 1045. This is sometimes written as winnt.wptc.com:1045,
where winnt.wptc.com is the host name on the Internet and 1035 is the socket.
A colon separates the two. There is a corresponding request to unregister.
http://winnt.wptc.com/.Esrimap
?UnReg=gifviewims411c8239
&Machine=winnt
&Port=1045
&Timeout=20
&Retry=5
&MaxPending=10
that frees up the mapping of the port to the name of the project.
This registration process builds a table in the DLL associating the name
parameter with a TCP/IP socket. An integral part of the AvIMS extension
is support for sockets. The Socket class allows ArcView to listen
to a port. When the DLL writes to this port, the AvIMS extension receives
the information and passes it to a Dispatcher. The default script provided
with the AvIMS is called AVINETMP.Dispatcher, but this script can be replaced
by any script using the WebLink.Make request. The contents of the conversation
between the DLL and ArcView can be traced or observed using the following
command:
Socket.Log("CON")
If this single statement is put in an Avenue script, compiled and executed,
the next time a URL arrives at the DLL and is passed to ArcView a console
window (it looks like a command line prompt window) opens, and the messages
being passed between the DLL and ArcView are displayed in the window.
Example 3.3: Tracing a Request
Short AvIMS URL
http://winnt.wptc.com/.Esrimap
?name=project411c8239
&cmd=test
Message sent from the DLL to ArcView
FD_READ
cmd=gifviewims.test&submit=Test
>> Going to execute MappingScript <<
Test: An Avenue Script (suppose this is what gets called by the Dispatcher)
'!Test
t = Script.The.GetName
p = av.GetProject
parms = SELF
rpt = {"<html><head><title>Metadata.TEST</title></head>"}
day = Date.Now.GetDay
today = Date.Now.SetFormat("M/d/yy").AsString
now = Date.Now.SetFormat("h:m:s").AsString
rpt.Add("<body><center><h1>Esri User Conference '97!!</h1><h1>On "
+ day + ",<br>" + today + ",<br>at " + now
+ ",<br>gifViewIMS Demo is up!</h1></center></body></html>")
parms.Set("Cmd",TRUE)
return rpt
Message sent from ArcView back to the DLL
FD_READ
cmd=gifviewims.test&submit=Test
>> Going to execute MappingScript <<
Content-type: text/html
<html><head><title>Metadata.TEST</title></head>
String send ok
<body><center><h1>Esri User Conference '97!!</h1><h1>On
Thursday,<br>4/17/97,<br>at 1:03:45,<br>
gifViewIMS Demo is up!</h1></center></body></html>
String send ok
<< Done executing MappingScript >>
Write Done ok
Write succeeded
What appears in the Client
Esri User Conference '97!!
On Thursday
4/17/97
at 1:03:45
gifViewIMS Demo is up!
Suppose the Dispatcher uses the value of the cmd
parameter to find and run an Avenue script. When the URL arrives at the
DLL, it sends the name/parameter part of the URL to the AvIMS extension
which passes it along to the Dispatcher. The Dispatcher launches the Test
script which writes a short HTML document back to the client. This simple
example is an excellent test of the connectivity of all the components
in the system.
To create Web compatible output, the GIS Server needs two more capabilities:
-
Create images: gif or JPEG
-
Write text strings containing HTML for a browser (see Test above) or
structured text to return to a Java client.
This is accomplished by the AvIMS with the following requests:
WriteHeader, Write,...
' WebLink.Script
t = Script.The.GetName
debug = true
debug = false
wl = WebLink.The
parms = Dictionary.Make(5) 'Make a dictionary to hold the name/value pairs
for each x in SELF 'Load the name/value pairs into the dictionary
parms.Add(x.Get(0),x.Get(1))
end
if (debug) then 'If debug=true, stop everything and show a list box
rpt = {} 'with the name/value pairs. Good for debugging???
for each key in parms.ReturnKeys
rpt.Add(key + " --> " + parms.get(key))
end
MsgBox.ListAsString(rpt,"Parms",t)
end
cmd = parms.Get("Cmd") 'Get the cmd-parameter and use its value in av.Run
result = av.Run(cmd,parms) 'Run the cmd-parameter script
cmd = parms.Get("Cmd") 'Cmd gets changed during the running of this script
'This is not a good idea!!! Use another variable.
wl.WriteResponseHeader("Content-type: TEXT.HTM"+CR+NL+CR+NL)
'Writes the response header that identifies the mime
'type of the datastream that follows
if (cmd = NIL) then
elseif (cmd = "Error") then
elseif (cmd) then 'If the script executed successfully, it returned a list
for each ln in result 'list of strings. Write them back to the client using
wl.writestring(ln) 'the writestring reques.
end
elseif (cmd.Not) then
else
end
return true
ExportTogif,ExportToJPEG
The ExportTogif and ExportToJPEG are View requests. They take the contents
of a view's DocWin and convert it to the gif or JPEG file. (Consult your
AvIMS documentation for details on using the gif format.)
The following Avenue code loads a set of themes into a View, sets the size
of the View's DocWin, zooms in and then converts the result to a graphics
image:
Example 3.4: Getting the Image
' Set the extent by reading the parameters for left, bottom, width and height
left = parms.Get("Left").AsNumber
bottom = parms.Get("Bottom").AsNumber
width = parms.Get("Width").AsNumber
height = parms.Get("Height").AsNumber
r = Rect.Make(left@bottom,width@height)
' Set the dimension
xSize = parms.Get("xSize").AsNumber
ySize = parms.Get("ySize").AsNumber
dimension = xSize@ySize
' Get the number of themes in the View
numThemes = parms.Get("num").AsNumber
' Make a view. v is empty to start with and its table of contents is set to 0 width
v = View.Make
v.SetTocWidth(0)
' Set Window Size
v.GetWin.ReSize(dimension.getX,dimension.getY)
' Zoom to area of interest
v.GetDisplay.ZoomToRect(r)
for each iTh in (0 .. (numThemes - 1))
iThNum = iTh
iThNum.SetFormat("d")
themeLegend = parms.Get("Th" + iThNum.AsString)
if (themeLegend.IndexOf("+") = -1) then
continue
end
th = _themelegend.Get(themelegend)
'_themelegend requires some explanation. First it is a global variable, hence persistent
'across executions of this script. Second, it is a dictionary. In it are stored key/value
'pairs: the key is a combination of a theme ID and a legend ID. The value is a reference
'to a theme object with that legend. The theme may also have a GraphicSet (annotation)
'attached to it. _themelegend could be stored in an object tag. We store it in an ODB. There
'are tools in our gifViewIMS project to publish themes with legends and annotation and
'store them in this ODB.
if (th = NIL) then
continue
end
th.SetActive(true)
th.SetVisible(true)
th.SetLegendVisible(false)
v.AddTheme(th)
end
v.GetWin.Open
v.Invalidate
' ------------------- Create the Graphic --------------------------
SafetyOn = true
'***************************
'* Create and Send the JPEG *
'***************************
gifDir = (_metaviewRoot + "gif\").AsFileName
gif = gifDir.MakeTmp("loc","jpg")
url = (_metaviewURL + "gif/") + gif.GetBaseName
tmpfn = v.ExportToJPEG(r,xsize,ysize,100,SafetyOn)
With this background, we are ready to sketch a set of core commands that
most AvIMS applications will implement.
4. The DRAW Command
AvIMS can export anything drawn in the View's DocWin. The Avenue requests,
aView.ExportTogif and aView.ExportToJPEG, capture the pixel map in the
MapDisplay for the visible extent. The result is stored in a temporary
file on the file system. The three key ingredients of this request are:
-
A rectangle r:
r marks the bounds of the extent of the visible area. One way to describe
r is with four number (in map coordinates). The left hand boundary, the
bottom boundary, the width and the height. The Avenue request that displays
the portion of the map inside the rectangle r is
aView.GetDisplay.ZoomToRect(r)
-
The dimensions, pixels, of the required graphic.
A DocWin has a size that can be obtained from its ReturnExtent request:
dimension = aView.GetWin.ReturnExtent 'Actually a point
width = dimension.GetX 'The x-coordinate is the width
'of the window
height = dimension.GetY 'The y-coordinate is the height
'of the window
-
The list of visible themes:
These are the layers in the map that we actually want to draw. For our
purposes, we'll assume this is a list of names. In the example above we
used Id's instead of names.
Example 4.1: The DRAW command (finishing the last example
in Section 3)
'!DRAW
t = Script.The.GetName
p = av.GetProject
Script.The.SetNumberFormat("d.dddddd")
debug = TRUE
debug = FALSE
if (debug or (SELF=NIL)) then
keyListClient = "Extent,Left,Bottom,Width,Height,xSize,ySize,num".AsTokens(",")
Test = Dictionary.Make(1)
Test.Add("1. Polygons",{"Client","-99","30","2","1","800","600","1","DET0000060+LEG0000025"})
Test.Add("2. Canada",{"Client","-140","40","87","47","800","600","1","DET0000061+LEG0000027"})
Test.Add("3. Canada/Zoom",{"Client","-71","42","10","7","800","600","1","DET0000061+LEG0000027"})
Test.Add("4. World/US/Canada/Mexico",{"Client","-180","-90","360","180","800","600","4","DET0000074+LEG0000029","DET0000098+LEG0000031","DET0000061+LEG0000027","DET0000068+LEG0000034"})
Test.Add("5. US/Counties",{"Client","-105","25","20","18","800","600","3","DET0000079+LEG0000033","DET0000098+LEG0000032","DET0000078+LEG0000040"})
Test.Add("6. US/Tx Counties/Travis",{"Client","-105","25","20","10","800","600","3","DET0000098+LEG0000031","DET0000077+LEG0000036","DET0000078+LEG0000040"})
Test.Add("7. Travis BG/Travis St",{"Client","-97.777121","30.306699","0.035443","0.025608","800","600","3","DET0000075+LEG0000030","DET0000073+LEG0000037","DET0000099+LEG0000035"})
Test.Add("8. Travis BG/St/Anno",{"Client","-97.777121","30.306699","0.035443","0.025608","800","600","2","DET0000075+LEG0000030","DET0000073+LEG0000039"})
Test.Add("9. CAD",{"Client","1211303.86","172087.93","24000","16200","800","600","2","DET0000113+LEG0000041","DET0000114+LEG0000042"})
keys = Test.ReturnKeys
keys.Sort(TRUE)
aTest = MsgBox.ListAsString(keys,"Pick a test:",t)
if (aTest = NIL) then exit end
parmList = Test.Get(aTest)
parms = Dictionary.Make(1)
for each x in keyListClient
parms.Add(x,parmList.Get(keyListClient.FindByValue(x)))
end
n = parms.Get("num").AsNumber
for each i in (0 .. (n - 1))
i.SetFormat("d")
parms.Add("Th" + i.AsString,parmList.Get(8+i))
end
else
parms = SELF
end 'DEBUG
_gDict = Dictionary.Make(1)
' Set the extent
left = parms.Get("Left").AsNumber
bottom = parms.Get("Bottom").AsNumber
width = parms.Get("Width").AsNumber
height = parms.Get("Height").AsNumber
r = Rect.Make(left@bottom,width@height)
' Set the dimension
xSize = parms.Get("xSize").AsNumber
ySize = parms.Get("ySize").AsNumber
dimension = xSize@ySize
numThemes = parms.Get("num").AsNumber
v = View.Make
v.SetTocWidth(0)
' Set Window Size
v.GetWin.ReSize(dimension.getX,dimension.getY)
' Zoom to area of interest
v.GetDisplay.ZoomToRect(r)
vList = v.GetGraphics
for each iTh in (0 .. (numThemes - 1))
iThNum = iTh
iThNum.SetFormat("d")
themeLegend = parms.Get("Th" + iThNum.AsString)
if (themeLegend.IndexOf("+") = -1) then
continue
end
th = _themelegend.Get(themelegend)
if (th = NIL) then
continue
end
for each x in th.GetGraphics
if (r.Intersects(x.GetBounds)) then
v.getGraphics.add(x)
end
end
th.SetActive(true)
th.SetVisible(true)
th.SetLegendVisible(false)
v.AddTheme(th)
end
v.GetWin.Open
vList.Invalidate
v.Invalidate
' ------------------- Create the Graphic --------------------------
SafetyOn = true
for each t in v.GetActiveThemes
if (t.Is(ITheme)) then
SafetyOn = false
break
end
if (t.GetClass.GetClassName = "GTheme") then
if (t.GetLegend.GetSymbols.Count > 10) then
SafetyOn = false
break
end
end
end
'***************************
'* Create and Send the gif *
'***************************
gifDir = (_metaviewRoot + "gif\").AsFileName
gif = gifDir.MakeTmp("loc","jpg")
url = (_metaviewURL + "gif/") + gif.GetBaseName
tmpfn = v.ExportToJPEG(r,xsize,ysize,100,SafetyOn)
wlink = WebLink.The
file.copy(tmpfn,gif)
file.delete(tmpfn)
p.RemoveDoc(v)
if (DEBUG and ((parms.Get("Java") = "N") or (parms.Get("Java")=NIL))) then
ns = DDEClient.Make("Netscape","WWW_OpenURL")
ns.Request(url + ",,-1")
ns.Close
ns = DDEClient.Make("Netscape","WWW_Activate")
ns.Request("-1,0")
ns.Close
elseif (DEBUG.Not and (parms.Get("Java") = "Y")) then
java = """" + left.AsString + "," + bottom.asString + "," + width.asString + "," + height.asString
java = java + "," + xSize.setFormat("d").asString + "," + ySize.setFormat("d").asString
java = java + ",1,ALL " + url
java = java + """"
parms.Set("CMD",true)
rpt = {java}
return rpt
else
parms.Set("CMD",true)
rpt = {"<html><head><title>ArcView Internet Map Server</title></head>"}
rpt.Add("<body bgcolor=#000000><table border=5 cellpadding=5 cellspacing=5><tr><td bgcolor=white><img src=" + url + "></td></tr></table>")
rpt.Add("</body></html>")
return rpt
end
One interesting characteristic of this code is the debug flag.The
default is for debug to be false, and the debugging blocks are skipped.
When debug is set to true, the script loads a list of possible tests of
this script. Picking one from the list, invokes the body of the script,
which produces a JPEG. At the bottom, if debug is true and the Netscape
browser is open, an HTML document containing the image is written to the
filesystem and using a DDE request the browser opens the document. When
you have finished testing several possibilities, the values and names of
the parameters that need to be encoded in the URL are obvious. This all
done completely within ArcView without any attachment to the Web.
This is the AvIMS side -- the Avenue code. The client needs to package
up a URL that encapsulates this information for transmission over the Web.
First we need a short digression on coordinate transformations:
Example 4.2: Coordinate Transformations
Suppose a map of the lower 48 states is drawn on a screen 600x400 pixels
in size. It might look like this:
The pixel coordinates of this image run from (0,0) in the upper left corner
to (399,299) in the lower right corner. x-values increase from left to
right and y-values increase from top to bottom. Roughly, the extent of
this image in longitude and latitude is:
left = -125.0
bottom = 24.0
width = 58.0
height = 26.0
The map coordinates of any pixel can be computed as follows. Suppose the
pixel coordinates of the point are (x,y), then
longitude = left + (x/400)*width
latitude = bottom + ((300-y)/300)*height
On the client-side, the map is usually redrawn as a result of some event,
some mouse click or keyboard action that results in a new display. It could
be something as simple as turning off or on a layer. This doesn't change
the map coordinates of the display or the pixel size of the screen. To
process this request we may need to send the server a request to draw a
new set of map layers.
Other possibilities include zooms or pans. For a zoom, the user drags a
rectangle on the map. The applet (written in Java) captures the pixel coordinates
of two corners of the rectangle and using a calculation like Example 4.2
computes the map coordinates of the rectangle. This is the r in the draw
request. The dimension of the window are known: 400x300 in our example.
The maplayers are US States and Texas Counties. A URL that captures all
this is:
http://winnt.wptc.com/.Esrimap
?name=project411c8239
&cmd=draw
&left=-125.0
&bottom=24.0
&width=58.0
&height=26.0
&xSize=400
&ySize=300
&layers=US+States%5ETexas+Counties
Putting it all together:
-
The user drags a rectangle on the map in the Client
-
The applet constructs the URL and sends it to the Web Server
-
The Web Server passes it to the DLL
-
The DLL looks up the socket and passes it to the appropriate instance
of AvIMS
-
The URL's contents are finally available to the Dispatcher
-
The Dispatcher launces the draw script and passes it the name/value
pairs encoded in the URL (in our case, we repackaged them in a dictionary
called parms).
-
The draw script dynamically looks up the themes, creates a view,
sets the window the right size, zooms to the requested extent, draws the
themes, scraps the screen to get the graphic and returns something to the
Java applet -- maybe the graphic, maybe just a reference to the graphic.
-
The applet displays the new map in the graphics window.
Slight variations on this sequence can be used to create support for the
following tools:
-
Pan
-
Zoom-Out
-
Zoom-To-Extent-Of-Active
-
Zoom-To-Extent--Of-Selected
Most of the changes are in the handling of the user's mouse input or menu
selections at the client, not in the URL or the server processing.
5. Other Basic Commands
Basic commands are functions used frequently by spatial processes.
DRAW is certainly an example of that. Others are:
-
Identify
Click on a feature and have the system return the attributes for the feature
URL:
http://winnt.wptc.com/.Esrimap
?name=project411c8239
&cmd=identify
&pt=93.56+32.131
&activelayer=Texas+Counties
This URL encodes a point someplace over Travis County, Texas. If the client
has the capabilities to make any layer active, at any time, then this basic
command supplies an identify function.
Select
Using graphics like points, lines, polylines, polygons, circles and rectangles,
select features in a spatial dataset. We'll only consider a selection criteria
where the feature intersects the graphic, but there are many other possibilities
URL:
http://winnt.wptc.com/.Esrimap
?name=project411c8239
&cmd=select
&graphic=rectangle+left+bottom+width+height
&seltype=intersect
&tolerance=distance
&activelayer=US+States
In this example, the graphic captures the type of shape as its first value
(remember the +'s get converted to blanks when the URL's are decoded),
and then a string of numbers. When these are paired together, they form
a list of points that define the shape. For the rectangle, we use the (left,bottom)
lower left hand corner and the width and height of the rectangle. For a
circle, it might be the center and a point on the boundary. The seltype
defines the type of selection -- features that intersect the graphic, features
that contain the graphic, ... A tolerance allows the select all features
within the tolerance distance of the graphic.
ReturnExtent
Given a feature (or more generally, a list of features), return the map
coordinates of the smallest rectangle containing all the features
URL:
http://winnt.wptc.com/.Esrimap
?name=project411c8239
&cmd=returnextent
&feature=f
This is simple. The extents of the feature are computed by AvIMS and returned
to the client.
Locate
Given an address, geocode the address and return a point in map coordinates.
There are many other similar possibilities for a locate function.
URL:
http://winnt.wptc.com/.Esrimap
?name=project411c8239
&cmd=locate
&address=3305+Hancock+Drive
&matchable=Travis+County+Streets
The client obtains the address, sends it to the server, specifying a theme
that supports address matching and geocoding. In ArcView, this is called
a MatchTheme.
6. Writing Scripts with Basic Commands
With a set of basic commands, it is now possible to write simple scripts,
package them up as part of the URL, and execute these scripts at the GIS
Server. As an example we show how to write a script that geocodes an address,
selects a feature using the location of the address and then draws a map
using the extent of the feature.
Sample Script
Suppose we are given the dimension d of the screen (say 600x400 pixels)
and the MapLayers to draw. MapLayers is a list with ArcView Theme ID's
or Name's. For example, MapLayers = USA+Canada+Mexico requests the drawing
of three layers: the USA, Canada and Mexico
pt=locate(anAddress,aMatchableTheme)
f=select(pt,aMapLayer)
r=returnExtent(f)
result=draw(r,d,MapLayers)
Sample URL
http://winnt.wptc.com/.Esrimap
?name=avims043c1234
&cmd=execute
&script=pt%3Dlocate%28anAddress%2CaMatchableLayer%29%0Df%3Dselect%2Dpt%2CaMapLayer%29%0D
r%3DreturnExtent%28f%29%0Dresult%3Ddraw%28r%2Cd%2CMapLayers%29
&anAddress=3305+Hancock+Drive
&aMatchableTheme=Travis+County+Streets
&aMapLayer=USA
&dimension=600+400
&maplayers=USA+Canada+Mexico
What this sample URL does is encode the script, specify the execute
method on AvIMS be invoked to process the script with the specified variables.
To help with the decoding:
-
%3D is an =
-
%28 is (
-
%29 is )
-
%2C is ,
-
...and remember that +'s replace blanks (600+400 and USA+Canada+Mexico).
Finally, the name/value pairs each start on a separate line to improve
readability. The actual URL would be on one impossibly long line that was
difficult to decipher.
Execute
Here is a skeleton of what the Avenue code might look like to process the
script included in this URL. Dictionaries are used extensively to store
and retrieve variables and their values by name. This is very similar to
what is called a Symbol Table in most languages. Our rendition of a scripting
language is simplistic, without error checking, completely lacking in looping
and flow of control constructs, but it does illustrate how basic atomic
commands on the server might be combined by scripts written on the client!
(Note: it isn't really necessary to invent a new scripting language: Avenue
scripts written at the client, could also be passed to the server for execution.)
Example 6.1
'!Execute
t = Script.The.GetName
p = av.GetProject
debug=true
'debug=false
if (debug) then
parms=Dictionary.Make(2)
parms.add("anAddress","3305 Hancock Drive")
parms.add("aMatchableTheme","Travis County Streets")
parms.add("aMapLayer","USA")
parms.add("dimension","400 300")
parms.add("maplayers","USA Canada Mexico") 'This is awful. Never use blanks as a delimiter!
s = "pt=locate(anAddress,aMatchableTheme)" + nl
s = s + "f=select(pt,aMapLayer)" + nl
s = s + "r=returnExtent(f)" + nl
s = s + "result=draw(r,dimension,MapLayers)" + nl
parms.add("script",s)
else
parms=SELF
end
source = parms.get("script").AsTokens(nl)
for each ln in source
x = ln.AsTokens("=")
retVal = x.Get(0)
y = x.Get(1).AsTokens("()") ' the function call split on opening and closing parens
fcn = y.Get(0)
vars = y.Get(1).AsTokens(",")
vals={}
for each v in vars
vals.Add(parms.Get(v))
end
answer = av.Run(fcn,vals)
parms.add(retVal,answer)
end
return parms.Get("result")
This script was checked with debug=true. The implementation of locate,
select, returnextent and draw are what remains to be done.
7. The Future of Client/Server GIS
The ArcView IMS (and also MapObjects Internet Map Server) is the application
server in a three-tier client/server architecture. By passing URL's to
a Web Server and then to AVIMS, Web Clients can request a wide variety
of services from ArcView. Objects in the client are expressed as name/value
pairs in the URL. This is referred to as serialization or marshalling.
The objects are then reconstructed within ArcView. This protocol, although
crude and somewhat cumbersome, is an example of distributed objects and
remote method invocation. The future for client/server GIS is the following:
-
Multiple servers accessed by a single client
-
A GIS Object Model in the client that allows vector features to be
transported to the client and managed exactly like any other layers created
as graphics layers at a network host.
-
A common GIS Object Model in the client and the server and less distinction
between their client and server roles. To support functions like a shared
whiteboard all participants must be both clients and servers.
-
Less knowledge of the network protocols will be required as networked
objects become more prevalent. For example, DCOM, CORBA or the Java's Remote
Method Invocation.
-
Components for distributed GIS built on Java Beans or ActiveX.
Client/server GIS is challenging and exciting. It opens new opportunities
for using GIS in networked, distributed application development.
John P. Alexander
wptc
3305 Hancock Drive
Austin, Texas 78731
Telephone: 512-459-9027
Fax: 512-452-1655
EMail: john@wptc.com
Valerie J. Warwick
wptc
3305 Hancock Drive
Austin, Texas 78731
Telephone: 512-459-9027
Fax: 512-452-1655
EMail: valerie@wptc.com
©Copyright 1996,wptc, Austin, Texas