|
Imagemaps
chapter 15
·
Server or Client?
·
Imagemaps Using Forms
·
Pretty Tic-Tac-Toe
·
ISMAP
·
A Simple Imagemap CGI
·
NCSA Imagemap
·
Summary
People
often call the medium of the World Wide Web hypermedia because
it not only supports hypertext (textual links to other documents) but also
graphical links to other documents. The ability to link certain parts of a
graphic to other documents is called hyperimage, more commonly called
imagemapping.
The idea behind hyperimages is
that where you click on a certain image determines what document the browser
accesses next. You can summarize the normal behavior of an imagemap as
follows:
·
A user clicks on an image.
·
The browser determines where on the image the user
clicked.
·
The browser somehow determines what to
do next according to where the user clicked. Usually, the browser just
accesses another document
You can use imagemaps for a
variety of purposes from a clickable hypertext map to a fancy graphical
index. In this chapter, you learn about the various implementations of
imagemaps and using forms and the HTML attribute, ISMAP, for creating and
controlling imagemaps.
Server or
Client?
What should take the
responsibility of handling imagemaps: the server or the client? Clearly, the
client needs to handle at least part of the processing. It needs to
determine where the user has clicked on the image.
Handling imagemaps through the
server using CGI is inefficient. It requires the client telling the server
where the user clicked, the server spawning a new program, this program
opening a map file that contains the defined regions and URLs, and finally
the server accessing the new URL. If you program the server to handle
imagemap processing internally, the process speeds up tremendously.
|
Note |
|
Many servers do support
imagemaps internally (meaning that you don't need to use a CGI program
to process an imagemap) including NCSA, Apache, Netsite, and WN. Refer
to the documentation for your particular server for more information. |
Having the client handle imagemaps
internally gives you the same (or better) speed benefits as a server that
handles imagemaps internally. However, having either the client or the
server deal with imagemaps internally means a loss in flexibility handling
the imagemap. The client-side imagemap extensions to HTML "Proprietary
Extensions"), for example, support only three shapes: rectangles, circles,
and polygons. You might want the imagemap processor to determine whether the
user has clicked closer to one point than another, for example. You might
have hundreds of imagemaps that have the same basic look and that access the
same type of information in different directories. Instead of having to
specify the coordinates and different files for these hundreds of images, it
would be easier to have one program that knows the specified format of the
imagemap and the general locations of every file and would need only one
parameter to find the right document locations.
Finally, your users might not have
a browser that supports client-side imagemaps, or you might not be able to
find a server that has the built-in imagemap capabilities that you need. A
CGI imagemap program, however, works on all current Web servers that support
CGI.
|
Note |
|
If you use a client-side
application such as a Java applet, you can extend the imagemap
functionality to perform tasks you can't do with server-side imagemaps.
For example, you can emphasize regions on the image when your mouse
pointer lies above them. Or you can have messages pop up when you pass
a certain region of the image. |
Because of this flexibility, most
imagemaps are implemented using CGI programs. All that is necessary is for
the client to somehow pass to the CGI program the coordinates where the user
clicks. Using CGI, you can obtain this information in two ways: forms and
ISMAP. Both are discussed in the following sections.
Imagemaps
Using Forms
You can pass the coordinates of a
user's selection using HTML forms using the <input type=image> tag.
The proper syntax for this tag is as follows:
<INPUT TYPE=IMAGE SRC=" . . .
" NAME=" . . . " [ALIGN=" . . . "]>
SRC is the location of the image
relative to document root, NAME is the name of the field, and ALIGN is
equivalent to the ALIGN parameter for the <img> tag. On a browser that
supports this tag, the image is displayed. Clicking on any part of the image
is equivalent to clicking on a Submit button. If NAME is name, the values
name.x and name.y are transmitted to the CGI program through normal form and
URL encoding. These two fields contain the coordinates where the user
clicked.
Pretty Tic-Tac-Toe
Using the <input type=image>
tag, you can extend (and greatly simplify) the tic-tac-toe game from Chapter
13, "Multipart Forms and Maintaining State." The new specifications for a
tic-tac-toe game using forms and images are as follows:
·
The game
should dynamically generate one large image with the current board
positions.
·
It should
enable the user to click on a part of the image to select his or her next
move.
The first new requirement does not
really take advantage of any imagemapping feature. To generate the
tic-tac-toe board dynamically, I use Thomas Boutell's gd library; the code
is shown in Listing 15.1.
Listing 15.1. Tic-tac-toe board
using board.c.
#include <stdio.h>
#include <math.h>
#include "gd.h"
#include "string-lib.h"
#define LENGTH 170
#define CLENGTH 44
#define D1 55
#define D2 115
static int loc[3] = {0,60,120};
void draw_xo(gdImagePtr board,char xo,int x,int y,int color)
{
if (xo == 'x') {
gdImageLine(board,x,y,x+CLENGTH,y+CLENGTH,color);
gdImageLine(board,x+CLENGTH,y,x,y+CLENGTH,color);
}
else if (xo == 'o')
gdImageArc(board,x+CLENGTH/2,y+CLENGTH/2,CLENGTH,CLENGTH,0,360,color);
}
int main()
{
char *state = getenv("QUERY_STRING");
gdImagePtr board;
int white,black;
int i;
/* create GIF */
board = gdImageCreate(LENGTH,LENGTH);
white = gdImageColorAllocate(board,255,255,255);
gdImageColorTransparent(board,white);
black = gdImageColorAllocate(board,0,0,0);
gdImageLine(board,D1,0,D1,LENGTH-1,black);
gdImageLine(board,D2,0,D2,LENGTH-1,black);
gdImageLine(board,0,D1,LENGTH-1,D1,black);
gdImageLine(board,0,D2,LENGTH-1,D2,black);
if (state != NULL)
for (i=0; i<9; i++)
draw_xo(board,state[i],loc[i%3],loc[i/3],black);
/* send GIF */
printf("Content-Type: image/gif\r\n\r\n");
gdImageGif(board,stdout);
}
Given the state information in the
same form as provided in the Chapter 13 example. In other words, given a
nine-character state string consisting of Xs, Os, and underscores, the board
program displays the proper board.
Now that you have a program that
dynamically generates the desired image, you can modify tictactoe.c to take
the coordinates, update the board accordingly, and send a new form and
image. Because the program is in C, I use read_cgi_input() to get the values
of board.x and board.y. After I have these values, I determine where these
coordinates are relative to the board position and take the appropriate
action. The revised tic-tac-toe program, tictactoe.c, is shown in Listing
15.2.
Listing 15.2. The tictactoe.c
program.
#include <stdio.h>
#include <math.h>
#include "cgi-lib.h"
#include "html-lib.h"
#define LENGTH 170
#define D1 55
#define D2 115
void set_board(int board[3][3],char *state)
{
int i;
for (i = 0; i<9; i++) {
if (state[i] == 'x')
board[i%3][i/3] = 1;
else if (state[i] == 'o')
board[i%3][i/3] = -1;
else
board[i%3][i/3] = 0;
}
}
char *board2string(int board[3][3])
{
char *str = malloc(10);
int i,j;
for (j=0; j<3; j++)
for (i=0; i<3; i++) {
if (board[i][j] == 1)
str[i+j*3] = 'x';
else if (board[i][j] == -1)
str[i+j*3] = 'o';
else
str[i+j*3] = '_';
}
str[9] = '\0';
return str;
}
int adjust_coord(int num)
{
if (num > D2)
return 2;
else if (num > D1)
return 1;
else
return 0;
}
int check_winner(int board[3][3])
{
int i,j;
short FOUND = 0;
/* 0 = go on, 1 = human wins, -1 = computer wins, 2 = stalemate */
/* sum up horizontals */
for (j = 0; j<3; j++) {
if (board[0][j]+board[1][j]+board[2][j] == 3)
return 1;
else if (board[0][j]+board[1][j]+board[2][j] == -3)
return -1;
}
/* try verticals */
for (i = 0; i<3; i++) {
if (board[i][0]+board[i][1]+board[i][2] == 3)
return 1;
else if (board[i][0]+board[i][1]+board[i][2] == -3)
return -1;
}
/* now test diagonals */
i = board[0][0]+board[1][1]+board[2][2];
j = board[2][0]+board[1][1]+board[0][2];
if ( (i==3) || (j==3) )
return 1;
else if ( (i==-3) || (j==-3) )
return -1;
for (j = 0; j<3; j++)
for (i = 0; i<3; i++)
if (board[i][j] == 0)
FOUND = 1;
if (FOUND)
return 0;
else
return 2;
}
void computer_move(int board[3][3])
{
int positions[9];
int i,j,move;
int num = 0;
/* we can assume there are empty positions; otherwise, this function
would not have been called */
/* find empty positions */
for (j=0; j<3; j++)
for (i=0; i<3; i++)
if (board[i][j] == 0) {
positions[num] = i+j*3;
num++;
}
/* pick random number from 0 to num-1 */
move = (int) ((double) num*rand()/(RAND_MAX+1.0));
board[positions[move]%3][positions[move]/3] = -1;
}
void print_play(char *msg,char *state)
{
html_begin(msg);
h1(msg);
printf("<p><img src=\"/cgi-bin/board?%s\"></p>\n",state);
printf("<p><a href=\"/cgi-bin/tictactoe\">");
printf("Play again?</a></p>\n");
}
void print_move(char *msg,char *state)
{
html_begin(msg);
h1(msg);
printf("<form action=\"/cgi-bin/tictactoe?%s\" method=POST>\n",state);
printf("<input type=image name=\"board\" ");
printf("src=\"/cgi-bin/board?%s\">\n",state);
printf("</form>\n");
}
void print_board(int board[3][3], int x, int y)
{
int winner;
char state[9];
html_header();
strcpy(state,board2string(board));
if (x != -1) { /* check for valid move and winner */
if (board[x][y] == 0) { /* valid move */
board[x][y] = 1;
strcpy(state,board2string(board));
winner = check_winner(board);
if (winner == 1) /* human wins */
print_play("You Win!",state);
else if (winner == 2)
print_play("Stalemate",state);
else if (winner == 0) { /* computer's turn */
computer_move(board);
strcpy(state,board2string(board));
winner = check_winner(board);
if (winner == -1)
print_play("Computer Wins!",state);
else if (winner == 2)
print_play("Stalemate",state);
else
print_move("Your Move",state);
}
}
else
print_move("Invalid Move. Try again.",state);
}
else
print_move("Your Move",state);
html_end();
}
int main()
{
int board[3][3];
int x,y;
llist coordinates;
if (QUERY_STRING == NULL)
set_board(board,"_________");
else
set_board(board,QUERY_STRING);
if (read_cgi_input(&coordinates)) {
x = adjust_coord(atoi(cgi_val(coordinates,"board.x")));
y = adjust_coord(atoi(cgi_val(coordinates,"board.y")));
}
else {
x = -1; /* user didn't click on anything */
y = -1;
}
print_board(board,x,y);
list_clear(&coordinates);
}
I changed very little of
tictactoe.c to incorporate the interactive imagemap, yet now the game is
much more usable and better looking (although still as silly as ever).
You cannot implement this
tic-tac-toe game using a generic implementation of imagemaps (such as
client-side HTML imagemaps, the NCSA imagemap CGI program, or any other
standard imagemap implementation). You need a custom CGI program to
interpret and respond to the user's clicks properly.
ISMAP
Although the <input type=image>
form tag provides a nice, consistent interface to imagemaps, it has a few
flaws. Historically, this tag has not been supported by browsers, and
consequently, it is not a commonly used tag. This tag also does not enable
you to specify an alternative tag for text-only browsers.
The more common way of
implementing imagemaps is by using an attribute of the <img> tag called
ISMAP. When you have a hyperlinked inline image normally, clicking anywhere
on the image takes you to the link. For example, if you click anywhere on
the image specified by the following you go to happy.html:
<a href="happy.html"><img src="happyface.gif"></a>
If you add the ISMAP parameter to
the <img> tag, then the X and Y coordinates where the user clicks on the
image are appended to the URL specified in the <a href> tag. For example, if
you click on the pixel located at (10,15) on the image specified by the
following:
<a href="http://myserver.org/happy.html">
<img src="happyface.gif" ISMAP></a>
the browser sends the server the
following request and the coordinates are sent to QUERY_STRING:
http://myserver.org/happy.html?10,15
At this point, it is up to the
server to interpret this request properly. Normally, you specify a CGI
program in the <a href> tag to process the coordinates, although servers
with built-in imagemap capabilities automatically know how to process these
requests without the aid of CGI.
A Simple Imagemap CGI
Processing the results from an
ISMAP request is easy. The coordinates are sent to QUERY_STRING. Everything
before the comma is the X coordinate, and everything after the comma is the
Y coordinate. What you do after you have this coordinate is slightly more
complex and depends on the nature of your application. In general, though,
you want to go to another document depending on where the user clicks.
Where are you going to define the
regions and associated documents? You can hard code this information in your
program, but for most applications, doing so is not very useful. Having a
configuration file for every imagemap that defines the regions and
associated URLs is nice. After your imagemap program determines the region
in which the user clicks, it sends a Location header followed by the URL. To
specify the location of the configuration file, you can append it to the URL
and check the PATH_TRANSLATED environment variable.
Because you want to develop a
simple imagemap program (as opposed to a complex one, for purposes of this
chapter), this imagemap program checks only for rectangular regions. The
configuration file, specified in the PATH_TRANSLATED environment variable,
looks something like this:
0 10 300 http://www.mcp.com/
25 10 40 /images/brown.gif
The format for each line is as
follows:
xmin ymin xmax ymax URL
In other words, the first two
numbers define the upper-left corner of the rectangular region, the second
two numbers define the lower-right corner, and the last item is either a URL
or a document location relative to document root.
What should this imagemap program
do if the user clicks on an undefined region? The best response is probably
nothing, in this case, so just have it send a Status: 204 no content header.
Finally, how should this CGI
respond when it is accessed from a text-only browser such as Lynx? This
answer is somewhat of a dilemma. On the one hand, you want your pages to be
as accessible as possible; on the other hand, by nature, a textual browser
simply cannot take advantage of all the features a multimedia browser can. A
good compromise is for the imagemap program to test whether the browser is a
text browser by checking the HTTP_ACCEPT for the substring image (as in the
MIME types image/gif or image/jpeg). If the browser is a text browser, then
display all the URLs available in the map configuration file.
Here's a quick summary of the
features of a simple imagemap program:
·
Define
rectangular regions and associated URLs in a separate configuration file,
happyface.map. Specify this configuration file in the <a href> link using
the PATH_TRANSLATED environment variable.
·
If the user
clicks on an undefined area, the imagemap should ignore it by sending a
Status: 204 header.
·
The
imagemap should display a list of URLs for text browsers.
The Perl code for this program-imagemap.pl-which
checks the HTTP_ACCEPT ENV variable, is shown in Listing 15.3. The proper
HTML code for correct usage of imagemap is as follows:
<a href="/cgi-bin/imagemap.pl/happyface.map">
<img src="happyface.gif ISMAP></a>
Listing 15.3. The imagemap.pl
program.
#!/usr/local/bin/perl
require 'cgi-lib.pl';
# get info
$mapfile = $ENV{'PATH_TRANSLATED'};
&CgiDie("Error","No .map file specified.") unless ($mapfile);
$server = $ENV{'SERVER_NAME'};
$port = $ENV{'SERVER_PORT'};
if ($ENV{'HTTP_ACCEPT'} =~ /image/) {
$TEXT = 0;
}
else {
$TEXT = 1;
}
$QUERY_STRING = $ENV{'QUERY_STRING'};
if (!$TEXT && !($QUERY_STRING =~ /,/)) {
&CgiDie("Error","Your browser does not handle imagemaps correctly.");
}
($x = $QUERY_STRING) =~ s/,.*$//;
($y = $QUERY_STRING) =~ s/^.*,//;
# open .map file
open(MAP,$mapfile) || &CgiDie("Error","Can't open $mapfile");
$FOUND = 0;
$i = 0;
while ($line = <MAP>) {
$line =~ s/[\r\n]//g;
($xmin,$ymin,$xmax,$ymax,$url) = split(/\s+/,$line);
if ($TEXT) {
$urls[$i] = $url;
$i++;
}
elsif (&within($x,$y,$xmin,$ymin,$xmax,$ymax)) {
$FOUND = 1;
if ($url =~ /:/) { # full URL
print "Location: $url\n\n";
}
else { # virtual URL
print "Location: http://$server:$port$url\n\n";
}
}
}
close(MAP);
if ($TEXT) {
print &PrintHeader,&HtmlTop("Imagemap: $ENV{'PATH_INFO'}");
print "<ul>\n";
foreach $url (@urls) {
print " <li><a href=\"$url\">$url</a>\n";
}
print "</ul>\n";
print &HtmlBot;
}
elsif (!$FOUND) {
print "Status: 204 Do nothing\n\n";
}
sub within {
local($x,$y,$xmin,$ymin,$xmax,$ymax) = @_;
if (($x>=$xmin) && ($x<=$xmax) &&
($y>=$ymin) && ($y<=$ymax)) {
return 1;
}
else {
return 0;
}
}
Although imagemap.pl is simplistic
in that it is not very configurable, it is fairly powerful in many other
ways. For example, it is robust. If someone clicks an undefined area,
imagemap.pl ignores the click rather than sends an error message. If you
specify an invalid or nonexistent configuration file, it sends an error
message. It also makes your pages accessible to text browsers.
NCSA Imagemap
Perhaps the most well-used CGI
program is imagemap.c, the imagemap program that is included in the NCSA
server package (which, at the time of this writing, is the most popular
server in use on the Internet). It is fairly full-featured, thanks to
patches contributed by many people over the last few years.
NCSA's imagemap has both a central
configuration file and a decentralized configuration. You can specify
separate configuration files on your own the same way you do with
imagemap.pl-by including it in the PATH_TRANSLATED variable. If you don't
specify the location of the .map file, it checks a central configuration
file for its location. If it can't find the location there (or if it can't
find a central configuration file), then it gives an error.
The imagemap.c program also
handles several different kinds of shapes: rectangles, circles, polygons,
and points. It also lets you specify a default URL for the undefined
regions. A typical .map file might look like the following:
default /index.html
rect /chapter1/index.html 0,0 100,40
circle http://www.harvard.edu/ 80,80 60,80
poly http://www.math.harvard.edu/ 120,0 140,0 145,20 115,25
point http://www.thespot.com/ 227,227
With rectangles, the first point
is the upper-left corner, and the second is the lower-right corner. The
first point on the circle coordinate is the center point, and the second
point is any point on the edge of the circle. Each point following the poly
attribute is a vertex of the polygon, and point specifies whether the user
clicks near that point.
Finally, because imagemap.c is
written in C, it is much more responsive than imagemap.pl (written in Perl).
If you use a lot of imagemapping on your site, then you probably want to use
a C version of imagemap. The code for imagemap.c is shown in Listing 15.4.
Listing 15.4. The imagemap.c
program.
/*
** mapper 1.2
** 7/26/93 Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
** "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
** All suggestions, help, etc. gratefully accepted!
**
** 1.1 : Better formatting, added better polygon code.
** 1.2 : Changed isname(), added config file specification.
**
** 11/13/93: Rob McCool, robm@ncsa.uiuc.edu
**
** 1.3 : Rewrote configuration stuff for NCSA /htbin script
**
** 12/05/93: Rob McCool, robm@ncsa.uiuc.edu
**
** 1.4 : Made CGI/1.0 compliant.
**
** 06/27/94: Chris Hyams, cgh@rice.edu
** Based on an idea by Rick Troth (troth@rice.edu)
**
** 1.5 : Imagemap configuration file in PATH_INFO. Backwards compatible.
**
** Old-style lookup in imagemap table:
** <a href="http://foo.edu/cgi-bin/imagemap/oldmap">
**
** New-style specification of mapfile relative to DocumentRoot:
** <a href="http://foo.edu/cgi-bin/imagemap/path/for/new.map">
**
** New-style specification of mapfile in user's public HTML directory:
** <a href="http://foo.edu/cgi-bin/imagemap/~username/path/for/new.map">
**
** 07/11/94: Craig Milo Rogers, Rogers@ISI.Edu
**
** 1.6 : Added "point" datatype: the nearest point wins. Overrides
"default".
**
** 08/28/94: Carlos Varela, cvarela@ncsa.uiuc.edu
**
** 1.7 : Fixed bug: virtual URLs are now understood.
** Better error reporting when not able to open configuration file.
**
** 03/07/95: Carlos Varela, cvarela@ncsa.uiuc.edu
**
** 1.8 : Fixed bug (strcat->sprintf) when reporting error.
** Included getline() function from util.c in NCSA httpd distribution.
**
*/
#include <stdio.h>
#include <string.h>
#ifndef pyr
#include <stdlib.h>
#else
#include <ctype.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#define CONF_FILE "/usr/local/etc/httpd/conf/imagemap.conf"
#define MAXLINE 500
#define MAXVERTS 100
#define X 0
#define Y 1
#define LF 10
#define CR 13
int isname(char);
int main(int argc, char **argv)
{
char input[MAXLINE], *mapname, def[MAXLINE], conf[MAXLINE], errstr[MAXLINE];
double testpoint[2], pointarray[MAXVERTS][2];
int i, j, k;
FILE *fp;
char *t;
double dist, mindist;
int sawpoint = 0;
if (argc != 2)
servererr("Wrong number of arguments, client may not support ISMAP.");
mapname=getenv("PATH_INFO");
if((!mapname) || (!mapname[0]))
servererr("No map name given. Please read the <A HREF=\"http://hoohoo.ncsa.uiuc.edu/docs/setup/admin/Imagemap.html\
Â">instructions</A>.<P>");
mapname++;
if(!(t = strchr(argv[1],',')))
servererr("Your client doesn't support image mapping properly.");
*t++ = '\0';
testpoint[X] = (double) atoi(argv[1]);
testpoint[Y] = (double) atoi(t);
/*
* if the mapname contains a '/', it represents a unix path -
* we get the translated path, and skip reading the configuration file.
*/
if (strchr(mapname,'/')) {
strcpy(conf,getenv("PATH_TRANSLATED"));
goto openconf;
}
if ((fp = fopen(CONF_FILE, "r")) == NULL){
sprintf(errstr, "Couldn't open configuration file: %s", CONF_FILE);
servererr(errstr);
}
while(!(getline(input,MAXLINE,fp))) {
char confname[MAXLINE];
if((input[0] == '#') || (!input[0]))
continue;
for(i=0;isname(input[i]) && (input[i] != ':');i++)
confname[i] = input[i];
confname[i] = '\0';
if(!strcmp(confname,mapname))
goto found;
}
/*
* if mapname was not found in the configuration file, it still
* might represent a file in the server root directory -
* we get the translated path, and check to see if a file of that
* name exists, jumping to the opening of the map file if it does.
*/
if(feof(fp)) {
struct stat sbuf;
strcpy(conf,getenv("PATH_TRANSLATED"));
if (!stat(conf,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
goto openconf;
else
servererr("Map not found in configuration file.");
}
found:
fclose(fp);
while(isspace(input[i]) || input[i] == ':') ++i;
for(j=0;input[i] && isname(input[i]);++i,++j)
conf[j] = input[i];
conf[j] = '\0';
openconf:
if(!(fp=fopen(conf,"r"))){
sprintf(errstr, "Couldn't open configuration file: %s", conf);
servererr(errstr);
}
while(!(getline(input,MAXLINE,fp))) {
char type[MAXLINE];
char url[MAXLINE];
char num[10];
if((input[0] == '#') || (!input[0]))
continue;
type[0] = '\0';url[0] = '\0';
for(i=0;isname(input[i]) && (input[i]);i++)
type[i] = input[i];
type[i] = '\0';
while(isspace(input[i])) ++i;
for(j=0;input[i] && isname(input[i]);++i,++j)
url[j] = input[i];
url[j] = '\0';
if(!strcmp(type,"default") && !sawpoint) {
strcpy(def,url);
continue;
}
k=0;
while (input[i]) {
while (isspace(input[i]) || input[i] == ',')
i++;
j = 0;
while (isdigit(input[i]))
num[j++] = input[i++];
num[j] = '\0';
if (num[0] != '\0')
pointarray[k][X] = (double) atoi(num);
else
break;
while (isspace(input[i]) || input[i] == ',')
i++;
j = 0;
while (isdigit(input[i]))
num[j++] = input[i++];
num[j] = '\0';
if (num[0] != '\0')
pointarray[k++][Y] = (double) atoi(num);
else {
fclose(fp);
servererr("Missing y value.");
}
}
pointarray[k][X] = -1;
if(!strcmp(type,"poly"))
if(pointinpoly(testpoint,pointarray))
sendmesg(url);
if(!strcmp(type,"circle"))
if(pointincircle(testpoint,pointarray))
sendmesg(url);
if(!strcmp(type,"rect"))
if(pointinrect(testpoint,pointarray))
sendmesg(url);
if(!strcmp(type,"point")) {
/* Don't need to take square root. */
dist = ((testpoint[X] - pointarray[0][X])
* (testpoint[X] - pointarray[0][X]))
+ ((testpoint[Y] - pointarray[0][Y])
* (testpoint[Y] - pointarray[0][Y]));
/* If this is the first point, or the nearest, set the default. */
if ((! sawpoint) || (dist < mindist)) {
mindist = dist;
strcpy(def,url);
}
sawpoint++;
}
}
if(def[0])
sendmesg(def);
servererr("No default specified.");
}
sendmesg(char *url)
{
if (strchr(url, ':')) /*** It is a full URL ***/
printf("Location: ");
else /*** It is a virtual URL ***/
printf("Location: http://%s:%s", getenv("SERVER_NAME"),
getenv("SERVER_PORT"));
printf("%s%c%c",url,10,10);
printf("This document has moved <A HREF=\"%s\">here</A>%c",url,10);
exit(1);
}
int pointinrect(double point[2], double coords[MAXVERTS][2])
{
return ((point[X] >= coords[0][X] && point[X] <= coords[1][X]) &&
(point[Y] >= coords[0][Y] && point[Y] <= coords[1][Y]));
}
int pointincircle(double point[2], double coords[MAXVERTS][2])
{
int radius1, radius2;
radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] -
coords[1][Y])) + ((coords[0][X] - coords[1][X]) * (coords[0][X] -
coords[1][X]));
radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y])) +
((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
return (radius2 <= radius1);
}
int pointinpoly(double point[2], double pgon[MAXVERTS][2])
{
int i, numverts, inside_flag, xflag0;
int crossings;
double *p, *stop;
double tx, ty, y;
for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++)
;
numverts = i;
crossings = 0;
tx = point[X];
ty = point[Y];
y = pgon[numverts - 1][Y];
p = (double *) pgon + 1;
if ((y >= ty) != (*p >= ty)) {
if ((xflag0 = (pgon[numverts - 1][X] >= tx)) ==
(*(double *) pgon >= tx)) {
if (xflag0)
crossings++;
}
else {
crossings += (pgon[numverts - 1][X] - (y - ty) *
(*(double *) pgon - pgon[numverts - 1][X]) /
(*p - y)) >= tx;
}
}
stop = pgon[numverts];
for (y = *p, p += 2; p < stop; y = *p, p += 2) {
if (y >= ty) {
while ((p < stop) && (*p >= ty))
p += 2;
if (p >= stop)
break;
if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
if (xflag0)
crossings++;
}
else {
crossings += (*(p - 3) - (*(p - 2) - ty) *
(*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
}
}
else {
while ((p < stop) && (*p < ty))
p += 2;
if (p >= stop)
break;
if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
if (xflag0)
crossings++;
}
else {
crossings += (*(p - 3) - (*(p - 2) - ty) *
(*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
}
}
}
inside_flag = crossings & 0x01;
return (inside_flag);
}
servererr(char *msg)
{
printf("Content-type: text/html%c%c",10,10);
printf("<title>Mapping Server Error</title>");
printf("<h1>Mapping Server Error</h1>");
printf("This server encountered an error:<p>");
printf("%s", msg);
exit(-1);
}
int isname(char c)
{
return (!isspace(c));
}
int getline(char *s, int n, FILE *f) {
register int i=0;
while(1) {
s[i] = (char)fgetc(f);
if(s[i] == CR)
s[i] = fgetc(f);
if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}
Although imagemap.c is very
powerful, it lacks two important features that imagemap.pl has. First, if no
default is specified, it sends an error if the user clicks on an undefined
region. Second, it is not friendly for text browsers. Being able to define
an alternative text file for your imagemaps in the .map file would be nice.
Modifying imagemap.c to handle both problems is fairly easy, and I encourage
you to try it as an exercise.
Summary
Imagemapping literally enables you
to add a new dimension to your Web sites. Although it is not clear whether
it is the role of the client or the server to handle imagemapping (both have
been implemented), you can find some advantages to handling imagemaps on the
server side using CGI applications.
You can get imagemap information
from the client in two ways: HTML forms and the ISMAP attribute of the <IMG>
tag. Both methods are almost equally usable and powerful, although the
latter method is preferable because it is supported by more browsers and is
slightly more flexible. |