[GRASS5] embedding GRASS display into my app

I want to write an application that uses GRASS as the back end to an application I plan to write. I need to embed the GRASS display into my application. I haven't started yet, though C++ would be easiest for me. If I have to learn Tcl/Tk, that's fine too. From what I understand, I should be able to modify the XDRIVER in GRASS to handle events in my application's main window, and I should be able to use TkSteal to actually embed the graphics display for GRASS into my application's main window. I'm not exactly clear on how to do all this considering I'm new to GRASS and Tcl/Tk. I found a paper online saying it has been done, but I haven't heard of anyone else that's done it, and I haven't seen any documentation on how to do it. Any help you could provide would be greatly appreciated.

Thanks,
David

Hello,

On Tue, Apr 06, 2004 at 02:15:36PM -0700, David Piasecki wrote:

I want to write an application that uses GRASS as the back end to an
application I plan to write. I need to embed the GRASS display into my
application. I haven't started yet, though C++ would be easiest for me.
If I have to learn Tcl/Tk, that's fine too. From what I understand, I
should be able to modify the XDRIVER in GRASS to handle events in my
application's main window, and I should be able to use TkSteal to
actually embed the graphics display for GRASS into my application's
main window. I'm not exactly clear on how to do all this considering
I'm new to GRASS and Tcl/Tk. I found a paper online saying it has been
done, but I haven't heard of anyone else that's done it, and I haven't
seen any documentation on how to do it. Any help you could provide
would be greatly appreciated.

Since you do not specify which paper you've found, I answer since I have
written that I was rewriting XDRIVER and I don't know if you refer to
this.

I don't know either what is the status with Tcl/Tk but I will give you
some tips about the display stuff.

The XDRIVER by itself does _just_ the display. Commands are passed from
the applications to the driver (IPC) using a GRASS defined protocol
requesting for drawing on the hardware (for X). The commands are
"interpreted" by a SWITCHER (SWITCHER.c) which calls the matching
commands implemented to drive the hardware (here hardware is indeed X11
server, a small Xlib only client being part of XDRIVER).

For the most important, the applications don't deal directly with the
X11 colormap, but with a GRASS colormap which indirectly points to
entries in X11 colormap (a GRASS colormap index gives a X11 pixel which
is itself an index in the X11 colormap).

The X11 color accepts 2^48 colors. A X11 colormap can handle a
number of colors depending on the depth, red, green and blue being
not automatically evenly treated (with a 8 bits depth on a visual class
that is TrueColor or DirectColor, you have 8 shades of red, 8 shades
of green but 4 shades of blue). And the "hard" hardware (the monitor)
handles another plage of colors (a subset of the theorical 2^48).

XDRIVER and the GRASS "internal" colormap accept 2^24 colors evenly
distributed. (as of the original code; may have changed now).

To extend further the XDRIVER (and treat it more like a classic X11
client) you have to throw upon it a widget (there comes the decision
about what toolkit to use).

FWIW, I have rewritten the XDRIVER (in CWEB --- this is NOT, for the
ones who don't know what it is, a kind of Internet, C#, .NET, Java...;
nothing to do with "the Web" or other. This is pure C (could be C++,
but I stick to C since it's God's own programming language) + TeX for
the documentation --- and this is simply great!).

And I have changed the colors handling in (KerGIS), and the default
colors (to match ISO 6429 extensions in xterm) etc...
And the toolkit chosen is Motif (for the future).

But since it's not what you are looking for, I hope the explanations
above will give you some hints.

Cheers,
--
Thierry Laronde (Alceste) <tlaronde@polynum.org>
http://www.kergis.org/ | http://www.kergis.com/
Key fingerprint = 0FF7 E906 FBAF FE95 FD89 250D 52B1 AE95 6006 F40C

The paper I found is at http://geomatica.ing.unico.it/workbooks2/n1/articoli/mv.pdf, but now I'm thinking it's rather old. I read something online about BLT? All I want to do is to have the user interact with the GRASS display within the confines of my own desktop application.

I had thought I would have to do this using Tcl/Tk, but from your description, it sounds like I can use anything I like so long as I interface with the XDRIVER for GRASS. Could use Qt instead of Tcl/Tk? Since GRASS already uses Tcl/Tk, wouldn't it be easier to use what's already available, or is it simple enough to do in any toolkit? Do you know of where I can get more information and possibly some documentation on how to write widgets like this?

David

On Apr 6, 2004, at 4:17 PM, Thierry Laronde wrote:

Hello,

Since you do not specify which paper you've found, I answer since I have
written that I was rewriting XDRIVER and I don't know if you refer to
this.

I don't know either what is the status with Tcl/Tk but I will give you
some tips about the display stuff.

The XDRIVER by itself does _just_ the display. Commands are passed from
the applications to the driver (IPC) using a GRASS defined protocol
requesting for drawing on the hardware (for X). The commands are
"interpreted" by a SWITCHER (SWITCHER.c) which calls the matching
commands implemented to drive the hardware (here hardware is indeed X11
server, a small Xlib only client being part of XDRIVER).

For the most important, the applications don't deal directly with the
X11 colormap, but with a GRASS colormap which indirectly points to
entries in X11 colormap (a GRASS colormap index gives a X11 pixel which
is itself an index in the X11 colormap).

The X11 color accepts 2^48 colors. A X11 colormap can handle a
number of colors depending on the depth, red, green and blue being
not automatically evenly treated (with a 8 bits depth on a visual class
that is TrueColor or DirectColor, you have 8 shades of red, 8 shades
of green but 4 shades of blue). And the "hard" hardware (the monitor)
handles another plage of colors (a subset of the theorical 2^48).

XDRIVER and the GRASS "internal" colormap accept 2^24 colors evenly
distributed. (as of the original code; may have changed now).

To extend further the XDRIVER (and treat it more like a classic X11
client) you have to throw upon it a widget (there comes the decision
about what toolkit to use).

FWIW, I have rewritten the XDRIVER (in CWEB --- this is NOT, for the
ones who don't know what it is, a kind of Internet, C#, .NET, Java...;
nothing to do with "the Web" or other. This is pure C (could be C++,
but I stick to C since it's God's own programming language) + TeX for
the documentation --- and this is simply great!).

And I have changed the colors handling in (KerGIS), and the default
colors (to match ISO 6429 extensions in xterm) etc...
And the toolkit chosen is Motif (for the future).

But since it's not what you are looking for, I hope the explanations
above will give you some hints.

Cheers,
--
Thierry Laronde (Alceste) <tlaronde@polynum.org>
http://www.kergis.org/ | http://www.kergis.com/
Key fingerprint = 0FF7 E906 FBAF FE95 FD89 250D 52B1 AE95 6006 F40C

On Tue, Apr 06, 2004 at 04:45:52PM -0700, David Piasecki wrote:

I had thought I would have to do this using Tcl/Tk, but from your
description, it sounds like I can use anything I like so long as I
interface with the XDRIVER for GRASS. Could use Qt instead of Tcl/Tk?
Since GRASS already uses Tcl/Tk, wouldn't it be easier to use what's
already available, or is it simple enough to do in any toolkit? Do you
know of where I can get more information and possibly some
documentation on how to write widgets like this?

It's depend upon how much you want to dive in the code. Since, by
construction, in GRASS everything is accessible via a command line, the
GUI is generally simply a graphical way to select and launch, in the
background, GRASS CLI programs (exec <program> and so on). This is
undoubtely the quickest way, and if the toolkit is itself a scripting
language you can have a result quickly.

Doing the same (`exec' programs) from a more powerful GUI (Qt, GTK,
Motif and so on) will take more time (but if you have peculiar needs it
is perhaps worth doing it).

For long term (not prototyping and so on), "real" programming with real
toolkit is, IMHO, far better.

But you can always start in prototyping mode by studying our the 5.7
developers have done the job with Tcl/Tk. But I'm not a 5.7 developer,
nor a Tcl/Tk guy so other are better placed to answer.

And note that you have, perhaps not the greatest documentation, but a
least a great deal of documentation: the source code :wink:

Cheers,

[Disconnecting, it's 2:00am here in France and I need to sleep a
little...]
--
Thierry Laronde (Alceste) <tlaronde@polynum.org>
http://www.kergis.org/ | http://www.kergis.com/
Key fingerprint = 0FF7 E906 FBAF FE95 FD89 250D 52B1 AE95 6006 F40C

David Piasecki wrote:

I want to write an application that uses GRASS as the back end to an
application I plan to write. I need to embed the GRASS display into my
application. I haven't started yet, though C++ would be easiest for me.
If I have to learn Tcl/Tk, that's fine too. From what I understand, I
should be able to modify the XDRIVER in GRASS to handle events in my
application's main window, and I should be able to use TkSteal to
actually embed the graphics display for GRASS into my application's
main window. I'm not exactly clear on how to do all this considering
I'm new to GRASS and Tcl/Tk. I found a paper online saying it has been
done, but I haven't heard of anyone else that's done it, and I haven't
seen any documentation on how to do it. Any help you could provide
would be greatly appreciated.

XDRIVER can be made to use an existing X window by setting the
environment variable XDRIVER_WINDOW to the XID of the existing window.
The value can be in any format acceptable to sscanf("%i"), i.e.
decimal, hex preceded by "0x", or octal preceded by "0".

However, note that X doesn't allow multiple clients to select
ButtonPress events for a given window, so the Get_location_with_*
functions (used by d.where, d.what.* etc) won't work if those events
have already been selected by the program which creates the window.

An alternative approach for a front-end would be to use the PNG driver
for all graphical output, and have your program display the PNG
images.

This is likely to be somewhat tidier than either embedding the XDRIVER
window (which is highly X-specific; this won't work with e.g. the
"generic" Windows port), or trying to use the XDRIVER code (which uses
the "raw" Xlib API).

It is also more flexible. E.g. if you want to support "layers",
rendering each layer as a separate PNG file then composing them in
your program may be preferable to re-rendering the entire set whenever
a layer is added, removed or changed.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

I had thought I would have to do this using Tcl/Tk, but from your
description, it sounds like I can use anything I like so long as I
interface with the XDRIVER for GRASS. Could use Qt instead of Tcl/Tk?
Since GRASS already uses Tcl/Tk, wouldn't it be easier to use what's
already available, or is it simple enough to do in any toolkit?

There isn't any compelling reason to use Tcl/Tk for your purposes.

While some parts of GRASS use Tcl/Tk, it isn't particularly central.
You could compile and use GRASS without having Tcl/Tk installed, and
the only missing features would be:

1. NVIZ.
2. The tcltkgrass menu interface.
3. The d.dm/d.m interface.
4. The option of using a Tcl/Tk startup dialog instead of the
   curses-based startup screen.

In particular, XDRIVER doesn't use (or have any integration with)
Tcl/Tk.

--
Glynn Clements <glynn.clements@virgin.net>

Hello David,

I'm working on a simmilar problem. I try to do content control and visualisation from at second PC running Windows2000. This application is programmed with Agilent VEE, a programming environment mainly used for instrument control but also able to handle most of the other pc stuff. It does handle C routines written as a dll very good, but unfortunately it is not available for linux.

Looks to me that grass is grown around the user's keyboard (sorry for that if I'm wrong!) so there would be the need for writing a interface both to send and collect the grass in/output. I will do that around TCP/IP so I can use this functionallity both local and remote on the w2k pc.

The hint earlier using png output should be perfect for the first shot.

I never thougth about learning tcltk, because it looks to me that I have to built everyting from the scratch which is eating too much time & I'm not very fond of learning it, too (Generation M$, grown up with Turbo-Pascal 3 ;_)) ).

I'm not shure now if my approach is useful, so any comment on this would be cordially welcome.

Mit freundlichen Grüßen / With kindest regards

Stefan Paulick

stefan.paulick@urbeli.com
http://www.urbeli.com
/*----------------------*/

David Piasecki schrieb:

I want to write an application that uses GRASS as the back end to an application I plan to write. I need to embed the GRASS display into my application. I haven't started yet, though C++ would be easiest for me. If I have to learn Tcl/Tk, that's fine too. From what I understand, I should be able to modify the XDRIVER in GRASS to handle events in my application's main window, and I should be able to use TkSteal to actually embed the graphics display for GRASS into my application's main window. I'm not exactly clear on how to do all this considering I'm new to GRASS and Tcl/Tk. I found a paper online saying it has been done, but I haven't heard of anyone else that's done it, and I haven't seen any documentation on how to do it. Any help you could provide would be greatly appreciated.

Thanks,
David

_______________________________________________
grass5 mailing list
grass5@grass.itc.it
http://grass.itc.it/mailman/listinfo/grass5

On Wed, Apr 07, 2004 at 03:25:48AM +0100, Glynn Clements wrote:

David Piasecki wrote:

[...]
1. NVIZ.
2. The tcltkgrass menu interface.
3. The d.dm/d.m interface.
4. The option of using a Tcl/Tk startup dialog instead of the
   curses-based startup screen.

  5. v.digit (?)

--
Glynn Clements <glynn.clements@virgin.net>

Jáchym
--
Jachym Cepicky
e-mail: jachym.cepicky@centrum.cz
URL: http://les-ejk.cz

All I want to do is to have the user interact with the GRASS display
within the confines of my own desktop application.

Have a look at d.rast.edit

?
Hamish

Will I be able to have responsive user control in moving vector objects if I use PNG? It seems like it would respond rather slow if every time I edit a road or point of interest in the graphical display, it's got to re-render the layer as a PNG, and I've got to import it again. Or perhaps this is faster than I realize. If you think it will work, then I can write my application in anything! What do you think?

David

On Apr 6, 2004, at 6:16 PM, Glynn Clements wrote:

David Piasecki wrote:

I want to write an application that uses GRASS as the back end to an
application I plan to write. I need to embed the GRASS display into my
application. I haven't started yet, though C++ would be easiest for me.
If I have to learn Tcl/Tk, that's fine too. From what I understand, I
should be able to modify the XDRIVER in GRASS to handle events in my
application's main window, and I should be able to use TkSteal to
actually embed the graphics display for GRASS into my application's
main window. I'm not exactly clear on how to do all this considering
I'm new to GRASS and Tcl/Tk. I found a paper online saying it has been
done, but I haven't heard of anyone else that's done it, and I haven't
seen any documentation on how to do it. Any help you could provide
would be greatly appreciated.

XDRIVER can be made to use an existing X window by setting the
environment variable XDRIVER_WINDOW to the XID of the existing window.
The value can be in any format acceptable to sscanf("%i"), i.e.
decimal, hex preceded by "0x", or octal preceded by "0".

However, note that X doesn't allow multiple clients to select
ButtonPress events for a given window, so the Get_location_with_*
functions (used by d.where, d.what.* etc) won't work if those events
have already been selected by the program which creates the window.

An alternative approach for a front-end would be to use the PNG driver
for all graphical output, and have your program display the PNG
images.

This is likely to be somewhat tidier than either embedding the XDRIVER
window (which is highly X-specific; this won't work with e.g. the
"generic" Windows port), or trying to use the XDRIVER code (which uses
the "raw" Xlib API).

It is also more flexible. E.g. if you want to support "layers",
rendering each layer as a separate PNG file then composing them in
your program may be preferable to re-rendering the entire set whenever
a layer is added, removed or changed.

--
Glynn Clements <glynn.clements@virgin.net>

I guess I could use v.to.rast to convert any vector files to raster and then r.out.png so I can load it in my application, but it seems like the time it would take to do all that conversion and loading might be too slow?

David

On Apr 6, 2004, at 6:16 PM, Glynn Clements wrote:

It is also more flexible. E.g. if you want to support "layers",
rendering each layer as a separate PNG file then composing them in
your program may be preferable to re-rendering the entire set whenever
a layer is added, removed or changed.

--
Glynn Clements <glynn.clements@virgin.net>

This seems to be working pretty well! Thanks! It only takes 2-3 seconds for the map to render itself in GRASS and save as a PNG file, and then a split second to load in my Cocoa-Java application :slight_smile:

Now I'm starting to learn about how to execute GRASS commands from the terminal window, and next from within Java. Then I just have to reload the PNG base map any time I resize or rescale. I'm doubtful the refreshing of icons will be fast enough if I want the user to be able to move things around, but I guess that's getting fancy. In any case, this should provide enough of a start. Thanks again!

David

On Apr 6, 2004, at 6:16 PM, Glynn Clements wrote:

An alternative approach for a front-end would be to use the PNG driver
for all graphical output, and have your program display the PNG
images.

This is likely to be somewhat tidier than either embedding the XDRIVER
window (which is highly X-specific; this won't work with e.g. the
"generic" Windows port), or trying to use the XDRIVER code (which uses
the "raw" Xlib API).

It is also more flexible. E.g. if you want to support "layers",
rendering each layer as a separate PNG file then composing them in
your program may be preferable to re-rendering the entire set whenever
a layer is added, removed or changed.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

Will I be able to have responsive user control in moving vector objects
if I use PNG?

No; but I doubt that you will get that using XDRIVER either.

It seems like it would respond rather slow if every time
I edit a road or point of interest in the graphical display, it's got
to re-render the layer as a PNG, and I've got to import it again. Or
perhaps this is faster than I realize. If you think it will work, then
I can write my application in anything! What do you think?

The best approach for this situation would be to write your own code
for drawing vector maps. I.e. take the code for d.vect and replace the
R_* functions with native (Xlib/GDK/etc) drawing functions.

If you were using XDRIVER, at what level would you be using it? Would
you be running d.erase/d.vect whenever a change was made, or using the
libraster/libraster functions?

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

I guess I could use v.to.rast to convert any vector files to raster and
then r.out.png so I can load it in my application, but it seems like
the time it would take to do all that conversion and loading might be
too slow?

For an interactive application, it would probably be too slow, even if
the PNG driver was made to write uncompressed PNG files or PPM files
(for compressed PNG files, the bulk of the time would be spent on the
compression step).

The PNG driver approach would be more useful for something like d.m,
where you want to be able to redraw a fixed (unchanging) layer
quickly. If the layer is likely to change between redraws (i.e. an
interactive vector map editor), you would be better off rendering
directly to the screen each time.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

This seems to be working pretty well! Thanks! It only takes 2-3 seconds
for the map to render itself in GRASS and save as a PNG file, and then
a split second to load in my Cocoa-Java application :slight_smile:

If you have the GRASS source code, you can speed up the writing of the
PNG image by adding:

  png_set_compression_level(png_ptr, Z_NO_COMPRESSION);

immediately before the line:

  png_write_info(png_ptr, info_ptr);

in src/display/devices/PNGdriver/Graph_Clse.c.

I haven't analysed exactly how much difference this will make, but it
may be worth trying.

Alternatively, using Z_BEST_SPEED instead of Z_NO_COMPRESSION will
provide some degree of compression at the expense of some CPU usage
but, if the PNG file survives long enough to be written to disk, the
reduced file size may actually improve performance overall.

--
Glynn Clements <glynn.clements@virgin.net>

Thanks. I think the greatest amount of time is spent on drawing the 13MB vector file, but I might give it a shot. It might be more noticeable with smaller file sizes.

David

On Apr 7, 2004, at 4:07 PM, Glynn Clements wrote:

David Piasecki wrote:

This seems to be working pretty well! Thanks! It only takes 2-3 seconds
for the map to render itself in GRASS and save as a PNG file, and then
a split second to load in my Cocoa-Java application :slight_smile:

If you have the GRASS source code, you can speed up the writing of the
PNG image by adding:

  png_set_compression_level(png_ptr, Z_NO_COMPRESSION);

immediately before the line:

  png_write_info(png_ptr, info_ptr);

in src/display/devices/PNGdriver/Graph_Clse.c.

I haven't analysed exactly how much difference this will make, but it
may be worth trying.

Alternatively, using Z_BEST_SPEED instead of Z_NO_COMPRESSION will
provide some degree of compression at the expense of some CPU usage
but, if the PNG file survives long enough to be written to disk, the
reduced file size may actually improve performance overall.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

Thanks. I think the greatest amount of time is spent on drawing the
13MB vector file, but I might give it a shot. It might be more
noticeable with smaller file sizes.

Yes; since adding that option to the PNG driver, I've found that for
typical image sizes (e.g. 800x600x24bpp), the compression time isn't
really significant (on a P3/800).

For large vector files, I suspect that the biggest gains would come
from:

1. Calling the system's drawing functions (XDrawLine, gdk_draw_line
etc) directly from your application.

2. Only drawing the sections of the map which need to be redrawn, i.e.
the bounding rectangle of anything which may have changed.

OTOH, if using e.g. d.vect plus XDRIVER/PNGdriver is adequate, writing
system("d.vect ...") is a lot less code than re-implementing it.

--
Glynn Clements <glynn.clements@virgin.net>

Do you know of a way to convert the pixel coordinates of the PNG image into geo coordinates? I imagine d.where won't work since it's only interactive within the grass program itself.

David

On Apr 7, 2004, at 10:47 PM, Glynn Clements wrote:

Yes; since adding that option to the PNG driver, I've found that for
typical image sizes (e.g. 800x600x24bpp), the compression time isn't
really significant (on a P3/800).

For large vector files, I suspect that the biggest gains would come
from:

1. Calling the system's drawing functions (XDrawLine, gdk_draw_line
etc) directly from your application.

2. Only drawing the sections of the map which need to be redrawn, i.e.
the bounding rectangle of anything which may have changed.

OTOH, if using e.g. d.vect plus XDRIVER/PNGdriver is adequate, writing
system("d.vect ...") is a lot less code than re-implementing it.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

Do you know of a way to convert the pixel coordinates of the PNG image
into geo coordinates? I imagine d.where won't work since it's only
interactive within the grass program itself.

Unfortunately, the correspondence is non-trivial, and the functions
which d.where, d.what.* etc use require a monitor to be selected.

The simplest solution is probably to query the map coordinates of the
top-left and bottom-right pixels at the time that the map is drawn,
using essentially the same code as d.where, then using linear
interpolation to convert arbitrary pixel coordinates to their
corresponding map coordinates.

The key issue is that you have to perform the translation at a point
where the map is still "displayed" on the monitor; if you wait until
you actually need the coordinates, the transformation which was
actually used to generate the map may no longer be known.

[In the above, I am assuming that the PNG image is from the PNG
driver. For images generated with r.out.png, the situation is somewhat
simpler, as the image's boundaries exactly correspond to the region's
boundaries.]

--
Glynn Clements <glynn.clements@virgin.net>

I started thinking about writing my own d.where that doesn't require the interaction with the GRASS monitor. I took a look at the current d.where, and it has a function that accepts screen (x,y) pixel coordinates, and you can convert to Lat/Lon. I was thinking I could remove a good chunk of code that interacts with the user and simply have my program pass in the screen coords. That doesn't seem too bad to me. I'm just nervous about getting it to compile since I haven't actually been able to compile GRASS on my Mac yet (I only have the binary). What do you think?

Yes, my PNG image was created with the PNG driver. Basically, I want to be able to get Lat/Lon values for locations clicked on, and I want to be able to zoom, create a new PNG map, and reload.

It sounds like you're telling me that once I write the PNG file, it will no longer be present on the monitor, so I won't be able to get geo coords from it. So I should grab the corners before creating the PNG and interpolate the rest. What happens when I want to display a larger map? If I had a map the size of the U.S. or the world, then I imagine the results of standard linear interpolation will vary depending on which projection I choose to use.

Expanding on that idea, perhaps before doing a stop=PNG (which I assume closes the display), I can get a small number of geo coordinates at intervals in the image (say 100x100 points). I imagine that wouldn't add too much time. Then I could close the monitor and interpolate between the various points. I might get a little better accuracy with the bigger maps that way. What do you think?

David

On Apr 8, 2004, at 3:39 PM, Glynn Clements wrote:

David Piasecki wrote:

Do you know of a way to convert the pixel coordinates of the PNG image
into geo coordinates? I imagine d.where won't work since it's only
interactive within the grass program itself.

Unfortunately, the correspondence is non-trivial, and the functions
which d.where, d.what.* etc use require a monitor to be selected.

The simplest solution is probably to query the map coordinates of the
top-left and bottom-right pixels at the time that the map is drawn,
using essentially the same code as d.where, then using linear
interpolation to convert arbitrary pixel coordinates to their
corresponding map coordinates.

The key issue is that you have to perform the translation at a point
where the map is still "displayed" on the monitor; if you wait until
you actually need the coordinates, the transformation which was
actually used to generate the map may no longer be known.

[In the above, I am assuming that the PNG image is from the PNG
driver. For images generated with r.out.png, the situation is somewhat
simpler, as the image's boundaries exactly correspond to the region's
boundaries.]

--
Glynn Clements <glynn.clements@virgin.net>