[DebianGIS-dev] r916 - in packages: . openev openev/branches openev/branches/upstream openev/branches/upstream/current openev/branches/upstream/current/CVS openev/branches/upstream/current/contrib openev/branches/upstream/current/contrib/S52 openev/branches/upstream/current/contrib/S52/doc openev/branches/upstream/current/delivery openev/branches/upstream/current/delivery/CVS openev/branches/upstream/current/doc openev/branches/upstream/current/doc/CVS openev/branches/upstream/current/html openev/branches/upstream/current/html/CVS openev/branches/upstream/current/html/developer_info openev/branches/upstream/current/html/developer_info/CVS openev/branches/upstream/current/pics openev/branches/upstream/current/pics/CVS openev/branches/upstream/current/pymod openev/branches/upstream/current/pymod/CVS openev/branches/upstream/current/ramps openev/branches/upstream/current/ramps/CVS openev/branches/upstream/current/symbols openev/branches/upstream/current/symbols/CVS openev/branches/upstream/current/tools openev/branches/upstream/current/tools/CVS openev/branches/upstream/current/xmlconfig openev/branches/upstream/current/xmlconfig/CVS

frankie at alioth.debian.org frankie at alioth.debian.org
Sat Jun 23 22:26:31 UTC 2007


Author: frankie
Date: 2007-06-23 22:26:29 +0000 (Sat, 23 Jun 2007)
New Revision: 916

Added:
   packages/openev/
   packages/openev/branches/
   packages/openev/branches/upstream/
   packages/openev/branches/upstream/current/
   packages/openev/branches/upstream/current/.cvsignore
   packages/openev/branches/upstream/current/COPYING
   packages/openev/branches/upstream/current/CVS/
   packages/openev/branches/upstream/current/CVS/Entries
   packages/openev/branches/upstream/current/CVS/Entries.Log
   packages/openev/branches/upstream/current/CVS/Repository
   packages/openev/branches/upstream/current/CVS/Root
   packages/openev/branches/upstream/current/ChangeLog
   packages/openev/branches/upstream/current/Makefile
   packages/openev/branches/upstream/current/Makefile.in
   packages/openev/branches/upstream/current/TODO
   packages/openev/branches/upstream/current/aclocal.m4
   packages/openev/branches/upstream/current/appcurlayer.c
   packages/openev/branches/upstream/current/appcurlayer.h
   packages/openev/branches/upstream/current/configure
   packages/openev/branches/upstream/current/configure.in
   packages/openev/branches/upstream/current/contrib/
   packages/openev/branches/upstream/current/contrib/S52/
   packages/openev/branches/upstream/current/contrib/S52/COPYING
   packages/openev/branches/upstream/current/contrib/S52/ChangeLog
   packages/openev/branches/upstream/current/contrib/S52/Makefile
   packages/openev/branches/upstream/current/contrib/S52/README
   packages/openev/branches/upstream/current/contrib/S52/S52.c
   packages/openev/branches/upstream/current/contrib/S52/S52.h
   packages/openev/branches/upstream/current/contrib/S52/S52CS.c
   packages/openev/branches/upstream/current/contrib/S52/S52CS.h
   packages/openev/branches/upstream/current/contrib/S52/S52GL.c
   packages/openev/branches/upstream/current/contrib/S52/S52GL.h
   packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp
   packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp.h
   packages/openev/branches/upstream/current/contrib/S52/S52OSG.h
   packages/openev/branches/upstream/current/contrib/S52/S52PL.c
   packages/openev/branches/upstream/current/contrib/S52/S52PL.h
   packages/openev/branches/upstream/current/contrib/S52/S52glxsimple.c
   packages/openev/branches/upstream/current/contrib/S52/S52raz-3.2.rle
   packages/openev/branches/upstream/current/contrib/S52/S52raz.s
   packages/openev/branches/upstream/current/contrib/S52/S52type.h
   packages/openev/branches/upstream/current/contrib/S52/S52utils.c
   packages/openev/branches/upstream/current/contrib/S52/S52utils.h
   packages/openev/branches/upstream/current/contrib/S52/S57data.c
   packages/openev/branches/upstream/current/contrib/S52/S57data.h
   packages/openev/branches/upstream/current/contrib/S52/S57gv.c
   packages/openev/branches/upstream/current/contrib/S52/S57gv.h
   packages/openev/branches/upstream/current/contrib/S52/S57ogr.c
   packages/openev/branches/upstream/current/contrib/S52/S57ogr.h
   packages/openev/branches/upstream/current/contrib/S52/TODO
   packages/openev/branches/upstream/current/contrib/S52/doc/
   packages/openev/branches/upstream/current/contrib/S52/doc/C1.lup_collision.txt
   packages/openev/branches/upstream/current/contrib/S52/doc/att.txt
   packages/openev/branches/upstream/current/contrib/S52/doc/boylat24.dia
   packages/openev/branches/upstream/current/contrib/S52/doc/boylat24.png
   packages/openev/branches/upstream/current/contrib/S52/doc/libS52-layout.dia
   packages/openev/branches/upstream/current/contrib/S52/doc/obj.txt
   packages/openev/branches/upstream/current/contrib/S52/ex0_xwin.c
   packages/openev/branches/upstream/current/contrib/S52/gvS57layer.c
   packages/openev/branches/upstream/current/contrib/S52/gvS57layer.h
   packages/openev/branches/upstream/current/contrib/S52/ogrS57layers.c
   packages/openev/branches/upstream/current/contrib/S52/s52test.c
   packages/openev/branches/upstream/current/contrib/S52/s52test.conf
   packages/openev/branches/upstream/current/crs.c
   packages/openev/branches/upstream/current/crs.h
   packages/openev/branches/upstream/current/dbfopen.c
   packages/openev/branches/upstream/current/delivery/
   packages/openev/branches/upstream/current/delivery/CVS/
   packages/openev/branches/upstream/current/delivery/CVS/Entries
   packages/openev/branches/upstream/current/delivery/CVS/Repository
   packages/openev/branches/upstream/current/delivery/CVS/Root
   packages/openev/branches/upstream/current/delivery/INSTALL.TXT
   packages/openev/branches/upstream/current/delivery/install
   packages/openev/branches/upstream/current/delivery/license.txt
   packages/openev/branches/upstream/current/delivery/mkdist
   packages/openev/branches/upstream/current/delivery/mksrcdist.sh
   packages/openev/branches/upstream/current/delivery/package
   packages/openev/branches/upstream/current/delivery/setup_openev
   packages/openev/branches/upstream/current/doc/
   packages/openev/branches/upstream/current/doc/CVS/
   packages/openev/branches/upstream/current/doc/CVS/Entries
   packages/openev/branches/upstream/current/doc/CVS/Repository
   packages/openev/branches/upstream/current/doc/CVS/Root
   packages/openev/branches/upstream/current/doc/Makefile
   packages/openev/branches/upstream/current/doc/asilogo.eps
   packages/openev/branches/upstream/current/doc/future.tex
   packages/openev/branches/upstream/current/doc/gvclass.fig
   packages/openev/branches/upstream/current/doc/gvdata.fig
   packages/openev/branches/upstream/current/doc/l2h-init.perl
   packages/openev/branches/upstream/current/doc/layerdlg.eps
   packages/openev/branches/upstream/current/doc/lodgen.fig
   packages/openev/branches/upstream/current/doc/openev.tex
   packages/openev/branches/upstream/current/doc/openev_bigpicture.fig
   packages/openev/branches/upstream/current/doc/openevlogo.eps
   packages/openev/branches/upstream/current/doc/openevreport.cls
   packages/openev/branches/upstream/current/doc/openevreport.perl
   packages/openev/branches/upstream/current/doc/phaseclip.fig
   packages/openev/branches/upstream/current/doc/toolbar.eps
   packages/openev/branches/upstream/current/gextra.c
   packages/openev/branches/upstream/current/gextra.h
   packages/openev/branches/upstream/current/gtkcolorwell.c
   packages/openev/branches/upstream/current/gtkcolorwell.h
   packages/openev/branches/upstream/current/gv_config.h.in
   packages/openev/branches/upstream/current/gv_config.h_win32
   packages/openev/branches/upstream/current/gvarealayer.c
   packages/openev/branches/upstream/current/gvarealayer.h
   packages/openev/branches/upstream/current/gvareas.c
   packages/openev/branches/upstream/current/gvareas.h
   packages/openev/branches/upstream/current/gvareatool.c
   packages/openev/branches/upstream/current/gvareatool.h
   packages/openev/branches/upstream/current/gvautopan.c
   packages/openev/branches/upstream/current/gvautopan.h
   packages/openev/branches/upstream/current/gvdata.c
   packages/openev/branches/upstream/current/gvdata.h
   packages/openev/branches/upstream/current/gview.h
   packages/openev/branches/upstream/current/gvlayer.c
   packages/openev/branches/upstream/current/gvlayer.h
   packages/openev/branches/upstream/current/gvlinelayer.c
   packages/openev/branches/upstream/current/gvlinelayer.h
   packages/openev/branches/upstream/current/gvlinetool.c
   packages/openev/branches/upstream/current/gvlinetool.h
   packages/openev/branches/upstream/current/gvmanager.c
   packages/openev/branches/upstream/current/gvmanager.h
   packages/openev/branches/upstream/current/gvmesh.c
   packages/openev/branches/upstream/current/gvmesh.h
   packages/openev/branches/upstream/current/gvnodetool.c
   packages/openev/branches/upstream/current/gvnodetool.h
   packages/openev/branches/upstream/current/gvogr.c
   packages/openev/branches/upstream/current/gvpointlayer.c
   packages/openev/branches/upstream/current/gvpointlayer.h
   packages/openev/branches/upstream/current/gvpoints.c
   packages/openev/branches/upstream/current/gvpoints.h
   packages/openev/branches/upstream/current/gvpointtool.c
   packages/openev/branches/upstream/current/gvpointtool.h
   packages/openev/branches/upstream/current/gvpoitool.c
   packages/openev/branches/upstream/current/gvpoitool.h
   packages/openev/branches/upstream/current/gvpolylines.c
   packages/openev/branches/upstream/current/gvpolylines.h
   packages/openev/branches/upstream/current/gvpquerylayer.c
   packages/openev/branches/upstream/current/gvpquerylayer.h
   packages/openev/branches/upstream/current/gvprint.c
   packages/openev/branches/upstream/current/gvproperties.c
   packages/openev/branches/upstream/current/gvproperties.h
   packages/openev/branches/upstream/current/gvraster.c
   packages/openev/branches/upstream/current/gvraster.h
   packages/openev/branches/upstream/current/gvrasteraverage.c
   packages/openev/branches/upstream/current/gvrasteraverage.h
   packages/openev/branches/upstream/current/gvrastercache.c
   packages/openev/branches/upstream/current/gvrastercache.h
   packages/openev/branches/upstream/current/gvrasterconvert.c
   packages/openev/branches/upstream/current/gvrasterize.c
   packages/openev/branches/upstream/current/gvrasterize.h
   packages/openev/branches/upstream/current/gvrasterlayer.c
   packages/openev/branches/upstream/current/gvrasterlayer.h
   packages/openev/branches/upstream/current/gvrasterlut.c
   packages/openev/branches/upstream/current/gvrasterlut.h
   packages/openev/branches/upstream/current/gvrastersource.c
   packages/openev/branches/upstream/current/gvrastertypes.h
   packages/openev/branches/upstream/current/gvrecords.c
   packages/openev/branches/upstream/current/gvrecords.h
   packages/openev/branches/upstream/current/gvrecttool.c
   packages/openev/branches/upstream/current/gvrecttool.h
   packages/openev/branches/upstream/current/gvrenderinfo.c
   packages/openev/branches/upstream/current/gvrenderinfo.h
   packages/openev/branches/upstream/current/gvroitool.c
   packages/openev/branches/upstream/current/gvroitool.h
   packages/openev/branches/upstream/current/gvrotatetool.c
   packages/openev/branches/upstream/current/gvrotatetool.h
   packages/openev/branches/upstream/current/gvselecttool.c
   packages/openev/branches/upstream/current/gvselecttool.h
   packages/openev/branches/upstream/current/gvshape.c
   packages/openev/branches/upstream/current/gvshapefile.c
   packages/openev/branches/upstream/current/gvshapelayer.c
   packages/openev/branches/upstream/current/gvshapelayer.h
   packages/openev/branches/upstream/current/gvshapes.c
   packages/openev/branches/upstream/current/gvshapes.h
   packages/openev/branches/upstream/current/gvshapeslayer.c
   packages/openev/branches/upstream/current/gvshapeslayer.h
   packages/openev/branches/upstream/current/gvskirt.c
   packages/openev/branches/upstream/current/gvsymbolmanager.c
   packages/openev/branches/upstream/current/gvsymbolmanager.h
   packages/openev/branches/upstream/current/gvtess.c
   packages/openev/branches/upstream/current/gvtess.h
   packages/openev/branches/upstream/current/gvtessshape.c
   packages/openev/branches/upstream/current/gvtexturecache.c
   packages/openev/branches/upstream/current/gvtool.c
   packages/openev/branches/upstream/current/gvtool.h
   packages/openev/branches/upstream/current/gvtoolbox.c
   packages/openev/branches/upstream/current/gvtoolbox.h
   packages/openev/branches/upstream/current/gvtracktool.c
   packages/openev/branches/upstream/current/gvtracktool.h
   packages/openev/branches/upstream/current/gvtypes.h
   packages/openev/branches/upstream/current/gvundo.c
   packages/openev/branches/upstream/current/gvundo.h
   packages/openev/branches/upstream/current/gvutils.c
   packages/openev/branches/upstream/current/gvutils.h
   packages/openev/branches/upstream/current/gvviewarea.c
   packages/openev/branches/upstream/current/gvviewarea.h
   packages/openev/branches/upstream/current/gvviewlink.c
   packages/openev/branches/upstream/current/gvviewlink.h
   packages/openev/branches/upstream/current/gvwinprint.c
   packages/openev/branches/upstream/current/gvzoompantool.c
   packages/openev/branches/upstream/current/gvzoompantool.h
   packages/openev/branches/upstream/current/html/
   packages/openev/branches/upstream/current/html/CVS/
   packages/openev/branches/upstream/current/html/CVS/Entries
   packages/openev/branches/upstream/current/html/CVS/Entries.Log
   packages/openev/branches/upstream/current/html/CVS/Repository
   packages/openev/branches/upstream/current/html/CVS/Root
   packages/openev/branches/upstream/current/html/Tool_Export.html
   packages/openev/branches/upstream/current/html/busy.gif
   packages/openev/branches/upstream/current/html/classify.gif
   packages/openev/branches/upstream/current/html/customization.html
   packages/openev/branches/upstream/current/html/developer_info/
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_big_picture.png
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_gdal_datamodel.html
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_lib_inherit.gif
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_numpy.html
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_oev_talk.html
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_openev.html
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_python.html
   packages/openev/branches/upstream/current/html/developer_info/COURSE1_test.png
   packages/openev/branches/upstream/current/html/developer_info/CVS/
   packages/openev/branches/upstream/current/html/developer_info/CVS/Entries
   packages/openev/branches/upstream/current/html/developer_info/CVS/Repository
   packages/openev/branches/upstream/current/html/developer_info/CVS/Root
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_building_maintenance.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_classify_example.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_colour_raster.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_colour_raster.tif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_components.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_gdal.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_greyscale_raster.tif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss1.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss2.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss3.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss4.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss5.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_mini_raster.tif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss1.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss2.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss3.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_openev_files.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.dbf
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.shp
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.shx
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_print_ss1.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_example.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss1.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss2.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss3.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss4.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss5.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_python_bindings.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss1.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss2.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss3.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss4.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_scripts.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.dbf
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.shp
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.shx
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shapefile_ss1.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_subpixel_interpolation.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tools_commands.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial1.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial2.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial3.html
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.dbf
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.shp
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.shx
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classify.gif
   packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_layerstyles.gif
   packages/openev/branches/upstream/current/html/developer_info/pyshell_example.gif
   packages/openev/branches/upstream/current/html/developer_info/pyshell_full.gif
   packages/openev/branches/upstream/current/html/developer_info/pyshell_help.gif
   packages/openev/branches/upstream/current/html/edittools.gif
   packages/openev/branches/upstream/current/html/edittools.html
   packages/openev/branches/upstream/current/html/equalize.gif
   packages/openev/branches/upstream/current/html/exporttool_advgui.gif
   packages/openev/branches/upstream/current/html/exporttool_basicgui.gif
   packages/openev/branches/upstream/current/html/files.html
   packages/openev/branches/upstream/current/html/gvogrdlg.gif
   packages/openev/branches/upstream/current/html/gvpquerypropdlg.html
   packages/openev/branches/upstream/current/html/gvpquerypropdlg_drawstyle.gif
   packages/openev/branches/upstream/current/html/gvpquerypropdlg_general.gif
   packages/openev/branches/upstream/current/html/gvprint.gif
   packages/openev/branches/upstream/current/html/gvprint.html
   packages/openev/branches/upstream/current/html/gvrasterpropdlg.html
   packages/openev/branches/upstream/current/html/gvrasterpropdlg_drawstyle.gif
   packages/openev/branches/upstream/current/html/gvrasterpropdlg_general.gif
   packages/openev/branches/upstream/current/html/gvrasterpropdlg_imageinfo.gif
   packages/openev/branches/upstream/current/html/gvrasterpropdlg_lut.jpg
   packages/openev/branches/upstream/current/html/gvrasterpropdlg_source.gif
   packages/openev/branches/upstream/current/html/gvvectorpropdlg.html
   packages/openev/branches/upstream/current/html/gvvectorpropdlg_drawstyle.gif
   packages/openev/branches/upstream/current/html/gvvectorpropdlg_general.gif
   packages/openev/branches/upstream/current/html/help.gif
   packages/openev/branches/upstream/current/html/idle.gif
   packages/openev/branches/upstream/current/html/layerdlg.gif
   packages/openev/branches/upstream/current/html/layerdlg.html
   packages/openev/branches/upstream/current/html/layerdlg_delete.gif
   packages/openev/branches/upstream/current/html/layerdlg_lower.gif
   packages/openev/branches/upstream/current/html/layerdlg_new.gif
   packages/openev/branches/upstream/current/html/layerdlg_raise.gif
   packages/openev/branches/upstream/current/html/legend.gif
   packages/openev/branches/upstream/current/html/linear.gif
   packages/openev/branches/upstream/current/html/log.gif
   packages/openev/branches/upstream/current/html/logo.jpg
   packages/openev/branches/upstream/current/html/mainwindow.gif
   packages/openev/branches/upstream/current/html/mainwindow.html
   packages/openev/branches/upstream/current/html/nonelut.gif
   packages/openev/branches/upstream/current/html/onetoone.gif
   packages/openev/branches/upstream/current/html/open3d.gif
   packages/openev/branches/upstream/current/html/open3d.html
   packages/openev/branches/upstream/current/html/openevmain.html
   packages/openev/branches/upstream/current/html/openfile.gif
   packages/openev/branches/upstream/current/html/performance.html
   packages/openev/branches/upstream/current/html/pref_cache.gif
   packages/openev/branches/upstream/current/html/pref_help.gif
   packages/openev/branches/upstream/current/html/pref_raster.gif
   packages/openev/branches/upstream/current/html/pref_tracking.gif
   packages/openev/branches/upstream/current/html/preferences.html
   packages/openev/branches/upstream/current/html/print.gif
   packages/openev/branches/upstream/current/html/pyshell.html
   packages/openev/branches/upstream/current/html/pyshell_default.gif
   packages/openev/branches/upstream/current/html/refresh.gif
   packages/openev/branches/upstream/current/html/rotatetool.gif
   packages/openev/branches/upstream/current/html/seeall.gif
   packages/openev/branches/upstream/current/html/tool_hist.gif
   packages/openev/branches/upstream/current/html/tool_roigeneral.gif
   packages/openev/branches/upstream/current/html/tools_openev.gif
   packages/openev/branches/upstream/current/html/veclayerselect.html
   packages/openev/branches/upstream/current/html/viewarea_keys.html
   packages/openev/branches/upstream/current/html/windowed.gif
   packages/openev/branches/upstream/current/html/worldg.gif
   packages/openev/branches/upstream/current/html/worldrgb.gif
   packages/openev/branches/upstream/current/html/zoom_control.gif
   packages/openev/branches/upstream/current/html/zoomin.gif
   packages/openev/branches/upstream/current/html/zoomout.gif
   packages/openev/branches/upstream/current/install-sh
   packages/openev/branches/upstream/current/invdistance.c
   packages/openev/branches/upstream/current/invdistance.h
   packages/openev/branches/upstream/current/ipgcplayer.c
   packages/openev/branches/upstream/current/ipgcplayer.h
   packages/openev/branches/upstream/current/llrasterize.c
   packages/openev/branches/upstream/current/makefile.vc
   packages/openev/branches/upstream/current/nmake.opt
   packages/openev/branches/upstream/current/pics/
   packages/openev/branches/upstream/current/pics/CVS/
   packages/openev/branches/upstream/current/pics/CVS/Entries
   packages/openev/branches/upstream/current/pics/CVS/Repository
   packages/openev/branches/upstream/current/pics/CVS/Root
   packages/openev/branches/upstream/current/pics/atlantis_logo.xpm
   packages/openev/branches/upstream/current/pics/busy.xpm
   packages/openev/branches/upstream/current/pics/ck_off_l.xpm
   packages/openev/branches/upstream/current/pics/ck_on_l.xpm
   packages/openev/branches/upstream/current/pics/classify.xpm
   packages/openev/branches/upstream/current/pics/delete.xpm
   packages/openev/branches/upstream/current/pics/equalize.xpm
   packages/openev/branches/upstream/current/pics/eye.xpm
   packages/openev/branches/upstream/current/pics/geo_innovation.xpm
   packages/openev/branches/upstream/current/pics/help.xpm
   packages/openev/branches/upstream/current/pics/idle.xpm
   packages/openev/branches/upstream/current/pics/legend.xpm
   packages/openev/branches/upstream/current/pics/linear.xpm
   packages/openev/branches/upstream/current/pics/log.xpm
   packages/openev/branches/upstream/current/pics/lower.xpm
   packages/openev/branches/upstream/current/pics/new.xpm
   packages/openev/branches/upstream/current/pics/node_cursor.xbm
   packages/openev/branches/upstream/current/pics/node_cursor_mask.xbm
   packages/openev/branches/upstream/current/pics/nonelut.xpm
   packages/openev/branches/upstream/current/pics/onetoone.xpm
   packages/openev/branches/upstream/current/pics/openev.xpm
   packages/openev/branches/upstream/current/pics/openfile.xpm
   packages/openev/branches/upstream/current/pics/pan_left.xpm
   packages/openev/branches/upstream/current/pics/pan_rght.xpm
   packages/openev/branches/upstream/current/pics/print.xpm
   packages/openev/branches/upstream/current/pics/raise.xpm
   packages/openev/branches/upstream/current/pics/recenter.xpm
   packages/openev/branches/upstream/current/pics/refresh.xpm
   packages/openev/branches/upstream/current/pics/root.xpm
   packages/openev/branches/upstream/current/pics/save.xpm
   packages/openev/branches/upstream/current/pics/seeall.xpm
   packages/openev/branches/upstream/current/pics/sym_circle.xpm
   packages/openev/branches/upstream/current/pics/sym_cross.xpm
   packages/openev/branches/upstream/current/pics/sym_filled_circle.xpm
   packages/openev/branches/upstream/current/pics/sym_filled_square.xpm
   packages/openev/branches/upstream/current/pics/sym_filled_star.xpm
   packages/openev/branches/upstream/current/pics/sym_filled_triangle.xpm
   packages/openev/branches/upstream/current/pics/sym_square.xpm
   packages/openev/branches/upstream/current/pics/sym_star.xpm
   packages/openev/branches/upstream/current/pics/sym_triangle.xpm
   packages/openev/branches/upstream/current/pics/sym_vertical.xpm
   packages/openev/branches/upstream/current/pics/sym_x.xpm
   packages/openev/branches/upstream/current/pics/vexcel_logo.xpm
   packages/openev/branches/upstream/current/pics/warning.xpm
   packages/openev/branches/upstream/current/pics/windowed.xpm
   packages/openev/branches/upstream/current/pics/worldg.xpm
   packages/openev/branches/upstream/current/pics/worldrgb.xpm
   packages/openev/branches/upstream/current/pics/zoomin.xpm
   packages/openev/branches/upstream/current/pics/zoomout.xpm
   packages/openev/branches/upstream/current/pics/zoomrect.xpm
   packages/openev/branches/upstream/current/pymod/
   packages/openev/branches/upstream/current/pymod/.cvsignore
   packages/openev/branches/upstream/current/pymod/CVS/
   packages/openev/branches/upstream/current/pymod/CVS/Entries
   packages/openev/branches/upstream/current/pymod/CVS/Repository
   packages/openev/branches/upstream/current/pymod/CVS/Root
   packages/openev/branches/upstream/current/pymod/Makefile.in
   packages/openev/branches/upstream/current/pymod/_gtkmissing.def
   packages/openev/branches/upstream/current/pymod/_gview.def
   packages/openev/branches/upstream/current/pymod/filedlg.py
   packages/openev/branches/upstream/current/pymod/generate.py
   packages/openev/branches/upstream/current/pymod/gtkmissing.c
   packages/openev/branches/upstream/current/pymod/gtkmissing.py
   packages/openev/branches/upstream/current/pymod/gv.defs
   packages/openev/branches/upstream/current/pymod/gv_ciet.c
   packages/openev/branches/upstream/current/pymod/gvalg.py
   packages/openev/branches/upstream/current/pymod/gvbitlayerlut.py
   packages/openev/branches/upstream/current/pymod/gvclassification.py
   packages/openev/branches/upstream/current/pymod/gvclassifydlg.py
   packages/openev/branches/upstream/current/pymod/gvcommand.py
   packages/openev/branches/upstream/current/pymod/gvconst.py
   packages/openev/branches/upstream/current/pymod/gvcorecmds.py
   packages/openev/branches/upstream/current/pymod/gvhtml.py
   packages/openev/branches/upstream/current/pymod/gview.py
   packages/openev/branches/upstream/current/pymod/gviewapp.py
   packages/openev/branches/upstream/current/pymod/gvlabeledit.py
   packages/openev/branches/upstream/current/pymod/gvlegenddlg.py
   packages/openev/branches/upstream/current/pymod/gvmaptools.py
   packages/openev/branches/upstream/current/pymod/gvmodule.c
   packages/openev/branches/upstream/current/pymod/gvmodule_defs.c
   packages/openev/branches/upstream/current/pymod/gvmodule_impl.c
   packages/openev/branches/upstream/current/pymod/gvogrdlg.py
   packages/openev/branches/upstream/current/pymod/gvogrfs.py
   packages/openev/branches/upstream/current/pymod/gvogrfsgui.py
   packages/openev/branches/upstream/current/pymod/gvplot.py
   packages/openev/branches/upstream/current/pymod/gvpquerypropdlg.py
   packages/openev/branches/upstream/current/pymod/gvprint.py
   packages/openev/branches/upstream/current/pymod/gvrasterpropdlg.py
   packages/openev/branches/upstream/current/pymod/gvsdsdlg.py
   packages/openev/branches/upstream/current/pymod/gvselbrowser.py
   packages/openev/branches/upstream/current/pymod/gvshell.py
   packages/openev/branches/upstream/current/pymod/gvsignaler.py
   packages/openev/branches/upstream/current/pymod/gvutils.py
   packages/openev/branches/upstream/current/pymod/gvvectorpropdlg.py
   packages/openev/branches/upstream/current/pymod/gvviewwindow.py
   packages/openev/branches/upstream/current/pymod/ibrowse.py
   packages/openev/branches/upstream/current/pymod/layerdlg.py
   packages/openev/branches/upstream/current/pymod/makefile.vc
   packages/openev/branches/upstream/current/pymod/mkgv.py
   packages/openev/branches/upstream/current/pymod/nls.py
   packages/openev/branches/upstream/current/pymod/oe_about.py
   packages/openev/branches/upstream/current/pymod/oeattedit.py
   packages/openev/branches/upstream/current/pymod/openev.py
   packages/openev/branches/upstream/current/pymod/pathutils.py
   packages/openev/branches/upstream/current/pymod/pgu.py
   packages/openev/branches/upstream/current/pymod/pgucolor.py
   packages/openev/branches/upstream/current/pymod/pgucolorsel.py
   packages/openev/branches/upstream/current/pymod/pgucolourswatch.py
   packages/openev/branches/upstream/current/pymod/pgucombo.py
   packages/openev/branches/upstream/current/pymod/pguentry.py
   packages/openev/branches/upstream/current/pymod/pgufilesel.py
   packages/openev/branches/upstream/current/pymod/pgufont.py
   packages/openev/branches/upstream/current/pymod/pgugrid.py
   packages/openev/branches/upstream/current/pymod/pgumenu.py
   packages/openev/branches/upstream/current/pymod/pguprogress.py
   packages/openev/branches/upstream/current/pymod/pgushapesgrid.py
   packages/openev/branches/upstream/current/pymod/pgutextarea.py
   packages/openev/branches/upstream/current/pymod/pgutogglebutton.py
   packages/openev/branches/upstream/current/pymod/pyshell.py
   packages/openev/branches/upstream/current/pymod/scmexpr.py
   packages/openev/branches/upstream/current/pymod/testmain.py
   packages/openev/branches/upstream/current/pymod/toolexample.py
   packages/openev/branches/upstream/current/pymod/toolfile_example.txt
   packages/openev/branches/upstream/current/pymod/vecplot.py
   packages/openev/branches/upstream/current/pymod/vrtutils.py
   packages/openev/branches/upstream/current/ramps/
   packages/openev/branches/upstream/current/ramps/CVS/
   packages/openev/branches/upstream/current/ramps/CVS/Entries
   packages/openev/branches/upstream/current/ramps/CVS/Repository
   packages/openev/branches/upstream/current/ramps/CVS/Root
   packages/openev/branches/upstream/current/ramps/blue_green_ramp.txt
   packages/openev/branches/upstream/current/ramps/blue_ramp.txt
   packages/openev/branches/upstream/current/ramps/blue_white_red_ramp.txt
   packages/openev/branches/upstream/current/ramps/brown_ramp.txt
   packages/openev/branches/upstream/current/ramps/cyan_ramp.txt
   packages/openev/branches/upstream/current/ramps/discrete_1.txt
   packages/openev/branches/upstream/current/ramps/discrete_2.txt
   packages/openev/branches/upstream/current/ramps/gray_ramp.txt
   packages/openev/branches/upstream/current/ramps/green_ramp.txt
   packages/openev/branches/upstream/current/ramps/green_yellow_red_ramp.txt
   packages/openev/branches/upstream/current/ramps/lavender_ramp.txt
   packages/openev/branches/upstream/current/ramps/orange_brown_ramp.txt
   packages/openev/branches/upstream/current/ramps/orange_ramp.txt
   packages/openev/branches/upstream/current/ramps/pink_red_ramp.txt
   packages/openev/branches/upstream/current/ramps/purple_ramp.txt
   packages/openev/branches/upstream/current/ramps/real_blue_ramp.txt
   packages/openev/branches/upstream/current/ramps/real_red_ramp.txt
   packages/openev/branches/upstream/current/ramps/red_blue_ramp.txt
   packages/openev/branches/upstream/current/ramps/red_green_ramp.txt
   packages/openev/branches/upstream/current/ramps/red_ramp.txt
   packages/openev/branches/upstream/current/ramps/rose_red_ramp.txt
   packages/openev/branches/upstream/current/ramps/yellow_ramp.txt
   packages/openev/branches/upstream/current/shapefil.h
   packages/openev/branches/upstream/current/shpopen.c
   packages/openev/branches/upstream/current/symbols/
   packages/openev/branches/upstream/current/symbols/CVS/
   packages/openev/branches/upstream/current/symbols/CVS/Entries
   packages/openev/branches/upstream/current/symbols/CVS/Repository
   packages/openev/branches/upstream/current/symbols/CVS/Root
   packages/openev/branches/upstream/current/symbols/circle.xml
   packages/openev/branches/upstream/current/symbols/circle_filled.xml
   packages/openev/branches/upstream/current/symbols/cross.xml
   packages/openev/branches/upstream/current/symbols/dash.xml
   packages/openev/branches/upstream/current/symbols/square.xml
   packages/openev/branches/upstream/current/symbols/square_filled.xml
   packages/openev/branches/upstream/current/symbols/triangle.xml
   packages/openev/branches/upstream/current/symbols/triangle_filled.xml
   packages/openev/branches/upstream/current/symbols/x.xml
   packages/openev/branches/upstream/current/testmain.c
   packages/openev/branches/upstream/current/tools/
   packages/openev/branches/upstream/current/tools/.cvsignore
   packages/openev/branches/upstream/current/tools/CVS/
   packages/openev/branches/upstream/current/tools/CVS/Entries
   packages/openev/branches/upstream/current/tools/CVS/Repository
   packages/openev/branches/upstream/current/tools/CVS/Root
   packages/openev/branches/upstream/current/tools/Tool_DriverList.py
   packages/openev/branches/upstream/current/tools/Tool_Export.py
   packages/openev/branches/upstream/current/tools/Tool_ShapesGrid.py
   packages/openev/branches/upstream/current/tools/Tool_autopan.py
   packages/openev/branches/upstream/current/tools/calculator.py
   packages/openev/branches/upstream/current/tools/compose.py
   packages/openev/branches/upstream/current/tools/fft.py
   packages/openev/branches/upstream/current/tools/gvrastertools.py
   packages/openev/branches/upstream/current/tools/imgproctemplate.py
   packages/openev/branches/upstream/current/tools/isodata.py
   packages/openev/branches/upstream/current/tools/mil_symbols.py
   packages/openev/branches/upstream/current/tools/open_raw.py
   packages/openev/branches/upstream/current/tools/open_subarea.py
   packages/openev/branches/upstream/current/tools/rendertest.py
   packages/openev/branches/upstream/current/xmlconfig/
   packages/openev/branches/upstream/current/xmlconfig/CVS/
   packages/openev/branches/upstream/current/xmlconfig/CVS/Entries
   packages/openev/branches/upstream/current/xmlconfig/CVS/Repository
   packages/openev/branches/upstream/current/xmlconfig/CVS/Root
   packages/openev/branches/upstream/current/xmlconfig/DefaultIconFile.xml
   packages/openev/branches/upstream/current/xmlconfig/DefaultMenuFile.xml
   packages/openev/branches/upstream/current/xmlconfig/DefaultPyshellFile.xml
   packages/openev/tags/
Log:
[svn-inject] Installing original source of openev

Added: packages/openev/branches/upstream/current/.cvsignore
===================================================================
--- packages/openev/branches/upstream/current/.cvsignore	                        (rev 0)
+++ packages/openev/branches/upstream/current/.cvsignore	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,9 @@
+*.lib
+*.pdb
+*.exe
+*.ilk
+gv_config.h
+config.status
+config.log
+config.cache
+gvtest

Added: packages/openev/branches/upstream/current/COPYING
===================================================================
--- packages/openev/branches/upstream/current/COPYING	                        (rev 0)
+++ packages/openev/branches/upstream/current/COPYING	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,438 @@
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS

Added: packages/openev/branches/upstream/current/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,131 @@
+/.cvsignore/1.4/Tue Jul 25 14:22:32 2000//
+/COPYING/1.2/Sat Sep 16 14:03:43 2000//
+/ChangeLog/1.249/Fri Jan 14 15:46:09 2005//
+/Makefile/1.19/Mon Apr  5 05:09:49 2004//
+/Makefile.in/1.41/Thu Aug 18 03:28:36 2005//
+/TODO/1.18/Sun Jun 27 20:18:13 2004//
+/aclocal.m4/1.6/Tue Jan 20 16:05:55 2004//
+/appcurlayer.c/1.4/Thu Feb 27 03:59:21 2003//
+/appcurlayer.h/1.1/Fri Aug 25 20:02:22 2000//
+/configure/1.16/Thu Jun 24 21:09:58 2004//
+/configure.in/1.16/Thu Jun 24 21:09:58 2004//
+/crs.c/1.2/Fri Oct 26 13:14:24 2001//
+/crs.h/1.2/Fri Oct 26 13:14:24 2001//
+/dbfopen.c/1.4/Thu Sep 30 17:06:33 2004/-ko/
+/gextra.c/1.4/Tue Jun 20 13:26:54 2000//
+/gextra.h/1.4/Tue Jun 20 13:27:08 2000//
+/gtkcolorwell.c/1.7/Thu Sep 27 00:39:14 2001//
+/gtkcolorwell.h/1.3/Wed Sep 26 20:29:35 2001//
+/gv_config.h.in/1.3/Wed Jun 28 13:10:42 2000//
+/gv_config.h_win32/1.1/Sun Apr 16 21:59:14 2000//
+/gvarealayer.c/1.18/Mon Nov  4 21:42:06 2002//
+/gvarealayer.h/1.4/Tue Jun 20 13:27:08 2000//
+/gvareas.c/1.15/Mon Nov  4 21:42:06 2002//
+/gvareas.h/1.11/Mon Nov  4 21:42:06 2002//
+/gvareatool.c/1.25/Thu Oct 28 21:59:52 2004//
+/gvareatool.h/1.7/Thu Aug 10 15:56:53 2000//
+/gvautopan.c/1.3/Mon Sep 12 15:33:10 2005//
+/gvautopan.h/1.3/Mon Sep 12 15:33:10 2005//
+/gvdata.c/1.15/Fri Feb  7 20:06:49 2003//
+/gvdata.h/1.13/Fri Feb  7 20:06:49 2003//
+/gview.h/1.16/Tue Feb 22 13:22:36 2005//
+/gvlayer.c/1.13/Fri Oct 12 17:44:18 2001//
+/gvlayer.h/1.13/Fri Oct 12 17:44:18 2001//
+/gvlinelayer.c/1.12/Tue Nov  5 18:56:24 2002//
+/gvlinelayer.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvlinetool.c/1.19/Mon Nov  4 21:42:06 2002//
+/gvlinetool.h/1.6/Tue Jun 20 13:27:08 2000//
+/gvmanager.c/1.14/Tue Feb 10 15:38:30 2004//
+/gvmanager.h/1.6/Tue Feb 10 15:38:31 2004//
+/gvmesh.c/1.37/Tue Aug 30 12:31:32 2005//
+/gvmesh.h/1.19/Fri Apr 12 14:40:36 2002//
+/gvnodetool.c/1.13/Mon Nov  4 21:42:06 2002//
+/gvnodetool.h/1.4/Mon Sep 30 20:09:27 2002//
+/gvogr.c/1.5/Tue Jan 20 16:05:02 2004//
+/gvpointlayer.c/1.6/Tue Nov  5 18:56:24 2002//
+/gvpointlayer.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvpoints.c/1.7/Mon Nov  4 21:42:06 2002//
+/gvpoints.h/1.4/Mon Nov  4 21:42:06 2002//
+/gvpointtool.c/1.11/Mon Apr  9 18:15:20 2001//
+/gvpointtool.h/1.4/Tue Jun 20 13:27:08 2000//
+/gvpoitool.c/1.3/Tue Jan 14 16:16:24 2003//
+/gvpoitool.h/1.1/Thu Feb 28 18:52:22 2002//
+/gvpolylines.c/1.10/Tue Nov  5 18:56:21 2002//
+/gvpolylines.h/1.7/Mon Nov  4 21:42:06 2002//
+/gvpquerylayer.c/1.11/Fri Aug 20 13:53:47 2004//
+/gvpquerylayer.h/1.5/Fri Aug 20 13:53:48 2004//
+/gvprint.c/1.8/Thu Feb 15 16:36:51 2001//
+/gvproperties.c/1.5/Mon Sep  9 16:22:45 2002//
+/gvproperties.h/1.6/Mon Nov  4 21:42:06 2002//
+/gvraster.c/1.75/Mon Sep 20 13:15:35 2004//
+/gvraster.h/1.28/Thu Jan 22 19:57:11 2004//
+/gvrasteraverage.c/1.18/Wed Feb 18 16:07:27 2004//
+/gvrasteraverage.h/1.8/Tue Jul 24 02:21:54 2001//
+/gvrastercache.c/1.12/Tue Jun 20 13:26:55 2000//
+/gvrastercache.h/1.10/Tue Jun 20 13:27:08 2000//
+/gvrasterconvert.c/1.20/Wed Jun 23 14:35:03 2004//
+/gvrasterize.c/1.5/Fri Nov 19 23:59:37 2004//
+/gvrasterize.h/1.3/Fri Nov 19 23:59:37 2004//
+/gvrasterlayer.c/1.87/Tue Aug 30 12:44:11 2005//
+/gvrasterlayer.h/1.32/Wed Jun 23 14:35:05 2004//
+/gvrasterlut.c/1.31/Tue Aug 30 12:58:56 2005//
+/gvrasterlut.h/1.15/Tue Aug 30 12:58:56 2005//
+/gvrastersource.c/1.16/Wed Jun 23 14:35:05 2004//
+/gvrastertypes.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvrecords.c/1.5/Wed Aug  6 22:26:03 2003//
+/gvrecords.h/1.5/Wed Aug  6 22:26:03 2003//
+/gvrecttool.c/1.7/Mon Nov  4 21:42:06 2002//
+/gvrecttool.h/1.3/Thu Jul 27 20:06:23 2000//
+/gvrenderinfo.c/1.23/Wed Jun 25 16:42:18 2003//
+/gvrenderinfo.h/1.15/Wed Jun 25 16:42:18 2003//
+/gvroitool.c/1.11/Mon Nov  4 21:42:06 2002//
+/gvroitool.h/1.8/Thu Jul 27 20:06:23 2000//
+/gvrotatetool.c/1.2/Wed Jun 25 17:07:22 2003//
+/gvrotatetool.h/1.1/Wed Jun 25 16:40:44 2003//
+/gvselecttool.c/1.17/Tue Sep 16 15:43:11 2003//
+/gvselecttool.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvshape.c/1.21/Tue Jan  4 18:50:29 2005//
+/gvshapefile.c/1.21/Tue May 27 21:32:40 2003//
+/gvshapelayer.c/1.26/Fri May 16 18:26:33 2003//
+/gvshapelayer.h/1.11/Thu Feb 27 04:00:19 2003//
+/gvshapes.c/1.20/Fri Jan 14 16:51:51 2005//
+/gvshapes.h/1.26/Fri Jan 14 16:51:51 2005//
+/gvshapeslayer.c/1.73/Thu Apr  8 18:03:18 2004//
+/gvshapeslayer.h/1.18/Fri Sep 12 17:35:43 2003//
+/gvskirt.c/1.6/Mon Oct  7 06:08:07 2002//
+/gvsymbolmanager.c/1.12/Mon Feb 16 17:08:12 2004//
+/gvsymbolmanager.h/1.5/Tue Sep  2 17:25:08 2003//
+/gvtess.c/1.12/Tue Dec 10 02:57:33 2002//
+/gvtess.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvtessshape.c/1.11/Tue Feb 25 19:41:23 2003//
+/gvtexturecache.c/1.4/Thu Dec 13 03:29:17 2001//
+/gvtool.c/1.11/Mon Jan 17 18:37:43 2005//
+/gvtool.h/1.8/Mon Jan 17 18:37:43 2005//
+/gvtoolbox.c/1.8/Thu Aug  3 18:39:49 2000//
+/gvtoolbox.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvtracktool.c/1.8/Mon Nov  4 21:42:07 2002//
+/gvtracktool.h/1.2/Tue Jun 20 13:27:08 2000//
+/gvtypes.h/1.15/Fri Mar  5 23:40:12 2004//
+/gvundo.c/1.6/Mon Sep 30 20:50:32 2002//
+/gvundo.h/1.3/Tue Jun 20 13:27:08 2000//
+/gvutils.c/1.22/Wed Jun 23 14:35:05 2004//
+/gvutils.h/1.5/Sun Apr 22 17:32:25 2001//
+/gvviewarea.c/1.125/Fri Jan 14 15:27:26 2005//
+/gvviewarea.h/1.60/Fri Jan 14 15:27:28 2005//
+/gvviewlink.c/1.12/Tue Jan 18 19:55:42 2005//
+/gvviewlink.h/1.5/Fri Feb 21 22:35:31 2003//
+/gvwinprint.c/1.4/Tue May  1 19:21:51 2001//
+/gvzoompantool.c/1.7/Mon Jul 10 13:37:05 2000//
+/gvzoompantool.h/1.2/Tue Jun 20 13:27:08 2000//
+/install-sh/1.1/Mon Apr  5 02:18:04 2004//
+/invdistance.c/1.3/Sun Apr 22 17:33:24 2001//
+/invdistance.h/1.2/Sun Apr 22 17:33:24 2001//
+/ipgcplayer.c/1.15/Mon Feb 23 20:16:36 2004//
+/ipgcplayer.h/1.1/Fri Jul 14 18:24:57 2000//
+/llrasterize.c/1.9/Tue Nov 23 06:14:55 2004//
+/makefile.vc/1.31/Tue Feb 22 13:22:36 2005//
+/nmake.opt/1.3/Tue Feb 22 13:22:37 2005//
+/shapefil.h/1.4/Thu Sep 12 15:17:21 2002/-ko/
+/shpopen.c/1.3/Thu Sep 12 15:17:21 2002/-ko/
+/testmain.c/1.33/Wed Jun 25 17:52:08 2003//
+D

Added: packages/openev/branches/upstream/current/CVS/Entries.Log
===================================================================
--- packages/openev/branches/upstream/current/CVS/Entries.Log	                        (rev 0)
+++ packages/openev/branches/upstream/current/CVS/Entries.Log	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,9 @@
+A D/delivery////
+A D/doc////
+A D/html////
+A D/pics////
+A D/pymod////
+A D/ramps////
+A D/symbols////
+A D/tools////
+A D/xmlconfig////

Added: packages/openev/branches/upstream/current/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev

Added: packages/openev/branches/upstream/current/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/ChangeLog
===================================================================
--- packages/openev/branches/upstream/current/ChangeLog	                        (rev 0)
+++ packages/openev/branches/upstream/current/ChangeLog	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1254 @@
+2005-01-14  Frank Warmerdam  <warmerdam at pobox.com>
+
+	* gviewarea/gview: Added accessors on GvViewArea for flip flags,
+	and modified so they are saved/restored in project file.
+
+2004-10-07  Frank Warmerdam  <warmerdam at pobox.com>
+
+	* pymod/gvviewwindow.py: added case for opening raster files
+	with 2 bands where the second is an alpha band.  It creates it as
+	an RGBA layer.
+
+2004-09-30  Paul Spencer <pspencer at dmsolutions.ca>
+
+    * dbfopen.c: updated DBFIsAttributeNULL with code from
+    equivalent file from gdal so that empty integer values will
+    be interpreted as NULLs (instead of 0).
+
+2004-09-20  Paul Spencer <pspencer at dmsolutions.ca>
+
+    * gvraster.c: added patch for isnan support on win32
+
+2004-08-27  Paul Spencer <pspencer at dmsolutions.ca>
+
+    * pymod/gvclassifydlg.py: changed use of repr to str to prevent
+    floating point numbers from having excessive decimal places.  Also
+    modified the logic in the reclassify dialog to build the list of
+    choices in correct order (previously it came from dictionary keys)
+
+2004-08-20  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvpquerylayer.{c,h}, pymod/gv.defs, pymod/gview.py:
+    GvPqueryLayer() constructor accepts passed in GvShapes.  
+    GvPqueryLayer now serializes and deserializes correctly.
+
+2004-08-17  Paul Spencer <pspencer at dmsolutions.ca>
+
+    * pymod/gv_ciet.c, pymod/gvmodule.c: added function to return arbitrary
+    columns of a gvrecords object as a python dictionary
+
+2004-08-16  Paul Spencer <pspencer at dmsolutions.ca>
+
+    * pymod/filedlg.py: modified handling of filters to repect order in which
+    they are specified when populating the dropdown list
+
+2004-07-24  Andrey Kiselev  <dron at remotesensing.org>
+
+    * tools/fft.py: Added new 'Fast Fourier Transform' tool.
+
+    * pymod/gvprint.py: Added possibility to explicitly specify printed image
+    size; several GUI tweaks.
+
+2004-07-23  Paul Spencer <pspencer at dmsolutions.ca>
+
+    * pymod/gvclassifydlg.py: restructured signals for OK and apply
+    to prevent a problem where listeners to the ok signal or classifcation
+    -changed signal could be called before the classification had actually
+    been applied to the layer
+
+2004-07-20  Paul Spencer <pspencer at dmsolutions.ca>
+    
+    * pymod/filedly.py: added patch from Zak (zjames at dmsolutions.ca)
+    to handle manual input in a more reliable way
+    
+    * pymod/pgufont.py: added ability to load a font and report statistics
+    on the font such as text width and height
+
+2004-07-02  Julien Demaria  <dem at acri-st.fr>
+
+    * pymod/{gview.py, gviewapp.py, gvutils.py, gvviewwindow.py,
+    pathutils.py}: Enhance project files portability : now filesystem
+    filenames can be retrieved relatively to the project file if the
+    absolute path doesn't exist. Implement also path separator portability.
+    Use internally a PortablePath object (in pymod/pathutils.py).
+    Backward compatibility with old project files.
+    Tested between Linux, Solaris and Win32 systems.
+
+    * pymod/gview.py: last_strech restored in projects reloading
+
+    * pymod/gvutils: Change to don't crash OpenEV when a deserialization
+    of a layer fails in projects reloading.
+    Popup a gvutils.warning instead.
+
+    * pymod/gviewapp.py, xmlconfig/DefaultMenuFile.xml: add a
+    "File/Save Project as..." menu.
+
+    * pymod/gviewapp.py: Fixed a little bug in project filename saving :
+    .opf extension wasn't well added.
+
+2004-05-18  Andrey Kiselev  <dron at remotesensing.org>
+
+    * tools/open_subarea.py: New 'Open Subarea' tool added.
+
+2004-05-12  Andrey Kiselev  <dron at remotesensing.org>
+
+    * pymod/gvrasterpropdlg.py: Preliminary support for changing
+    projection on the fly.
+
+2004-05-12  Julien Demaria  <dem at acri-st.fr>
+
+    * pymod/gvmodule.c: Fix a bug in _wrap_gv_raster_layer_get_mesh_lod :
+    this wrapper created a python double from a C int and then the
+    mesh_lod was wrong (example 223.454).
+    The wrapper now returns an int.
+    This bug crashed OpenEV when for example we save a project,
+    then reload it : if the wrong mesh is big on a lot of images of the
+    project, this uses a lot of memory and done an allocation error.
+    Note that all projects saved until this fix have wrong mesh_lods...
+
+2004-04-21  Andrey Kiselev  <dron at remotesensing.org>
+
+    * gvutils.c: Fix comparisons with NODATA value.
+
+    * pymod/gview.py: Fixed problem with reading NODATA value from
+    the project file.
+
+    * pymod/{gviewapp.py, gvviewwindow.py}: New option added: save
+    last visited directory in the config file.
+
+    * gvrastersource.c, pymod/gview.py: Fixed erroneous setting of the
+    minimum and maximum levels in greyscale lock mode as per bug
+    http://bugzilla.remotesensing.org/show_bug.cgi?id=270
+
+2004-04-04  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Makefile.in: added various stuff related to installing into an 
+    OpenEV style tree.
+
+2004-02-18  Andrey Kiselev  <dron at remotesensing.org>
+
+    * gvrastersource.c, pymod/gview.py: Fixed erroneous setting of the
+    minimum and maximum levels in greyscale lock mode as per bug
+    http://bugzilla.remotesensing.org/show_bug.cgi?id=270
+
+    * gvmesh.c, gvrasteraverage.c: Use gv_raster_get_nodata() instead of
+    GDALGetRasterNoDataValue().
+
+2004-02-10  Andrey Kiselev  <dron at remotesensing.org>
+
+    * gvmanager.c, gvmanager.h, pymod/{gvmodule.c, gview.py,
+    gvviewwindow.py, gviewapp.py}: Added method open_gdal_dataset() to
+    load GDAL dataset without referencing by filename.
+
+2004-01-23  Andrey Kiselev  <dron at remotesensing.org>
+
+    * pymod/gvrasterpropdlg.py: New control to display and change NODATA
+    value for viewed RasterSource.
+
+2004-01-22  Andrey Kiselev  <dron at remotesensing.org>
+
+    * gvraster.c, gvraster.h: Use gv_raster_get_nodata() function to fetch
+    the NODATA value from the image using GDALGetRasterNoDataValue().
+
+    * gvrastersource.c, gvrasterlayer.h: Added methods
+    gv_raster_layer_nodata_set() and gv_raster_layer_nodata_get() to work
+    with nodata_* layer properties and method gv_raster_layer_type_get()
+    to query raster data type.
+
+    * gvutils.c: gv_format_point_query() returns "[NODATA]" label if value
+    marked as NODATA.
+
+    * pymod/gviewapp.py: New switch to control displaying "[NODATA]" marks
+    in the tracker tool. Tweaks in 'Preferences' dialog.
+
+    * pymod/gview.py: New methods in GvRasterLayer class: nodata_get(),
+    nodata_set(), type_get(). get_nodata() method now deprecated (though,
+    it does the same as nodata_get).
+
+    * pymod/{gv.defs, gvmodule.c}: Added wrappers for
+    gv_raster_layer_nodata_set(), gv_raster_layer_nodata_get() and
+    gv_raster_layer_type_get().
+
+2003-06-25  Paul Spencer  <pgs at magma.ca>
+
+    * gvshapes.c: added define of M_PI if not already defined.
+
+2003-05-27  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrenderinfo.c: Fixed support for getting ogr-pen-* pen names
+    from the "id" attribute as well as from the "p" pattern attribute.
+
+    * gvrecords.c: Added new support for efficient fixed schema/fixed
+    field length records.  Not yet complete. 
+
+    * gvshapefile.c: Added support for reading multi-part arcs as
+    a COLLECTION of lines.
+
+2003-05-22  Diana Esch-Mosher  <desch-mosher at lanl.gov>
+
+    * pymod/gvutils.py:  return from parse_accelerator 
+    incorrectly shifted
+
+2003-05-08  Paul Spencer    <pgs at magma.ca>
+
+    * gvviewarea.c: modified logic controlling adjustments
+    to use a step size of 1/4 the page size
+    
+    * pymod/gvogrfs.py: fixed a bug when params had attributes
+    with no values.
+
+2003-04-09  Paul Spencer    <pgs at magma.ca>
+
+    * gvrenderinfo.h,gvrenderinfo.c: added shadow, halo and
+    background color attributes to label tool
+    
+    * gvshapelayer.c: added initialization of shadow, halo
+    and background color attributes to label tool
+    
+    * gvshapeslayer.c: added rendering of halo and shadow
+    effects for label tool
+
+2003-04-08  Andrey Kiselev  <dron at remotesensing.org>
+
+    * gvsymbolmanager.c: Added gv_symbol_manager_save_vector_symbol()
+    function to save vector symbols with new names.
+
+2003-04-07  Andrey Kiselev  <dron at remotesensing.org>
+
+    * gvsymbolmanager.c: gv_symbol_manager_get_symbol() now able
+    to load and inject vector symbols.
+
+2003-04-07  Paul Spencer  <pgs at magma.ca>
+
+    * gvrenderinfo.h,gvrenderinfo.c: added pattern attribute to
+    pen objects
+    
+    * gvshapelayer.c: added initialization of pattern attribute
+    on new pen objects
+    
+    * gvshapeslayer.c: added rendering of patterns in pen objects
+
+2003-04-02  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvmodule.c, pymod/gview.py: added wrapper for 
+    gv_format_point_query to gvmodule.c and added method to
+    GvViewArea to call it.
+
+2003-02-14  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/filedlg.py: added ability to allow users to select mulitple 
+    files
+    
+    * pymodpgutextarea.py: added page_up/page_down functions.
+    
+    * gvrenderinfo.c, gvrenderinfo.h, gvshapelayer.c, gvshapeslayer.c added
+    support for line widths in PENs and _area_edge_width and _line_width
+    
+    * pymod/gvvectorpropdlg.py: added support for _area_edge_width,
+    _line_width, and _gl_antialias
+
+2003-01-06  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvogr.c, pymod/gvviewwindow.py, pymod/gvogrdlg.py: added new dialog 
+    to allow selection from amoung OGR layers, and loading using an
+    user SQL query.
+
+2002-11-15  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrenderinfo/gvshapeslayer.c: added support for LABEL anchor
+    (justification) attribute. 
+
+2002-11-14  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvsymbolmanager.c: Incorporated Pauls GvSymbolManager object, and
+    support for raster symbols.  Some work still outstanding.
+
+2002-10-30  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvraster.c: modified to fill partial tiles with data extended out
+    from the valid data.  This fixes some esoteric "streaking" problems. 
+
+2002-10-08  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvraster.c: Modified gv_raster_build_poly_transform() to 
+    back off using the highest possible order polynomial if the
+    CRS_compute_georef_equations() call fails.  This ensures that 
+    underdetermined sets of GCPs (ie. those with linear dependencies)
+    can still produce useful polynomials, even if they are only 1st order.
+
+2002-09-30  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvnodetool.c, gvareatoolc, gvshapesfile.c: try to maintain closed
+    ring integrity while editing areas, and enforce it when saving to 
+    shapefiles.
+
+    * gvshapeslayer.c: Fixed some quirks with display list support.  
+    Ensure that selected shapes are drawn (get rid of hit_selected). 
+    Also ensure that the display list is wiped in case of selection or
+    display change events. 
+
+2002-09-27  Paul Spencer  <pgs at magma.ca>
+    
+    * gvshapeslayer.c, gvshapeslayer.h: added display lists to
+    speed up rendering of shapes layers and added antialiasing
+    support for line layers
+    * gvskirt.c: changed how skirts polygons are built and removed
+    the white lines.
+
+2002-09-16  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: Ensure depth buffer is cleared before doing
+    a render_to_func. This fixes problems printing 3D views at 1:1. 
+
+2002-09-12  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * oeattedit.py: Gillian added the ability to create new fields.
+
+2002-09-11  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gview.py, etc: modifications so that 3D views can be saved to
+    project files, and restored properly.
+
+    * gvviewarea.c: ensure GL_NORMALIZE and GL_DEPTH_TEST are
+    disabled in 2D mode (gl_view_area_expose()).
+
+2002-09-10  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * added get_height_scale() method on GvViewArea.
+
+2002-08-09  Paul Spencer <pgs at magma.ca>
+
+    * pymod/pgucombo.py: fixed a problem in set_popdown_strings
+
+    * pymod/pgucolor.py: added discrete color ramp support
+    
+    * pymod/gvclassification.py and gvclassifydlg.py: finished vector
+    classification support started by Frank.
+
+2002-07-24  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Reimplemented the GvProperties using symbol tables and integer
+    quark ids.  Must faster performance with many properties in one list.
+    Memory for strings never recovered, but only one copy of any given
+    string in the string table.
+
+2002-07-23  Paul Spencer <pgs at magma.ca>
+
+    * added pgutextarea.py, a simple scrollable text widget (read-only)
+
+2002-07-18  Paul Spencer <pgs at magma.ca>
+
+    * gvshapefile.c, gvshapes.h: added gv_shapes_to_dbf to allow a
+    GvShapes container's attributes to be saved directly to a DBF
+    file without needing to save the whole shape file.
+    
+    * gvmodule.c: added wrapper for gv_shapes_to_dbf
+    
+    * gview.py: added GvShapes.save_to_dbf()
+
+2002-07-08  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gviewapp.py, gvviewwindow.py: Added load/save project support.
+
+    * gvviewarea.c/h: Added properties to the GvViewArea.
+
+2002-05-06  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvshape.c/gvshapes.c/gvshape.h/gvshapeslayer.c: Added partial
+    support for the new GVSHAPE_COLLECTION primitive type.
+
+2002-03-21  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrenderinfo/gvvectorpropdlg: Added concept of _gv_ogrfs_point, 
+    _gv_ogrfs_line and _gv_ogrfs_area properties on GvShapesLayers to
+    indicate the styling for particular geometry types (as opposed to
+    the _gv_ogrfs which would apply to all). Use this for symbols and
+    labels in gvvectorpropdlg.py.  This responds to 528432 on SourceForge.
+
+2002-03-20  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea/gvrasterlayer: added the render_exact flag to GvViewArea
+    indicating the proper resolution textures should be loaded and
+    used immediately rather than deferring their load till after the
+    current render.  This is used by the printing engine to ensure 
+    appropriate output.
+
+2002-03-07  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvshape.c: added preliminary gv_shape_clip_to_rect() implementation.
+
+2002-03-06  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvmesh.c/gvshapes.c: added default_height argument to add_height()
+    functions.  This is used when no value can be found for a mesh or
+    shape vertex.
+
+2002-02-22  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrenderinfo/gvshapelayer/gvshapeslayer: Added support for PEN and
+    BRUSH tools.  Currently just gets color ... other styling is ignored.
+
+2002-01-30  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewlink.c: Implement more sophisticated state copying that is
+    aware of raw/geo, and projections.  It also preserves viewstate shape.
+
+    * gvviewarea: added set_state() and get_primary_raster() methods.
+
+2002-01-18  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gview.py: added the GvShapes.get_extents() method.
+
+2002-01-17  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvselecttool.c: Fixed bug that was preventing shift-leftclick from
+    working to add/subtract a feature to/from the selection.
+
+2001-12-12  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvtexturecache.c/gvviewarea.c: avoid purging textures used within this
+    render operation.  Avoid texture thrashing hell in at least some 
+    situations.
+
+    * gvviewarea.c: fixed fit_all_layers to fetch/compute the volume
+    for the 3d view setting.  Was uninitialized in some cases before.
+
+    * pymod/gvviewwindow.py: fixed invocation of 3d positioning dialog.
+
+    * html/*: updated mainwindow dialog and associated materials.
+
+2001-11-29  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvraster.c, gvrasterlayer.c: Add use of autoscale_samples to
+    determine the number of samples requested from a raster for scaling.
+    Currently there is no gui element to set this preference.
+
+2001-11-28  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasterlayer.c: added mesh_is_dirty flag, and code to update mesh
+    on redraw if it is dirty.  Flag is set on geotransform-changed signal
+    from prototype GvRaster.
+
+    * gvraster.c: added set_gcps(), get_gcps() and geotransform-changed 
+    signal.
+
+2001-11-14  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/pyshell.py: fixed overwrite problem on windows using
+    freeze()/thaw() on text widget.
+
+2001-11-12  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gviewapp.py: moved GvViewApp and related code here from
+    openev.py.
+
+    * pymod/gvshell.py: moved shell instrinsics like display() and roi()
+    here from openev.py.
+
+    * pymod/pyshell.py: added locals pseudo-command to dump local vars.
+
+2001-11-09  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvogr.cpp: copy over style string as _gv_ogrfs if available.
+
+2001-11-07  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gvmodule.c: fixed serious memory leak in 
+    gv_shapes_get_properties.
+
+2001-10-25  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasterlayer.c, pymod/openev.py: added support for a global 
+    interp_mode preference to control the default subpixel interpolation
+    mode of raster layers on creation.
+
+2001-10-22  Paul Spencer  <pgs at magma.ca>
+
+    * gvselecttool.c: in gv_selection_tool_deactivate(), moved call to 
+        base class deactivate to after the tool specific deactivation to 
+        ensure that the tool properly deregisters callbacks.
+
+2001-10-22  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: Call gv_view_area_state_changed() in 
+    gv_view_area_add_layer() to ensure a refresh.  
+    Bug 527 in DMSG Bugzilla.
+
+2001-10-17  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * added support for composing 2D complex PCT with 1D enhancement LUT.
+    GUI image enhancements now apply to complex layers as well.
+
+2001-10-16  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gvviewwindow.py: added new enhancement options.
+
+    * pymod/gview.py: added various enhancement related stuff.
+
+    * gvrasterlayer.c: added autoscale and histogram functions on the
+    view. 
+
+    * gvraster.c: autoscale() can now be passed a sample set.
+
+2001-10-12  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gvrasterpropdlg.py: improved logic for setting the initial
+    min and max of the scaling controls.  
+
+    * gviewarea.c/gvrasterlayer.c: 
+
+    GvLayer's now maintain a pending_idle flag indicating if they have 
+    pending idle work that will lead to a refined redraw. 
+
+    GvViewArea's now establish a timer after a redraw to keep track of
+    the elapsed time since the last redraw.  This can be used via
+    the gv_view_area_redraw_timeout() function for layer idle work handlers
+    to find out if it is time for an intermediate redraw. 
+
+    The gv_view_area_pending_idle_work() function may be used to check
+    if there are layers with pending idle work for a view.  
+
+    The GvRasterLayer idle work function now utilizes the above to ensure
+    that a redraw occurs only onces all idle work is finished or the
+    view wide timeout has occured.  This prevents extra redraws from
+    occuring when many raster layers are displayed.
+
+2001-10-11  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: modified to avoid doing re-render on WIN32 if
+    the view hasn't really changed.  This avoids slow rerenders with Mesa
+    on windows in response to window expose events generated by covering
+    dialogs.
+
+2001-09-26  Paul Spencer  <pgs at magma.ca>
+
+    * gtkcolorwell.c: fixed drawing code in _set_ functions to eliminate
+    gtkcritical when changing color of a color well and fixed 'new' to
+    not try to resize the preview widget.
+    
+    * gtkcolorwell.h: removed da member of gtkcolorwell class as it was
+    not referenced (related to above)
+
+2001-09-25  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasterlayer.c: Ensure idle tasks are cleaned up in finalize.
+    Was causing occasional crashes. 
+
+2001-09-17  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvogrfsgui.py: updated to work with ColorButton changes
+    
+    * pymod/pgucolor.py: moved a couple of lines from ColorButton to
+    gtkmissing.py
+    
+    * pymod/gtkmissing.py: added get_color and set_color to GtkColorWell
+    
+    * pymod/gvmodule.c: removed extraneous initialization in gtk_color_well_get_d
+
+2001-09-16  Paul Spencer  <pgs at magma.ca>
+    
+    * gtkcolorwell.c: modified render to honour use_alpha setting and
+    modified gtk_color_well_set_* to only call render/draw if widget
+    is realized.
+    
+    * pymod/gvmodule.c: removed extra declaration in gtk_color_well_get_d
+    
+    * pymod/gtkmissing.py: added get_d to GtkColorWell
+    
+    * pymod/pgucolor.py: removed GtkColorWell (in gtkmissing) and added
+    __init__ and get_color to ColorButton
+
+2001-09-15  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvmodule.c: added python bindings for gtk_color_well_get_d
+    function.
+    
+    * pymod/pgucolor.py: added python wrapper class GtkColorWell and
+    modified ColorButton to extend GtkColorWell
+    
+    * gtkcolorwell.c: modified render code to honour transparency
+    Note that this is not quite complete as it currently always 
+    renders using transparency.
+
+2001-09-14  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gv.defs,gtkmissing.py: implement python bindings for
+    the GtkColorWell class.
+
+2001-08-23  Paul Spencer <pgs at magma.ca>
+    
+    * pymod/gvlabeledit.py: added default_ogrfs = None to __init__ to allow
+    caller to override the default ogr feature spec
+
+2001-08-22  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gv_raster_layer_lut_put(): If any of the palette values have a
+    non-255 alpha, enable alpha blending. 
+
+    * gvmesh.c: Implement gv_mesh_reset_to_identity().  Use this 
+    in gv_raster_layer_new() instead of reversing the geo to pixel
+    transform for cases where it isn't cleanly invertible. 
+
+2001-08-16  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasterconvert.c: fixed leakage of nodata_mask in convert
+    functions.
+
+2001-08-14  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * added standard deviation scaling support.
+
+2001-08-08  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvshapefile.c: call SHPDestroyShape() on shapes after done converting
+    them into GvShapes. 
+
+    * gvshape.c, etc: modified GvShape objects to be reference counted.
+    Python GvShape maintains a reference to the C GvShape while the
+    python object is alive.  Resolves some serious memory leaks.
+
+    * gvmesh.c: fixed serious bug with edge tiles that are not of a
+    reduced size being collapsed to zero space.  Affects images that 
+    508 wide for instance. 
+
+    * gvrasterize.c: fixed leak of polyInts array.
+
+2001-08-07  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvdata.c: implemented gv_data_registry_dump() for debugging 
+    creation and destruction of all GvData derived objects.
+
+    * gvmesh.c: implement finalize, remove normals support.
+
+    * pymod/gview.py: GvTool.get_view() now returns None if the view isn't
+    set yet.
+
+2001-08-07  Paul Spencer <pgs at magma.ca>
+    
+    * pymod/gvlabeledit.py: added code to pick out default colors for labels
+    and added a missing signal in the close() method
+
+2001-07-25  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gvviewarea.py: fixed zoom ratio display so it is correct
+    when a layer is first loaded.
+
+2001-07-24  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasterlut.c: added EV style 2D colormap for complex images.
+    Applied it in gvrasterpropdlg.py, gview.py, gvrasterlayer.c, etc.
+
+2001-07-23  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasteraverage.c: added 8bit averaging scheme that works for
+    scaled phase.
+
+2001-07-17  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: fixed bug with flip_y introduced in mesh_is_raw 
+    changing support.
+
+2001-07-13  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvskirt.c: new - build 3D skirt for raster layer.
+
+    * gvmesh.c: added gv_mesh_get_height().
+
+2001-07-03  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c/gvrasterlayer.c: Added gv_*_{set,get}_raw() support.
+
+2001-06-30  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvogrfsgui.py - hacked around an artefact of GtkSpinBox to
+    make the x and y offset widgets editable by hand.  
+
+    * pymod/gvclassificationdlg.py - modified the Ramp Menu to ensure
+    first item is visible (sometimes it was not rendering correctly).
+
+    * pymod/gvlegenddlg.py - modified window resize logic to layout the 
+    legend more accurately when the window is resized.          
+
+2001-06-27  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/openev.py, pymod/gvviewwindow.py, pymod/gvsdsdlg.py:
+
+    Add GDAL subdataset selection dialog, mostly for OGDI.
+
+2001-06-25  Gillian Walters <gwalter at atlsci.com>
+
+    * I've updated mkdist, install, and package in the openev/delivery
+    directory.  The changes I've made are:  
+
+    1) updated the python1.5 copies/compiles to python2.1
+    2) added the ramps directory
+    3) changed the old copy paths to the ones appropriate for where I am
+    building things
+    4) Removed the -z option in package's tar command (gsar3 and octane
+    don't like it).
+
+2001-06-22  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/layerdlg.py: Modified remove_view() to avoid calling 
+    self.viewmenu.get_active() when there may be no active menu item
+    left as this triggers a crash.
+
+2001-06-20  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvdata.c: Fixed gv_data_set_name() to avoid emitting a metadata
+    changed event if the object is in the destroyed state. 
+
+2001-06-14  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: Added GDK_GLX_RED/GREEN/BLUE_SIZE attributes when
+    choosing visual to ensure we get the most capable, not least capable
+    x visual (especially when using raw X Mesa support)
+
+2001-06-11  Paul Spencer <pgs at magma.ca>
+
+    * pymod/pguentry.py: new file - added to simplify managing unsightly
+    gtk'isms on text boxes.
+    
+    * pymod/gvclassifydlg.py: modified to use pguentry.
+
+2001-05-25  Paul Spencer <pgs at magma.ca>
+
+    * pymod/gvclassifydlg.py: close reclassify dlg if required when the
+    classify dialog is closing.
+
+2001-05-24  Paul Spencer <pgs at magma.ca>
+
+    * pymod/gvclassifydlg.py: removed ramps from reclassify dialog and
+    enabled the first ramp in the config file to be the default ramp.
+
+    * pymod/gvlegenddlg.py: added resizing code to grow window to accomodate
+    more classes
+    
+2001-05-22  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/pyshell.py: Cntl-D behaviour fix from Jeffrey D. Collins.
+
+2001-05-15  Paul Spencer  <pgs at magma.ca>
+
+    * gvdata.c: added meta-changed signal for signaling changes to meta-data not related
+    to drawing (such as layer name changes) and modified set_name to send this signal.
+    
+    * gvdata.h: added meta-changed signal support
+
+    * pymod/gvutils.py: modified is_shapefile to use lower instead of
+    tolower
+
+2001-05-15  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewwindow.py: modified to allow selection of shapefiles based
+    on .shp, .shx or .dbf.
+
+2001-05-14  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvlegenddlg.py: modified to handle multi-line titles and 
+    improved the handling of spacing with large fonts selected.
+
+2001-05-12  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvlegenddlg.py: modified to allow user defineable color sample
+    sizes through two preferences - 'legend-sample-x-size' and 
+    'legend-sample-y-size'.  If not available, the default size is 20 in 
+    both directions.
+
+2001-05-08  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvlegenddlg.py: fixed problems with font handling that caused 
+    a crash when opening the legend dialog.
+
+    * pics/sym-*.xpm: icons added for each built in symbol for use with 
+    the GvSymbolStyle widget in gvogrfsgui.py
+    
+    * pymod/pgucolor.py: fixed bug that allowed multiple color choosers 
+    for a single color button at the same time.
+
+2001-05-07  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c, gvshapeslayer.c, gvselecttool.c: Modifications to
+    ensure that text can be drawn if the origin is off the view port, and
+    to ensure that offsets for text being dragged is properly handled.
+
+    * pymod/gvclassifydlg.py: enable rescaling. 
+
+    * pymod/gvclassification.py: fixed problems with rescaling.
+
+2001-05-05  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/gvlabeledit.py: added interactive mode where text changes are
+    reported on character basis, turned off by default.
+
+    * pymod/gvogrfsgui.py: added interactive text update plus changed direction
+    of dy offset
+
+2001-05-04  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/pgufilesel.py: implement support for remembering the 
+    last directory accessed whe file dialog reopened (Robert Rowe).
+    
+    * pymod/gvviewwindow.py: Allow opening multiples files based on
+    wildcards (Robert Rowe).
+
+2001-05-04  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/ogrfsgui.py: bug fixes plus optional label offsets for 
+    GvLabelStyle.
+
+    * pymod/gvlabeledit.py: bug fixes plus added new method for starting
+    a new label.
+
+2001-05-02  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/pgumenu.py: added pgumenu to provide a MenuFactory that supports
+    images in the menu.
+
+2001-05-01  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvtessshape.c, gvtess.c: Fixed the Mesa 3.3 compatibility fixes. :-)
+
+    * gvwinprint.c: reimplemented to use StretchDIBits(), and to avoid
+    using negative heights to represent upsidedown coordinate systems.
+
+    * gvrenderinfo.c: modified to create an "X" sized box for empty
+    strings.
+
+2001-04-29  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/pgufont.py: added to provide new font handling for labels.
+
+    * pymod/pgucombo.py: added to provide a convenient way of making
+    combo boxes that can't be edited.
+
+    * pymod/ogrfs.py: added has_part() to OGRFeatureStyle
+
+2001-04-26  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: call glFinish() before swapbuffer to fix lots of
+    problems under Mesa on Windows.  Note that Mesa bug report 419222
+    provides the correct fix in Mesa, but we workaround the problem. 
+
+2001-04-25  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrenderinfo.c, gvshapeslayer.c: added proper support for descenders.
+
+2001-04-24  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvtess.c, gvtessshape.c: hopefully fixed compatibility issue with
+    Mesa 3.3 (see bug report 418325). 
+
+2001-04-23  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Added set_properties(), and __delattr__ support for the GvShape
+    from python.
+
+    * pymod/gvselbrowser.py: added callback on oid text field.
+
+2001-04-22  Paul Spencer  <pgs at magma.ca>
+    * invdistance.c: added optional parameter for exponent to d in
+    IDW algorithm
+    
+    * invdistance.h: changed header for above change
+    
+    * gvutils.c: added gv_short_path_name for determining the 8.3 name
+    of a path/file in win32 (defaults to full path in linux)
+
+    * pymod/gvmodule.c: added _wrap_gv_short_path_name and changed
+     _wrap_WIDInterpolate
+
+2001-04-19  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvmesh.h: don't include gvmesh.h!
+
+    * pymod/*.py: avoid use of += for compatibility with python 1.5.x.
+
+2001-04-16  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gvvecpropdlg.py: Paul added symbol support.
+
+    * gvshapeslayer.c: Paul added filled start support.
+
+    * pymod/gvogrfs.py: Paul added OGRFeatureStyle class.
+
+2001-04-14  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/pgucolor.py: added color_string_to_tuple() utility method
+
+2001-04-11  Paul Spencer  <pgs at magma.ca>
+
+    * pymod/pgucolor.py: modified to support opacity correctly
+
+2001-04-09  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gview.py: expose new entry points (ie subselection).
+
+    * gvogrfs.py: improved color handling.
+
+    * pymod/gvviewwindow.py: Use oeattedit.launch().
+
+    * pymod/openev.py: Instantiate GvSelectionManager.
+
+    * pymod/oeattedit.py: Upgraded to support subselection and use of
+    GvSelectionManager.
+
+    * pymod/gvselbrowser.py: New control for subselection browsing, 
+    and GvSelectionManager.
+
+    * pics/pan_{right,left}.xpm: New, for gvselbrowser right/left arrows.
+
+    * gvrenderinfo.c/h: New, code for parsing rendering information.
+
+    * gvshapeslayer.c/h: overhauled ogrfs drawing to use renderinfo, 
+    added selection box for text.
+
+    * gvshapelayer.c: added renderinfo, and subselection support.
+
+    * gvviewarea.c,h: added ability to query available fonts.
+
+    * gvselecttool.c: Don't allow deletes or moves in read-only layers.
+
+    * gvlayer.{c,h}: Added explicit view to GvLayer.
+
+2001-04-02  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvraster/vecpropdlg.py: fixed some geometry quirks.
+
+    * gvraster.c/gv.defs/gview.py: Expose gv_raster_autoscale() to 
+    python.
+
+2001-03-29  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvshapefile.c: Look for first non-NULL shape instead of the very
+    first shape when getting a template shape for setting output type.
+
+    * llrasterize.c/gvrasterize.c: Modified to support "fill_short" flag
+    indicating if slivers should be burned in or ignored.
+
+2001-03-28  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvshapes.c: Modified to "look around a couple of pixels" when 
+    hitting no data values in add_height().
+
+    * gvmesh.c: Changed to try and fill in nodata values on mesh with
+    average of adjacent mesh points if possible.
+
+    * gvlayer.c/h: added explicit pointer to GvViewArea.
+
+    * gvviewarea.c: don't reset view transform in render_to_func at 1:1.
+
+2001-03-26  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: restructure bmfont handling to preserve GdkFont handle
+
+2001-03-22  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvviewarea.c: fixed bug in gv_view_load_bmfont() with reporting
+    the error when a load fails.
+
+2001-03-21  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gvvectorpropdlg.py: add support for labels in the drawing
+    style section.
+
+    * pymod/gvogrfs.py: new - parse OGR Feature Style strings.
+
+    * gvshapeslayer.c: allow setting _gv_ogrfs on layer, and add support 
+    for text from fields.
+
+    * Added legend and classification dialog into OpenEV itself. 
+
+2001-03-20  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gview.py: added __str__ method on GvShape.  
+
+    * gvshapefile.c: fixed to use binary flag for dbf file. 
+
+    * pymod/gview.py, gvmodule.c: Fixed handling of fetching out-of-range
+    shapes from a GvShapes to return an IndexError exception instead of
+    crashing.  
+
+2001-03-13  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * pymod/gview.py: added GvShapes.add_field() and GvShapes.get_schema()
+    methods.
+
+2001-02-15  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvprint.c: Fixed problem with print_handler not being de-initialized
+    after a file is complete. 
+
+2001-02-06  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Build in OGR support on Windows.
+
+2001-02-03  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Added gv_view_area_get_mode().
+
+2001-01-31  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Added rough attempt to find and include libproj.so in 
+    delivery/mkdist.sh.
+
+    * delivery/setup_openev: modified to pass arguments other than -h on
+    to openev.py.
+
+2001-01-30  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvaverage.c: added logic to try and preserve the standard deviation
+    in the complex averaging code. 
+
+    * gvmodule.c: Added access to GvManager.queue_task() from python.
+
+    * gvrasterlayer.c: No longer purges textures on a display-change.  
+    Added gv_raster_layer_purge_all_textures(), to clear textures on in 
+    cases where this is necessary.  Now can change alpha with 
+    texture_mode_set() without throwing away textures.
+
+    * gvmanager.c: added gv_manager_dump()
+
+2001-01-26  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvshapeslayer.c: Draw points and symbols with z taken into account.
+
+    * gvshapes.c: Fixed add_height() code.  Was corrupting (transforming)
+    x and y coordinates.
+
+    * gvviewarea.c: ensure fit_all_layers(), Home, and fit_extents all
+    produce compatible results.  Route all view setting through 
+    gv_viewarea_set_3d_view().  Fix up default view.  Remove view setting
+    in get_volume() function.
+
+2001-01-23  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Added GIF print support to gvprint.py.
+
+2001-01-19  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Improved 3D near range calculation a bit - better for stuff with
+    small numbers like images georeferenced in lat/long.
+
+2001-01-18  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Added gv_shapes_add_height() and python wrappers.
+
+2001-01-15  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Added support for the view_background_color preference in 
+    gvviewwindow.py.
+
+2001-01-08  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * fixed additional window edge conditions in gv_raster_tile_get_gdal.
+
+2000-12-08  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * Fixed configure/configure.in to use $GL_LIBS as per the bug 
+    from Bernhard.
+
+2000-12-03  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * gvrasterize.c: fixed bug with allocation of nParts.
+
+2000-11-27  Frank Warmerdam  <warmerdam at pobox.com>
+
+    * fixed edge handling bugs with rasters smaller than one tile
+    in gvraster.c::gv_raster_tile_get_gdal().
+
+2000-11-13  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * Fixed memory leak in gvogr.cpp with geometryless features.
+
+2000-10-31  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * gvraster.c: Fixed serious bug with memory corruption
+    that is mostly likely to occur with large images.  See
+    Bug 120968 on SourceForge.
+
+2000-10-30  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * pymod/gvviewwindow.py: Use new gdal.GetLastErrorMsg() call to
+    report a more detailed error message to the user if an open fails.
+
+2000-10-29  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * gvshapefile.c: Modified to add one attribute if there are none,
+    to avoid problem reported by Alan R. of J2-Geomatics with saving
+    shapefiles and there not being useable in ArcView.
+
+2000-10-25    <srawlin at srawlin.atlsci.com>
+
+    * Release 1.0.0  Also created a branch in CVS (asi-rel-1-0-patches)
+    for Atlantis to maintain and patch.
+
+2000-10-24  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * Modified gvogr.cpp to discard input features without any geometry.
+    Don't apply unset attributes as properties.  Modified gvviewwindow.py
+    ogr load code to not add empty layers to the view.
+    
+
+2000-10-18  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * gvhtml.py: Fixed to avoid launching two browsers on NT.  Report
+    error if no browser found on unix.
+
+2000-10-06  Frank Warmerdam  <warmerda at cs46980-c>
+
+    * Added concept of background color to the GvViewArea.
+
+    * Set nodata value on GvRasterLayer (in gv_raster_layer_new()) if
+    one is present on the prototype GDALRasterBand.
+
+2000-09-29  Frank Warmerdam  <warmerda at home.com>
+
+    * Changed PrefDialog to allocate 900K+25% of file cache to GDAL
+    and remainder to GvRaster caching.  The old amount of 900K+10%
+    was too little in many cases.
+
+    * Changed the import to temporarily up GDAL cache to at least 
+    20MB. 
+
+2000-09-29  Steve Rawlinson  <srawlin at atlsci.com>
+
+    * Added Edit/GoTo function.  Involved adding new dialog to
+          gvviewwindow.py and gv_view_area_map_location function.
+          Also fixed focus problem with new Zoom Ratio menu item,
+          and changed position of boxes in Track Tool preference
+          tab to be more intuitive.
+
+2000-09-29  Frank Warmerdam  <warmerda at home.com>
+
+    * Fixed bugs in nodata handling in gvmodule, and gvrastersource.c.
+
+2000-09-28  Frank Warmerdam  <warmerda at home.com>
+
+    * Ensure that GvShapes.__getitem__() returns None for missing
+    GvShape's instead of a python GvShape with a "NULL" _o field. 
+
+2000-09-27  Frank Warmerdam  <warmerda at home.com>
+
+    * The GvRaster will down let GDAL down the decimation when the
+    sample type is GvSMSample instead of GvSMAverage.  This gives a
+    major performance boost for large images.
+
+    * Added "_sample()" functions for float, and complex data, and
+    use in gv_raster_new() 
+
+    * Added "default_raster_sample" preference.  It is used by
+    GvManager to default GvRaster sampling method, and is set by
+    the preferences dialog.  Updated help as well.
+
+    * Added gvclassifydlg.py, though it isn't hooked into the UI of
+    regular OpenEV yet.
+
+    * GvLegendDialog now locks down view, and auto-regenerates when
+    the legend properties information changes.
+
+2000-09-20  Frank Warmerdam  <warmerda at home.com>
+
+    * Added preliminary implementation of GvClassification and
+    GvLegendDialog. 
+
+    * Modified GvViewArea to allow any GDK support font name, not
+      just those in a predefined list. 
+
+    * Added support for text labels on GvPointShapes via the
+      _gv_ogrfs property based on the OGR label specification, and
+      bitmap fonts.
+
+    * Added GvData.set_properties() method. 
+
+2000-09-15  Frank Warmerdam  <warmerda at home.com>
+
+    * Added GvShape.destroy() method.
+
+2000-09-14  Frank Warmerdam  <warmerda at home.com>
+
+    * Added gvrasterize.c/h, and invdistance.c, algorithms for
+    rasterizing polygons and for interpolating rasters from point
+    data. 
+
+2000-08-25  Frank Warmerdam  <warmerda at home.com>
+
+    * gvraster*: added preliminary support for nodata values in raster
+    layers.  Currently not automatically initialized.
+
+    * gvrasterlut.c: Changed phase colour mapping to match what EV does.
+
+    * gvutils.c: added reporting of phase and magnitude in 
+    tracktool, and point query layer ... should be optional!
+
+    * gvviewwindow.c: fixed some serious bugs with sliders.
+
+    * gvraster.c: HasArbitraryOverviews() support added.
+
+    * ATP CDROM Finalized, and CVS tagged (Steve really)
+
+2000-08-23  Frank Warmerdam  <warmerda at home.com>
+
+    * gvraster/gvutils: PIXEL now a special projection name.
+
+    * Open3D: Wrote help topic (open3d.html).
+
+    * gvmesh.c: Fixed problems with detail > 8.
+
+    * gvmesh.c: Use nodata value if available, and avoid off-by-one
+    errors when setting mesh elevations from file.
+
+2000-08-18  Frank Warmerdam  <warmerda at home.com>
+
+    * gvrasterlayer.c: hack to work around loss of syncronization with
+    Xi Graphics X servers when loading too many textures in a row.
+
+2000-08-17  Frank Warmerdam  <warmerda at home.com>
+
+    * added scrollbar support to GvViewArea, and added a GtkScrolledWindow
+    around the view area in gvviewwindow.py.
+
+2000-07-27  Frank Warmerdam  <warmerda at home.com>
+
+    * Added boundary constraints for all editing modes (but not dragging
+    of selections).
+
+    * added editing of existing rectangles.
+
+2000-07-26  Frank Warmerdam  <warmerda at home.com>
+
+    * Added Paul Spencer's color swatch support. 
+
+    * Added rectangle editing mode.
+
+2000-07-24  Frank Warmerdam  <warmerda at home.com>
+
+    * Created a 0.8.2 binary release on windows.
+
+2000-07-25  Frank Warmerdam  <warmerda at rommel.atlsci.com>
+
+    * gvmanager/gvrasterlayer: added support for dequeuing old task
+    on the same raster layer.
+
+2000-07-18  Frank Warmerdam  <warmerda at home.com>
+
+    * gvrasterlayer.c: modified rules in idle handler to improve texture 
+    cache utilization.
+
+2000-07-17  Frank Warmerdam  <warmerda at home.com>
+
+    * gvareatool.c: <delete> now deletes last vertex of ring.  Ring
+    deleted when vertex count drops to zero.
+
+    * Added code to maintain last_draw_time in GvViewArea, and to
+    scale wait time by it in gvrasterlayer.c.
+
+    * gvpointtool.c/gvlinetool.c/gvnodetool.c: <delete> key will
+    now delete a node, and the whole ring or shape if their node
+    count drops to zero.
+
+2000-07-13  Frank Warmerdam 
+
+    * Issued 0.8.0 binary release on Windows.
+
+    

Added: packages/openev/branches/upstream/current/Makefile
===================================================================
--- packages/openev/branches/upstream/current/Makefile	                        (rev 0)
+++ packages/openev/branches/upstream/current/Makefile	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2 @@
+default:
+	configure

Added: packages/openev/branches/upstream/current/Makefile.in
===================================================================
--- packages/openev/branches/upstream/current/Makefile.in	                        (rev 0)
+++ packages/openev/branches/upstream/current/Makefile.in	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,88 @@
+
+INST_PREFIX =	@prefix@
+
+SRC = gvviewarea.c  gvdata.c gvlayer.c gvutils.c gvrecords.c \
+      gvpolylines.c  gvlinelayer.c  gvtool.c  gvlinetool.c \
+      gvtoolbox.c   gvshapelayer.c  gvselecttool.c  gextra.c \
+      gvareas.c  gvarealayer.c  gvareatool.c  gvtess.c gvtessshape.c \
+      gvnodetool.c  gvundo.c  gvpoints.c  gvpointlayer.c gvpointtool.c \
+      gvpquerylayer.c gvviewlink.c gvroitool.c gvmesh.c gvrasterlayer.c \
+      gvraster.c gvrasterlut.c gvrasteraverage.c gvrasterconvert.c \
+      gvrastercache.c gvtracktool.c gtkcolorwell.c crs.c gvprint.c \
+      gvproperties.c gvshape.c gvshapes.c gvshapeslayer.c gvzoompantool.c \
+      gvmanager.c gvrastersource.c gvtexturecache.c ipgcplayer.c \
+      gvrecttool.c appcurlayer.c gvrenderinfo.c gvskirt.c gvogr.c \
+      invdistance.c gvrasterize.c llrasterize.c gvpoitool.c gvsymbolmanager.c \
+      gvrotatetool.c gvautopan.c \
+	\
+	gvshapefile.c shpopen.c dbfopen.c
+
+CXXSRC =  
+
+OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o)
+
+CC	=	@CC@
+CXX	=	@CXX@
+
+OPTFLAGS =	@OPTFLAGS@
+
+CFLAGS = $(OPTFLAGS) @C_WFLAGS@ @CFLAGS@
+LDFLAGS = @LDFLAGS@
+
+
+LIBS = $(LDFLAGS) @LIBS@ 
+
+INSTALL = 	./install-sh -c
+INSTALL_LIB 	=	./install-sh -c
+INSTALL_DIR	= 	./install-sh -d
+
+default: configure libgv.a gvtest
+	(cd pymod; $(MAKE))
+
+all:	default
+
+%.o : %.c
+	$(CC) $(CFLAGS) -c $<
+
+libgv.a: $(OBJ)
+	rm -f $@
+	ar cq $@ $(OBJ)
+	@RANLIB@ $@
+
+gvtest:	testmain.o libgv.a
+	$(CXX) -g -o $@ testmain.o libgv.a $(LIBS)
+
+clean:
+	rm -f gvtest *.o *.so *.a
+	(cd pymod; $(MAKE) clean)
+
+distclean: dist-clean
+
+dist-clean: clean
+	rm Makefile config.log config.cache config.status
+
+configure:	configure.in
+	-autoconf
+
+install:
+	$(INSTALL_DIR) $(INST_PREFIX)/bin
+	$(INSTALL_DIR) $(INST_PREFIX)/ramps
+	$(INSTALL_DIR) $(INST_PREFIX)/tools
+	$(INSTALL_DIR) $(INST_PREFIX)/pymod
+	$(INSTALL_DIR) $(INST_PREFIX)/html
+	$(INSTALL_DIR) $(INST_PREFIX)/pics
+	$(INSTALL_DIR) $(INST_PREFIX)/symbols
+	$(INSTALL_DIR) $(INST_PREFIX)/xmlconfig
+	$(INSTALL) gvtest $(INST_PREFIX)/bin
+	$(INSTALL_LIB) pymod/_gvmodule.so $(INST_PREFIX)/pymod
+	$(INSTALL_LIB) pymod/_gtkmissing.so $(INST_PREFIX)/pymod
+	cp pymod/*.py $(INST_PREFIX)/pymod
+	cp pymod/*.so $(INST_PREFIX)/pymod
+	chmod a+x $(INST_PREFIX)/pymod/openev.py
+	chmod a+x $(INST_PREFIX)/pymod/ibrowse.py
+	chmod a+x $(INST_PREFIX)/pymod/gvplot.py
+	cp tools/*.* $(INST_PREFIX)/tools
+	cp ramps/*.* $(INST_PREFIX)/ramps
+	cp html/*.* $(INST_PREFIX)/html
+	cp symbols/*.* $(INST_PREFIX)/symbols
+	cp xmlconfig/*.* $(INST_PREFIX)/xmlconfig

Added: packages/openev/branches/upstream/current/TODO
===================================================================
--- packages/openev/branches/upstream/current/TODO	                        (rev 0)
+++ packages/openev/branches/upstream/current/TODO	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,90 @@
+
+Thinking about (Gillian):
+
+* Update Open Raw to use VRT (optionally?) instead of Paux so that binary 
+  files with more data types can be opened.  This would possibly require 
+  updates to the VRT format.
+
+* Line of interest (LOI) tool and possibly a general polygon Area of 
+  interest (AOI) tool similar to existing POI/ROI tools.
+  LOI: Should have "changing" (new node added to incomplete line) and 
+  "changed" signals (line has been completed by right clicking, or an 
+  already complete line has been modified).  Should allow one line.  Once 
+  a line is complete, should behave like node edit tool (allow moving nodes 
+  or adding new nodes) except that left clicking that doesn't fall on the 
+  line should start a new loi and delete the old one.  Tool should not 
+  require a shape layer.
+
+* Add linestyles with points at nodes only to methods of drawing lines
+  (follow ogr spec).
+
+* Graphical tool for combining files into a single dataset, splitting a
+  dataset into several files, importing ground control points into a 
+  dataset, adding a colourtable or metadata, reprojecting existing 
+  geocoding information (not resampling- just reprojecting gcp's). 
+  Basically, a tool for manipulating VRT's without the user having to 
+  know so much about them.
+
+Would like to have (Frank):
+
+* The Tabular Shapes Attribute Grid Demo should have the ability to 
+  edit the attributes added, and it should move into the Edit menu to
+  replace the existing crappy Vector Layer Attributes dialog.  It should
+  also likely report the layer it is displaying somewhere on the dialog. 
+
+Short-term (Andrey)
+
+* Tool for manual image enchancements (both for the whole image and
+  in the ROI area)
+
+* GUI to edit geocoding information
+
+* `Open Sub-area' dialog.
+
+Long-term (Andrey)
+
+* Vector layer reprojection.
+
+* Image calibration.
+
+* GUI frontend for GDAL warping interface.
+
+* GUI for image registration.
+
+* Filter tool.
+
+* Spectral plots, profile plots.
+
+* Angle measurement.
+
+* Image classification, pixel editor, pseudocolor table editor.
+
+Old to-do list (Sept. 2000)
+
+-*-mode: text; mode: auto-fill-*-
+
+* ROI mode shouldn't stop showing roi when user clicks in a
+  different window.
+
+* Select mode should be able to select from any layer ... perhaps
+  based on a setup option.
+
+* Line tool: continue existing line if the first click is over
+an end vertex.
+
+* toolbox: preserve selection state across certain tools
+
+* Select tool: it's easy to accidentally translate a line when you
+just want to select it.  Possibly add some kind of sensitivity
+measure, e.g. no translation if only one motion event recieved.
+
+* Tools: should use the view draw signal to invoke a draw instead of
+connecting to the layer draw method?  Or make the base tool class
+handle this somehow?
+
+* View area: change cursor when panning/rotating/scaling.
+
+* Undo: add redo capablility.
+
+* Node tool: Add and drag a new node is entered as two operations to
+undo.  This might be more intuative as one compound operation.

Added: packages/openev/branches/upstream/current/aclocal.m4
===================================================================
--- packages/openev/branches/upstream/current/aclocal.m4	                        (rev 0)
+++ packages/openev/branches/upstream/current/aclocal.m4	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,507 @@
+
+dnl
+dnl Major portions of this file taken from the pygtk aclocal.m4 file. 
+dnl Other parts derived from libgeotiff, and GDAL. 
+dnl
+
+AC_DEFUN(AC_COMPILER_WFLAGS,
+[
+	# Remove -g from compile flags, we will add via CFG variable if
+	# we need it.
+	CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+	CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+
+	# check for GNU compiler, and use -Wall
+	if test "$GCC" = "yes"; then
+		C_WFLAGS="-Wall"
+		AC_DEFINE(USE_GNUCC)
+	fi
+	if test "$GXX" = "yes"; then
+		CXX_WFLAGS="-Wall"
+		AC_DEFINE(USE_GNUCC)
+	fi
+	AC_SUBST(CXX_WFLAGS,$CXX_WFLAGS)
+	AC_SUBST(C_WFLAGS,$C_WFLAGS)
+])
+
+AC_DEFUN(AC_COMPILER_PIC,
+[
+	echo 'void f(){}' > conftest.c
+	if test -z "`${CC-cc} -fPIC -c conftest.c 2>&1`"; then
+	  C_PIC=-fPIC
+	else
+	  C_PIC=
+	fi
+	if test -z "`${CXX-g++} -fPIC -c conftest.c 2>&1`"; then
+	  CXX_PIC=-fPIC
+	else
+	  CXX_PIC=
+	fi
+	rm -f conftest*
+
+	AC_SUBST(CXX_PIC,$CXX_PIC)
+	AC_SUBST(C_PIC,$C_PIC)
+])
+
+AC_DEFUN(AC_LD_SHARED,
+[
+  echo 'int main(){ g(); return 0; }' > conftest1.c
+
+  echo 'void g(){}' > conftest2.c
+  ${CC} ${C_PIC} -c conftest2.c
+
+  LD_SHARED="/bin/true"
+  if test -z "`ld -shared conftest2.o -o libconftest.so 2>&1`" ; then
+    if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      LD_LIBRARY_PATH="`pwd`"
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ld -shared ... yes"
+        LD_SHARED="ld -shared"
+      else
+        echo "checking for ld -shared ... no"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    else
+      echo "checking for ld -shared ... no"
+    fi
+  else
+    echo "checking for ld -shared ... no"
+  fi
+  rm -f conftest* libconftest* 
+
+  AC_SUBST(LD_SHARED,$LD_SHARED)
+])
+
+AC_DEFUN(AM_PATH_PYTHON,
+  [AC_CHECK_PROGS(PYTHON, python python2.2 python2.1 python2.0 python1.5,no)
+  if test "$PYTHON" != no; then
+    AC_MSG_CHECKING([where .py files should go])
+changequote(, )dnl
+    pythondir=`$PYTHON -c '
+import sys
+print "%s/lib/python%s/site-packages" % (sys.prefix, sys.version[:3])'`
+changequote([, ])dnl
+    AC_MSG_RESULT($pythondir)
+    AC_MSG_CHECKING([where python extensions should go])
+changequote(, )dnl
+    pyexecdir=`$PYTHON -c '
+import sys
+if sys.version[0] > "1" or sys.version[2] > "4":
+  print "%s/lib/python%s/site-packages" % (sys.exec_prefix, sys.version[:3])
+else:
+  print "%s/lib/python%s/sharedmodules" % (sys.exec_prefix, sys.version[:3])'`
+changequote([, ])dnl
+    AC_MSG_RESULT($pyexecdir)
+  else
+    # these defaults are version independent ...
+    AC_MSG_CHECKING([where .py files should go])
+    pythondir='$(prefix)/lib/site-python'
+    AC_MSG_RESULT($pythondir)
+    AC_MSG_CHECKING([where python extensions should go])
+    pyexecdir='$(exec_prefix)/lib/site-python'
+    AC_MSG_RESULT($pyexecdir)
+  fi
+  AC_SUBST(pythondir)
+  AC_SUBST(pyexecdir)])
+
+
+dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]])
+dnl Check if a module containing a given symbol is visible to python.
+AC_DEFUN(AM_CHECK_PYMOD,
+[AC_REQUIRE([AM_PATH_PYTHON])
+py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'`
+AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1)
+AC_CACHE_VAL(py_cv_mod_$py_mod_var, [
+ifelse([$2],[], [prog="
+import sys
+try:
+	import $1
+except ImportError:
+	sys.exit(1)
+except:
+	sys.exit(0)
+sys.exit(0)"], [prog="
+import $1
+$1.$2"])
+if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC
+  then
+    eval "py_cv_mod_$py_mod_var=yes"
+  else
+    eval "py_cv_mod_$py_mod_var=no"
+  fi
+])
+py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"`
+if test "x$py_val" != xno; then
+  AC_MSG_RESULT(yes)
+  ifelse([$3], [],, [$3
+])dnl
+else
+  AC_MSG_RESULT(no)
+  ifelse([$4], [],, [$4
+])dnl
+fi
+])
+
+# AM_PATH_GTKGL([ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])
+AC_DEFUN(AM_PATH_GTKGL,
+[
+AC_REQUIRE([AM_PATH_GTK])
+AC_PROVIDE([AM_PATH_GTKGL])
+
+AC_ARG_WITH(gl-prefix,    [  --with-gl-prefix=PFX   Prefix where OpenGL or Mesa is installed],
+ gl_prefix="$withval",
+ gl_prefix="")
+
+AC_ARG_WITH(gtkgl-prefix, [  --with-gtkgl-prefix=PFX Prefix where GtkGLArea is installed],
+ gtkgl_prefix="$withval",
+ gtkgl_prefix="")
+
+
+
+if test x$gl_prefix != x ; then
+ GL_CFLAGS="-I$gl_prefix/include"
+ GL_LDOPTS="-L$gl_prefix/lib"
+else
+ GL_CFLAGS=""
+ GL_LDOPTS=""
+fi
+
+saved_LIBS="$LIBS"
+saved_CFLAGS="$CFLAGS"
+
+# test for plain OpenGL
+AC_MSG_CHECKING([GL])
+LIBS="$GTK_LIBS $GL_LDOPTS -lGLU -lGL $saved_LIBS"
+AC_TRY_LINK( ,[ char glBegin(); glBegin(); ], have_GL=yes, have_GL=no)
+AC_MSG_RESULT($have_GL)
+
+if test x$have_GL = xyes; then
+
+ GL_LIBS="-lGLU -lGL"
+
+else
+
+ # test for Mesa
+ AC_MSG_CHECKING([Mesa])
+ LIBS="$saved_LIBS $GTK_LIBS $GL_LDOPTS -lMesaGLU -lMesaGL"
+ AC_TRY_LINK( ,[ char glBegin(); glBegin(); ], have_Mesa=yes, have_Mesa=no)
+ AC_MSG_RESULT($have_Mesa)
+
+ if test x$have_Mesa = xyes; then
+
+  GL_LIBS="-lMesaGLU -lMesaGL"
+
+ else
+
+  # test for Mesa with threads
+  AC_MSG_CHECKING([Mesa with pthreads])
+  LIBS="$GTK_LIBS $GL_LDOPTS -lMesaGL -lMesaGLU -lpthread $saved_LIBS"
+  AC_TRY_LINK( ,[ char glBegin(); glBegin(); ], have_Mesa_pthread=yes, have_Mesa_pthread=no)
+  AC_MSG_RESULT($have_Mesa_pthread)
+
+  if test x$have_Mesa_pthread = xyes; then
+    
+    GL_LIBS="-lMesaGLU -lMesaGL -lpthread"
+
+  else
+
+   #all failed
+   LIBS="$saved_LIBS"
+   CFLAGS="$saved_CFLAGS"
+   GTKGL_LIBS=""
+   GTKGL_CFLAGS=""
+   ifelse([$2], , :, [$2])
+
+  fi
+ fi
+fi
+
+
+if test x$gtkgl_prefix != x; then
+ GTKGL_CFLAGS="-I$gtkgl_prefix/include"
+ GTKGL_LDOPTS="-L$gtkgl_prefix/lib"
+else
+ GTKGL_CFLAGS=""
+ GTKGL_LDOPTS=""
+fi
+
+AC_MSG_CHECKING([GtkGLArea])
+LIBS="$GTK_LIBS $GL_LDOPTS -lgtkgl $GL_LIBS $GTKGL_LDOPTS $saved_LIBS"
+AC_TRY_LINK( ,[ char gtk_gl_area_new(); gtk_gl_area_new(); ], have_gtkgl=yes, have_gtkgl=no)
+AC_MSG_RESULT($have_gtkgl)
+
+if test x$have_gtkgl = xyes; then
+
+ LIBS="$saved_LIBS"
+ CFLAGS="$saved_CFLAGS"
+ GTKGL_CFLAGS="$GTKGL_CFLAGS $GL_CFLAGS"
+ GTKGL_LIBS="$GTKGL_LDOPTS -lgtkgl $GL_LDOPTS $GL_LIBS"
+ ifelse([$1], , :, [$1])
+
+else
+
+ LIBS="$saved_LIBS"
+ CFLAGS="$saved_CFLAGS"
+ GTKGL_LIBS=""
+ GTKGL_CFLAGS=""
+ ifelse([$2], , :, [$2])
+
+fi
+
+AC_SUBST(GTKGL_CFLAGS)
+AC_SUBST(GTKGL_LIBS)
+
+])
+
+# Configure paths for GTK+
+# Owen Taylor     97-11-3
+
+dnl AM_PATH_GTK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
+dnl Test for GTK, and define GTK_CFLAGS and GTK_LIBS
+dnl
+AC_DEFUN(AM_PATH_GTK,
+[dnl 
+dnl Get the cflags and libraries from the gtk-config script
+dnl
+AC_ARG_WITH(gtk-prefix,[  --with-gtk-prefix=PFX   Prefix where GTK is installed (optional)],
+            gtk_config_prefix="$withval", gtk_config_prefix="")
+AC_ARG_WITH(gtk-exec-prefix,[  --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)],
+            gtk_config_exec_prefix="$withval", gtk_config_exec_prefix="")
+AC_ARG_ENABLE(gtktest, [  --disable-gtktest       Do not try to compile and run a test GTK program],
+		    , enable_gtktest=yes)
+
+  for module in . $4
+  do
+      case "$module" in
+         gthread) 
+             gtk_config_args="$gtk_config_args gthread"
+         ;;
+      esac
+  done
+
+  if test x$gtk_config_exec_prefix != x ; then
+     gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
+     if test x${GTK_CONFIG+set} != xset ; then
+        GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
+     fi
+  fi
+  if test x$gtk_config_prefix != x ; then
+     gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
+     if test x${GTK_CONFIG+set} != xset ; then
+        GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
+     fi
+  fi
+
+  AC_PATH_PROG(GTK_CONFIG, gtk-config, no)
+  min_gtk_version=ifelse([$1], ,0.99.7,$1)
+  AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
+  no_gtk=""
+  if test "$GTK_CONFIG" = "no" ; then
+    no_gtk=yes
+  else
+    GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
+    GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
+    gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_gtktest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GTK_CFLAGS"
+      LIBS="$GTK_LIBS $LIBS"
+dnl
+dnl Now check if the installed GTK is sufficiently new. (Also sanity
+dnl checks the results of gtk-config to some extent
+dnl
+      rm -f conf.gtktest
+      AC_TRY_RUN([
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int 
+main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.gtktest");
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = g_strdup("$min_gtk_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_gtk_version");
+     exit(1);
+   }
+
+  if ((gtk_major_version != $gtk_config_major_version) ||
+      (gtk_minor_version != $gtk_config_minor_version) ||
+      (gtk_micro_version != $gtk_config_micro_version))
+    {
+      printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n", 
+             $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
+             gtk_major_version, gtk_minor_version, gtk_micro_version);
+      printf ("*** was found! If gtk-config was correct, then it is best\n");
+      printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
+      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+      printf("*** required on your system.\n");
+      printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
+      printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
+      printf("*** before re-running configure\n");
+    } 
+#if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
+  else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
+	   (gtk_minor_version != GTK_MINOR_VERSION) ||
+           (gtk_micro_version != GTK_MICRO_VERSION))
+    {
+      printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
+	     GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
+      printf("*** library (version %d.%d.%d)\n",
+	     gtk_major_version, gtk_minor_version, gtk_micro_version);
+    }
+#endif /* defined (GTK_MAJOR_VERSION) ... */
+  else
+    {
+      if ((gtk_major_version > major) ||
+        ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
+        ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
+               gtk_major_version, gtk_minor_version, gtk_micro_version);
+        printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
+	       major, minor, micro);
+        printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
+        printf("*** correct copy of gtk-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+      }
+    }
+  return 1;
+}
+],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_gtk" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$GTK_CONFIG" = "no" ; then
+       echo "*** The gtk-config script installed by GTK could not be found"
+       echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the GTK_CONFIG environment variable to the"
+       echo "*** full path to gtk-config."
+     else
+       if test -f conf.gtktest ; then
+        :
+       else
+          echo "*** Could not run GTK test program, checking why..."
+          CFLAGS="$CFLAGS $GTK_CFLAGS"
+          LIBS="$LIBS $GTK_LIBS"
+          AC_TRY_LINK([
+#include <gtk/gtk.h>
+#include <stdio.h>
+],      [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding GTK or finding the wrong"
+          echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+	  echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+          echo "***"
+          echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
+          echo "*** came with the system with the command"
+          echo "***"
+          echo "***    rpm --erase --nodeps gtk gtk-devel" ],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means GTK was incorrectly installed"
+          echo "*** or that you have moved GTK since it was installed. In the latter case, you"
+          echo "*** may want to edit the gtk-config script: $GTK_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     GTK_CFLAGS=""
+     GTK_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(GTK_CFLAGS)
+  AC_SUBST(GTK_LIBS)
+  rm -f conf.gtktest
+])
+
+
+# serial 1
+
+dnl finds information needed for compilation of shared library style python
+dnl extensions.  AM_PATH_PYTHON should be called before hand.
+dnl NFW: Modified from original to avoid overridding CC, SO and OPT
+AC_DEFUN(AM_INIT_PYEXEC_MOD,
+  [AC_REQUIRE([AM_PATH_PYTHON])
+  AC_MSG_CHECKING([for python headers])
+  AC_CACHE_VAL(am_cv_python_includes,
+    [changequote(,)dnl
+    am_cv_python_includes="`$PYTHON -c '
+import sys
+includepy = \"%s/include/python%s\" % (sys.prefix, sys.version[:3])
+if sys.version[0] > \"1\" or sys.version[2] > \"4\":
+  libpl = \"%s/include/python%s\" % (sys.exec_prefix, sys.version[:3])
+else:
+  libpl = \"%s/lib/python%s/config\" % (sys.exec_prefix, sys.version[:3])
+print \"-I%s -I%s\" % (includepy, libpl)'`"
+    changequote([, ])])
+  PYTHON_INCLUDES="$am_cv_python_includes"
+  AC_MSG_RESULT(found)
+  AC_SUBST(PYTHON_INCLUDES)
+
+  AC_MSG_CHECKING([definitions from Python makefile])
+changequote(, )dnl
+  py_makefile="`$PYTHON -c '
+import sys
+print \"%s/lib/python%s/config/Makefile\"%(sys.exec_prefix, sys.version[:3])'`"
+changequote([, ])dnl
+
+  if test ! -f "$py_makefile"; then
+      AC_MSG_ERROR([*** Couldnt find the python config makefile.  Maybe you are
+*** missing the development portion of the python installation])
+  fi
+  eval `sed -n \
+-e "s/^CC=[ 	]*\(.*\)/am_cv_python_CC='\1'/p" \
+-e "s/^OPT=[ 	]*\(.*\)/am_cv_python_OPT='\1'/p" \
+-e "s/^CCSHARED=[ 	]*\(.*\)/am_cv_python_CCSHARED='\1'/p" \
+-e "s/^LDSHARED=[ 	]*\(.*\)/am_cv_python_LDSHARED='\1'/p" \
+-e "s/^SO=[ 	]*\(.*\)/am_cv_python_SO='\1'/p" \
+    $py_makefile`
+
+  AC_MSG_RESULT(done)
+  PYTHON_CC="$am_cv_python_CC"
+  PYTHON_OPT="$am_cv_python_OPT"
+  PYTHON_SO="$am_cv_python_SO"
+  PYTHON_CFLAGS="$am_cv_python_CCSHARED \$(OPT)"
+  PYTHON_LINK="$am_cv_python_LDSHARED -o \[$]@"
+
+  AC_SUBST(PYTHON_CC)dnl
+  AC_SUBST(PYTHON_OPT)dnl
+  AC_SUBST(PYTHON_SO)dnl
+  AC_SUBST(PYTHON_CFLAGS)dnl
+  AC_SUBST(PYTHON_LINK)])

Added: packages/openev/branches/upstream/current/appcurlayer.c
===================================================================
--- packages/openev/branches/upstream/current/appcurlayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/appcurlayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,269 @@
+/******************************************************************************
+ * $Id: appcurlayer.c,v 1.4 2003/02/27 03:59:21 warmerda Exp $
+ *
+ * Project:  APP/Currents
+ * Purpose:  Implementation of current layer.
+ *
+ *           This layer is intended specifically for use in APP, and
+ *           should only be changed on behalf of Atlantis, though it can 
+ *           serve as an example of custom layer drawing for others.
+ *
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: appcurlayer.c,v $
+ * Revision 1.4  2003/02/27 03:59:21  warmerda
+ * added view to gv_shapes_layer_get_draw_info
+ *
+ * Revision 1.3  2002/11/05 04:05:31  warmerda
+ * geocoord update
+ *
+ * Revision 1.2  2001/08/08 17:44:12  warmerda
+ * use gv_shape_type() macro
+ *
+ * Revision 1.1  2000/08/25 20:02:22  warmerda
+ * New
+ *
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include "appcurlayer.h"
+#include "gvutils.h"
+#include <GL/gl.h>
+
+static void app_cur_layer_class_init(AppCurLayerClass *klass);
+static void app_cur_layer_init(AppCurLayer *layer);
+static void app_cur_layer_setup(GvLayer *layer, GvViewArea *view);
+static void app_cur_layer_draw(GvLayer *layer, GvViewArea *view);
+static void app_cur_layer_draw_selected(GvShapeLayer *layer, 
+                                          GvViewArea *view);
+
+#ifndef PI
+#define PI  3.1415927
+#endif
+
+GtkType
+app_cur_layer_get_type(void)
+{
+    static GtkType cur_layer_type = 0;
+
+    if (!cur_layer_type)
+    {
+	static const GtkTypeInfo cur_layer_info =
+	{
+	    "AppCurLayer",
+	    sizeof(AppCurLayer),
+	    sizeof(AppCurLayerClass),
+	    (GtkClassInitFunc) app_cur_layer_class_init,
+	    (GtkObjectInitFunc) app_cur_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	cur_layer_type = gtk_type_unique(gv_shapes_layer_get_type(),
+                                         &cur_layer_info);
+    }
+    return cur_layer_type;
+}
+
+static void
+app_cur_layer_class_init(AppCurLayerClass *klass)
+{
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    layer_class->setup = app_cur_layer_setup;
+    layer_class->draw = app_cur_layer_draw;
+    shape_layer_class->draw_selected = app_cur_layer_draw_selected;
+}
+
+static void
+app_cur_layer_init(AppCurLayer *layer)
+{
+}
+
+GtkObject *
+app_cur_layer_new(GvShapes *data)
+{
+    AppCurLayer *layer = APP_CUR_LAYER(gtk_type_new(
+	app_cur_layer_get_type()));
+
+    if( data == NULL )
+    {
+        data = GV_SHAPES(gv_shapes_new());
+        gv_data_set_name( GV_DATA(data), "Currents" );
+    }
+
+    gv_shapes_layer_set_data( GV_SHAPES_LAYER(layer), data );
+    gv_data_set_name( GV_DATA(layer), gv_data_get_name(GV_DATA(data)) );
+    
+    return GTK_OBJECT(layer);
+}
+
+/*******************************************************/
+
+static void
+app_cur_layer_setup(GvLayer *rlayer, GvViewArea *view)
+{
+}
+
+static void 
+app_cur_draw_arrow( gfloat base_x, gfloat base_y, 
+                    gfloat head_x, gfloat head_y )
+
+{
+    gfloat     vx1, vy1, rl1, vx2, vy2, angle2;
+
+    glBegin( GL_LINES );
+
+    glVertex2f( head_x, head_y );
+    glVertex2f( base_x, base_y );
+
+    vx1 = base_x - head_x;
+    vy1 = base_y - head_y;
+    rl1 = sqrt(vx1*vx1+vy1*vy1);
+
+    angle2 = atan2(vy1,vx1) + PI/4;
+    vy2 = (rl1/3.0) * sin(angle2);
+    vx2 = (rl1/3.0) * cos(angle2);
+    
+    glVertex2f( head_x, head_y );
+    glVertex2f( head_x + vx2, head_y + vy2 );
+
+    angle2 = atan2(vy1,vx1) - PI/4;
+    vy2 = (rl1/3.0) * sin(angle2);
+    vx2 = (rl1/3.0) * cos(angle2);
+    
+    glVertex2f( head_x, head_y );
+    glVertex2f( head_x + vx2, head_y + vy2 );
+    
+    glEnd();
+}
+
+static void 
+app_cur_layer_draw_arrow( GvViewArea *view, AppCurLayer *layer, 
+                          GvShape *shape, int selected )
+
+{
+    gfloat       x, y, x_head, y_head;
+    GLgeocoord   dx, dy;
+    GvColor      color;
+    GvShapeDrawInfo drawinfo;
+
+    /* get and set color */
+    gv_shapes_layer_get_draw_info( view, GV_SHAPES_LAYER(layer), &drawinfo );
+    gv_color_copy( color, drawinfo.point_color );
+
+    gv_shapes_layer_override_color( shape, color, "_gv_color" );
+
+    glColor4fv( color );
+    
+    /* Compute main drawing location */
+    x = gv_shape_get_x(shape, 0, 0 );
+    y = gv_shape_get_y(shape, 0, 0 );
+
+    /* Compute head location */
+    
+    x_head = x - 30;
+    y_head = y + 20;
+
+    app_cur_draw_arrow( x, y, x_head, y_head );
+
+    /* compute selected box size */
+    dx = 5.0;
+    dy = 5.0;
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);    
+    
+    /* Draw selection box around base point, if selected */
+    if( selected )
+    {
+        glBegin(GL_LINE_LOOP);
+        glVertex2f( x-dx, y-dy );
+        glVertex2f( x+dy, y-dx );
+        glVertex2f( x+dx, y+dy );
+        glVertex2f( x-dy, y+dx );
+        glVertex2f( x-dx, y-dy );
+        glEnd();
+    }
+}
+
+static void
+app_cur_layer_draw(GvLayer *rlayer, GvViewArea *view)
+{
+    AppCurLayer *layer = APP_CUR_LAYER(rlayer);
+    gint i, points;
+    gint *selected, presentation;
+    gint hit_selected = FALSE;
+    GvShape           *shape;
+    
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+
+    points = gv_shapes_num_shapes(GV_SHAPES_LAYER(layer)->data);
+    
+    for (i=0; i < points; ++i)
+    {
+	if (selected[i] && !presentation)
+	{
+	    hit_selected = 1;
+	    continue;
+	}
+
+        shape = gv_shapes_get_shape(GV_SHAPES_LAYER(layer)->data, i);
+        if( shape != NULL && gv_shape_type(shape) == GVSHAPE_POINT )
+            app_cur_layer_draw_arrow( view, layer, shape, FALSE );
+    }
+
+    if (hit_selected && ! GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+	app_cur_layer_draw_selected(GV_SHAPE_LAYER(layer), view);
+    }     
+}
+
+static void
+app_cur_layer_draw_selected(GvShapeLayer *rlayer, GvViewArea *view)
+{
+    AppCurLayer *layer = APP_CUR_LAYER(rlayer);
+    gint i, points;
+    gint *selected;
+    GvShape           *shape;
+    
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+
+    points = gv_shapes_num_shapes(GV_SHAPES_LAYER(layer)->data);
+    
+    for (i=0; i < points; ++i)
+    {
+	if ( !selected[i] )
+	    continue;
+
+        shape = gv_shapes_get_shape(GV_SHAPES_LAYER(layer)->data, i);
+        if( shape != NULL && gv_shape_type(shape) == GVSHAPE_POINT )
+            app_cur_layer_draw_arrow( view, layer, shape, TRUE );
+    }
+}
+
+

Added: packages/openev/branches/upstream/current/appcurlayer.h
===================================================================
--- packages/openev/branches/upstream/current/appcurlayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/appcurlayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * $Id: appcurlayer.h,v 1.1 2000/08/25 20:02:22 warmerda Exp $
+ *
+ * Project:  APP/Currents
+ * Purpose:  APP Currents Layer declarations.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: appcurlayer.h,v $
+ * Revision 1.1  2000/08/25 20:02:22  warmerda
+ * New
+ *
+ */
+
+#ifndef __APPCURLAYER_H__
+#define __APPCURLAYER_H__
+
+#include "gvshapeslayer.h"
+
+#define APP_TYPE_CUR_LAYER          (app_cur_layer_get_type ())
+#define APP_CUR_LAYER(obj)          (GTK_CHECK_CAST ((obj), APP_TYPE_CUR_LAYER, AppCurLayer))
+#define APP_CUR_LAYER_CLASS(klass)  (GTK_CHECK_CLASS_CAST ((klass), APP_TYPE_CUR_LAYER, AppCurLayerClass))
+#define APP_IS_CUR_LAYER(obj)       (GTK_CHECK_TYPE ((obj),APP_TYPE_CUR_LAYER))
+#define APP_IS_CUR_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), \
+                                                           APP_TYPE_CUR_LAYER))
+
+typedef struct _AppCurLayer       AppCurLayer;
+typedef struct _AppCurLayerClass  AppCurLayerClass;
+
+struct _AppCurLayer
+{
+    GvShapesLayer layer;
+};
+
+struct _AppCurLayerClass
+{
+    GvShapesLayerClass parent_class;
+};
+
+GtkType app_cur_layer_get_type(void);
+GtkObject* app_cur_layer_new(GvShapes *);
+
+#endif /* __APPCURLAYER_H__ */
+

Added: packages/openev/branches/upstream/current/configure
===================================================================
--- packages/openev/branches/upstream/current/configure	                        (rev 0)
+++ packages/openev/branches/upstream/current/configure	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,6408 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="Makefile.in"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX RANLIB ac_ct_RANLIB CPP EGREP CXX_WFLAGS C_WFLAGS CXX_PIC C_PIC LD_SHARED PYTHON pythondir pyexecdir PYTHON_INCLUDES PYTHON_CC PYTHON_OPT PYTHON_SO PYTHON_CFLAGS PYTHON_LINK GTK_CONFIG GTK_CFLAGS GTK_LIBS GTKGL_CFLAGS GTKGL_LIBS GDAL_INC OGR_SOURCE OPTFLAGS LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-gtktest       Do not try to compile and run a test GTK program
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-gtk-prefix=PFX   Prefix where GTK is installed (optional)
+  --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)
+  --with-gl-prefix=PFX   Prefix where OpenGL or Mesa is installed
+  --with-gtkgl-prefix=PFX Prefix where GtkGLArea is installed
+  --with-gdal=ARG         Path to GDAL installation tree
+  --with-ogr              Enable OGR Linkage
+  --with-double-geocoord  Use double precision GL coordinates
+  --with-render-plugin    Enable plugin (rendering)
+  --with-debug            Build debug (else optimized)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+          ac_config_headers="$ac_config_headers gv_config.h"
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+  CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cxx_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+LIBS="$LIBS -L/usr/local/lib"
+CFLAGS="$CFLAGS -I/usr/local/include"
+CPPFLAGS="$CPPFLAGS -I/usr/local/include"
+
+
+
+echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBDL 1
+_ACEOF
+
+  LIBS="-ldl $LIBS"
+
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+
+for ac_header in fcntl.h unistd.h dbmalloc.h dlfcn.h string.h strings.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+  # try to guess the endianness by grepping values into an object file
+  ac_cv_c_bigendian=unknown
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+  ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+  if test "$ac_cv_c_bigendian" = unknown; then
+    ac_cv_c_bigendian=no
+  else
+    # finding both strings is unlikely to happen, but who knows?
+    ac_cv_c_bigendian=unknown
+  fi
+fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+main ()
+{
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=no
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+  yes)
+
+cat >>confdefs.h <<\_ACEOF
+#define WORDS_BIGENDIAN 1
+_ACEOF
+ ;;
+  no)
+     ;;
+  *)
+    { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+
+for ac_func in vprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+echo "$as_me:$LINENO: checking for _doprnt" >&5
+echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6
+if test "${ac_cv_func__doprnt+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+char (*f) () = _doprnt;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != _doprnt;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func__doprnt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func__doprnt=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+echo "${ECHO_T}$ac_cv_func__doprnt" >&6
+if test $ac_cv_func__doprnt = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+
+	# Remove -g from compile flags, we will add via CFG variable if
+	# we need it.
+	CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+	CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+
+	# check for GNU compiler, and use -Wall
+	if test "$GCC" = "yes"; then
+		C_WFLAGS="-Wall"
+		cat >>confdefs.h <<\_ACEOF
+#define USE_GNUCC 1
+_ACEOF
+
+	fi
+	if test "$GXX" = "yes"; then
+		CXX_WFLAGS="-Wall"
+		cat >>confdefs.h <<\_ACEOF
+#define USE_GNUCC 1
+_ACEOF
+
+	fi
+	CXX_WFLAGS=$CXX_WFLAGS
+
+	C_WFLAGS=$C_WFLAGS
+
+
+
+	echo 'void f(){}' > conftest.c
+	if test -z "`${CC-cc} -fPIC -c conftest.c 2>&1`"; then
+	  C_PIC=-fPIC
+	else
+	  C_PIC=
+	fi
+	if test -z "`${CXX-g++} -fPIC -c conftest.c 2>&1`"; then
+	  CXX_PIC=-fPIC
+	else
+	  CXX_PIC=
+	fi
+	rm -f conftest*
+
+	CXX_PIC=$CXX_PIC
+
+	C_PIC=$C_PIC
+
+
+
+  echo 'int main(){ g(); return 0; }' > conftest1.c
+
+  echo 'void g(){}' > conftest2.c
+  ${CC} ${C_PIC} -c conftest2.c
+
+  LD_SHARED="/bin/true"
+  if test -z "`ld -shared conftest2.o -o libconftest.so 2>&1`" ; then
+    if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      LD_LIBRARY_PATH="`pwd`"
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ld -shared ... yes"
+        LD_SHARED="ld -shared"
+      else
+        echo "checking for ld -shared ... no"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    else
+      echo "checking for ld -shared ... no"
+    fi
+  else
+    echo "checking for ld -shared ... no"
+  fi
+  rm -f conftest* libconftest*
+
+  LD_SHARED=$LD_SHARED
+
+
+
+for ac_prog in python python2.2 python2.1 python2.0 python1.5
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_PYTHON+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$PYTHON"; then
+  ac_cv_prog_PYTHON="$PYTHON" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_PYTHON="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+PYTHON=$ac_cv_prog_PYTHON
+if test -n "$PYTHON"; then
+  echo "$as_me:$LINENO: result: $PYTHON" >&5
+echo "${ECHO_T}$PYTHON" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$PYTHON" && break
+done
+test -n "$PYTHON" || PYTHON="no"
+
+  if test "$PYTHON" != no; then
+    echo "$as_me:$LINENO: checking where .py files should go" >&5
+echo $ECHO_N "checking where .py files should go... $ECHO_C" >&6
+    pythondir=`$PYTHON -c '
+import sys
+print "%s/lib/python%s/site-packages" % (sys.prefix, sys.version[:3])'`
+    echo "$as_me:$LINENO: result: $pythondir" >&5
+echo "${ECHO_T}$pythondir" >&6
+    echo "$as_me:$LINENO: checking where python extensions should go" >&5
+echo $ECHO_N "checking where python extensions should go... $ECHO_C" >&6
+    pyexecdir=`$PYTHON -c '
+import sys
+if sys.version[0] > "1" or sys.version[2] > "4":
+  print "%s/lib/python%s/site-packages" % (sys.exec_prefix, sys.version[:3])
+else:
+  print "%s/lib/python%s/sharedmodules" % (sys.exec_prefix, sys.version[:3])'`
+    echo "$as_me:$LINENO: result: $pyexecdir" >&5
+echo "${ECHO_T}$pyexecdir" >&6
+  else
+    # these defaults are version independent ...
+    echo "$as_me:$LINENO: checking where .py files should go" >&5
+echo $ECHO_N "checking where .py files should go... $ECHO_C" >&6
+    pythondir='$(prefix)/lib/site-python'
+    echo "$as_me:$LINENO: result: $pythondir" >&5
+echo "${ECHO_T}$pythondir" >&6
+    echo "$as_me:$LINENO: checking where python extensions should go" >&5
+echo $ECHO_N "checking where python extensions should go... $ECHO_C" >&6
+    pyexecdir='$(exec_prefix)/lib/site-python'
+    echo "$as_me:$LINENO: result: $pyexecdir" >&5
+echo "${ECHO_T}$pyexecdir" >&6
+  fi
+
+
+
+
+  echo "$as_me:$LINENO: checking for python headers" >&5
+echo $ECHO_N "checking for python headers... $ECHO_C" >&6
+  if test "${am_cv_python_includes+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      am_cv_python_includes="`$PYTHON -c '
+import sys
+includepy = \"%s/include/python%s\" % (sys.prefix, sys.version[:3])
+if sys.version[0] > \"1\" or sys.version[2] > \"4\":
+  libpl = \"%s/include/python%s\" % (sys.exec_prefix, sys.version[:3])
+else:
+  libpl = \"%s/lib/python%s/config\" % (sys.exec_prefix, sys.version[:3])
+print \"-I%s -I%s\" % (includepy, libpl)'`"
+
+fi
+
+  PYTHON_INCLUDES="$am_cv_python_includes"
+  echo "$as_me:$LINENO: result: found" >&5
+echo "${ECHO_T}found" >&6
+
+
+  echo "$as_me:$LINENO: checking definitions from Python makefile" >&5
+echo $ECHO_N "checking definitions from Python makefile... $ECHO_C" >&6
+  py_makefile="`$PYTHON -c '
+import sys
+print \"%s/lib/python%s/config/Makefile\"%(sys.exec_prefix, sys.version[:3])'`"
+
+  if test ! -f "$py_makefile"; then
+      { { echo "$as_me:$LINENO: error: *** Couldnt find the python config makefile.  Maybe you are
+*** missing the development portion of the python installation" >&5
+echo "$as_me: error: *** Couldnt find the python config makefile.  Maybe you are
+*** missing the development portion of the python installation" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  eval `sed -n \
+-e "s/^CC= 	*\(.*\)/am_cv_python_CC='\1'/p" \
+-e "s/^OPT= 	*\(.*\)/am_cv_python_OPT='\1'/p" \
+-e "s/^CCSHARED= 	*\(.*\)/am_cv_python_CCSHARED='\1'/p" \
+-e "s/^LDSHARED= 	*\(.*\)/am_cv_python_LDSHARED='\1'/p" \
+-e "s/^SO= 	*\(.*\)/am_cv_python_SO='\1'/p" \
+    $py_makefile`
+
+  echo "$as_me:$LINENO: result: done" >&5
+echo "${ECHO_T}done" >&6
+  PYTHON_CC="$am_cv_python_CC"
+  PYTHON_OPT="$am_cv_python_OPT"
+  PYTHON_SO="$am_cv_python_SO"
+  PYTHON_CFLAGS="$am_cv_python_CCSHARED \$(OPT)"
+  PYTHON_LINK="$am_cv_python_LDSHARED -o \$@"
+
+
+
+
+py_mod_var=`echo thread'_' | sed 'y%./+-%__p_%'`
+echo "$as_me:$LINENO: checking for python module thread" >&5
+echo $ECHO_N "checking for python module thread... $ECHO_C" >&6
+if eval "test \"\${py_cv_mod_$py_mod_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+prog="
+import sys
+try:
+	import thread
+except ImportError:
+	sys.exit(1)
+except:
+	sys.exit(0)
+sys.exit(0)"
+if $PYTHON -c "$prog" 1>&5 2>&5
+  then
+    eval "py_cv_mod_$py_mod_var=yes"
+  else
+    eval "py_cv_mod_$py_mod_var=no"
+  fi
+
+fi
+
+py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"`
+if test "x$py_val" != xno; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  extra_mods=gthread
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  extra_mods=
+fi
+
+
+
+# Check whether --with-gtk-prefix or --without-gtk-prefix was given.
+if test "${with_gtk_prefix+set}" = set; then
+  withval="$with_gtk_prefix"
+  gtk_config_prefix="$withval"
+else
+  gtk_config_prefix=""
+fi;
+
+# Check whether --with-gtk-exec-prefix or --without-gtk-exec-prefix was given.
+if test "${with_gtk_exec_prefix+set}" = set; then
+  withval="$with_gtk_exec_prefix"
+  gtk_config_exec_prefix="$withval"
+else
+  gtk_config_exec_prefix=""
+fi;
+# Check whether --enable-gtktest or --disable-gtktest was given.
+if test "${enable_gtktest+set}" = set; then
+  enableval="$enable_gtktest"
+
+else
+  enable_gtktest=yes
+fi;
+
+  for module in . $extra_mods
+  do
+      case "$module" in
+         gthread)
+             gtk_config_args="$gtk_config_args gthread"
+         ;;
+      esac
+  done
+
+  if test x$gtk_config_exec_prefix != x ; then
+     gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
+     if test x${GTK_CONFIG+set} != xset ; then
+        GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
+     fi
+  fi
+  if test x$gtk_config_prefix != x ; then
+     gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
+     if test x${GTK_CONFIG+set} != xset ; then
+        GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
+     fi
+  fi
+
+  # Extract the first word of "gtk-config", so it can be a program name with args.
+set dummy gtk-config; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_GTK_CONFIG+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $GTK_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_GTK_CONFIG="$GTK_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_GTK_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_path_GTK_CONFIG" && ac_cv_path_GTK_CONFIG="no"
+  ;;
+esac
+fi
+GTK_CONFIG=$ac_cv_path_GTK_CONFIG
+
+if test -n "$GTK_CONFIG"; then
+  echo "$as_me:$LINENO: result: $GTK_CONFIG" >&5
+echo "${ECHO_T}$GTK_CONFIG" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  min_gtk_version=1.2.1
+  echo "$as_me:$LINENO: checking for GTK - version >= $min_gtk_version" >&5
+echo $ECHO_N "checking for GTK - version >= $min_gtk_version... $ECHO_C" >&6
+  no_gtk=""
+  if test "$GTK_CONFIG" = "no" ; then
+    no_gtk=yes
+  else
+    GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
+    GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
+    gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
+    gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
+    gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
+    if test "x$enable_gtktest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GTK_CFLAGS"
+      LIBS="$GTK_LIBS $LIBS"
+      rm -f conf.gtktest
+      if test "$cross_compiling" = yes; then
+  echo $ac_n "cross compiling; assumed OK... $ac_c"
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.gtktest");
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = g_strdup("$min_gtk_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_gtk_version");
+     exit(1);
+   }
+
+  if ((gtk_major_version != $gtk_config_major_version) ||
+      (gtk_minor_version != $gtk_config_minor_version) ||
+      (gtk_micro_version != $gtk_config_micro_version))
+    {
+      printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
+             $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
+             gtk_major_version, gtk_minor_version, gtk_micro_version);
+      printf ("*** was found! If gtk-config was correct, then it is best\n");
+      printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
+      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+      printf("*** required on your system.\n");
+      printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
+      printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
+      printf("*** before re-running configure\n");
+    }
+#if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
+  else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
+	   (gtk_minor_version != GTK_MINOR_VERSION) ||
+           (gtk_micro_version != GTK_MICRO_VERSION))
+    {
+      printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
+	     GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
+      printf("*** library (version %d.%d.%d)\n",
+	     gtk_major_version, gtk_minor_version, gtk_micro_version);
+    }
+#endif /* defined (GTK_MAJOR_VERSION) ... */
+  else
+    {
+      if ((gtk_major_version > major) ||
+        ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
+        ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
+               gtk_major_version, gtk_minor_version, gtk_micro_version);
+        printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
+	       major, minor, micro);
+        printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
+        printf("*** correct copy of gtk-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+      }
+    }
+  return 1;
+}
+
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+no_gtk=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_gtk" = x ; then
+     echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+     :
+  else
+     echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+     if test "$GTK_CONFIG" = "no" ; then
+       echo "*** The gtk-config script installed by GTK could not be found"
+       echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the GTK_CONFIG environment variable to the"
+       echo "*** full path to gtk-config."
+     else
+       if test -f conf.gtktest ; then
+        :
+       else
+          echo "*** Could not run GTK test program, checking why..."
+          CFLAGS="$CFLAGS $GTK_CFLAGS"
+          LIBS="$LIBS $GTK_LIBS"
+          cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+int
+main ()
+{
+ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version));
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+   echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding GTK or finding the wrong"
+          echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+	  echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+          echo "***"
+          echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
+          echo "*** came with the system with the command"
+          echo "***"
+          echo "***    rpm --erase --nodeps gtk gtk-devel"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means GTK was incorrectly installed"
+          echo "*** or that you have moved GTK since it was installed. In the latter case, you"
+          echo "*** may want to edit the gtk-config script: $GTK_CONFIG"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     GTK_CFLAGS=""
+     GTK_LIBS=""
+     :
+  fi
+
+
+  rm -f conf.gtktest
+
+
+
+
+
+
+
+# Check whether --with-gl-prefix or --without-gl-prefix was given.
+if test "${with_gl_prefix+set}" = set; then
+  withval="$with_gl_prefix"
+  gl_prefix="$withval"
+else
+  gl_prefix=""
+fi;
+
+
+# Check whether --with-gtkgl-prefix or --without-gtkgl-prefix was given.
+if test "${with_gtkgl_prefix+set}" = set; then
+  withval="$with_gtkgl_prefix"
+  gtkgl_prefix="$withval"
+else
+  gtkgl_prefix=""
+fi;
+
+
+
+if test x$gl_prefix != x ; then
+ GL_CFLAGS="-I$gl_prefix/include"
+ GL_LDOPTS="-L$gl_prefix/lib"
+else
+ GL_CFLAGS=""
+ GL_LDOPTS=""
+fi
+
+saved_LIBS="$LIBS"
+saved_CFLAGS="$CFLAGS"
+
+# test for plain OpenGL
+echo "$as_me:$LINENO: checking GL" >&5
+echo $ECHO_N "checking GL... $ECHO_C" >&6
+LIBS="$GTK_LIBS $GL_LDOPTS -lGLU -lGL $saved_LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+ char glBegin(); glBegin();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  have_GL=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+have_GL=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $have_GL" >&5
+echo "${ECHO_T}$have_GL" >&6
+
+if test x$have_GL = xyes; then
+
+ GL_LIBS="-lGLU -lGL"
+
+else
+
+ # test for Mesa
+ echo "$as_me:$LINENO: checking Mesa" >&5
+echo $ECHO_N "checking Mesa... $ECHO_C" >&6
+ LIBS="$saved_LIBS $GTK_LIBS $GL_LDOPTS -lMesaGLU -lMesaGL"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+ char glBegin(); glBegin();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  have_Mesa=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+have_Mesa=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+ echo "$as_me:$LINENO: result: $have_Mesa" >&5
+echo "${ECHO_T}$have_Mesa" >&6
+
+ if test x$have_Mesa = xyes; then
+
+  GL_LIBS="-lMesaGLU -lMesaGL"
+
+ else
+
+  # test for Mesa with threads
+  echo "$as_me:$LINENO: checking Mesa with pthreads" >&5
+echo $ECHO_N "checking Mesa with pthreads... $ECHO_C" >&6
+  LIBS="$GTK_LIBS $GL_LDOPTS -lMesaGL -lMesaGLU -lpthread $saved_LIBS"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+ char glBegin(); glBegin();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  have_Mesa_pthread=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+have_Mesa_pthread=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  echo "$as_me:$LINENO: result: $have_Mesa_pthread" >&5
+echo "${ECHO_T}$have_Mesa_pthread" >&6
+
+  if test x$have_Mesa_pthread = xyes; then
+
+    GL_LIBS="-lMesaGLU -lMesaGL -lpthread"
+
+  else
+
+   #all failed
+   LIBS="$saved_LIBS"
+   CFLAGS="$saved_CFLAGS"
+   GTKGL_LIBS=""
+   GTKGL_CFLAGS=""
+   build_gtkgl=false
+
+  fi
+ fi
+fi
+
+
+if test x$gtkgl_prefix != x; then
+ GTKGL_CFLAGS="-I$gtkgl_prefix/include"
+ GTKGL_LDOPTS="-L$gtkgl_prefix/lib"
+else
+ GTKGL_CFLAGS=""
+ GTKGL_LDOPTS=""
+fi
+
+echo "$as_me:$LINENO: checking GtkGLArea" >&5
+echo $ECHO_N "checking GtkGLArea... $ECHO_C" >&6
+LIBS="$GTK_LIBS $GL_LDOPTS -lgtkgl $GL_LIBS $GTKGL_LDOPTS $saved_LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+ char gtk_gl_area_new(); gtk_gl_area_new();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  have_gtkgl=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+have_gtkgl=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $have_gtkgl" >&5
+echo "${ECHO_T}$have_gtkgl" >&6
+
+if test x$have_gtkgl = xyes; then
+
+ LIBS="$saved_LIBS"
+ CFLAGS="$saved_CFLAGS"
+ GTKGL_CFLAGS="$GTKGL_CFLAGS $GL_CFLAGS"
+ GTKGL_LIBS="$GTKGL_LDOPTS -lgtkgl $GL_LDOPTS $GL_LIBS"
+ build_gtkgl=true
+
+else
+
+ LIBS="$saved_LIBS"
+ CFLAGS="$saved_CFLAGS"
+ GTKGL_LIBS=""
+ GTKGL_CFLAGS=""
+ build_gtkgl=false
+
+fi
+
+
+
+
+
+
+if test "$build_gtkgl" != "true" ; then
+  echo "=============================================================="
+  echo "GView will not work without GtkGLArea widget, please install."
+  echo "See http://www.student.oulu.fi/~jlof/gtkglarea/"
+  echo "=============================================================="
+fi
+
+LIBS="$GTKGL_LDOPTS -lgtkgl $GL_LDOPTS $GL_LIBS $GTK_LIBS $LIBS"
+CFLAGS="$GTKGL_CFLAGS $GTK_CFLAGS $CFLAGS"
+
+
+
+
+# Check whether --with-gdal or --without-gdal was given.
+if test "${with_gdal+set}" = set; then
+  withval="$with_gdal"
+
+fi;
+
+if test "x$with_gdal" != "x" ; then
+  GDAL_HOME=$with_gdal
+  { echo "$as_me:$LINENO: Using requested GDAL_HOME of $GDAL_HOME" >&5
+echo "$as_me: Using requested GDAL_HOME of $GDAL_HOME" >&6;}
+elif test "x$GDAL_HOME" != "x" ; then
+  { echo "$as_me:$LINENO: Using predefined GDAL_HOME=$GDAL_HOME" >&5
+echo "$as_me: Using predefined GDAL_HOME=$GDAL_HOME" >&6;}
+elif test -f ../gdal/GDALmake.opt.in -o -f ../gdal/include/gdal.h ; then
+  GDAL_HOME=`pwd`/../gdal
+  { echo "$as_me:$LINENO: Found local GDAL_HOME=$GDAL_HOME" >&5
+echo "$as_me: Found local GDAL_HOME=$GDAL_HOME" >&6;}
+fi
+
+GDAL_LIB="-lgdal"
+
+ORIG_LIBS="$LIBS"
+LIBS="$GDAL_LIB -L$GDAL_HOME/lib $ORIG_LIBS"
+echo "$as_me:$LINENO: checking for GDALOpen in -lgdal" >&5
+echo $ECHO_N "checking for GDALOpen in -lgdal... $ECHO_C" >&6
+if test "${ac_cv_lib_gdal_GDALOpen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgdal  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char GDALOpen ();
+int
+main ()
+{
+GDALOpen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_gdal_GDALOpen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_gdal_GDALOpen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_gdal_GDALOpen" >&5
+echo "${ECHO_T}$ac_cv_lib_gdal_GDALOpen" >&6
+if test $ac_cv_lib_gdal_GDALOpen = yes; then
+  gdal_found=1
+else
+  gdal_found=0
+fi
+
+
+if test "$gdal_found" != "1" ; then
+  LIBS="$GDAL_LIB -L$GDAL_HOME $ORIG_LIBS"
+  echo "$as_me:$LINENO: checking for GDALOpen in -lgdal" >&5
+echo $ECHO_N "checking for GDALOpen in -lgdal... $ECHO_C" >&6
+if test "${ac_cv_lib_gdal_GDALOpen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgdal  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char GDALOpen ();
+int
+main ()
+{
+GDALOpen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_gdal_GDALOpen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_gdal_GDALOpen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_gdal_GDALOpen" >&5
+echo "${ECHO_T}$ac_cv_lib_gdal_GDALOpen" >&6
+if test $ac_cv_lib_gdal_GDALOpen = yes; then
+  gdal_found=1
+else
+  gdal_found=0
+fi
+
+fi
+
+if test "$gdal_found" != "1" ; then
+  LIBS="$ORIG_LIBS"
+  echo "$as_me:$LINENO: checking for GDALOpen in -lgdal" >&5
+echo $ECHO_N "checking for GDALOpen in -lgdal... $ECHO_C" >&6
+if test "${ac_cv_lib_gdal_GDALOpen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgdal  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char GDALOpen ();
+int
+main ()
+{
+GDALOpen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_gdal_GDALOpen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_gdal_GDALOpen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_gdal_GDALOpen" >&5
+echo "${ECHO_T}$ac_cv_lib_gdal_GDALOpen" >&6
+if test $ac_cv_lib_gdal_GDALOpen = yes; then
+  gdal_found=1
+else
+  gdal_found=0
+fi
+
+fi
+
+gdal_missing_msg='
+OpenEV requires GDAL library, but it was not found.  Please download
+and install it.  See http://www.remotesensing.org/gdal
+'
+if test "$gdal_found" != "1" ; then
+  { { echo "$as_me:$LINENO: error: $gdal_missing_msg" >&5
+echo "$as_me: error: $gdal_missing_msg" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+gdal_h_missing_msg='
+OpenEV requires GDAL, but gdal.h was not found.  Please ensure GDAL,
+and the development include files are installed, or that you run configure
+with --with-gdal=/path/to/gdal/installation.
+See http://www.remotesensing.org/gdal
+'
+
+GDAL_INC=""
+
+if test "x$GDAL_HOME" != "x" ; then
+
+  if test -f "$GDAL_HOME/include/gdal.h" ; then
+    GDAL_INC="-I$GDAL_HOME/include"
+  elif test -f "$GDAL_HOME/port/cpl_port.h" \
+	  -a -f "$GDAL_HOME/gcore/gdal.h" \
+	  -a -f "$GDAL_HOME/ogr/ogr_api.h" ; then
+    GDAL_INC="-I$GDAL_HOME/port -I$GDAL_HOME/gcore -I$GDAL_HOME/ogr"
+  else
+    { { echo "$as_me:$LINENO: error: $gdal_h_missing_msg" >&5
+echo "$as_me: error: $gdal_h_missing_msg" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+
+fi
+
+CPPFLAGS="$GDAL_INC $CPPFLAGS"
+
+
+for ac_header in gdal.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+if test "x$ac_cv_header_gdal_h" != "xyes" ; then
+  { { echo "$as_me:$LINENO: error: $gdal_h_missing_msg" >&5
+echo "$as_me: error: $gdal_h_missing_msg" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+GDAL_INC=$GDAL_INC
+
+cat >>confdefs.h <<_ACEOF
+#define USE_GDAL 1
+_ACEOF
+
+
+
+
+# Check whether --with-ogr or --without-ogr was given.
+if test "${with_ogr+set}" = set; then
+  withval="$with_ogr"
+
+fi;
+
+if test "$with_ogr" != no ; then
+  echo "checking for OGR ... enabled"
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_OGR 1
+_ACEOF
+
+  CFLAGS="-I$GDAL_HOME/ogr/ogrsf_frmts $CFLAGS"
+  OGR_SOURCE=gvogr.cpp
+else
+  echo "checking for OGR ... disabled by user"
+  OGR_SOURCE=
+fi
+
+OGR_SOURCE=$OGR_SOURCE
+
+
+
+
+# Check whether --with-double-geocoord or --without-double-geocoord was given.
+if test "${with_double_geocoord+set}" = set; then
+  withval="$with_double_geocoord"
+
+fi;
+
+echo "$as_me:$LINENO: checking geoocord type" >&5
+echo $ECHO_N "checking geoocord type... $ECHO_C" >&6
+if test "$with_double_geocoord" == "yes" ; then
+  CFLAGS="$CFLAGS -DGV_USE_DOUBLE_PRECISION_COORD"
+  echo "$as_me:$LINENO: result: double" >&5
+echo "${ECHO_T}double" >&6
+else
+  echo "$as_me:$LINENO: result: float" >&5
+echo "${ECHO_T}float" >&6
+fi
+
+
+
+# Check whether --with-render-plugin or --without-render-plugin was given.
+if test "${with_render_plugin+set}" = set; then
+  withval="$with_render_plugin"
+
+fi;
+
+echo "$as_me:$LINENO: checking for plugin enabled" >&5
+echo $ECHO_N "checking for plugin enabled... $ECHO_C" >&6
+if test "$with_render_plugin" == "yes" ; then
+    # Will enable g_module_* code.
+     CFLAGS="$CFLAGS -malign-double -fPIC -DGV_USE_RENDER_PLUGIN"
+    # This is needed to access stuff inside libgv.a from the plugin.
+    LDFLAGS="$LDFLAGS -rdynamic"
+
+    echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+    echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+
+
+# Check whether --with-debug or --without-debug was given.
+if test "${with_debug+set}" = set; then
+  withval="$with_debug"
+
+fi;
+
+echo "$as_me:$LINENO: checking debug/opt compile options" >&5
+echo $ECHO_N "checking debug/opt compile options... $ECHO_C" >&6
+
+CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-O2 //"`
+CFLAGS=`echo "$CFLAGS " | sed "s/-O2 //"`
+CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-O //"`
+CFLAGS=`echo "$CFLAGS " | sed "s/-O //"`
+
+if test "$with_debug" == "yes" ; then
+
+  OPTFLAGS=-g
+  echo "$as_me:$LINENO: result: adding -g" >&5
+echo "${ECHO_T}adding -g" >&6
+
+elif test "$with_debug" == "no" ; then
+
+  OPTFLAGS=-O2
+  echo "$as_me:$LINENO: result: adding -O2" >&5
+echo "${ECHO_T}adding -O2" >&6
+
+elif test "$with_debug" != "" ; then
+
+  OPTFLAGS=$with_debug
+  echo "$as_me:$LINENO: result: adding $with_debug" >&5
+echo "${ECHO_T}adding $with_debug" >&6
+
+else
+  OPTFLAGS="-O2 -g"
+  echo "$as_me:$LINENO: result: adding -O2 -g" >&5
+echo "${ECHO_T}adding -O2 -g" >&6
+fi
+
+
+CFLAGS=$CFLAGS
+
+OPTFLAGS=$OPTFLAGS
+
+                    ac_config_files="$ac_config_files Makefile pymod/Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+		   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf at gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "pymod/Makefile" ) CONFIG_FILES="$CONFIG_FILES pymod/Makefile" ;;
+  "gv_config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS gv_config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s, at SHELL@,$SHELL,;t t
+s, at PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s, at PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s, at PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s, at PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s, at PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s, at PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s, at exec_prefix@,$exec_prefix,;t t
+s, at prefix@,$prefix,;t t
+s, at program_transform_name@,$program_transform_name,;t t
+s, at bindir@,$bindir,;t t
+s, at sbindir@,$sbindir,;t t
+s, at libexecdir@,$libexecdir,;t t
+s, at datadir@,$datadir,;t t
+s, at sysconfdir@,$sysconfdir,;t t
+s, at sharedstatedir@,$sharedstatedir,;t t
+s, at localstatedir@,$localstatedir,;t t
+s, at libdir@,$libdir,;t t
+s, at includedir@,$includedir,;t t
+s, at oldincludedir@,$oldincludedir,;t t
+s, at infodir@,$infodir,;t t
+s, at mandir@,$mandir,;t t
+s, at build_alias@,$build_alias,;t t
+s, at host_alias@,$host_alias,;t t
+s, at target_alias@,$target_alias,;t t
+s, at DEFS@,$DEFS,;t t
+s, at ECHO_C@,$ECHO_C,;t t
+s, at ECHO_N@,$ECHO_N,;t t
+s, at ECHO_T@,$ECHO_T,;t t
+s, at LIBS@,$LIBS,;t t
+s, at CC@,$CC,;t t
+s, at CFLAGS@,$CFLAGS,;t t
+s, at LDFLAGS@,$LDFLAGS,;t t
+s, at CPPFLAGS@,$CPPFLAGS,;t t
+s, at ac_ct_CC@,$ac_ct_CC,;t t
+s, at EXEEXT@,$EXEEXT,;t t
+s, at OBJEXT@,$OBJEXT,;t t
+s, at CXX@,$CXX,;t t
+s, at CXXFLAGS@,$CXXFLAGS,;t t
+s, at ac_ct_CXX@,$ac_ct_CXX,;t t
+s, at RANLIB@,$RANLIB,;t t
+s, at ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s, at CPP@,$CPP,;t t
+s, at EGREP@,$EGREP,;t t
+s, at CXX_WFLAGS@,$CXX_WFLAGS,;t t
+s, at C_WFLAGS@,$C_WFLAGS,;t t
+s, at CXX_PIC@,$CXX_PIC,;t t
+s, at C_PIC@,$C_PIC,;t t
+s, at LD_SHARED@,$LD_SHARED,;t t
+s, at PYTHON@,$PYTHON,;t t
+s, at pythondir@,$pythondir,;t t
+s, at pyexecdir@,$pyexecdir,;t t
+s, at PYTHON_INCLUDES@,$PYTHON_INCLUDES,;t t
+s, at PYTHON_CC@,$PYTHON_CC,;t t
+s, at PYTHON_OPT@,$PYTHON_OPT,;t t
+s, at PYTHON_SO@,$PYTHON_SO,;t t
+s, at PYTHON_CFLAGS@,$PYTHON_CFLAGS,;t t
+s, at PYTHON_LINK@,$PYTHON_LINK,;t t
+s, at GTK_CONFIG@,$GTK_CONFIG,;t t
+s, at GTK_CFLAGS@,$GTK_CFLAGS,;t t
+s, at GTK_LIBS@,$GTK_LIBS,;t t
+s, at GTKGL_CFLAGS@,$GTKGL_CFLAGS,;t t
+s, at GTKGL_LIBS@,$GTKGL_LIBS,;t t
+s, at GDAL_INC@,$GDAL_INC,;t t
+s, at OGR_SOURCE@,$OGR_SOURCE,;t t
+s, at OPTFLAGS@,$OPTFLAGS,;t t
+s, at LIBOBJS@,$LIBOBJS,;t t
+s, at LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s, at configure_input@,$configure_input,;t t
+s, at srcdir@,$ac_srcdir,;t t
+s, at abs_srcdir@,$ac_abs_srcdir,;t t
+s, at top_srcdir@,$ac_top_srcdir,;t t
+s, at abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s, at builddir@,$ac_builddir,;t t
+s, at abs_builddir@,$ac_abs_builddir,;t t
+s, at top_builddir@,$ac_top_builddir,;t t
+s, at abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([	 ]*\)#\([	 ]*define[	 ][	 ]*\)'
+ac_dB='[	 ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([	 ]*\)#\([	 ]*\)undef\([	 ][	 ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 # Do quote $f, to prevent DOS paths from being IFS'd.
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[	 ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*\)\(([^)]*)\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[	 ]*#[	 ]*undef[	 ][	 ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[	 ]*#[	 ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[	 ]*#[	 ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[	 ]*#[	 ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+


Property changes on: packages/openev/branches/upstream/current/configure
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/configure.in
===================================================================
--- packages/openev/branches/upstream/current/configure.in	                        (rev 0)
+++ packages/openev/branches/upstream/current/configure.in	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,212 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+AC_CONFIG_HEADER(gv_config.h)
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_CXX
+
+AC_PROG_RANLIB
+
+dnl We always want to check /usr/local for stuff.
+LIBS="$LIBS -L/usr/local/lib"
+CFLAGS="$CFLAGS -I/usr/local/include"
+CPPFLAGS="$CPPFLAGS -I/usr/local/include"
+
+dnl Checks for libraries.
+AC_CHECK_LIB(dl,dlopen,,,)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h unistd.h dbmalloc.h dlfcn.h string.h strings.h)
+
+dnl Checks for library functions.
+AC_C_BIGENDIAN
+AC_FUNC_VPRINTF
+
+AC_COMPILER_WFLAGS
+AC_COMPILER_PIC
+AC_LD_SHARED
+
+dnl
+dnl Python related checks.
+dnl
+AM_PATH_PYTHON
+
+AM_INIT_PYEXEC_MOD
+
+AM_CHECK_PYMOD(thread,,extra_mods=gthread,extra_mods=)
+
+AM_PATH_GTK(1.2.1,,,$extra_mods)
+
+AM_PATH_GTKGL(build_gtkgl=true, build_gtkgl=false)
+
+if test "$build_gtkgl" != "true" ; then 
+  echo "=============================================================="
+  echo "GView will not work without GtkGLArea widget, please install."
+  echo "See http://www.student.oulu.fi/~jlof/gtkglarea/"
+  echo "=============================================================="
+fi
+
+LIBS="$GTKGL_LDOPTS -lgtkgl $GL_LDOPTS $GL_LIBS $GTK_LIBS $LIBS"
+CFLAGS="$GTKGL_CFLAGS $GTK_CFLAGS $CFLAGS"
+
+dnl ---------------------------------------------------------------------------
+
+dnl
+dnl Find the required GDAL libraries, and include files.
+dnl
+
+AC_ARG_WITH(gdal,[  --with-gdal[=ARG]         Path to GDAL installation tree],,)
+
+if test "x$with_gdal" != "x" ; then
+  GDAL_HOME=$with_gdal
+  AC_MSG_NOTICE([Using requested GDAL_HOME of $GDAL_HOME])
+elif test "x$GDAL_HOME" != "x" ; then
+  AC_MSG_NOTICE([Using predefined GDAL_HOME=$GDAL_HOME])
+elif test -f ../gdal/GDALmake.opt.in -o -f ../gdal/include/gdal.h ; then
+  GDAL_HOME=`pwd`/../gdal
+  AC_MSG_NOTICE([Found local GDAL_HOME=$GDAL_HOME])
+fi
+
+GDAL_LIB="-lgdal"
+
+dnl Check $GDAL_HOME/lib for library binary first.
+ORIG_LIBS="$LIBS"
+LIBS="$GDAL_LIB -L$GDAL_HOME/lib $ORIG_LIBS"
+AC_CHECK_LIB(gdal,GDALOpen,gdal_found=1,gdal_found=0)
+ 
+dnl Not found? Now check $GDAL_HOME.
+if test "$gdal_found" != "1" ; then
+  LIBS="$GDAL_LIB -L$GDAL_HOME $ORIG_LIBS"
+  AC_CHECK_LIB(gdal,GDALOpen,gdal_found=1,gdal_found=0)
+fi
+
+dnl Still not found? Check for system wide installation.
+if test "$gdal_found" != "1" ; then
+  LIBS="$ORIG_LIBS"
+  AC_CHECK_LIB(gdal,GDALOpen,gdal_found=1,gdal_found=0)
+fi
+ 
+gdal_missing_msg='
+OpenEV requires GDAL library, but it was not found.  Please download 
+and install it.  See http://www.remotesensing.org/gdal
+'
+if test "$gdal_found" != "1" ; then
+  AC_MSG_ERROR([$gdal_missing_msg])
+fi
+
+gdal_h_missing_msg='
+OpenEV requires GDAL, but gdal.h was not found.  Please ensure GDAL,
+and the development include files are installed, or that you run configure
+with --with-gdal=/path/to/gdal/installation.  
+See http://www.remotesensing.org/gdal
+'
+ 
+GDAL_INC=""
+
+if test "x$GDAL_HOME" != "x" ; then
+
+  if test -f "$GDAL_HOME/include/gdal.h" ; then
+    GDAL_INC="-I$GDAL_HOME/include"
+  elif test -f "$GDAL_HOME/port/cpl_port.h" \
+	  -a -f "$GDAL_HOME/gcore/gdal.h" \
+	  -a -f "$GDAL_HOME/ogr/ogr_api.h" ; then
+    GDAL_INC="-I$GDAL_HOME/port -I$GDAL_HOME/gcore -I$GDAL_HOME/ogr"
+  else
+    AC_MSG_ERROR([$gdal_h_missing_msg])
+  fi
+
+fi
+
+CPPFLAGS="$GDAL_INC $CPPFLAGS"
+
+AC_CHECK_HEADERS(gdal.h)
+if test "x$ac_cv_header_gdal_h" != "xyes" ; then
+  AC_MSG_ERROR([$gdal_h_missing_msg])
+fi
+
+AC_SUBST(GDAL_INC,$GDAL_INC)
+AC_DEFINE_UNQUOTED(USE_GDAL)
+
+dnl --------------------------------------------------------------------------
+
+AC_ARG_WITH(ogr,[  --with-ogr              Enable OGR Linkage],,)
+
+if test "$with_ogr" != no ; then
+  echo "checking for OGR ... enabled"
+  AC_DEFINE_UNQUOTED(HAVE_OGR)
+  CFLAGS="-I$GDAL_HOME/ogr/ogrsf_frmts $CFLAGS"
+  OGR_SOURCE=gvogr.cpp
+else
+  echo "checking for OGR ... disabled by user"
+  OGR_SOURCE=
+fi
+
+AC_SUBST(OGR_SOURCE,$OGR_SOURCE)
+
+dnl --------------------------------------------------------------------------
+
+AC_ARG_WITH(double-geocoord,[  --with-double-geocoord  Use double precision GL coordinates],,)
+
+AC_MSG_CHECKING([geoocord type])
+if test "$with_double_geocoord" == "yes" ; then
+  CFLAGS="$CFLAGS -DGV_USE_DOUBLE_PRECISION_COORD"
+  AC_MSG_RESULT([double])
+else
+  AC_MSG_RESULT([float])
+fi
+
+dnl --------------------------------------------------------------------------
+
+AC_ARG_WITH(render-plugin,[  --with-render-plugin    Enable plugin (rendering)],,)
+
+AC_MSG_CHECKING([for plugin enabled])
+if test "$with_render_plugin" == "yes" ; then
+    # Will enable g_module_* code.
+     CFLAGS="$CFLAGS -malign-double -fPIC -DGV_USE_RENDER_PLUGIN"
+    # This is needed to access stuff inside libgv.a from the plugin.
+    LDFLAGS="$LDFLAGS -rdynamic"
+    
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+dnl --------------------------------------------------------------------------
+
+AC_ARG_WITH(debug,[  --with-debug            Build debug (else optimized)],,)
+
+AC_MSG_CHECKING([debug/opt compile options])
+
+CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-O2 //"`
+CFLAGS=`echo "$CFLAGS " | sed "s/-O2 //"`
+CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-O //"`
+CFLAGS=`echo "$CFLAGS " | sed "s/-O //"`
+
+if test "$with_debug" == "yes" ; then
+
+  OPTFLAGS=-g
+  AC_MSG_RESULT([adding -g])
+
+elif test "$with_debug" == "no" ; then
+
+  OPTFLAGS=-O2
+  AC_MSG_RESULT([adding -O2])
+
+elif test "$with_debug" != "" ; then
+
+  OPTFLAGS=$with_debug
+  AC_MSG_RESULT([adding $with_debug])
+
+else
+  OPTFLAGS="-O2 -g"
+  AC_MSG_RESULT([adding -O2 -g])
+fi
+
+
+AC_SUBST(CFLAGS,$CFLAGS)
+AC_SUBST(OPTFLAGS,$OPTFLAGS)
+AC_OUTPUT(Makefile pymod/Makefile)

Added: packages/openev/branches/upstream/current/contrib/S52/COPYING
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/COPYING	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/COPYING	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: packages/openev/branches/upstream/current/contrib/S52/ChangeLog
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/ChangeLog	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/ChangeLog	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,160 @@
+05OCT2004
+    -update new code to CVS 
+    
+04OCT2004
+    -rtag freeze_V-1_0
+    
+22AUG2004
+    -add S52_obj handle
+    -refactor code to work with S52_obj
+    -change ex0 traget and code to S52glxsimple
+    -add target s52osg
+    -add code to create an .osg file for osgviewer
+    -reorganize Makefile
+    
+21JUL2004
+    -add color palette switch 'on-the-fly'
+    
+12JUL2004
+    -code clean up
+    -add better text handling (GL)
+    -add light translation into text (CS)
+    -various bug fix
+    
+01JUL2004
+    -removed S52rgb
+    -add display supression of object (PL)
+    -add palette selection (utils)
+    -add font creation in gvS57layer
+    -code cleanup
+    
+27JUN2004
+    -fix pattern/projection
+    -code cleanup
+    -fix unlink object after pick
+    -set correct extend by loading M_COVR first 
+    -optimize tess to strip/fan
+    
+26JUN2004
+    -add a bounch of update from Christian Tveen
+    	-indexed color
+    	-pick return list of S57 object
+    	-parse attribute list for official att
+    	-some work on priority handling
+    	-fix leak
+    	-add solaris switch
+    -clean up some projection code
+    -clean up code formatting
+    
+21JUN2004
+    -move PROJ stuff in S57data 
+    
+20JUN2004
+    -generalise S57 loading (GV/OGR)
+    -mod initGL to S52_GL_initGL
+    -fix pattern
+    
+22MAI2004
+    -move Christian code in main branch
+    -update README
+    -mod Makefile
+
+19MAI2004
+    -add Christian code to split GV/OGR dependency
+    	--add ex0_xwin.c, ogrS57layers.c, S57data.[ch], S57ogr.[ch]
+    
+07MAI2004
+    -mod LUP table name for indexing  (PL)
+    -add mariner selection for are/line (conf)
+    -mod for futur object layer switching code (GL)
+    -bunch of code cleanup
+    -add S52_MAR_SYMPLIFIED_PNT (utils)
+
+03MAI2004
+    -started cursor pick
+    
+27APR2004
+    -add glcontext switching
+    -removed dependency to openev STENCIL glcontext
+    -start color handling (CIE->RGB)
+    
+26APR2004
+    -extend size feedback buffer (GL)
+    -add Z clip plane for line removal (GL)
+    
+25APR2004
+    -add GL selection for OBSTRN04 (CS)
+    -add GL selection for WRECKS02 (CS)
+
+20APR2004
+    -add GL selection for DEPCNT02 (CS)
+
+12APR2004
+    -add line mask for line object and area object
+    
+09APR2004
+    -merge obj attributs with obj geo
+
+07APR2004
+    -move vector parser into PL module (GL/PL)
+    -code clean up
+
+02APR2004
+    -mariner parameter handling
+    -attribute handling
+    -start coding LIGHTS05 
+    
+18MAR2004
+    -fix area pattern upside down (GL)
+    -add layer override logic
+
+16MAR2004
+    -add antialiase
+    -fix line orientation for area object
+    
+12MAR2004
+    -fix upside down complex line style (GL)
+
+10MAR2004
+    -fix typo: FSHRES51, ACHRES51, DIAMOND1 (CS)
+    
+08MAR2004
+    -add symbol orientation
+    
+07MAR2004
+    -add default color (PL)
+    -add nature of seabed abbreviation (PL)
+    -fix symbol creation (GL)
+    -fix symbol tansparency 
+    
+26FEB2004
+    -fix symbol drawn with point (SPRING)
+
+25FEB2004
+    -fix area obstruction in AA5C1JKL.000 (CS)
+    
+20FEB2004
+    -fix list number (dec to octal in CS)
+    -check data type correctness w/ g++ (CS)
+    
+16FEB2004
+    -fix depth symbols (CS)
+
+15FRB2004
+    -exclude stuff outside view window (GL)
+    
+10FEB2004
+    -fix tess problem in Chart No 1
+    
+20JAN2004
+    -patch removed since Frank commited it to OpenEV
+    
+04JAN2004
+    -opaque pointer for global PLib struct
+    -opaque pointer for global geo data struct
+    
+23DEC2003
+    -add code for plugin,
+    -add projection (mercator)
+    -code clean up
+

Added: packages/openev/branches/upstream/current/contrib/S52/Makefile
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/Makefile	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/Makefile	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,182 @@
+# Makefile: build libS52.so for openev plugin
+#
+# SD AUG2004
+
+### TARGET SELECTION SECTION ###
+#all: default # normal (openev plugin) --using GV & GTK
+#all: test    # debug (static) --using GV & GTK
+#all: osg     # debug (static) --using GV, GTK & OSG
+all: glx     # debug (static) --using only GLX & OGR
+
+
+### VARIABLES SECTION ###
+#CC =  tcc
+#CC  = gcc
+#CC  = gcc-2.95
+CC  = gcc-3.4
+
+#CXX = g++-2.95 -O0
+#CXX = g++ -O0
+#CXX = g++-3.4 -O0 -fpermissive
+CXX = g++-3.4
+
+TAGS = ctags
+
+OPENEV_HOME = ../../openev
+
+CFLAGS = -malign-double -fPIC `gtk-config --cflags` \
+         -I. -I$(OPENEV_HOME) \
+         -I/usr/X11R6/include -DGV_USE_DOUBLE_PRECISION_COORD\
+         -I/usr/local/include
+
+LDFLAGS  = -rdynamic
+
+OGL_LIBS = -lGL -lGLU
+OSG_LIBS = -losg -losgProducer
+GTK_LIBS = -lgtkgl -lgtk -lgdk
+GV__LIBS =  $(OPENEV_HOME)/libgv.a
+
+LIBS = -L/usr/lib -L/usr/local/lib              \
+       -lgmodule -lgthread -lglib -lgdal -lproj \
+       $(OGL_LIBS)
+
+OBJS = S52.o S52raz-3.2.rle.o S57data.o S52GL.o S52PL.o S52CS.o S52utils.o  
+
+
+### TARGETS' VARIABLES ###
+# this is the normal build (openev plugin)
+default: CC      += -O2
+default: CFLAGS  += -DS52_USE_GV
+default: objs     = $(OBJS) gvS57layer.o S57gvgeo.o
+default: libs     = $(LIBS) $(GTK_LIBS) $(GV__LIBS) ./libS52.so
+default: gvS57layer.o S57gvgeo.o libS52.so s52plugin
+
+# this wil build a 'stand-alone' for debuging (see bellow)
+test: CC      += -O0 -g -Wall
+test: CFLAGS  += -DS52_USE_GV
+test: objs     = $(OBJS) gvS57layer.o S57gvgeo.o
+test: libs     = $(LIBS) $(GTK_LIBS) $(GV__LIBS) ./libS52.a
+test: gvS57layer.o S57gv.o libS52.a s52test
+
+# this will build a 'stand alone' that use OSG
+osg: CC      += -O0 -g -Wall
+osg: CFLAGS  += -DS52_USE_GV -DS52_USE_OSG
+osg: objs     = $(OBJS) gvS57layer.o S57gvgeo.o S52OSG.o
+osg: libs     = $(LIBS) $(GTK_LIBS) ./libS52.a $(GV__LIBS) -losg -losgProducer 
+osg: gvS57layer.o S57gv.o S52OSG.o libS52.a s52osg
+
+# this will build a 'stand alone' for GLX and OGR only
+# (ie will not depend on GTK or GV)
+glx: CC      += -O0 -g -Wall
+glx: objs     = $(OBJS) S57ogr.o
+glx: libs     = $(LIBS) ./libS52.a
+glx: S57ogr.o libS52.a s52glxsimple
+
+### COMPILE SECTION ###
+#S57gvgeo.o: S57gvgeo.c S57src.h
+#S57gvgeo.o:S57gvgeo.c
+#	$(CC) $(CFLAGS) -c S57gvgeo.c
+
+#S57ogr.o: S57ogr.c S57src.h
+#	$(CC) $(CFLAGS) -c S57ogr.c
+
+#s52test.o: s52test.c
+#	$(CC) $(CFLAGS) -c s52test.c
+
+#%.o : %.c
+%.o : %.c %.h
+	-rm libS52.a
+	$(CC) $(CFLAGS) -c $<
+
+%.o: %.cpp S52OSG.cpp.h %.h
+	$(CXX) $(CFLAGS) -c $<
+
+# precompile header
+S52OSG.cpp.h:
+	$(CXX) $(CFLAGS) -c $@
+
+S52OSG.h:
+	$(CXX) $(CFLAGS) -c $@
+
+# assembler
+#S52raz-3.2.rle.o: S52raz-3.2.rle
+#	-rm -f data2obj.s
+#	sed -e 's/VAR_NAME/S52raz/'         \
+#	    -e 's/VAR_LEN/S52razLen/'       \
+#	    -e 's/DATAFILE/S52raz-3.2.rle/' \
+#	     data2obj.tmpl > data2obj.s
+#	gcc -c data2obj.s -o $@
+
+#S52raz-3.2.rle.o: S52raz-3.2.rle S52raz.s
+S52raz-3.2.rle.o:
+	gcc -c S52raz.s -o $@
+
+
+### LINK SECTION ###
+# dynamic --libgv.a will load libS52.so plug-in
+# Note: S52utils is needed before libS52.so is up
+s52plugin: s52test.o libS52.so $(GV__LIBS)
+#	@echo LIBS: $(LIBS)
+	$(CC) $(LDFLAGS) s52test.o S52utils.o $(libs) -o $@
+
+libS52.so: $(OBJS)
+	$(CC) -shared -o libS52.so $(objs)
+
+### DEBUG SECTION ###
+#
+# For this next target (s52test & s52osg) to link staticaly 
+# replace the code in gv_shapes_layer_set_data() inside the 
+# #define GV_USE_RENDER_PLUGIN with:
+#
+#        extern void gv_S57_layer_init(GvShapesLayer *layer);
+#        gv_S57_layer_init(layer);
+#
+# Then run 'make libgv.a' from openev before building s52test.
+# (Note that this break the gvtest build because the symbol
+#  gv_S57_layer_init is unknown to GV. But will allow to build
+#  s52test staticaly.)
+#
+# static --debug, libgv.a is linked to libS52.a
+
+# debug --libS52.a
+s52test: s52test.o libS52.a $(GV__LIBS)
+	$(CC) s52test.o $(libs) -o $@
+
+# debug --same + OSG
+s52osg: s52test.o libS52.a $(GV__LIBS)
+	$(CC) s52test.o $(libs) -o $@
+
+# debug --this target depend on only on GLX & OGR (not GV, GTK)
+#       --no need to edit code in OpenEV
+s52glxsimple: S52glxsimple.o libS52.a
+	$(CC) S52glxsimple.o $(libs) -o $@
+
+# debug (static)
+libS52.a: $(OBJS)
+#	@echo "OBJS: $(objs)"
+	rm  -f $@
+	ar  cq $@ $(objs)
+	ranlib $@
+
+
+
+### UTILS SECTION ###
+clean:
+	rm -f s52plugin s52test s52osg s52glxsimple *.o *.s *.gch \
+	libS52.a libS52.so tags try openc.*
+
+install:
+	install libS52.so `gdal-config --prefix`/lib
+
+uninstall:
+	rm -f `gdal-config --prefix`/lib/libS52.so
+
+backup: clean
+	tar cvf openc.tar *
+	bzip2 openc.tar
+
+tags:
+	$(TAGS) *.c *.h
+
+run:
+	./s52test

Added: packages/openev/branches/upstream/current/contrib/S52/README
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/README	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/README	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,184 @@
+README: OpENCview project, libS52.so installation notes and disclaimer
+
+Spetember 2004
+
+
+INTRODUCTION:
+=============
+
+OpENCview is a project to build an Open Source viewer of
+Electronic Navigational Chart (ENC).
+
+This release implement basic rendering of IHO' S-52  
+(IHO, ECDIS PRESENTATION LIBRARY, USERS' MANUAL, Edition / revision 3.2, 
+March 2000, Special Publication No. 52 ANNEX A of APPENDIX 2, 
+published by the International Hydrographic Bureau, MONACO) 
+via the plugin libS52.so for OpenEV. Note that the current Edition of
+the S-52 specification is 4.2 and is not available for free download as
+was Ed. 3.2 (pslb03_2.pdf) from IHO web site.
+
+This software is not intended for general public but to
+interested party in the GIS/marine field.
+
+
+DISCLAIMER:
+===========
+
+OpENCview is not indented for navigational use. It cannot
+be used as a paper chart replacement.
+
+
+SPECIFICATION:
+==============
+
+All specification used to code libS52.so are available
+for free from the Internet:
+
+organization   specs          specs URL / Implementation URL
+------------   -----          ----------------------------
+
+IHO            S-52           IHO S-52 USERS' MANUAL, Ed. 3.2
+                              www.iho.shom.fr/general/ecdis/pslb03_2.pdf 
+                              Implementor: Sylvain Duclos
+                              cvs.sourceforge.net:/cvsxroot/openev/contrib/S52
+
+IHO            S-57           IHO S-57 Appendix B.1
+                              www.iho.shom.fr/general/ecdis/20SOC.pdf
+                              Implementor: Frank Warmerdam
+                              gdal.velocet.ca/projects/s57/
+
+OpenGIS        OpenGIS        OpenGIS Abstract Specification
+                              www.opengis.org/techno/specs.htm
+                              Implementor: Frank Warmerdam
+                              gdal.velocet.ca/projects/opengis/
+
+SGI            OpenGL         OpenGL Specification
+                              www.opengl.org/developers/documentation/index.html
+                              Implementor: Brian Paul
+                              www.mesa3d.org
+
+X.Org          X Window Sys.  X Window System
+                              www.x.org
+                              Implementor: The XFree86 Project Inc.
+                              www.xfree86.org
+
+
+
+INSTALLATION:
+=============
+
+Linux/Unix:
+-----------
+
+1- generate configure for openev:
+	$cd ../../openev
+	$autoconf
+
+2- configure openev:
+	$./configure  --with-gdal=<your_full_path_to>/gdal \
+                  --with-ogr --with-double-geocoord --with-render-plugin
+
+3- compile openev:
+	$make clean
+    $make
+
+4- compile libS52:
+    $cd openev/contrib/S52
+	$make
+
+5- install libS52.so in /usr/local/lib (as root):
+	#make install
+
+6- set environnement variable:
+	$export S57_CSV=<path_to_gdal_data_directory>
+    $export OGR_S57_OPTIONS=LNAM_REFS:ON,UPDATES:ON,\
+    		SPLIT_MULTIPOINT:ON,PRESERVE_EMPTY_NUMBERS:ON,\
+    		RETURN_LINKAGES:ON
+
+7- test:
+	$openev/openev/gvtest -ogr=<ENC>
+    -OR-
+    $openev/contrib/S52/s52testdyn (s52test.conf will need ENC path ajusted)
+
+Windows:
+--------
+
+1- FIXME!
+
+
+FILE LIST:
+==========
+
+In openev/contrib/S52 are the following files:
+
+   ChangeLog        -log of code change,
+   COYING           -GPL license,
+   Makefile         -make instruction to build libS52.so,
+   README           -this file,
+   S52CS.c          -S52 Conditional Symbology (CS) instruction
+   S52CS.h          -interface to CS,
+   S52GL.c          -OpenGL S52 renderer (GL),
+   S52GL.h          -interface to GL,
+   S52PL.c          -S52 Presentation Library parser (PL),
+   S52PL.h          -interface to PL,
+   S52raz-3.2.rle   -data: alternate rasterization rules,
+   S52type.h        -header for S52 primitive type,
+   S52utils.c       -utilitys,
+   S52utils.h       -interface to utilitys,
+   S57gvgeo.c       -connect to OpenEV (gv) geo data (S57),
+   S57ogr.c         -connect to OGR data,
+   S57src.h         -interface to S57gvgeo & S57ogr,
+   S57data.c        -handle S57 data,
+   S57data.h        -interface to S57 data,
+   TODO             -jot pad for missing stuff,
+   data2obj.tmpl    -template to create ELF object from data files (above)
+   doc/             -directory olding various notes
+   doc/C1.lup_collision.txt -list of objects that can switch layers (IHO)
+   doc/S57.note     -note on S57 DB structure,
+   doc/obj.txt      -S57 objects
+   doc/att.txt.     -S57 attibutes
+   doc/boylat24.dia -BOYLAT24 object construct seen from S52 (Dia)
+   doc/boylat24.png -boylat24.dia converted to PNG
+   gvS57layer.c     -plugin entree point / interface to openev (static link),
+   gvS57layer.h     -interface to plugin (for completness --useless in real life),
+   ogrS57layer.c    -handle OGR layer,
+   ex0_xwin.c       -test harness for debugging (OGR path),
+   s52test.c        -test harness for debuging (GV path),
+   s52test.conf     -local configuration for debuging.
+
+
+REQUIREMENT:
+============
+
+-OpenEV (falcultative),
+
+-GDAL,
+
+-PROJ4,
+
+-A Presentation Library .DAI file his also required for symbolizing S57.
+As of this writing the current Official Presentation Library
+is Edition 4.2, and is available directly from the IHB
+(International Hydrographic Bureau) at info at ihb.mc.
+In the absence of an Official IHO Presentation Library
+an alternate non-conforming ECDIS library is used (S52raz-3.2.rle.)
+
+
+LINKS:
+======
+
+Frank Warmerdam web site as bunch of links:
+gdal.velocet.ca/projects/s57/
+
+
+CREDIT:
+=======
+
+Frank Warmerdam (warmerdam at pobox dot com) OpenEV/GDAL/OGR/S-57.
+Christian Gebauer Tveen (cgt at navicon dot dk) GV/OGR split.
+
+
+Comment / inquiry are welcome,
+
+Sylvain Duclos
+sduclos at users.sourceforgue.net
\ No newline at end of file

Added: packages/openev/branches/upstream/current/contrib/S52/S52.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,186 @@
+// S52.c: top-level interface to libS52.so plug-in
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview porject, a viewer of ENC.
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+#include "S52.h"
+#include "S52utils.h"   // PRINTF()
+#include "S52PL.h"      // S52_PRIO_NUM
+#include "S57ogr.h"     // S57_ogrLoadCell
+#include "S52GL.h"      // S52_GL_draw()
+
+#include <glib.h>       // GString, GPtrArray
+#include <string.h>     // strcmp()
+
+typedef struct _cell {
+    GString   *filename;
+    GPtrArray *renderBin[S52_PRIO_NUM][N_OBJ_T];//[RAD_NUM];
+} _cell;
+
+static GArray *_cellList = NULL;
+static _cell  *_crntCell = NULL;
+
+static int _loadLayer()
+{
+    return 1;
+}
+
+static int _loadAux()
+{
+    return 1;
+}
+
+static int _doneAux()
+{
+    return 1;
+}
+
+static int _addCell(const char *filename)
+// add this cell if not allready loaded
+{
+    _cell cell;
+    int   i   = 0;
+
+    if (NULL == _cellList)
+        _cellList = g_array_new(FALSE, TRUE, sizeof(_cell));
+
+    // check if loaded
+    for (i=0; i<_cellList->len; ++i) {
+        _cell *c = &g_array_index(_cellList, _cell, i);
+
+        if (0 == strcmp(filename, c->filename->str))
+            return 0;
+    }
+
+    cell.filename = g_string_new(filename);
+    {   // init renderbin
+        int i,j;
+        for (i=0; i<S52_PRIO_NUM; ++i) {
+            for (j=0; j<N_OBJ_T; ++j)
+                cell.renderBin[i][j] = g_ptr_array_new();
+        }
+    }
+
+    // if not load it
+    _cellList =  g_array_append_val(_cellList, cell);
+    _crntCell = &g_array_index(_cellList, _cell, _cellList->len-1);
+
+    // load metadata
+    //S57_ogrLoadLayer("M_COVR");
+
+
+    // load data
+
+    return 1;
+}
+
+cellID  S52_loadCell(const char *filename, _loadLayer_cb cb)
+{
+    // MUTEX
+
+    _addCell(filename);
+
+    S57_ogrLoadCell(filename, cb);
+
+    // done aux. data
+
+    return 1;
+}
+
+int     S52_loadLayer(const char *layername, void *ogrlayer)
+{
+    PRINTF("NOTE: LAYER NAME: %s\n", layername);
+
+    S57_ogrLoadLayer(layername, ogrlayer, S52_loadObject);
+
+    return 1;
+}
+
+int     S52_loadObject(const char *objname, void *shape)
+{
+    if (NULL == shape)
+        return FALSE;
+
+#ifdef S52_USE_GV
+    S57_geo *geoData = S57_gvObjectClone(objname, (srcData*)shape);
+#else
+    S57_geo *geoData = S57_ogrLoadObject(objname, (srcData*)shape);
+#endif
+
+    // debug
+    if (0 == strcmp(objname,"M_COVR"))
+        S52_GL_objectSetup(geoData);
+
+    if (NULL != geoData) {
+        int        obj_t;
+        S52_Obj_t  ot         = S57_getObjtype(geoData);
+        S52_obj   *obj        = S52_PL_getObj(geoData);
+        S52_diPrio disPrioIdx = S52_PL_getDPRI(obj);
+
+        switch (ot) {
+            case _META_T: obj_t = 0; break; // meta geo stuff (ex: C_AGGR)
+            case AREAS_T: obj_t = 1; break;
+            case LINES_T: obj_t = 2; break;
+            case POINT_T: obj_t = 3; break;
+            default:
+                PRINTF("ERROR: unknown index of addressed object type\n");
+                exit(0);
+        }
+        g_ptr_array_add( (_crntCell->renderBin)[disPrioIdx][obj_t], obj);
+
+    }
+
+    return TRUE;
+}
+
+int     S52_doneCell(cellID cID)
+{
+    return 1;
+}
+
+int     S52_draw()
+{
+    int i;
+    // APP .. update moving object
+    // CULL
+
+    for (i=0; i<_cellList->len; ++i) {
+        int j,k;
+        _cell *c = &g_array_index(_cellList, _cell, i);
+        for (j=0; j<S52_PRIO_NUM; ++j) {
+            for (k=0; k<N_OBJ_T; ++k) {
+                int idx;
+                GPtrArray *rbin = c->renderBin[j][k];
+
+                for (idx=0; idx<rbin->len; ++idx) {
+                    S52_obj *obj = g_ptr_array_index(rbin, idx);
+                    S52_GL_draw(obj);
+                }
+            }
+        }
+
+    }
+    // DRAW
+
+    PRINTF("*** F I N I S H **** \n\n");
+
+    return 1;
+}

Added: packages/openev/branches/upstream/current/contrib/S52/S52.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,38 @@
+// S52.h: top-level interface to libS52.so plug-in
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview porject, a viewer of ENC.
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S52_H_
+#define _S52_H_
+
+typedef int cellID;
+
+#include "S52type.h"    // _loadLayer_cb()
+
+// load a chart
+extern cellID S52_loadCell(const   char *filename, _loadLayer_cb cb);
+extern int    S52_loadLayer(const  char *layername, void *ogrLayer);
+extern int    S52_loadObject(const char *objname,   void *shape);
+extern int    S52_doneCell(cellID cID);
+extern int    S52_draw();
+#endif //_S52_H_

Added: packages/openev/branches/upstream/current/contrib/S52/S52CS.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52CS.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52CS.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2483 @@
+// S52CS.c : Conditional Symbologie procedure 3.2 (CS)
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+// NOTE: remarks commenting each CS are extracted from pslb03_2.pdf (sec. 12)
+
+// FIXME: DEPCNT02: call DB for area DEPARE & DRGARE that intersect this line
+// FIXME:_DEPVAL01: call DB for area DEPARE & UNSARE that intersect this area
+// FIXME:_UDWHAZ03: call DB for area DRGARE & DEPARE that intersect this point/area
+
+#include "S52CS.h"
+
+#include "S52utils.h"   // PRINTF()
+#include "S57src.h"     // S57_touche()
+//#include "S57ogr.h"     // S57_ogrTouche()
+
+#include <stdlib.h>     // atof()
+#include <math.h>       // fabsf(), HUGE_VAL
+#include <ctype.h>      // isdigit()
+#include <string.h>     // strncmp(), strpbrk()
+
+#define UNKNOWN HUGE_VAL   // INFINITY/NAN
+
+#define COALNE   30   // Coastline
+#define DEPARE   42   // Depth area
+#define DEPCNT   43   // Depth contour
+#define DRGARE   46   // Dredged area
+#define UWTROC  153   // Underwater rock / awash rock
+#define WRECKS  159   // Wreck
+
+// point list name
+#define LIGHTLIST 0
+#define SECTRLIST 1
+#define FLOATLIST 2   // floating platform
+#define RIGIDLIST 3   // rigid platform
+//static GPtrArray   *_lightList = NULL;
+//static GPtrArray   *_sectrList = NULL;
+//static GPtrArray   *_flaotList = NULL;  
+static GPtrArray *_ptList[] = {NULL, NULL, NULL, NULL};
+
+int S52_state = 1;
+
+// size of attributes value list buffer
+#define LISTSIZE   16   // list size
+
+#define version "3.2.0"
+char     *S52_CS_version()
+{
+    return version;
+}
+
+int       S52_CS_init()
+{
+    _ptList[LIGHTLIST] = g_ptr_array_new();
+    _ptList[SECTRLIST] = g_ptr_array_new();
+    _ptList[FLOATLIST] = g_ptr_array_new();
+    _ptList[RIGIDLIST] = g_ptr_array_new();
+
+    return 1;
+}
+
+int       S52_CS_done()
+{
+    g_ptr_array_free(_ptList[LIGHTLIST], TRUE);
+    g_ptr_array_free(_ptList[SECTRLIST], TRUE);
+    g_ptr_array_free(_ptList[FLOATLIST], TRUE);
+    g_ptr_array_free(_ptList[RIGIDLIST], TRUE);
+
+    return 1;
+}
+
+int       S52_CS_setPtPos(S57_geo *geoData, char *name)
+{
+    printf("name = %s\n", name);
+
+    if (POINT_T == S57_getObjtype(geoData)) {
+
+        // set floating platform
+        if ((0==strncmp(name, "LITFLT", 6)) ||
+            (0==strncmp(name, "LITVES", 6)) ||
+            (0==strncmp(name, "BOY",    3)))
+            g_ptr_array_add(_ptList[FLOATLIST], (gpointer) geoData);
+
+        // set rigid platform
+        if (0==strncmp(name, "BCN",    3))
+            g_ptr_array_add(_ptList[RIGIDLIST], (gpointer) geoData);
+    }
+
+    return 1;
+}
+
+static int      _overlap(S57_geo *geoNew, S57_geo *geoOld)
+{
+    // check for extend arc radius
+    GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1");
+    GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2");
+    GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1");
+    GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2");
+
+    // check  present
+    if (NULL == Asectr1str ||
+        NULL == Asectr2str ||
+        NULL == Bsectr1str ||
+        NULL == Bsectr2str)
+        return FALSE;
+
+    {
+        double Asectr1 = atof(Asectr1str->str);
+        double Asectr2 = atof(Asectr2str->str);
+        double Bsectr1 = atof(Bsectr1str->str);
+        double Bsectr2 = atof(Bsectr2str->str);
+        double Asweep = (Asectr1 > Asectr2) ?
+            Asectr2-Asectr1+360 : Asectr2-Asectr1;
+        double Bsweep = (Bsectr1 > Bsectr2) ?
+            Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1;
+
+        // check sector overlap
+        if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) {
+            if (Asweep == Bsweep) {
+                g_string_truncate(Bsectr2str, 0);
+                g_string_sprintf(Bsectr2str, "%f",Bsectr2-1);
+                S57_setAtt(geoNew, "SECTR2", Bsectr2str->str);
+            }
+
+            return FALSE;
+        }
+
+        // check if other/old sector larger
+        if (Asweep >= Bsweep)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static int      _atPtPos(S57_geo *geoNew, int listNm)
+// TRUE if there is a light at this position
+// or overlapping sector else FALSE
+{
+    int i;
+    GPtrArray *curntList = _ptList[listNm];
+
+    for (i=0; i<curntList->len; i++) {
+        S57_geo *geoOld = g_ptr_array_index(curntList, i);
+
+        if (S57_samePtPos(geoNew, geoOld)) {
+            if (SECTRLIST != listNm)
+                return TRUE;
+
+            if (TRUE == _overlap(geoNew, geoOld))
+                    return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+static int      _setPtPos(S57_geo *geo, int listNm)
+// TRUE if a new position of a light was set, else
+// FALSE (ie there is a light at this position)
+{
+    GPtrArray *curntList = _ptList[listNm];
+
+    if (TRUE == _atPtPos(geo, listNm))
+        return TRUE;
+    else
+        g_ptr_array_add(curntList, (gpointer) geo);
+
+    return FALSE;
+}
+
+static int      _parseList(const char *str, char *buf)
+// Put a string of comma delimited number in an array (buf).
+// Return: the number of value in buf.
+// Assume: - number < 256,
+//         - list size less then LISTSIZE-1 .
+// Note: buf is \0 terminated for strpbrk().
+{
+    int i = 0;
+
+    if (NULL != str && *str != '\0') {
+        do {
+            if ( i>= LISTSIZE-1) {
+                PRINTF("OVERFLOW --value in list lost!!\n");
+                break;
+            }
+
+            /*
+            if (255 <  (unsigned char) atoi(str)) {
+                PRINTF("value overflow (>255)\n");
+                exit(0);
+            }
+            */
+
+            buf[i++] = (unsigned char) atoi(str);
+
+            while(isdigit(*str))
+                ++str;   // next
+            //while( g_ascii_isdigit(c));   // next
+
+        } while(*str++ != '\0');      // skip ',' or exit
+    }
+
+    buf[i] = '\0';
+
+    return i;
+}
+
+static char    *_selSYcol(char *buf)
+{
+    // FIXME: C1 3.1 use LIGHTS0x          and specs 3.2 use LIGHTS1x
+
+    char *sym = ";SY(LIGHTDEF";            //sym = ";SY(LITDEF11";
+
+    // max 1 color
+    if ('\0' == buf[1]) { 
+        if (strpbrk(buf, "\003"))
+            sym = ";SY(LIGHTS01";          //sym = ";SY(LIGHTS11";
+        else if (strpbrk(buf, "\004"))
+            sym = ";SY(LIGHTS02";          //sym = ";SY(LIGHTS12";
+        else if (strpbrk(buf, "\001\006\013"))
+            sym = ";SY(LIGHTS03";          //sym = ";SY(LIGHTS13";
+    } else {
+        // max 2 color
+        if ('\0' == buf[2]) {
+            if (strpbrk(buf, "\001") && strpbrk(buf, "\003"))
+                sym = ";SY(LIGHTS01";          //sym = ";SY(LIGHTS11";
+            else if (strpbrk(buf, "\001") && strpbrk(buf, "\004"))
+                sym = ";SY(LIGHTS02";          //sym = ";SY(LIGHTS12";
+        }
+    }
+
+    return sym;
+}
+
+static GString *CLRLIN01 (S57_geo *geo)
+// Remarks: A clearing line shows a single arrow head at one of its ends. The direction
+// of the clearing line must be calculated from its line object in order to rotate
+// the arrow head symbol and place it at the correct end. This cannot be
+// achieved with a complex linestyle since linestyle symbols cannot be sized
+// to the length of the clearing line. Instead a linestyle with a repeating pattern
+// of arrow symbols had to be used which does not comply with the required
+// symbolization.
+{
+
+    PRINTF("Mariner's object not drawn\n");
+
+    return NULL;
+}
+
+static GString *DATCVR01 (S57_geo *geo)
+// Remarks: This conditional symbology procedure describes procedures for:
+// - symbolizing the limit of ENC coverage;
+// - symbolizing navigational purpose boundaries ("scale boundarie"); and
+// - indicating overscale display.
+//
+// Note that the mandatory meta object CATQUA is symbolized by the look-up table.
+//
+// Because the methods adopted by an ECDIS to meet the IMO and IHO requirements
+// listed on the next page will depend on the manufacturer's software, and cannot be
+// described in terms of a flow chart in the same way as other conditional procedures,
+// this procedure is in the form of written notes.
+{
+    //GString *datcvr01 = NULL;
+
+    // debug --return empty command for now
+    GString *datcvr01 = g_string_new(";OP(----)");
+
+    ///////////////////////
+    // 1- REQUIREMENT
+    // (IMO/IHO specs. explenation)
+
+    ///////////////////////
+    // 2- ENC COVERAGE
+    //
+    // 2.1- Limit of ENC coverage
+    //datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)");
+    // FIXME: get cell extend
+
+    // 2.2- No data areas
+    // This can be done outside of CS (ie when clearing the screen in Mesa)
+    // FIXME: ";OP(0---);AC(NODATA)"
+    // FIXME: set geo to cover earth (!)
+
+    //////////////////////
+    // 3- SCALE BOUNDARIES
+    //
+    // 3.1- Chart scale boundaties
+    // FIXME;
+    //g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)");
+    // -OR- LC(SCLBDYnn) (?)
+    //
+    // ;OP(3OS21030)
+
+    // 3.2- Graphical index of navigational purpose
+    // FIXME: draw extent of available SENC in DB
+
+    //////////////////////
+    // 4- OVERSCALE
+    //
+    // FIXME: get meta date CSCL of DSPM field
+    // FIXME: get object M_CSCL or CSCALE
+    //
+    // 4.1- Overscale indication
+    // FIXME: compute, scale = [denominator of the compilation scale] /
+    //                         [denominator of the display scale]
+    // FIXME: draw overscale indication (ie TX("X%3.1f",scale))
+    //
+    // 4.2- Ovescale area at a chart scale boundary
+    // FIXME: test if next chart is over scale (ie going from large scale chart
+    //        to a small scale chart)
+    // FIXME: draw AP(OVERSC01) on overscale part of display
+    //g_string(";OP(3OS21030)");
+
+    //
+    // 4.3- Larger scale data available
+    // FIXME: display indication of better scale available (?)
+
+    // FIXME
+    //PRINTF("NOTE: not computed\n");
+
+    return datcvr01;
+}
+
+static GString *_SEABED01(double drval1, double drval2);
+static GString *_RESCSP01(S57_geo *geo);
+static GString *DEPARE01 (S57_geo *geo)
+// Remarks: An object of the class "depth area" is coloured and covered with fill patterns
+// according to the mariners selections of shallow contour, safety contour and
+// deep contour. This requires a decision making process provided by the sub-procedure
+// "SEABED01" which is called by this symbology procedure.
+// Objects of the class "dredged area" are handled by this routine as well to
+// ensure a consistent symbolization of areas that represent the surface of the
+// seabed.
+{
+    GString *depare01  = NULL;
+    int      objl      = 0;
+    GString *objlstr   = NULL;
+    GString *drval1str = S57_getAttVal(geo, "DRVAL1");
+    double   drval1    = UNKNOWN;
+    GString *drval2str = S57_getAttVal(geo, "DRVAL2");
+    double   drval2    = UNKNOWN;
+
+    drval1 = (NULL == drval1str) ? -1.0        : atof(drval1str->str);
+    drval2 = (NULL == drval2str) ? drval1+0.01 : atof(drval2str->str);
+
+    depare01 = _SEABED01(drval1, drval2);
+
+    objlstr = S57_getAttVal(geo, "OBJL");
+    objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+    if (DRGARE == objl) {
+        g_string_append(depare01, ";AP(DRGARE01)");
+        g_string_append(depare01, ";LS(DASH,1,CHGRF)");
+
+        if (NULL != S57_getAttVal(geo, "RESTRN")) {
+            GString *rescsp01 = _RESCSP01(geo);
+            if (NULL != rescsp01) {
+                g_string_append(depare01, rescsp01->str);
+                g_string_free(rescsp01, TRUE);
+            }
+        }
+
+    }
+
+    return depare01;
+}
+
+static GString *_SNDFRM02(S57_geo *geo, double depth_value);
+static GString *DEPCNT02 (S57_geo *geo)
+// Remarks: An object of the class "depth contour" or "line depth area" is highlighted and must
+// be shown under all circumstances if it matches the safety contour depth value
+// entered by the mariner (see IMO PS 3.6). But, while the mariner is free to enter any
+// safety contour depth value that he thinks is suitable for the safety of his ship, the
+// SENC only contains a limited choice of depth contours. This symbology procedure
+// determines whether a contour matches the selected safety contour. If the selected
+// safety contour does not exist in the data, the procedure will default to the next deeper
+// contour. The contour selected is highlighted as the safety contour and put in
+// DISPLAYBASE. The procedure also identifies any line segment of the spatial
+// component of the object that has a "QUAPOS" value indicating unreliable
+// positioning, and symbolizes it with a double dashed line.
+//
+// Note: Depth contours are not normally labeled. The ECDIS may provide labels, on demand
+// only as with other text, or provide the depth value on cursor picking
+{
+    GString *depcnt02  = NULL;
+    int      safe      = FALSE;     // initialy not a safety contour
+    GString *objlstr   = NULL;
+    int      objl      = 0;
+    GString *quaposstr = NULL;
+    int      quapos    = 0;
+    double   depth_value;
+
+    objlstr = S57_getAttVal(geo, "OBJL");
+    objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
+
+    if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) {
+        GString *drval1str = S57_getAttVal(geo, "DRVAL1");
+        double   drval1    = (NULL == drval1str) ? 0.0    : atof(drval1str->str);
+        GString *drval2str = S57_getAttVal(geo, "DRVAL2");
+        double   drval2    = (NULL == drval2str) ? drval1 : atof(drval2str->str);
+
+        if (drval1 <= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+            if (drval2 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
+                safe = TRUE;
+        } else {
+            S57_geo *geoTmp = geo;
+
+            // collect group 1 area DEPARE & DRGARE that touche this line
+            S57_ogrTouche(geoTmp, GRP_1_T);
+            while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+                drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+                drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+
+                if (NULL == drval1str) {
+                    safe = TRUE;
+                    break;
+                }
+
+                if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+                    safe = TRUE;
+                    break;
+                }
+            //}
+            // debug trace
+            //if (safe) PRINTF("** DEPARE: SAFE FOUND**\n");
+            }
+            S57_unlinkObj(geo);
+
+        }
+
+        depth_value = drval1;
+
+    } else {
+        // continuation A (DEPCNT)
+        GString *valdcostr = S57_getAttVal(geo, "VALDCO");
+        double   valdco    = (NULL == valdcostr) ? 0.0 : atof(valdcostr->str);
+
+        depth_value = valdco;
+
+        if (valdco == S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
+            safe = TRUE;   // this is useless !?!?
+        else {
+            if (valdco > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+                //if (1 == S52_state)
+                //    return NULL;
+                //else {
+                S57_geo *geoTmp = geo;
+
+                // collect area DEPARE & DRGARE that touche this line
+                S57_ogrTouche(geoTmp, GRP_1_T);
+                while (NULL != (geoTmp = S57_nextObj(geoTmp))){
+                    GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+                    double   drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+
+                    if (NULL == drval1str) {
+                        safe = TRUE;
+                        break;
+                    }
+
+                    if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+                        safe = TRUE;
+                        break;
+                    }
+                //}
+                // debug trace
+                //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n");
+                }
+                S57_unlinkObj(geo);
+
+            }
+        }
+    }
+
+    // Continuation B
+    // ASSUME: OGR split lines to preserv different QUAPOS for a given line
+    // FIXME: check that the assumtion above is valid!
+    quaposstr = S57_getAttVal(geo, "QUAPOS");
+    if (NULL != quaposstr) {
+        quapos = atoi(quaposstr->str);
+        if ( 2 <= quapos && quapos < 10) {
+            if (safe)
+                depcnt02 = g_string_new(";LS(DASH,2,DEPSC)");
+            else
+                depcnt02 = g_string_new(";LS(DASH,1,DEPCN)");
+        }
+    } else {
+        if (safe)
+            depcnt02 = g_string_new(";LS(SOLD,2,DEPSC)");
+        else
+            depcnt02 = g_string_new(";LS(SOLD,1,DEPCN)");
+    }
+
+    if (safe) {
+        S57_setAtt(geo, "SCAMIN", "INFINITE");
+        depcnt02 = g_string_prepend(depcnt02, ";OP(8OD13010)");
+    } else
+        depcnt02 = g_string_prepend(depcnt02, ";OP(---33020)");
+
+    // facultative in S-52
+    //if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+    //    GString *sndfrm02 = _SNDFRM02(geo, depth_value);
+    //    depcnt02 = g_string_append(depcnt02, sndfrm02->str);
+    //    g_string_free(sndfrm02, TRUE);
+    //}
+
+    // debug
+    //PRINTF("depth= %f\n", depth_value);
+
+    //S57_unlinkObj(geo);
+
+    return depcnt02;
+}
+
+static double   _DEPVAL01(S57_geo *geo, double least_depth)
+// Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks be
+// encoded as area obstruction, and that area OBSTRNs and area WRECKS
+// be covered by either group 1 object DEPARE or group 1 object UNSARE.
+// If the value of the attribute VALSOU for an area OBSTRN or WRECKS
+// is missing, the DRVAL1 of an underlying DEPARE is the preferred default
+// for establishing a depth vale. This procedure either finds the shallowest
+// DRVAL1 of the one or more underlying DEPAREs, or returns an
+// "unknown"" depth value to the main procedure for the next default
+// procedure.
+
+// NOTE: UNSARE test is useless since least_depth is already UNKNOWN
+{
+    least_depth = UNKNOWN;
+
+    S57_geo *geoTmp = geo;
+
+    // NOTE: change procedure to use any incomming geometry
+    // on area DEPARE & DRGARE (S52 say to use area UNSARE & DEPARE
+    // but this sound awkward since USARE do not have any depth!
+    // so it has to default to UNKNOWN implicitly!!)
+
+    // collect group 1 area DEPARE & DRGARE that touch this point/line/area
+    S57_ogrTouche(geoTmp, GRP_1_T);
+    while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+        GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+        double   drval1    = (NULL == drval1str) ? UNKNOWN : atof(drval1str->str);
+
+        if (NULL != drval1str) {
+            if (UNKNOWN==least_depth || least_depth<drval1)
+                least_depth = drval1;
+        }
+    }
+    S57_unlinkObj(geo);
+
+    return least_depth;
+}
+
+static GString *LEGLIN02 (S57_geo *geo)
+
+// Remarks: The course of a leg is given by its start and end point. Therefore this
+// conditional symbology procedure calculates the course and shows it
+// alongside the leg. It also places the "distance to run" labels and cares for the
+// different presentation of planned & alternate legs.
+{
+    PRINTF("Mariner's object not drawn\n");
+    return NULL;
+}
+
+static GString *_LITDSN01(S57_geo *geo);
+static GString *LIGHTS05 (S57_geo *geo)
+// Remarks: A light is one of the most complex S-57 objects. Its presentation depends on
+// whether it is a light on a floating or fixed platform, its range, it's colour and
+// so on. This conditional symbology procedure derives the correct
+// presentation from these parameters and also generates an area that shows the
+// coverage of the light.
+//
+// Notes on light sectors:
+// 1.) The radial leg-lines defining the light sectors are normally drawn to only 25mm
+// from the light to avoid clutter (see Part C). However, the mariner should be able to
+// select "full light-sector lines" and have the leg-lines extended to the nominal range
+// of the light (VALMAR).
+//
+// 2.) Part C of this procedure symbolizes the sectors at the light itself. In addition,
+// it should be possible, upon request, for the mariner to be capable of identifying
+// the colour and sector limit lines of the sectors affecting the ship even if the light
+// itself is off the display.
+// [ed. last sentence in bold]
+
+// NOTE: why is this relationship not already encoded in S57 (ei. C_AGGR or C_STAC) ?
+
+{
+    GString *lights05          = NULL;
+    GString *valnmrstr         = S57_getAttVal(geo, "VALNMR");
+    double   valnmr            = 0.0;
+    GString *catlitstr         = S57_getAttVal(geo, "CATLIT");
+    char     catlit[LISTSIZE]  = {'\0'};
+    int      flare_at_45       = FALSE;
+    int      extend_arc_radius = TRUE;
+    GString *sectr1str         = NULL;
+    GString *sectr2str         = NULL;
+    double   sectr1            = 0.0;
+    double   sectr2            = 0.0;
+    GString *colourstr         = NULL;
+    char     colist[LISTSIZE]  = {'\0'};   // colour list
+    GString *orientstr         = NULL;
+    double   sweep             = 0.0;
+
+
+    lights05 = g_string_new("");
+
+    valnmr = (NULL == valnmrstr) ? 9.0 : atof(valnmrstr->str);
+
+    if ( NULL != catlitstr) {
+        _parseList(catlitstr->str, catlit);
+
+        // FIXME: OR vs AND/OR
+        if (strpbrk(catlit, "\010\013")) {
+            g_string_append(lights05, ";SY(LIGHTS82)");
+            return lights05;
+        }
+
+        if (strpbrk(catlit, "\011")) {
+            g_string_append(lights05, ";SY(LIGHTS81)");
+            return lights05;
+        }
+
+        if (strpbrk(catlit, "\001\020")) {
+            orientstr = S57_getAttVal(geo, "ORIENT");
+            if (NULL != orientstr) {
+                // FIXME: create a geo object (!?) LINE of lenght VALNMR
+                // using ORIENT (from seaward) & POINT_T position
+                g_string_append(lights05, ";LS(DASH,1,CHBLK)");
+            }
+        }
+    }
+
+    // Continuation A
+    colourstr = S57_getAttVal(geo, "COLOUR");
+    if (NULL != colourstr)
+        _parseList(colourstr->str, colist);
+    else {
+        colist[0] = '\014';  // maganta (12)
+        colist[1] = '\000';
+    }
+
+    sectr1str = S57_getAttVal(geo, "SECTR1");
+    sectr1    = (NULL == sectr1str) ? 0.0 : atof(sectr1str->str);
+    sectr2str = S57_getAttVal(geo, "SECTR2");
+    sectr2    = (NULL == sectr2str) ? 0.0 : atof(sectr2str->str);
+
+    if (NULL==sectr1str || NULL==sectr2str) {
+        // not a sector light
+        char *sym;
+
+        //if (1==S52_state) {
+        //    _setPtPos(geo, LIGHTLIST);
+        //    g_string_free(lights05, TRUE);
+        //    return NULL;
+        //} else
+        //    flare_at_45 = _atPtPos(geo, LIGHTLIST);
+
+        flare_at_45 = _setPtPos(geo, LIGHTLIST);
+
+        sym = _selSYcol(colist);
+
+        if (strpbrk(catlit, "\001\020")) {
+            if (NULL != orientstr){
+                g_string_append(lights05, sym);
+                g_string_sprintfa(lights05, ",%s)", orientstr->str);
+                g_string_append(lights05, ";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" );
+            } else
+                g_string_append(lights05, ";SY(QUSMRK1)");
+        } else {
+            g_string_append(lights05, sym);
+            if (flare_at_45)
+                g_string_append(lights05, ",145)");
+            else
+                g_string_append(lights05, ",135)");
+        }
+
+        if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+            GString *litdsn01 = _LITDSN01(geo);
+            if (NULL != litdsn01){
+                g_string_append(lights05, ";TX('");
+                g_string_append(lights05, litdsn01->str);
+                g_string_free(litdsn01, TRUE);
+
+                if (flare_at_45)
+                    g_string_append(lights05, "',3,3,3,'15110',2,-1,CHBLK,23)" );
+                else
+                    g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
+            }
+        }
+
+        return lights05;
+    }
+
+    // Continuation B --sector light
+    if (NULL == sectr1str) {
+        sectr1 = 0.0;
+        sectr2 = 0.0;
+    } else
+        sweep = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1;
+
+
+    if (sweep<1.0 || sweep==360.0) {
+        // handle all round light
+        char *sym = _selSYcol(colist);;
+
+        g_string_append(lights05, sym);
+        g_string_append(lights05, ",135)");
+
+        if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
+            GString *litdsn01 = _LITDSN01(geo);
+            if (NULL != litdsn01) {
+                g_string_append(lights05, ";TX('");
+                g_string_append(lights05, litdsn01->str);
+                g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
+                g_string_free(litdsn01, TRUE);
+            }
+
+        }
+
+        return lights05;
+    }
+
+    // scan for other lights with sector overlap at this position
+    // compute light sector radius according to other sector
+    //if (1 == S52_state) {
+    //    _setPtPos(geo, SECTRLIST);
+    //    g_string_free(lights05, TRUE);
+    //    return NULL;
+    //} else {
+    //    extend_arc_radius = _atPtPos(geo, SECTRLIST);
+
+
+    extend_arc_radius = _setPtPos(geo, SECTRLIST);
+
+    // passe value via attribs to _renderAC
+    if (extend_arc_radius)
+        // FIXME: draw radius 25 mm
+        S57_setAtt(geo, "extend_arc_radius", "Y");
+    else
+        // FIXME: draw radius 20 mm
+        S57_setAtt(geo, "extend_arc_radius", "N");
+
+    // setup sector
+    {
+        char litvis[LISTSIZE] = {'\0'};  // list visibility
+        GString *litvisstr = S57_getAttVal(geo, "LITVIS");
+
+        // sector leg --logic is _renderLS()
+        g_string_append(lights05, ";LS(DASH,1,CHBLK)");
+
+        // get light vis.
+        if (NULL != litvisstr) _parseList(litvisstr->str, litvis);
+
+        // faint light
+        // FIXME: spec say OR (ie 1 number) the code is AND/OR
+        if (strpbrk(litvis, "\003\007\010")) {
+            // NOTE: LS(DASH,1,CHBLK)
+            // pass flag to _renderAC()
+            g_string_append(lights05, ";AC(CHBLK)");
+            S57_setAtt(geo, "faint_light", "Y");
+
+        } else {
+            // set arc colour
+            char *sym = ";AC(CHMGD)";  // other
+
+            // max 1 color
+            if ('\0' == colist[1]) { 
+                if (strpbrk(colist, "\003"))
+                    sym = ";AC(LITRD)";
+                else if (strpbrk(colist, "\004"))
+                    sym = ";AC(LITGN)";
+                else if (strpbrk(colist, "\001\006\013"))
+                    sym = ";AC(LITYW)";
+            } else {
+                // max 2 color
+                if ('\0' == colist[2]) {
+                    if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
+                        sym = ";AC(LITRD)";
+                    else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
+                        sym = ";AC(LITGN)";
+                }
+            }
+
+            g_string_append(lights05, sym);
+        }
+    }
+
+    return lights05;
+}
+
+static GString *_LITDSN01(S57_geo *geo)
+// Remarks: In S-57 the light characteristics are held as a series of attributes values. The
+// mariner may wish to see a light description text string displayed on the
+// screen similar to the string commonly found on a paper chart. This
+// conditional procedure, reads the attribute values from the above list of
+// attributes and composes a light description string which can be displayed.
+// This procedure is provided as a C function which has as input, the above
+// listed attribute values and as output, the light description.
+{
+    GString *litdsn01         = g_string_new("");
+    GString *gstr             = NULL;  // tmp
+    GString *catlitstr        = S57_getAttVal(geo, "CATLIT");
+    char     catlit[LISTSIZE] = {'\0'};
+    GString *litchrstr        = S57_getAttVal(geo, "LITCHR");
+    char     litchr[LISTSIZE] = {'\0'};
+    GString *colourstr        = S57_getAttVal(geo, "COLOUR");
+    char     colour[LISTSIZE] = {'\0'};
+    GString *statusstr        = S57_getAttVal(geo, "STATUS");
+    char     status[LISTSIZE] = {'\0'};
+
+    // FIXME: need grammar to create light's text
+
+    // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR
+
+    // CATLIT
+    //gstr = S57_getAttVal(geo, "CATLIT");
+    //g_string_append(litdsn01, gstr->str);
+    if (NULL != catlitstr) {
+        char *tmp = NULL;
+
+        if (1 < _parseList(catlitstr->str, catlit))
+            PRINTF("ERROR: more then one 'category of light' (CATLIT), other not displayed\n");
+
+        switch (catlit[0]) {
+            //1: directional function    IP 30.1-3;  475.7;
+            case 1: tmp = "Dir "; break;
+
+            //2: rear/upper light
+            //3: front/lower light
+            //4: leading light           IP 20.1-3;  475.6;
+
+            //5: aero light              IP 60;      476.1;
+            case 5: tmp = "Aero "; break;
+
+            //6: air obstruction light   IP 61;      476.2;
+            //7: fog detector light      IP 62;      477;
+            //8: flood light             IP 63;      478.2;
+            //9: strip light             IP 64;      478.5;
+            //10: subsidiary light        IP 42;      471.8;
+            //11: spotlight
+            //12: front
+            //13: rear
+            //14: lower
+            //15: upper
+            //16: moire' effect	        IP 31;      475.8;
+            //17: emergency
+            //18: bearing light                       478.1;
+            //19: horizontally disposed
+            //20: vertically disposed
+
+            default:
+                // FIXME: what is a good default
+                // or should it be left empty!
+                tmp = "? ";
+                PRINTF("ERROR: no abreviation for CATLIT\n");
+        }
+        g_string_append(litdsn01, tmp);
+    }
+
+
+    // LITCHR
+    //gstr = S57_getAttVal(geo, "LITCHR");
+    //if (NULL != gstr)
+    //    g_string_append(litdsn01, gstr->str);
+
+    if (NULL != litchrstr) {
+        char *tmp = NULL;
+
+        if (1 < _parseList(litchrstr->str, litchr))
+            PRINTF("ERROR: more then one 'light characteristic' (LITCHR), other not displayed\n");
+
+        switch (litchr[0]) {
+            //1: fixed                             IP 10.1;
+            case 1: tmp = "F "; break;
+            //2: flashing                          IP 10.4;
+            case 2: tmp = "Fl "; break;
+            //3: long-flashing                     IP 10.5;
+            case 3: tmp = "LFl "; break;
+            //4: quick-flashing                    IP 10.6;
+            case 4: tmp = "Q ";   break;
+            //5: very quick-flashing               IP 10.7;
+            case 5: tmp = "VQ "; break;
+            //6: ultra quick-flashing              IP 10.8;
+            case 6: tmp = "UQ "; break;
+            //7: isophased                         IP 10.3;
+            case 7: tmp = "Iso "; break;
+            //8: occulting                         IP 10.2;
+            case 8: tmp = "Oc "; break;
+            //9: interrupted quick-flashing        IP 10.6;
+            case 9: tmp = "IQ "; break;
+            //10: interrupted very quick-flashing   IP 10.7;
+            case 10: tmp = "IVQ "; break;
+            //11: interrupted ultra quick-flashing  IP 10.8;
+            case 11: tmp = "IUQ "; break;
+            //12: morse                             IP 10.9;
+            case 12: tmp = "Mo "; break;
+            //13: fixed/flash                       IP 10.10;
+            case 13: tmp = "FFl "; break;
+            //14: flash/long-flash
+            case 14: tmp = "Fl+LFl "; break;
+            // FIXME: not mention of 'alternating' occulting/flash in S57 attributes
+            // but S52 say 'alternating occulting/flash' (p. 188)
+            //15: occulting/flash
+            case 15: tmp = "AlOc Fl "; break;
+            //16: fixed/long-flash
+            case 16: tmp = "FLFl "; break;
+            //17: occulting alternating
+            case 17: tmp = "AlOc "; break;
+            //18: long-flash alternating
+            case 18: tmp = "AlLFl "; break;
+            //19: flash alternating
+            case 19: tmp = "AlFl "; break;
+            //20: group alternating
+            case 20: tmp = "Al "; break;
+
+            //21: 2 fixed (vertical)
+            //22: 2 fixed (horizontal)
+            //23: 3 fixed (vertical)
+            //24: 3 fixed (horizontal)
+
+            //25: quick-flash plus long-flash
+            case 25: tmp = "Q+LFl "; break;
+            //26: very quick-flash plus long-flash
+            case 26: tmp = "VQ+LFl "; break;
+            //27: ultra quick-flash plus long-flash
+            case 27: tmp = "UQ+LFl "; break;
+            //28: alternating
+            case 28: tmp = "Al "; break;
+            //29: fixed and alternating flashing
+            case 29: tmp = "AlF Fl "; break;
+
+            default:
+                // FIXME: what is a good default
+                // or should it be left empty!
+                tmp = "? ";
+                PRINTF("ERROR: no abreviation for LITCHR\n");
+        }
+        g_string_append(litdsn01, tmp);
+    }
+
+    // COLOUR,
+    //gstr = S57_getAttVal(geo, "COLOUR");
+    //if (NULL != gstr)
+    //    g_string_append(litdsn01, gstr->str);
+
+    if (NULL != colourstr) {
+        char *tmp = NULL;
+
+        if (1 < _parseList(colourstr->str, colour))
+            PRINTF("ERROR: more then one 'colour' (COLOUR), other not displayed\n");
+
+        switch (colour[0]) {
+            //1: white   IP 11.1;    450.2-3;
+            case 1: tmp = "W "; break;
+
+            //2: black
+
+            //3: red     IP 11.2;   450.2-3;
+            case 3: tmp = "R "; break;
+            //4: green   IP 11.3;   450.2-3;
+            case 4: tmp = "G "; break;
+
+            //5: blue    IP 11.4;   450.2-3;
+
+            //6: yellow  IP 11.6;   450.2-3;
+            case 6: tmp = "Y "; break;
+
+            //7: grey
+            //8: brown
+            //9: amber   IP 11.8;   450.2-3;
+            //10: violet  IP 11.5;   450.2-3;
+            //11: orange  IP 11.7;   450.2-3;
+            //12: magenta
+            //13: pink
+
+            default:
+                // FIXME: what is a good default
+                // or should it be left empty!
+                tmp = "? ";
+                PRINTF("ERROR: no abreviation for COLOUR\n");
+        }
+        g_string_append(litdsn01, tmp);
+    }
+
+    // HEIGHT, xxx.x
+    gstr = S57_getAttVal(geo, "HEIGHT");
+    if (NULL != gstr) {
+        g_string_append(litdsn01, gstr->str);
+        g_string_append(litdsn01, "m ");
+    }
+
+
+    // SIGGRP, (c)(c) ...
+    gstr = S57_getAttVal(geo, "SIGGRP");
+    if (NULL != gstr) {
+        PRINTF("WARNING: SIGGRP not translated into text\n");
+        //g_string_append(litdsn01, gstr->str);
+    }
+
+    // SIGPER, xx.xx
+    gstr = S57_getAttVal(geo, "SIGPER");
+    if (NULL != gstr) {
+        PRINTF("WARNING: SIGPER not translated into text\n");
+        //g_string_append(litdsn01, gstr->str);
+    }
+
+    // STATUS,
+    //gstr = S57_getAttVal(geo, "STATUS");
+    //if (NULL != gstr)
+    //    g_string_append(litdsn01, gstr->str);
+
+    if (NULL != statusstr) {
+        char *tmp = NULL;
+
+        if (1 < _parseList(statusstr->str, status))
+            PRINTF("ERROR: more then one 'status' (STATUS), other not displayed\n");
+
+        switch (status[0]) {
+            //1: permanent
+
+            //2: occasional             IP 50;  473.2;
+            case 2: tmp = "occas"; break;
+
+            //3: recommended            IN 10;  431.1;
+            //4: not in use             IL 14, 44;  444.7;
+            //5: periodic/intermittent  IC 21; IQ 71;   353.3; 460.5;
+            //6: reserved               IN 12.9;
+
+            //7: temporary              IP 54;
+            case 7: tmp = "temp"; break;
+            //8: private                IQ 70;
+            case 8: tmp = "priv"; break;
+
+            //9: mandatory
+            //10: destroyed/ruined
+
+            //11: extinguished
+            case 11: tmp = "exting"; break;
+
+            //12: illuminated
+            //13: historic
+            //14: public
+            //15: synchronized
+            //16: watched
+            //17: un-watched
+            //18: existence doubtful
+            default:
+                // FIXME: what is a good default
+                // or should it be left empty!
+                tmp = "? ";
+                PRINTF("ERROR: no abreviation for STATUS\n");
+        }
+        g_string_append(litdsn01, tmp);
+    }
+
+    // VALNMR, xx.x
+    gstr = S57_getAttVal(geo, "VALNMR");
+    if (NULL != gstr) {
+        g_string_append(litdsn01, gstr->str);
+        // FIXME: conflict in specs, nominal range in nautical miles in S57
+        // S52 imply that it can be express in meter
+        g_string_append(litdsn01, "M");
+    }
+
+
+    //PRINTF("FIXME: lights description not translated into text\n");
+
+    return litdsn01;
+}
+
+static GString *_UDWHAZ03(S57_geo *geo, double depth_value);
+static GString *_QUAPNT01(S57_geo *geo);
+
+static GString *OBSTRN04 (S57_geo *geo)
+// Remarks: Obstructions or isolated underwater dangers of depths less than the safety
+// contour which lie within the safe waters defined by the safety contour are
+// to be presented by a specific isolated danger symbol and put in IMO
+// category DISPLAYBASE (see (3), App.2, 1.3). This task is performed
+// by the sub-procedure "UDWHAZ03" which is called by this symbology
+// procedure. Objects of the class "under water rock" are handled by this
+// routine as well to ensure a consistent symbolization of isolated dangers on
+// the seabed.
+//
+// NOTE: updated to Cs1_md.pdf (ie was OBSTRN03)
+
+{
+    GString *obstrn04str = g_string_new("");
+    GString *sndfrm02str = NULL;
+    GString *udwhaz03str = NULL;
+    GString *valsoustr   = S57_getAttVal(geo, "VALSOU");
+    double   valsou      = UNKNOWN;
+    double   depth_value = UNKNOWN;
+    double   least_depth = UNKNOWN;
+
+    // exit if not in drawing state
+    //if (1 == S52_state)
+    //    return NULL;
+
+    if (NULL != valsoustr) {
+        valsou      = atof(valsoustr->str);
+        depth_value = valsou;
+        sndfrm02str = _SNDFRM02(geo, depth_value);
+    } else {
+        if (AREAS_T == S57_getObjtype(geo))
+            least_depth = _DEPVAL01(geo, least_depth);
+
+        if (UNKNOWN != least_depth) {
+            GString *catobsstr = S57_getAttVal(geo, "CATOBS");
+            GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+            if (NULL != catobsstr && '6' == *catobsstr->str)
+                depth_value = 0.01;
+            else
+                if (NULL == watlevstr) // default
+                    depth_value = -15.0;
+                else {
+                    switch (*watlevstr->str){
+                        case '5': depth_value =   0.0 ; break;
+                        case '3': depth_value =   0.01; break;
+                        case '4':
+                        case '1':
+                        case '2':
+                        default : depth_value = -15.0 ; break;
+                    }
+                }
+        } else
+            depth_value = least_depth;
+    }
+
+    udwhaz03str = _UDWHAZ03(geo, depth_value);
+
+    if (POINT_T == S57_getObjtype(geo)) {
+        // Continuation A
+        int      sounding    = FALSE;
+        GString *quapnt01str = _QUAPNT01(geo);
+
+        if (NULL != udwhaz03str){
+            g_string_append(obstrn04str, udwhaz03str->str);
+            if (NULL != quapnt01str)
+                g_string_append(obstrn04str, quapnt01str->str);
+
+            if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+            if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+            if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+            return obstrn04str;
+        }
+
+        if (UNKNOWN != valsou) {
+            if (valsou <= 20.0) {
+                GString *objlstr   = S57_getAttVal(geo, "OBJL");
+                int      objl      = (NULL == objlstr)? 0 : atoi(objlstr->str);
+                GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+                if (UWTROC == objl) {
+                    if (NULL == watlevstr) {  // default
+                        g_string_append(obstrn04str, ";SY(DANGER01)");
+                        sounding = TRUE;
+                    } else {
+                        switch (*watlevstr->str){
+                            case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break;
+                            case '4':
+                            case '5': g_string_append(obstrn04str, ";SY(UWTROC04)"); sounding = FALSE; break;
+                            default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break;
+                        }
+                    }
+                } else { // OBSTRN
+                    if (NULL == watlevstr) { // default
+                        g_string_append(obstrn04str, ";SY(DANGER01)");
+                        sounding = TRUE;
+                    } else {
+                        switch (*watlevstr->str) {
+                            case '1':
+                            case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); sounding = FALSE; break;
+                            case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE;  break;
+                            case '4':
+                            case '5': g_string_append(obstrn04str, ";SY(DANGER03)"); sounding = TRUE; break;
+                            default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE; break;
+                        }
+                    }
+                }
+            } else {  // valsou > 20.0
+                g_string_append(obstrn04str, ";SY(DANGER02)");
+                sounding = FALSE;
+            }
+
+        } else {  // NO valsou
+                GString *objlstr   = S57_getAttVal(geo, "OBJL");
+                int     objl       = (NULL == objlstr)? 0 : atoi(objlstr->str);
+                GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+                if (UWTROC == objl) {
+                    if (NULL == watlevstr)  // default
+                       g_string_append(obstrn04str, ";SY(UWTROC04)");
+                    else {
+                        if ('3' == *watlevstr->str)
+                            g_string_append(obstrn04str, ";SY(UWTROC03)");
+                        else
+                            g_string_append(obstrn04str, ";SY(UWTROC04)");
+                    }
+
+                } else { // OBSTRN
+                    if ( NULL == watlevstr) // default
+                        g_string_append(obstrn04str, ";SY(OBSTRN01)");
+                    else {
+                        switch (*watlevstr->str) {
+                            case '1':
+                            case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); break;
+                            case '3': g_string_append(obstrn04str, ";SY(OBSTRN01)"); break;
+                            case '4':
+                            case '5':
+                            default : g_string_append(obstrn04str, ";SY(OBSTRN01)"); break;
+                        }
+                    }
+                }
+
+        }
+
+        if (sounding && NULL != sndfrm02str)
+            g_string_append(obstrn04str, sndfrm02str->str);
+
+        if (NULL != quapnt01str)
+            g_string_append(obstrn04str, quapnt01str->str);
+
+        if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+        if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+        if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+        return obstrn04str;
+
+    } else {
+        if (LINES_T == S57_getObjtype(geo)) {
+            // Continuation B
+            GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+            int      quapos    = 0;
+
+            if (NULL != quaposstr) {
+                quapos = atoi(quaposstr->str);
+                if ( 2 <= quapos && quapos < 10){
+                    if (NULL != udwhaz03str)
+                        g_string_append(obstrn04str, ";LC(LOWACC41)");
+                    else
+                        g_string_append(obstrn04str, ";LC(LOWACC31)");
+                }
+            }
+
+            if (NULL != udwhaz03str)
+                g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+
+            if (UNKNOWN != valsou) {
+                if (valsou <= 20.0)
+                    g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+                else
+                    g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
+            } else
+                g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+
+
+            if (NULL != udwhaz03str)
+                g_string_append(obstrn04str, udwhaz03str->str);
+            else {
+                if (UNKNOWN != valsou)
+                    if (valsou <= 20.0)
+                        g_string_append(obstrn04str, sndfrm02str->str);
+            }
+
+            if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+            if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+
+            return obstrn04str;
+
+        } else {
+            // Continuation C (AREAS_T)
+            GString *quapnt01str = _QUAPNT01(geo);
+            if (NULL != udwhaz03str) {
+                g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01)");
+                g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+                g_string_append(obstrn04str, udwhaz03str->str);
+                if (NULL != quapnt01str)
+                    g_string_append(obstrn04str, quapnt01str->str);
+
+                if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+                if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+                if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+                return obstrn04str;
+            }
+
+            if (UNKNOWN != valsou) {
+                // BUG in CA49995B.000 if we get here because there is no color
+                // beside NODATA (ie there is a hole in group 1 area!)
+                //g_string_append(obstrn04, ";AC(UINFR)");
+
+                if (valsou <= 20.0)
+                    g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
+                else
+                    g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
+
+                g_string_append(obstrn04str, sndfrm02str->str);
+
+            } else {
+                GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+                 
+                if (NULL == watlevstr)   // default
+                    g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");
+                else {
+                    if ('3' == *watlevstr->str) {
+                        GString *catobsstr = S57_getAttVal(geo, "CATOBS");
+                        if (NULL != catobsstr && '6' == *catobsstr->str)
+                            g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)");
+                    } else {
+                        switch (*watlevstr->str) {
+                            case '1':
+                            case '2': g_string_append(obstrn04str, ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break;
+                            case '4': g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break;
+                            case '5':
+                            case '3':
+                            default : g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");  break;
+                        }
+                    }
+                }
+            }
+
+            g_string_append(obstrn04str, quapnt01str->str);
+
+            if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+            if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+            if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+
+            return obstrn04str;
+        }
+    }
+
+    // FIXME: check if one exit point could do!!!
+    return NULL;
+}
+
+static GString *OWNSHP02 (S57_geo *geo)
+// Remarks:
+// 1. CONNING POSITION
+//    1.1 When own-ship is drawn to scale, the conning position must be correctly located in
+//        relation to the ship's outline. The conning position then serves as the pivot point for
+//        the own-ship symbol, to be located by the ECDIS at the correct latitude, longitude
+//        for the conning point, as computed from the positioning system, correcting for
+//        antenna offset.
+//    1.2 In this procedure it is assumed that the heading line, beam bearing line and course
+//        and speed vector originate at the conning point. If another point of origin is used,
+//        for example to account for the varying position of the ship’s turning centre, this must
+//        be made clear to the mariner.
+//
+// 2. DISPLAY OPTIONS
+//    2.1 Only the ship symbol is mandatory for an ECDIS. The mariner should be prompted
+//        to select from the following additional optional features:
+//    - display own-ship as:
+//        1. symbol, or
+//        2. scaled outline
+//    - select time period determining vector length for own-ship and other vessel course and speed
+//      vectors, (all vectors must be for the same time period),
+//    - display own-ship vector,
+//    - select ground or water stabilization for all vectors, and select whether to display the type of
+//      stabilization, (by arrowhead),
+//    - select one-minute or six-minute vector time marks,
+//    - select whether to show a heading line, to the edge of the display window,
+//    - select whether to show a beam bearing line, and if so what length (default: 10mm total
+//      length).
+{
+    PRINTF("Mariner's object not drawn\n");
+    return NULL;
+}
+
+static GString *PASTRK01 (S57_geo *geo)
+// Remarks: This conditional symbology procedure was designed to allow the mariner
+// to select time labels at the pasttrack (see (3) 10.5.11.1). The procedure also
+// cares for the presentation of primary and secondary pasttrack.
+//
+// The manufacturer should define his own data class (spatial primitive) in xyt
+// (position and time) in order to represent Pastrk.
+{
+
+    PRINTF("Mariner's object not drawn\n");
+    return NULL;
+}
+
+static GString *_QUALIN01(S57_geo *geo);
+static GString *QUAPOS01 (S57_geo *geo)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
+// to the spatial object, not the feature object.
+//
+// This procedure passes the object to procedure QUALIN01 or QUAPNT01,
+// which traces back to the spatial object, retrieves any QUAPOS attributes,
+// and returns the appropriate symbolization to QUAPOS01.
+{
+    GString *quapos01 = NULL;
+
+    if (LINES_T == S57_getObjtype(geo))
+        quapos01 = _QUALIN01(geo);
+    else
+        quapos01 = _QUAPNT01(geo);
+
+    return quapos01;
+}
+
+static GString *_QUALIN01(S57_geo *geo)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
+// only to the spatial component(s) of an object.
+//
+// A line object may be composed of more than one spatial object.
+//
+// This procedure looks at each of the spatial
+// objects, and symbolizes the line according to the positional accuracy.
+{
+    GString *qualino1  = NULL;
+    GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+    int      quapos    = 0;
+    char    *line      = NULL;
+
+    if (NULL != quaposstr) {
+        quapos = atoi(quaposstr->str);
+        if ( 2 <= quapos && quapos < 10)
+            line = ";LC(LOWACC21)";
+    } else {
+        GString *objlstr = S57_getAttVal(geo, "OBJL");
+        int      objl    = (NULL == objlstr)? 0 : atoi(objlstr->str);
+
+        if (COALNE == objl) {
+            GString *conradstr = S57_getAttVal(geo, "CONRAD");
+
+            if (NULL != conradstr) {
+                if ('1' == *conradstr->str)
+                    line = ";LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)";
+                else
+                    line = ";LS(SOLD,1,CSTLN)";
+            } else
+                line = ";LS(SOLD,1,CSTLN)";
+
+        } else  //LNDARE
+            line = ";LS(SOLD,1,CSTLN)";
+    }
+
+    if (NULL != line)
+        qualino1 = g_string_new(line);
+
+    return qualino1;
+}
+
+static GString *_QUAPNT01(S57_geo *geo)
+// Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
+// only to the spatial component(s) of an object.
+//
+// This procedure retrieves any QUAPOS attributes, and returns the
+// appropriate symbols to the calling procedure.
+{
+    GString *quapnt01  = NULL;
+    int      accurate  = TRUE;
+    GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+    int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+
+    if (NULL != quaposstr) {
+        if ( 2 <= quapos && quapos < 10)
+            accurate = FALSE;
+    }
+
+    if (accurate)
+        quapnt01 = g_string_new(";SY(LOWACC01)");
+
+    return quapnt01;
+}
+
+static GString *SLCONS03 (S57_geo *geo)
+// Remarks: Shoreline construction objects which have a QUAPOS attribute on their
+// spatial component indicating that their position is unreliable are symbolized
+// by a special linestyle in the place of the varied linestyles normally used.
+// Otherwise this procedure applies the normal symbolization.
+{
+    GString *slcons03  = NULL;
+    GString *valstr    = NULL;
+    char    *cmdw      = NULL;   // command word
+    GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+    int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+
+    if (POINT_T == S57_getObjtype(geo)) {
+        if (NULL != quaposstr) {
+            if (2 <= quapos && quapos < 10)
+                cmdw =";SY(LOWACC01)";
+        }
+    } else {
+        // LINE_T and AREA_T are the same
+        if (NULL != quaposstr) {
+            if (2 <= quapos && quapos < 10)
+                cmdw =";LC(LOWACC01)";
+        } else {
+            valstr = S57_getAttVal(geo, "CONDTN");
+
+            if (NULL != valstr && ( '1' == *valstr->str || '2' == *valstr->str))
+                    cmdw = ";LS(DASH,1,CSTLN)";
+            else {
+                int val = 0;
+                valstr  = S57_getAttVal(geo, "CATSLC");
+                val     = (NULL == valstr)? 0 : atoi(valstr->str);
+
+                if (NULL != valstr && ( 6  == val || 15 == val || 16 == val ))
+                        cmdw = ";LS(SOLD,4,CSTLN)";
+                else {
+                    valstr = S57_getAttVal(geo, "WATLEV");
+
+                    if (NULL != valstr && '2' == *valstr->str)
+                            cmdw = ";LS(SOLD,2,CSTLN)";
+                    else
+                        if (NULL != valstr && ('3' == *valstr->str || '4' == *valstr->str))
+                            cmdw = ";LS(DASH,2,CSTLN)";
+                        else
+                            cmdw = ";LS(SOLD,2,CSTLN)";  // default
+
+                }
+            }
+        }
+    }
+
+    // WARNING: not explicitly specified in S-52 !!
+    // FIXME: this is to put AC(DEPIT) --intertidal area
+
+    /*
+    if (AREAS_T == S57_getObjtype(geo)) {
+        GString    *seabed01  = NULL;
+        GString    *drval1str = S57_getAttVal(geo, "DRVAL1");
+        double      drval1    = (NULL == drval1str)? -UNKNOWN : atof(drval1str->str);
+        GString    *drval2str = S57_getAttVal(geo, "DRVAL2");
+        double      drval2    = (NULL == drval2str)? -UNKNOWN : atof(drval2str->str);
+        // NOTE: change sign of infinity (minus) to get out of bound in seabed01
+
+
+        PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2);
+        seabed01 = _SEABED01(drval1, drval2);
+        slcons03 = g_string_new(seabed01->str);
+        g_string_free(seabed01, TRUE);
+
+    }
+    */
+
+
+    if (NULL != cmdw) {
+        if (NULL == slcons03)
+            slcons03 = g_string_new(cmdw);
+        else
+            g_string_append(slcons03, cmdw);
+    }
+
+    return slcons03;
+}
+
+static GString *RESARE02 (S57_geo *geo)
+// Remarks: A list-type attribute is used because an area of the object class RESARE may
+// have more than one category (CATREA). For example an inshore traffic
+// zone might also have fishing and anchoring prohibition and a prohibited
+// area might also be a bird sanctuary or a mine field.
+//
+// This conditional procedure is set up to ensure that the categories of most
+// importance to safe navigation are prominently symbolized, and to pass on
+// all given information with minimum clutter. Only the most significant
+// restriction is symbolized, and an indication of further limitations is given by
+// a subscript "!" or "I". Further details are given under conditional
+// symbology procedure RESTRN01
+//
+// Other object classes affected by attribute RESTRN are handled by
+// conditional symbology procedure RESTRN01.
+{
+    GString *resare02         = g_string_new("");
+    GString *restrnstr        = S57_getAttVal(geo, "RESTRN");
+    char     restrn[LISTSIZE] = {'\0'};
+    GString *catreastr        = S57_getAttVal(geo, "CATREA");
+    char     catrea[LISTSIZE] = {'\0'};
+    char    *symb             = NULL;
+    char    *line             = NULL;
+    char    *prio             = NULL;
+
+    if ( NULL != restrnstr) {
+        _parseList(restrnstr->str, restrn);
+
+        if (NULL != catreastr) _parseList(catreastr->str, catrea);
+
+        if (strpbrk(restrn, "\007\010\016")) {
+            // Continuation A
+            if (strpbrk(restrn, "\001\002\003\004\005\006"))
+                symb = ";SY(ENTRES61)";
+            else {
+                if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+                        symb = ";SY(ENTRES61)";
+                else {
+                    if (strpbrk(restrn, "\011\012\013\014\015"))
+                        symb = ";SY(ENTRES71)";
+                    else {
+                        if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+                            symb = ";SY(ENTRES71)";
+                        else
+                            symb = ";SY(ENTRES51)";
+                    }
+                }
+            }
+
+            if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+                line = ";LC(CTYARE51)";
+            else
+                line = ";LS(DASH,2,CHMGD)";
+
+            prio = ";OP(6---)";  // display prio set to 6
+
+        } else {
+            if (strpbrk(restrn, "\001\002")) {
+                // Continuation B
+                if (strpbrk(restrn, "\003\004\005\006"))
+                    symb = ";SY(ACHRES61)";
+                else {
+                    if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+                            symb = ";SY(ACHRES61)";
+                    else {
+                        if (strpbrk(restrn, "\011\012\013\014\015"))
+                            symb = ";SY(ACHRES71)";
+                        else {
+                            if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+                                symb = ";SY(ACHRES71)";
+                            else
+                                symb = ";SY(ACHRES51)";
+                        }
+                    }
+                }
+
+                if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+                    line = ";LC(ACHRE51)";
+                else
+                    line = ";LS(DASH,2,CHMGD)";
+
+                prio = ";OP(6---)";  // display prio set to 6
+
+            } else {
+                if (strpbrk(restrn, "\003\004\005\006")) {
+                    // Continuation C
+                    if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
+                            symb = ";SY(FSHRES51)";
+                    else {
+                        if (strpbrk(restrn, "\011\012\013\014\015"))
+                            symb = ";SY(FSHRES71)";
+                        else{
+                            if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+                                symb = ";SY(FSHRES71)";
+                            else
+                                symb = ";SY(FSHRES51)";
+                        }
+                    }
+
+                    if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+                        line = ";LC(FSHRES51)";
+                    else
+                        line = ";LS(DASH,2,CHMGD)";
+
+                    prio = ";OP(6---)";  // display prio set to 6
+
+                } else {
+                    if (strpbrk(restrn, "\011\012\013\014\015"))
+                        symb = ";SY(INFARE51)";
+                    else
+                        symb = ";SY(RSRDEF51)";
+
+                    if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+                        line = ";LC(CTYARE51)";
+                    else
+                        line = ";LS(DASH,2,CHMGD)";
+
+                }
+            }
+        }
+
+    } else {
+        // Continuation D
+        if (NULL != catreastr) {
+            if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) {
+                if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+                    symb = ";SY(CTYARE71)";
+                else
+                    symb = ";SY(CTYARE51)";
+            } else {
+                if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
+                    symb = ";SY(INFARE71)";
+                else
+                    symb = ";SY(RSRDEF51)";
+            }
+        } else
+            symb = ";SY(RSRDEF51)";
+
+        if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
+            line = ";LC(CTYARE51)";
+        else
+            line = ";LS(DASH,2,CHMGD)";
+    }
+
+    // create command word
+    if (NULL != prio)
+        g_string_append(resare02, prio);
+    g_string_append(resare02, line);
+    g_string_append(resare02, symb);
+
+    return resare02;
+}
+
+static GString *RESTRN01 (S57_geo *geo)
+// Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process
+// RESCSP01, since the latter can also be accessed from other conditional
+// symbology procedures. RESTRN01 merely acts as a "signpost" for
+// RESCSP01.
+//
+// Object class RESARE is symbolised for the effect of attribute RESTRN in a separate
+// conditional symbology procedure called RESARE02.
+//
+// Since many of the areas concerned cover shipping channels, the number of symbols used
+// is minimised to reduce clutter. To do this, values of RESTRN are ranked for significance
+// as follows:
+// "Traffic Restriction" values of RESTRN:
+// (1) RESTRN 7,8: entry prohibited or restricted
+//     RESTRN 14: IMO designated "area to be avoided" part of a TSS
+// (2) RESTRN 1,2: anchoring prohibited or restricted
+// (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted
+// (4) "Other Restriction" values of RESTRN are:
+//     RESTRN 9, 10: dredging prohibited or restricted,
+//     RESTRN 11,12: diving prohibited or restricted,
+//     RESTRN 13   : no wake area.
+{
+    GString *restrn01str = S57_getAttVal(geo, "RESTRN");
+    GString *restrn01    = NULL;
+
+    if (NULL != restrn01str)
+        restrn01 = _RESCSP01(geo);
+    else
+        restrn01 = g_string_new(";OP(----)");  // return NOOP to silence error msg
+
+    return restrn01;
+}
+
+static GString *_RESCSP01(S57_geo *geo)
+// Remarks: See procedure RESTRN01
+{
+    GString *rescsp01         = NULL;
+    GString *restrnstr        = S57_getAttVal(geo, "RESTRN");
+    char     restrn[LISTSIZE] = {'\0'};   // restriction list
+    char    *symb             = NULL;
+
+    if ( NULL != restrnstr) {
+        _parseList(restrnstr->str, restrn);
+
+        if (strpbrk(restrn, "\007\010\016")) {
+            // continuation A
+            if (strpbrk(restrn, "\001\002\003\004\005\006"))
+                symb = ";SY(ENTRES61)";
+            else {
+                if (strpbrk(restrn, "\011\012\013\014\015"))
+                    symb = ";SY(ENTRES71)";
+                else
+                    symb = ";SY(ENTRES51)";
+
+            }
+        } else {
+            if (strpbrk(restrn, "\001\002")) {
+                // continuation B
+                if (strpbrk(restrn, "\003\004\005\006"))
+                    symb = ";SY(ACHRES61)";
+                else {
+                    if (strpbrk(restrn, "\011\012\013\014\015"))
+                        symb = ";SY(ACHRES71)";
+                    else
+                        symb = ";SY(ACHRES51)";
+                }
+
+
+            } else {
+                if (strpbrk(restrn, "\003\004\005\006")) {
+                    // continuation C
+                    if (strpbrk(restrn, "\011\012\013\014\015"))
+                        symb = ";SY(FSHRES71)";
+                    else
+                        symb = ";SY(FSHRES51)";
+
+
+                } else {
+                    if (strpbrk(restrn, "\011\012\013\014\015"))
+                        symb = ";SY(INFARE51)";
+                    else
+                        symb = ";SY(RSRDEF51)";
+
+                }
+            }
+        }
+
+        rescsp01 = g_string_new(symb);
+    }
+
+    return rescsp01;
+}
+
+static GString *_SEABED01(double drval1, double drval2)
+// Remarks: An area object that is part of the seabed is coloured as necessary according
+// to the mariners selection of two shades, (shallow contour, safety contour,
+// deep contour), or four shades (safety contour only). This requires a decision
+// making process provided by this conditional symbology procedure. Note
+// that this procedure is called as a sub-procedure by other conditional
+// symbology procedures.
+//
+// Note: The requirement to show four depth shades is not mandatory. Also,
+// the requirement to show the shallow pattern is not mandatory. However,
+// both these features are strongly recommended.
+
+// return: is never NULL
+
+{
+    GString *seabed01 = NULL;
+    gboolean shallow  = TRUE;
+    char    *arecol   = ";AC(DEPIT)";
+
+    if (drval1 >= 0.0 && drval2 > 0.0)
+        arecol  = ";AC(DEPVS)";
+
+    if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES)){
+        if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)  &&
+            drval2 >  S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+            arecol  = ";AC(DEPDW)";
+            shallow = FALSE;
+        }
+    } else {
+        if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) &&
+            drval2 >  S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR))
+            arecol  = ";AC(DEPMS)";
+
+            if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)  &&
+                drval2 >  S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
+                arecol  = ";AC(DEPMD)";
+                shallow = FALSE;
+            }
+
+            if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)  &&
+                drval2 >  S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)) {
+                arecol  = ";AC(DEPDW)";
+                shallow = FALSE;
+            }
+
+    }
+
+    seabed01 = g_string_new(arecol);
+
+    if (TRUE==S52_getMarinerParam(S52_MAR_SHALLOW_PATTERN) && TRUE==shallow)
+        g_string_append(seabed01, ";AP(DIAMOND1)");
+
+    return seabed01;
+}
+
+static GString *SOUNDG02 (S57_geo *geo)
+// Remarks: In S-57 soundings are elements of sounding arrays rather than individual
+// objects. Thus this conditional symbology procedure examines each
+// sounding of a sounding array one by one. To symbolize the depth values it
+// calls the procedure SNDFRM02 which in turn translates the depth values
+// into a set of symbols to be shown at the soundings position.
+{
+    guint   npt = 0;
+    double *ppt = NULL;
+
+    if (POINT_T != S57_getObjtype(geo)) {
+        PRINTF("invalid object type (not M_PNT_T)\n");
+        //return NULL;
+        exit(0);
+    }
+
+    S57_getGeoData(geo, 0, &npt, &ppt);
+
+    return _SNDFRM02(geo, ppt[2]);
+}
+
+static GString *_SNDFRM02(S57_geo *geo, double depth_value)
+// Remarks: Soundings differ from plain text because they have to be readable under all
+// circumstances and their digits are placed according to special rules. This
+// conditional symbology procedure accesses a set of carefully designed
+// sounding symbols provided by the symbol library and composes them to
+// sounding labels. It symbolizes swept depth and it also symbolizes for low
+// reliability as indicated by attributes QUASOU and QUAPOS.
+{
+    GString *sndfrm02         = g_string_new("");
+    char    *symbol_prefix    = NULL;
+    GString *tecsoustr        = S57_getAttVal(geo, "TECSOU");
+    char     tecsou[LISTSIZE] = {'\0'};
+    GString *quasoustr        = S57_getAttVal(geo, "QUASOU");
+    char     quasou[LISTSIZE] = {'\0'};
+    GString *statusstr        = S57_getAttVal(geo, "STATUS");
+    char     status[LISTSIZE] = {'\0'};
+    double   leading_digit    = 0.0;
+
+    // FIXME: test to fix the rounding error (!?)
+    depth_value  += (depth_value > 0.0)? 0.01: -0.01;
+    leading_digit = (int) depth_value;
+
+    if (depth_value <= S52_getMarinerParam(S52_MAR_SAFETY_DEPTH))
+        symbol_prefix = "SOUNDS";
+    else
+        symbol_prefix = "SOUNDG";
+
+    if (NULL != tecsoustr) {
+        _parseList(tecsoustr->str, tecsou);
+        if (strpbrk(tecsou, "\006"))
+            g_string_sprintfa(sndfrm02, ";SY(%sB1)", symbol_prefix);
+    }
+
+    if (NULL != quasoustr) _parseList(quasoustr->str, quasou);
+    if (NULL != statusstr) _parseList(statusstr->str, status);
+
+    if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022"))
+            g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix);
+    else {
+        GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+        int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+
+        if (NULL != quaposstr) {
+            if (2 <= quapos && quapos < 10)
+                g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix);
+        }
+    }
+
+    // Continuation A
+    if (depth_value < 10.0) {
+        // can be above water (negative)
+        int fraction = (int)ABS((depth_value - leading_digit)*10);
+
+        g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)ABS(leading_digit));
+        g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, fraction);
+
+        // above sea level (negative)
+        if (depth_value < 0.0)
+            g_string_sprintfa(sndfrm02, ";SY(%sA1)", symbol_prefix);
+
+        return sndfrm02;
+    }
+
+    if (depth_value < 31.0) {
+        double fraction = depth_value - leading_digit;
+
+        if (fraction != 0.0) {
+            fraction = fraction * 10;
+            // FIXME: use modulus '%' instead of '/'  --check this at 100m too
+            if (leading_digit >= 10.0)
+                g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)leading_digit/10);
+
+            g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)leading_digit);
+            g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, (int)fraction);
+
+            return sndfrm02;
+        }
+    }
+
+    // Continuation B
+    depth_value = leading_digit;    // truncate to integer
+    if (depth_value < 100.0) {
+        double first_digit = leading_digit / 10;
+        double secnd_digit = leading_digit - (first_digit * 10);
+
+        g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)first_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)secnd_digit);
+
+        return sndfrm02;
+    }
+
+    if (depth_value < 1000.0) {
+        double first_digit = leading_digit / 100;
+        double secnd_digit = leading_digit - (first_digit * 100);
+        double third_digit = leading_digit - (first_digit * 100) - (secnd_digit * 10);
+
+        g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit);
+
+        return sndfrm02;
+    }
+
+    if (depth_value < 10000.0) {
+        double first_digit = leading_digit / 1000;
+        double secnd_digit = leading_digit - (first_digit * 1000);
+        double third_digit = leading_digit - (first_digit * 1000) - (secnd_digit * 100);
+        double last_digit  = leading_digit - (first_digit * 1000) - (secnd_digit * 100) - (third_digit * 100) ;
+
+        g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit);
+
+        return sndfrm02;
+    }
+
+    // Continuation C
+    {
+        double first_digit  = leading_digit / 10000;
+        double secnd_digit  = leading_digit - (first_digit * 10000);
+        double third_digit  = leading_digit - (first_digit * 10000) - (secnd_digit * 1000);
+        double fourth_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) ;
+        double last_digit   = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) - (fourth_digit * 10) ;
+
+        g_string_sprintfa(sndfrm02, ";SY(%s3%1i)", symbol_prefix, (int)first_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)secnd_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)third_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)fourth_digit);
+        g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit);
+
+        return sndfrm02;
+    }
+
+    return sndfrm02;
+}
+
+static GString *TOPMAR01 (S57_geo *geo)
+// Remarks: Topmark objects are to be symbolized through consideration of their
+// platforms e.g. a buoy. Therefore this conditional symbology procedure
+// searches for platforms by looking for other objects that are located at the
+// same position.. Based on the finding whether the platform is rigid or
+// floating, the respective upright or sloping symbol is selected and presented
+// at the objects location. Buoy symbols and topmark symbols have been
+// carefully designed to fit to each other when combined at the same position.
+// The result is a composed symbol that looks like the traditional symbols the
+// mariner is used to.
+{
+    GString *topshpstr = S57_getAttVal(geo, "TOPSHP");
+    GString *topmar    = NULL;
+    char    *sy        = NULL;
+
+    if (NULL == topshpstr)
+        sy = ";SY(QUESMRK1)";
+    else {
+        int floating    = FALSE; // not a floating platform
+        int topshp      = (NULL==topshpstr) ? 0 : atoi(topshpstr->str);
+
+        if (TRUE == _atPtPos(geo, FLOATLIST))
+            floating = TRUE;
+        else
+            // FIXME: this test is wierd since it doesn't affect 'floating'
+            if (TRUE == _atPtPos(geo, RIGIDLIST))
+                floating = FALSE;
+
+
+        if (floating) {
+            // floating platform
+            switch (topshp) {
+                case 1 : sy = ";SY(TOPMAR02)"; break;
+                case 2 : sy = ";SY(TOPMAR04)"; break;
+                case 3 : sy = ";SY(TOPMAR10)"; break;
+                case 4 : sy = ";SY(TOPMAR12)"; break;
+
+                case 5 : sy = ";SY(TOPMAR13)"; break;
+                case 6 : sy = ";SY(TOPMAR14)"; break;
+                case 7 : sy = ";SY(TOPMAR65)"; break;
+                case 8 : sy = ";SY(TOPMAR17)"; break;
+
+                case 9 : sy = ";SY(TOPMAR16)"; break;
+                case 10: sy = ";SY(TOPMAR08)"; break;
+                case 11: sy = ";SY(TOPMAR07)"; break;
+                case 12: sy = ";SY(TOPMAR14)"; break;
+
+                case 13: sy = ";SY(TOPMAR05)"; break;
+                case 14: sy = ";SY(TOPMAR06)"; break;
+                case 17: sy = ";SY(TMARDEF2)"; break;
+                case 18: sy = ";SY(TOPMAR10)"; break;
+
+                case 19: sy = ";SY(TOPMAR13)"; break;
+                case 20: sy = ";SY(TOPMAR14)"; break;
+                case 21: sy = ";SY(TOPMAR13)"; break;
+                case 22: sy = ";SY(TOPMAR14)"; break;
+
+                case 23: sy = ";SY(TOPMAR14)"; break;
+                case 24: sy = ";SY(TOPMAR02)"; break;
+                case 25: sy = ";SY(TOPMAR04)"; break;
+                case 26: sy = ";SY(TOPMAR10)"; break;
+
+                case 27: sy = ";SY(TOPMAR17)"; break;
+                case 28: sy = ";SY(TOPMAR18)"; break;
+                case 29: sy = ";SY(TOPMAR02)"; break;
+                case 30: sy = ";SY(TOPMAR17)"; break;
+
+                case 31: sy = ";SY(TOPMAR14)"; break;
+                case 32: sy = ";SY(TOPMAR10)"; break;
+                case 33: sy = ";SY(TMARDEF2)"; break;
+                default: sy = ";SY(TMARDEF2)"; break;
+            }
+        } else {
+            // not a floating platform
+            switch (topshp) {
+                case 1 : sy = ";SY(TOPMAR22)"; break;
+                case 2 : sy = ";SY(TOPMAR24)"; break;
+                case 3 : sy = ";SY(TOPMAR30)"; break;
+                case 4 : sy = ";SY(TOPMAR32)"; break;
+
+                case 5 : sy = ";SY(TOPMAR33)"; break;
+                case 6 : sy = ";SY(TOPMAR34)"; break;
+                case 7 : sy = ";SY(TOPMAR85)"; break;
+                case 8 : sy = ";SY(TOPMAR86)"; break;
+
+                case 9 : sy = ";SY(TOPMAR36)"; break;
+                case 10: sy = ";SY(TOPMAR28)"; break;
+                case 11: sy = ";SY(TOPMAR27)"; break;
+                case 12: sy = ";SY(TOPMAR14)"; break;
+
+                case 13: sy = ";SY(TOPMAR25)"; break;
+                case 14: sy = ";SY(TOPMAR26)"; break;
+                case 15: sy = ";SY(TOPMAR88)"; break;
+                case 16: sy = ";SY(TOPMAR87)"; break;
+
+                case 17: sy = ";SY(TMARDEF1)"; break;
+                case 18: sy = ";SY(TOPMAR30)"; break;
+                case 19: sy = ";SY(TOPMAR33)"; break;
+                case 20: sy = ";SY(TOPMAR34)"; break;
+
+                case 21: sy = ";SY(TOPMAR33)"; break;
+                case 22: sy = ";SY(TOPMAR34)"; break;
+                case 23: sy = ";SY(TOPMAR34)"; break;
+                case 24: sy = ";SY(TOPMAR22)"; break;
+
+                case 25: sy = ";SY(TOPMAR24)"; break;
+                case 26: sy = ";SY(TOPMAR30)"; break;
+                case 27: sy = ";SY(TOPMAR86)"; break;
+                case 28: sy = ";SY(TOPMAR89)"; break;
+
+                case 29: sy = ";SY(TOPMAR22)"; break;
+                case 30: sy = ";SY(TOPMAR86)"; break;
+                case 31: sy = ";SY(TOPMAR14)"; break;
+                case 32: sy = ";SY(TOPMAR30)"; break;
+                case 33: sy = ";SY(TMARDEF1)"; break;
+                default: sy = ";SY(TMARDEF1)"; break;
+            }
+        }
+
+    }
+
+    topmar = g_string_new(sy);
+
+    return topmar;
+}
+
+static GString *_UDWHAZ03(S57_geo *geo, double depth_value)
+// Remarks: Obstructions or isolated underwater dangers of depths less than the safety
+// contour which lie within the safe waters defined by the safety contour are
+// to be presented by a specific isolated danger symbol as hazardous objects
+// and put in IMO category DISPLAYBASE (see (3), App.2, 1.3). This task
+// is performed by this conditional symbology procedure.
+{
+    GString *udwhaz03str    = NULL;
+    int      danger         = FALSE;
+    double   safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
+
+    if (depth_value <= safety_contour) {
+        // that intersect this point/line/area for OBSTRN04
+        // that intersect this point/area      for WRECKS02
+        S57_geo *geoTmp = geo;
+
+        //  collect area/line DEPARE & area DRGARE that touche this point/line/area
+        S57_ogrTouche(geoTmp, N_OBJ_T);
+        while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
+
+            if (LINES_T  == S57_getObjtype(geoTmp)) {
+                GString *drval2str = S57_getAttVal(geoTmp, "DRVAL2");
+                double   drval2    = (NULL == drval2str) ? 0.0 : atof(drval2str->str);
+
+                if (drval2 > safety_contour) {
+                    danger = TRUE;
+                    break;
+                }
+
+            } else {
+                // area DEPARE or DRGARE
+                GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
+                double   drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
+
+                if (drval1 >= safety_contour) {
+                    danger = TRUE;
+                    break;
+                }
+            }
+
+        }
+        S57_unlinkObj(geo);
+ 
+
+
+        //danger = TRUE;   // true
+        if (TRUE == danger) {
+            GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+            if (NULL != watlevstr && ('1' == *watlevstr->str || '2' == *watlevstr->str))
+                udwhaz03str = g_string_new(";OP(--D14050");
+            else {
+                udwhaz03str = g_string_new(";OP(8OD14010);SY(ISODGR01)");
+                S57_setAtt(geo, "SCAMIN", "INFINITE");
+            }
+        }
+    }
+
+    return udwhaz03str;
+}
+
+static GString *VESSEL01 (S57_geo *geo)
+// Remarks: The mariner should be prompted to select from the following options:
+// - ARPA target or AIS report (overall decision or vessel by vessel) (vesrce)
+// - *time-period determining vector-length for all vectors (vecper)
+// - whether to show a vector (overall or vessel by vessel) (vestat)
+// - *whether to symbolize vector stabilization (vecstb)
+// - *whether to show one-minute or six-minute vector time marks (vecmrk)
+// - whether to show heading line on AIS vessel reports (headng)
+// * Note that the same vector parameters should be used for own-ship and all vessel
+// vectors.
+{
+    PRINTF("Mariner's object not drawn\n");
+    return NULL;
+}
+
+static GString *VRMEBL01 (S57_geo *geo)
+// Remarks: This conditional symbology procedure symbolizes the three cases of range
+// circle, bearing line and range/bearing line. VRM's and EBL's can be ship-centred
+// or freely movable, and two line-styles are available
+{
+    PRINTF("Mariner's object not drawn\n");
+    return NULL;
+}
+
+static GString *WRECKS02 (S57_geo *geo)
+// Remarks: Wrecks of depths less than the safety contour which lie within the safe waters
+// defined by the safety contour are to be presented by a specific isolated
+// danger symbol and put in IMO category DISPLAYBASE (see (3), App.2,
+// 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is
+// called by this symbology procedure.
+{
+    GString *wrecks02str = NULL;
+    GString *sndfrm02str = NULL;
+    GString *udwhaz03str = NULL;
+    GString *quapnt01str = NULL;
+    double   least_depth = UNKNOWN;
+    double   depth_value = UNKNOWN;
+    GString *valsoustr   = S57_getAttVal(geo, "VALSOU");
+    double   valsou      = UNKNOWN;
+
+    // exit if not in drawing state
+    //if (1 == S52_state)
+    //    return NULL;
+
+    if (NULL != valsoustr) {
+        valsou      = atof(valsoustr->str);
+        depth_value = valsou;
+        sndfrm02str = _SNDFRM02(geo, depth_value);
+    } else {
+        if (AREAS_T == S57_getObjtype(geo))
+            least_depth = _DEPVAL01(geo, least_depth);
+
+        if (least_depth == UNKNOWN) {
+            // WARNING: ambiguity removed in WRECKS03 (see update)
+            GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+            GString *catwrkstr = S57_getAttVal(geo, "CATWRK");
+
+            if (NULL == watlevstr) // default
+                depth_value = -15.0;
+            else
+                switch (*watlevstr->str) { // ambiguous
+                    case '1':
+                    case '2': depth_value = -15.0 ; break;
+                    case '3': depth_value =   0.01; break;
+                    case '4': depth_value = -15.0 ; break;
+                    case '5': depth_value =   0.0 ; break;
+                    case '6': depth_value = -15.0 ; break;
+                    default :{
+                        if (NULL != catwrkstr) {
+                            switch (*catwrkstr->str) {
+                                case '1': depth_value =  20.0; break;
+                                case '2': depth_value =   0.0; break;
+                                case '4':
+                                case '5': depth_value = -15.0; break;
+                            }
+                        }
+                    }
+                }
+        } else
+            depth_value = least_depth;
+
+
+    }
+
+    udwhaz03str = _UDWHAZ03(geo, depth_value);
+    quapnt01str = _QUAPNT01(geo);
+
+    if (POINT_T == S57_getObjtype(geo)) {
+        if (NULL != udwhaz03str) {
+            wrecks02str = g_string_new(udwhaz03str->str);
+
+            if (NULL != quapnt01str)
+                g_string_append(wrecks02str, quapnt01str->str);
+
+        } else {
+            // Continuation A (POINT_T)
+            if (UNKNOWN != valsou) {
+
+                if (valsou <= 20.0) {
+                    wrecks02str = g_string_new(";SY(DANGER01)");
+                    if (NULL != sndfrm02str)
+                        g_string_append(wrecks02str, sndfrm02str->str);
+
+                } else
+                    wrecks02str = g_string_new(";SY(DANGER02)");
+
+                if (NULL != udwhaz03str)
+                    g_string_append(wrecks02str, udwhaz03str->str);
+                if (NULL != quapnt01str)
+                    g_string_append(wrecks02str, quapnt01str->str);
+
+            } else {
+                char    *sym       = NULL;
+                GString *catwrkstr = S57_getAttVal(geo, "CATWRK");
+                GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+                if (NULL!=catwrkstr && NULL!=watlevstr) {
+                    if ('1' == *catwrkstr->str && '3' == *watlevstr->str)
+                        sym =";SY(WRECKS04)";
+                    else
+                        if ('2' == *catwrkstr->str && '3' == *watlevstr->str)
+                            sym = ";SY(WRECKS05)";
+                } else {
+                    if (NULL!=catwrkstr && ('4' == *catwrkstr->str || '5' == *catwrkstr->str)) {
+                        sym = ";SY(WRECKS01)";
+                    } else {
+                        if (NULL != watlevstr) {
+                            if ('1' == *watlevstr->str ||
+                                '2' == *watlevstr->str ||
+                                '5' == *watlevstr->str ||
+                                '4' == *watlevstr->str )
+                                sym = ";SY(WRECKS01)";
+                        } else
+                            sym = ";SY(WRECKS05)"; // default
+
+                    }
+                }
+
+                wrecks02str = g_string_new(sym);
+                if (NULL != quapnt01str)
+                    g_string_append(wrecks02str, quapnt01str->str);
+
+            }
+
+        }
+
+
+    } else {
+        // Continuation B (AREAS_T)
+        GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
+        int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
+        char    *line      = NULL;
+
+        if (2 <= quapos && quapos < 10)
+            line = ";LC(LOWACC41)";
+        else {
+            if ( NULL != udwhaz03str)
+                line = ";LS(DOTT,2,CHBLK)";
+            else {
+                 if (UNKNOWN != valsou){
+                     if (valsou <= 20)
+                         line = ";LS(DOTT,2,CHBLK)";
+                     else
+                         line = ";LS(DASH,2,CHBLK)";
+                 } else {
+                     GString  *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+                     if (NULL == watlevstr)
+                         line = ";LS(DOTT,2,CSTLN)";
+                     else {
+                         switch (*watlevstr->str){
+                             case '1':
+                             case '2': line = ";LS(SOLD,2,CSTLN)"; break;
+                             case '4': line = ";LS(DASH,2,CSTLN)"; break;
+                             case '3':
+                             case '5':
+
+                             default : line = ";LS(DOTT,2,CSTLN)"; break;
+                         }
+                     }
+
+                 }
+            }
+        }
+        wrecks02str = g_string_new(line);
+
+        if (UNKNOWN != valsou) {
+            if (valsou <= 20) {
+                if (NULL != udwhaz03str)
+                    g_string_append(wrecks02str, udwhaz03str->str);
+
+                if (NULL != quapnt01str)
+                    g_string_append(wrecks02str, quapnt01str->str);
+                
+                if (NULL != sndfrm02str)
+                    g_string_append(wrecks02str, sndfrm02str->str);
+
+            } else {
+                // NOTE: ??? same as above ???
+                if (NULL != udwhaz03str)
+                    g_string_append(wrecks02str, udwhaz03str->str);
+
+                if (NULL != quapnt01str)
+                    g_string_append(wrecks02str, quapnt01str->str);
+            }
+        } else {
+            char    *ac        = NULL;
+            GString *watlevstr = S57_getAttVal(geo, "WATLEV");
+
+            if (NULL == watlevstr)
+                ac = ";AC(DEPVS)";
+            else
+                switch (*watlevstr->str) {
+                    case '1':
+                    case '2': ac = ";AC(CHBRN)"; break;
+                    case '4': ac = ";AC(DEPIT)"; break;
+                    case '5':
+                    case '3':
+                    default : ac = ";AC(DEPVS)"; break;
+                }
+
+            g_string_append(wrecks02str, ac);
+
+            if (NULL != udwhaz03str)
+                g_string_append(wrecks02str, udwhaz03str->str);
+
+            if (NULL != quapnt01str)
+                g_string_append(wrecks02str, quapnt01str->str);
+        }
+    }
+
+    if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
+    if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
+    if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
+          
+    return wrecks02str;
+}
+
+
+//--------------------------------
+//
+// JUMP TABLE SECTION
+//
+//--------------------------------
+
+
+CondSymb condTable[] = {
+   // name      call            Sub-Procedure
+   {"CLRLIN01", CLRLIN01},   //
+   {"DATCVR01", DATCVR01},   //
+   {"DEPARE01", DEPARE01},   // _RESCSP01, _SEABED01
+   {"DEPCNT02", DEPCNT02},   //
+   {"LEGLIN02", LEGLIN02},   //                          str
+   {"LIGHTS05", LIGHTS05},   // _LITDSN01
+   {"OBSTRN04", OBSTRN04},   // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03
+   {"OWNSHP02", OWNSHP02},   //
+   {"PASTRK01", PASTRK01},   //
+   {"QUAPOS01", QUAPOS01},   // _QUALIN01, _QUAPNT01
+   {"SLCONS03", SLCONS03},   //
+   {"RESARE02", RESARE02},   //
+   {"RESTRN01", RESTRN01},   // _RESCSP01
+   {"SOUNDG02", SOUNDG02},   // _SNDFRM02
+   {"TOPMAR01", TOPMAR01},   //
+   {"VESSEL01", VESSEL01},   //
+   {"VRMEBL01", VRMEBL01},   //
+   {"WRECKS02", WRECKS02},   // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03
+
+   {"########",NULL}
+};
+

Added: packages/openev/branches/upstream/current/contrib/S52/S52CS.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52CS.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52CS.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,55 @@
+// S52cs.h: interface to Conditional Symbology (CS) instruction
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview porject, a viewer of ENC.
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S52CS_H_
+#define _S52CS_H_
+
+#include "S57data.h"       // S57_geo
+#include <glib.h>           // GString, GData
+
+// get/set state LOAD or RENDER
+extern int   S52_state;
+
+// Cond. Symb. callback
+// WARNING: caller of the callback has to free the returned GString
+typedef GString *(*CScb_t)(S57_geo *geoData);
+
+// Conditional Symbologie
+typedef struct _CondSymb {
+    char    *name;         // [9] '\0' terminated !?
+    CScb_t   CScb;
+} CondSymb;
+
+extern CondSymb condTable[];
+
+extern char *S52_CS_version();
+
+extern int   S52_CS_init();
+
+extern int   S52_CS_done();
+
+// set position of floating and rigid platform
+extern int   S52_CS_setPtPos(S57_geo *geoData, char *name);
+
+#endif //_S52CS_H_

Added: packages/openev/branches/upstream/current/contrib/S52/S52GL.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52GL.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52GL.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,3791 @@
+// S52GL.c: display S57 data using S52 symbology and OpenGL.
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC.
+    Copyright (C) 2000-2004 Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+//#define INDEXMODE
+
+#include "S52GL.h"
+#include "S52utils.h"       // S52_getConfig(), PRINTF()
+
+#ifdef S52_USE_OSG
+    #include "S52OSG.h"     // glDrawArrays(), ...
+#else
+    #include <GL/gl.h>
+#endif
+
+#include <GL/glu.h>         // tess, quadric,...
+#include <GL/glx.h>         // glXUseXFont()
+#include <stdlib.h>         // atoi()
+#include <string.h>         // strncmp()
+#include <math.h>           // ceil()
+#include <assert.h>         // assert()
+
+#include <glib.h>
+
+// FIXME: mutex this!
+// experimental --cursor pick
+GLdouble     dx         = 3.0;
+GLdouble     dy         = 3.0;
+GStaticMutex sobj_mutex = G_STATIC_MUTEX_INIT;
+
+
+static GLenum _modes[8] = {GL_RENDER};  // assume we're rendering
+static int    _topMode  = 0;
+
+#include <X11/Xcms.h>
+#include <X11/Intrinsic.h>
+
+static Display  *_display    = 0;
+
+// debug --global for now, wiil move to a per-view struct
+// static
+Colormap  _cmap       = 0;
+
+static int       currentScreen = 0;
+static S52_obj   *_M_COVR      = NULL;
+
+/////////////////////////////////////////////////////
+// object rasterazing cmd
+/*
+typedef struct instruction{
+    // !!! data common to all oject of this class with same attributes
+    S52_LUP      *LUP;
+
+    // !!! data specific to this object
+    GString      *CSinst;     // LUP Cond. Symb. inst. command list (per object)
+    S52_CmdL     *cmdList;    // LUP list of command word (per object)
+    S52_Text     *text;       // text formating info
+    S57_geo      *geoData;    // coordinate & attributes
+
+} instruction;
+*/
+
+typedef struct _glCtx {
+    GArray *dispList;   // hold names of lists for this context
+} _glCtx;
+
+static S52_diPrio   _dprio = 0;  // current dip. prio.
+static GPtrArray   *instrucL[S52_PRIO_NUM][S52_LUP_NUM];//[RAD_NUM];
+static GPtrArray   *switchPrio = NULL;
+static int          _redraw    = FALSE; // TRUE will force redraw
+
+
+//////////////////////////////////////////////////////
+// tessalisation & quadric
+static GLUtriangulatorObj *tobj = NULL;
+static GLUquadricObj      *qobj = NULL;
+static GPtrArray          *tmpV = NULL;  // place holder during tesssalation
+
+// display list
+static int     patternList = FALSE;     // switch mode flag in _renderHPGL
+static GLuint  _listIndex  = 0;         // GL display index (0==error)
+
+// transparency factor
+#define TRNSP_FAC   255 * 0.25
+
+////////////////////////////////////////////////
+// feedback buffer object
+typedef struct _feedbackObj {
+    int       n;         // nbr of floats in buf
+    GLsizei   sz;        // size of buf
+    GLsizei   hint;      // nbr of pt that *will* be requested
+    int       cnt;       // number of floats read
+    //GLfloat *buf;      // feedback buffer
+    GLdouble *buf;       // feedback buffer
+    GLfloat   tok;       // current token (see note)
+    gboolean  read_tok;  // TRUE read next buffer value as token
+    int       poly_n;    // total number of vertex in polygon
+    int       poly_cnt;  // number of polygon vertex read
+    int       nvalue;    // number of value per vertex GL_2D/GL_3D
+    gboolean  eob;       // TRUE if end of buffer reached
+
+} fObj;
+static fObj fobj;
+
+#define FB_EMPTY_TOK        0x0000
+
+/* NOTE: tok is 0x0000 for no value (FB_EMPTY_TOK)
+ or one of:
+
+ GL_POINT_TOKEN             0x0701
+ GL_LINE_TOKEN              0x0702
+ GL_LINE_RESET_TOKEN        0x0707
+ GL_POLYGON_TOKEN           0x0703
+ GL_BITMAP_TOKEN            0x0704
+ GL_DRAW_PIXEL_TOKEN        0x0705
+ GL_COPY_PIXEL_TOKEN        0x0706
+ GL_PASS_THROUGH_TOKEN      0x0700
+*/
+
+////////////////////////////////////////////////
+// selection buffer object
+typedef struct _selectionObj {
+    GLsizei    sz;      // size of buf
+    GLuint    *buf;     // selection buffer
+    GLuint    *acc;     // accumulation buffer
+    GLsizei    nac;     // number (of GLuint) accumulated
+    GPtrArray *geoV;    // vector of pointer to object processed
+                        // during selection
+    GLuint     name;
+    GLint      hits;
+
+    // to protect mouse event
+    //GStaticMutex mutex;
+    GLdouble     x;
+    GLdouble     y;
+    int          doPick;
+
+} sObj;
+static sObj sobj;
+
+
+/////////////////////////////////////////////////////
+// projection (PROJ4)
+//static projPJ pj    = NULL;
+//static projPJ pjsrc = NULL;          // projection source
+//static projPJ pjdst = NULL;          // projection destination
+
+static projUV pmin  = {0.0, 0.0};    // projected view extend
+static projUV pmax  = {0.0, 0.0};    // projected view extend
+
+// viewport of current openev gldraw signal
+static GLint vp[4]; // x,y,width,height
+
+typedef enum _VP{   // set GL_PROJECTION matrix to
+    VP_PRJ,         // projected coordinate
+    VP_PRJ_ZCLIP,   // same but short depth volume for line clipping
+    VP_WIN,         // window coordinate
+    VP_REC          // rectangular coord. (deg)
+}VP;
+
+// line mask plane
+#define Z_CLIP_PLANE 10000   // clipped beyon this plane
+
+//////////////////////////////////////////////////////
+// font
+#define PICA   0.351
+static char *_font[] = {
+//    "-*-fixed-*-*-*-*-*-*-*-*-*-*-iso8859-*",
+//    "-*-times-*-*-*-*-20-*-*-*-*-*-iso8859-*",
+//    "-*-times-*-*-*-*-14-*-*-*-*-*-iso8859-*",
+//    "-*-helvetica-medium-r-*-*-*-80-*-*-*-*-iso8859-*",
+
+    "-*-helvetica-medium-r-narrow-*-*-*-*-*-*-*-iso8859-*",
+    "-*-helvetica-medium-r-*-*-17-*-*-*-*-*-iso8859-*",
+    "-*-helvetica-bold-r-*-*-17-*-*-*-*-*-iso8859-*"
+};
+
+static gint          _fontDList[3];
+//#include <gdk/gdk.h>
+//static GdkFont      *gdkfont    = NULL;
+//static GdkGLContext *_glcontext = NULL;
+//static GdkVisual    *_visual;
+//static GvViewArea   *view; // already commented out by SD
+//static GtkWidget    *view;
+
+// debug
+//static int segNo    = 1;
+static int countseg = 0;
+static int found    = 0;
+
+// debug GL crash
+//extern GMutex * mutex;
+
+// assume round pixel (mm)
+// FIXME: get dot pitch from system (?)
+static double dotpitch_x = 0.3;
+static double dotpitch_y = 0.3;
+static double _scale     = 0.0;
+
+typedef GLdouble coor3d[3];
+typedef struct _pt3{ GLdouble x,y,z; } pt3;
+typedef struct _pt2{ GLdouble x,y;   } pt2;
+
+// pattern reference point, all pattern are aligned to this point
+// so that pattern cliped on adjacent area are complete
+//typedef struct _point { double x,y; } Point;
+//static Point patt_ref = {0.0, 0.0};
+
+// line mask
+static GTree *_vertexSet = NULL;
+static int    _loadV     = 1;    // will load vertex
+
+// assoc object to hit record
+static GPtrArray *_objHitVec = NULL;    // hold object used during SELECT
+
+//-----------------------------------
+//
+// GL TESS, QUADRIC, ...  SECTION
+//
+//-----------------------------------
+
+static GLvoid    _checkError(char *msg)
+{
+    GLenum err = glGetError();
+    if (GL_NO_ERROR != err) {
+        char *name;
+
+        switch(err) {
+            case GL_INVALID_ENUM:      name = "GL_INVALID_ENUM";      break;
+            case GL_INVALID_VALUE:     name = "GL_INVALID_VALUE";     break;
+            case GL_INVALID_OPERATION: name = "GL_INVALID_OPERATION"; break;
+            default:
+                name = "GL_NO_ERROR";
+        }
+
+        PRINTF("from %s: %s (%d:%s)\n", msg, gluErrorString(err), err, name);
+        exit(0);
+    }
+}
+
+static GLvoid    _tessError(GLenum err)
+{
+
+//  int len,i;
+    const GLubyte *str = gluErrorString(err);
+    PRINTF("%s (%d)\n", str,err);
+
+    exit(0);
+
+//  glColor3f(0.9,0.9,0.9);
+//  glRasterPos2i(5,5);
+//  len = strlen(str);
+//  for(i=0; i<len; i++)
+//      glutBitmapCharacter(GLUT_BITMAP_9_BY_15, str[i]);
+
+}
+
+static GLvoid    _quadricError(GLenum err)
+{
+
+    const GLubyte *str = gluErrorString(err);
+
+    PRINTF("%s (%d) (%0x)\n", str,err, err);
+    //exit(0);
+
+    /*
+    {
+          int len,i;
+          glColor3f(0.9,0.9,0.9);
+          glRasterPos2i(5,5);
+          len = strlen(str);
+          for(i=0; i<len; i++)
+              glutBitmapCharacter(GLUT_BITMAP_9_BY_15,str[i]);
+    }
+    */
+}
+
+static GLvoid    _glEdgeFlag(GLboolean flag)
+{
+   //printf("_glEdgeFlag() called\n");
+   //glEdgeFlag(flag);
+   //glEdgeFlag(GL_FALSE);
+}
+
+
+static GLvoid    _combineCallback(GLdouble   coords[3],
+                               GLdouble  *vertex_data[4],
+                               GLfloat    weight[4],
+                               GLdouble **dataOut )
+{
+    // optimisation: alloc once --keep pos. of current index
+    pt3 *p = g_new(pt3, 1);
+    p->x   = coords[0];
+    p->y   = coords[1];
+    p->z   = coords[2];
+    *dataOut = (GLdouble*)p;
+    g_ptr_array_add(tmpV, (gpointer) p);
+
+/*
+    if (intess) {
+        pt3 *p = (pt3*) *dataOut; //coords;
+        PRINTF("combineCallback():");
+        printf("%f %f %f \n", p->x, p->y, p->z);
+        //printf("_combineCallback exiting ..\n");
+        //exit(0);
+    }
+*/
+
+}
+
+static GLvoid    _glBegin(GLenum data, S57_prim *prim)
+{
+    S57_begPrim(prim, data);
+}
+
+static GLvoid    _glEnd(S57_prim *prim)
+{
+    S57_endPrim(prim);
+}
+
+static GLvoid    _vertex3d(GLvoid *data, S57_prim *prim)
+{
+
+    //pt3 p = *(pt3*) data;
+
+    //p.z = (double) (_dprio /100000.0);
+
+    S57_addPrimVertex(prim, data);
+    //S57_addPrimVertex(prim, &p);
+
+    //printf("%f %f %f \n", p->x, p->y, p->z);
+}
+
+static int       _Xerror(Display *display, XErrorEvent* err)
+{
+  
+  char buf[80];
+  XGetErrorText(display, err->error_code, buf, 80);
+  
+  PRINTF("*** X error *** %d 0x%x %ld %d %d\n",
+         err->type , 
+         (unsigned int)err->resourceid,
+         (long int)err->error_code ,
+         (int)err->request_code,
+         err->minor_code
+         );
+  PRINTF("txt: %s\n", buf);
+    return 1;
+}
+
+#ifdef INDEXMODE
+static int  initScreen[] = {0,0};
+#endif
+
+static int       _toRGB(Display *dpy, Colormap cmap, GArray *colorTbl)
+{
+    int i = 0;
+
+    //int status = 0;
+    //unsigned long newCell, dummy1;
+    const int MAX_FREE_COL = 200;
+    unsigned long numFreeColors[MAX_FREE_COL];
+    //XColor colorss[150];
+    memset(numFreeColors, 0, sizeof(numFreeColors[MAX_FREE_COL]));
+
+    // test
+#ifdef INDEXMODE
+    if(initScreen[DefaultScreen(dpy)])
+        return 1;
+    else
+        initScreen[DefaultScreen(dpy)] += 1;
+#endif
+
+
+    for (i=0; i<colorTbl->len; ++i) {
+        char s[64];
+        S52_Color *c = &g_array_index(colorTbl, S52_Color, i);
+        Status     sta = 0;
+        XcmsColor  color_in_out;
+        XColor     color_exact_return;
+        XColor     color_screen_return;
+
+        // xyL --> XYZ
+        float Y = c->L;
+        float X = (c->x/c->y) * Y;
+        float Z = ((1 - c->x - c->y)/c->y) * Y;
+
+        color_in_out.pixel  = 0;
+        color_in_out.format = XcmsCIEXYZFormat;
+        color_in_out.spec.CIEXYZ.X = X/100.0;
+        color_in_out.spec.CIEXYZ.Y = Y/100.0;
+        color_in_out.spec.CIEXYZ.Z = Z/100.0;
+
+        sprintf(s, "CIEXYZ:%f/%f/%f",
+                color_in_out.spec.CIEXYZ.X,
+                color_in_out.spec.CIEXYZ.Y,
+                color_in_out.spec.CIEXYZ.Z);
+
+        //sta = XcmsQueryColor(dpy, cmap, &color_in_out, XcmsRGBFormat);
+        sta = XLookupColor(dpy, _cmap, s, &color_exact_return, &color_screen_return);
+
+#ifdef INDEXMODE
+
+        status  = XAllocColorCells(display, _cmap, 1, &dummy1, 0, &newCell, 1);
+        if (status == 0) 
+          printf("COULD NOT ALLOCATE !! \n");
+        else
+          ; //printf("Allocated cell: %d \n", newCell);
+        colorss[i] = color_exact_return;
+
+        colorss[i].pixel = newCell;
+        // TODO check on status
+//            int a = XAllocColor(dpy, cmap, &color_exact_return);
+//        printf("Setting %s: idx: %d \n", c->colName, color_exact_return.pixel);
+        c->idx[currentScreen] = colorss[i].pixel;
+        printf("Setting %s: idx: %ld \n", c->colName, colorss[i].pixel);
+#endif        
+        if (XcmsFailure == sta)
+            PRINTF("lookup color failure\n");
+        else {
+            c->R = (guchar) rint((color_exact_return.red   + 1)/256);
+            c->G = (guchar) rint((color_exact_return.green + 1)/256);
+            c->B = (guchar) rint((color_exact_return.blue  + 1)/256);
+//            c->idx = color_exact_return.pixel;
+
+#ifdef INDEXMODE
+            c->idx = colorss[i].pixel;
+            printf("Setting %s:  RGB: %d %d %d idx: %dn", c->colName,  c->R, c->G, c->B, c->idx);
+#else 
+            c->idx = 0;
+//            printf("Setting %s: idx: %d RGB: %d %d %d \n", c->colName, c->R, c->G, c->B);
+#endif
+        }
+    }
+
+#ifdef INDEXMODE
+    XStoreColors(dpy, _cmap, colorss,i );
+    XInstallColormap(dpy, _cmap);
+    printf("Using and storing into Cmap: 0x%x\n", _cmap);
+#endif
+    
+    return 1;
+}
+
+static GLint     _initGLobj()
+// initialize various GL object
+// replace glcontext if current one not valid
+// if NULL assume that the caller has a valid context
+{
+    typedef void(*f)();
+
+    ////////////////////////////////////////////////////////////////
+    //
+    // init tess stuff
+    //
+    {
+        // hold vertex comming from GLU_TESS_COMBINE callback
+        //tmpV = g_array_new(FALSE, FALSE, sizeof(coor3d));
+        tmpV = g_ptr_array_new();
+
+        tobj = gluNewTess();
+
+        gluTessCallback(tobj, GLU_TESS_BEGIN_DATA, (f) _glBegin);
+        gluTessCallback(tobj, GLU_TESS_END_DATA,   (f) _glEnd);
+        gluTessCallback(tobj, GLU_TESS_ERROR,      (f) _tessError);
+        gluTessCallback(tobj, GLU_TESS_VERTEX_DATA,(f) _vertex3d);
+        gluTessCallback(tobj, GLU_TESS_COMBINE,    (f) _combineCallback);
+        // NOTE: _not_ NULL to trigger GL_TRIANGLES tessallation
+        //gluTessCallback(tobj, GLU_TESS_EDGE_FLAG,  (f) glEdgeFlag);
+        //gluTessCallback(tobj, GLU_TESS_EDGE_FLAG,  (f) _glEdgeFlag);
+        gluTessCallback(tobj, GLU_TESS_EDGE_FLAG,  (f) NULL);
+
+
+        // no GL_LINE_LOOP
+        //gluTessProperty(tobj, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
+
+        //gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
+        // ODD needed for symb. ISODGR01
+        gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+        //gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
+        //gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE);
+
+        // set poly in x-y plane normal is Z (for performance)
+        gluTessNormal(tobj, 0.0, 0.0, 1.0);
+    }
+
+    ///////////////////////////////////////////////////////////////
+    //
+    // misc GL stuff
+    //
+    {
+        g_assert(sizeof(double) == sizeof(GLdouble));
+       
+        glShadeModel(GL_FLAT);   // same color between vertex
+
+        //glEnable(GL_LINE_SMOOTH);  // antialiase needed for line stippled
+    }
+
+    ////////////////////////////////////////////////////////////////
+    //
+    // init quadric stuff
+    //
+    {
+        qobj = gluNewQuadric();
+        gluQuadricCallback(qobj, GLU_ERROR,  (f) _quadricError);
+
+        /*
+         _tessError(0);
+
+         str = gluGetString(GLU_VERSION);
+         printf("GLU version:%s\n",str);
+         str = glGetString(GL_VERSION);
+         printf("GL version:%s\n",str);
+         */
+    }
+
+    /////////////////////////////////////////////////////////////////
+    //
+    // init feedback buffer stuff
+    //
+    {
+        fobj.n        = 0;
+        fobj.sz       = 0;
+        fobj.hint     = 0;
+        fobj.cnt      = 0;
+        fobj.buf      = NULL;
+        fobj.tok      = FB_EMPTY_TOK;
+        fobj.read_tok = TRUE;
+        fobj.poly_n   = 0;
+        fobj.poly_cnt = 0;
+        fobj.nvalue   = 0;
+        fobj.eob      = FALSE;
+    }
+
+    ///////////////////////////////////
+    //
+    // init selection buffer stuff
+    //
+    {
+        sobj.sz     = 4096;
+        sobj.buf    = g_new0(GLuint, sobj.sz);  // hits
+        sobj.acc    = g_new0(GLuint, sobj.sz);  // hits accumulated
+        sobj.nac    = 0;
+        sobj.geoV   = g_ptr_array_new();        // hits index this
+        sobj.name   = 0;
+        sobj.hits   = 0;
+
+        sobj.x      = 0.0;
+        sobj.y      = 0.0;
+        sobj.doPick = FALSE;
+    }
+
+    ////////////////////////////////////
+    //
+    // X stuff
+    //
+
+    {
+      //extern   GtkWidget *win;
+        Window   root    = 0;
+        Status   sta = 0;
+        int      screen  = 0;    //DefaultScreen(display);
+        Visual  *visual  = NULL;
+
+        //XcmsColor cms_color;
+        //        display = GDK_DISPLAY();//GDK_WINDOW_XDISPLAY(win);
+        screen  = DefaultScreen(_display);
+        root    = RootWindow(_display, screen); //GDK_WINDOW_XWINDOW(win);
+
+        {
+            Window root_return;
+            Window parent_return;
+            Window *children_return;
+            unsigned int nchildren_return;
+            XWindowAttributes window_attributes_return;
+            //                                  GTK_WIDGET(win)->window;
+
+            sta = XQueryTree(_display, root, &root_return, &parent_return,
+                             &children_return, &nchildren_return);
+
+            if (sta == BadDrawable)
+                PRINTF("BadDrawable\n");
+            if (sta == BadWindow)
+                PRINTF("BadWindow\n");
+
+            sta = XGetWindowAttributes(_display, root, &window_attributes_return);
+
+            if (sta == BadDrawable)
+                PRINTF("BadDrawable\n");
+            if (sta == BadWindow)
+                PRINTF("BadWindow\n");
+
+            visual  = DefaultVisual(_display, screen);
+
+            {
+                // Colormap cmap;
+                XVisualInfo vinfo_template;
+                XVisualInfo *vinfo;
+                long vinfo_mask = VisualClassMask;
+                //long vinfo_mask = VisualClassMask | VisualColormapSizeMask;
+                int  nitems_return;
+#ifdef INDEXMODE
+                vinfo_template.class = PseudoColor;
+#else
+                vinfo_template.class = DirectColor;
+                //vinfo_template.class = TrueColor;
+#endif
+                // vinfo_mask = VisualIDMask; vinfo_template.visualid = 0x2d;
+               vinfo = XGetVisualInfo(_display, vinfo_mask,
+                                       &vinfo_template, &nitems_return);
+
+                XSetErrorHandler(_Xerror);
+
+                XSynchronize(_display, True);
+
+                // get a direct color visual
+                if (nitems_return > 0) {
+                    if (_cmap == 0) {
+                        // FIXME: scan for one with color map size >= 64
+                        _cmap = XCreateColormap(_display, root,
+                                                vinfo->visual, AllocAll);
+                        // for TrueColor
+                        //_cmap = XCreateColormap(_display, root,
+                        //                        vinfo->visual, AllocNone);
+                    }
+                    XFree(vinfo);
+
+                    _toRGB(_display, _cmap, S52_PL_getColorTable());
+                }
+
+                XSynchronize(_display, False);
+            }
+
+        }
+
+    }
+
+    return 1;
+}
+
+static GLint     _doneGLobj()
+{
+//    int i;
+    g_ptr_array_free(tmpV      , TRUE);
+    g_ptr_array_free(_objHitVec, TRUE);
+
+    gluDeleteTess(tobj);
+    gluDeleteQuadric(qobj);
+
+    // delete feedback buffer object
+    g_free(fobj.buf);
+
+    // selection
+    g_free(sobj.buf);
+    g_free(sobj.acc);
+    g_ptr_array_free(sobj.geoV, TRUE);
+    //g_static_mutex_free(sobj_mutex);
+    return 1;
+}
+
+static int       _initGLmodule()
+// init GL module
+{
+    int i = 0;
+    int j = 0;
+
+    for (i=0; i<S52_PRIO_NUM; ++i) {
+        for (j=0; j<S52_LUP_NUM; ++j)
+            instrucL[i][j] = g_ptr_array_new();
+    }
+
+    switchPrio = g_ptr_array_new();
+
+    if (0 == _initGLobj()) {
+        exit(0);
+    }
+
+    return 1;
+}
+
+
+static double    _get_area(int npt, double *ppt)
+// return area; CW (+) or CCW (-)
+{
+    int     i;
+    double *d = ppt;
+    double  a = 0.0;
+
+    for (i=0; i<npt-1; ++i) {
+        a += *d * *(d+4) - *(d+3) * *(d+1);
+        d += 3;
+        //a = x1 * y2 - x2 * y1;
+    }
+    d+=3;
+    a += *d * *(ppt+1) - *ppt * *(d+1);
+
+    printf("ring: %i = %s\n", i, (a<0)? "CCW":"CW");
+    return a;
+}
+
+static GLint     _tessd(GLUtriangulatorObj *tobj, S52_obj *obj)
+// WARNING: not re-entrant (tmpV)
+{
+    guint i, j;
+    S57_geo  *geoData = S52_PL_getGeo(obj);
+    guint     nr      = S57_getRingNbr(geoData);
+    //GArray   *XY     = S57_getXY(geoData);
+    S57_prim *prim    = S57_initPrimGeo(geoData);
+
+    // assume vertex place holder existe and is empty
+    g_assert(NULL != tmpV);
+    g_assert(0    == tmpV->len);
+
+    gluTessBeginPolygon(tobj, prim);
+    for (i=0; i<nr; ++i) {
+        guint  npt;
+        pt3   *ppt;
+
+        S57_getGeoData(geoData, i, &npt, (double**)&ppt);
+        gluTessBeginContour(tobj);
+        for (j=0; j<npt; ++j, ++ppt)
+            gluTessVertex(tobj, (GLdouble*)ppt, (void*)ppt);
+        gluTessEndContour(tobj);
+    }
+    gluTessEndPolygon(tobj);
+
+    // empty vertex but keep array memory allocated
+    for (i=0; i<tmpV->len; ++i)
+        g_free(g_ptr_array_index(tmpV, i));
+    g_ptr_array_set_size(tmpV, 0);
+
+    return 1;
+}
+
+static GLint     _glMatrixSet(VP vpcoord)
+// push & reset matrix GL_PROJECTION & GL_MODELVIEW
+{
+    GLdouble left, right, bottom, top, near, far;
+
+    // debug
+    //if (VP_WIN != vpcoord) return 1;
+
+    switch (vpcoord) {
+        case VP_PRJ:
+            left   = pmin.u,       right = pmax.u,
+            bottom = pmin.v,       top   = pmax.v,
+            near   = Z_CLIP_PLANE, far   = -Z_CLIP_PLANE;
+            break;
+
+        case VP_PRJ_ZCLIP:
+            left   = pmin.u,       right = pmax.u,
+            bottom = pmin.v,       top   = pmax.v,
+            near   = Z_CLIP_PLANE, far   = 1 - Z_CLIP_PLANE;
+            break;
+
+        case VP_WIN:
+            left   = vp[0],        right = vp[2],
+            bottom = vp[1],        top   = vp[3];
+            near   = Z_CLIP_PLANE, far   = -Z_CLIP_PLANE;
+            break;
+        default:
+            PRINTF("ERROR: unknown viewport coodinate\n");
+            return 0;
+    }
+
+    _checkError("_glMatrixSet -a");
+    glMatrixMode(GL_PROJECTION);
+    _checkError("_glMatrixSet -b");
+    glPushMatrix();
+    _checkError("_glMatrixSet -1");
+    glLoadIdentity();
+
+    if (TRUE==sobj.doPick && GL_SELECT==_modes[_topMode])
+        gluPickMatrix(sobj.x, vp[3] - sobj.y, dx, dy, vp);
+
+    //if (VP_WIN == vpcoord)
+    //glOrtho(left, right, bottom, top, 2, -2);
+        glOrtho(left, right, bottom, top, near, far);
+    //else
+    //    gluPerspective(90.0, 1 /*(left-right)/(top-bottom)*/, 10.0, 3000000.0);
+        //glFrustum(left, right, bottom, top, 10, 0);
+
+    //printf("l/r, b/t: %f/%f, %f/%f\n", left, right, bottom, top);
+
+    _checkError("_glMatrixSet -2");
+
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    _checkError("_glMatrixSet -3");
+    glLoadIdentity();
+    _checkError("_glMatrixSet -4");
+
+    return 1;
+}
+
+static GLint     _glMatrixDel(VP vpcoord)
+// pop matrix GL_PROJECTION & GL_MODELVIEW
+// param vpcoord use for completness only
+{
+    //if (VP_WIN != vpcoord) return 1;
+
+
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    _checkError("_glMatrixDel -B- ");
+
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+    _checkError("_glMatrixDel -A- ");
+
+    return 1;
+}
+
+static GLint     _glMatrixDump(GLenum matrix)
+// debug
+{
+    double m1[16];
+    double m2[16];
+
+    glGetDoublev(matrix, m1);
+
+    memcpy(m2 ,m1, sizeof(m1));
+
+    if (0 == memcmp(m2, m1, sizeof(m1))) {
+        printf("%f\t %f\t %f\t %f \n",m2[0], m2[1], m2[2], m2[3]);
+        printf("%f\t %f\t %f\t %f \n",m2[4], m2[5], m2[6], m2[7]);
+        printf("%f\t %f\t %f\t %f \n",m2[8], m2[9], m2[10],m2[11]);
+        printf("%f\t %f\t %f\t %f \n",m2[12],m2[13],m2[14],m2[15]);
+        printf("-------------------\n");
+    }
+    return 1;
+}
+
+//-----------------------------------
+//
+// PROJECTION SECTION
+//
+//-----------------------------------
+
+
+static projXY    _prj2win(projXY p)
+// convert coordinate: projected --> window (pixel)
+{
+    GLdouble mvm[16];       // modelview matrix
+    GLdouble pjm[16];       // projection matrix
+    GLdouble z = 0;             // dummy
+
+    glGetDoublev(GL_MODELVIEW_MATRIX, mvm);
+    glGetDoublev(GL_PROJECTION_MATRIX, pjm);
+
+    if (GL_FALSE == gluProject(p.u, p.v, z, mvm, pjm, vp, &p.u, &p.v, &z)) {
+        PRINTF("ERROR\n");
+        exit(0);
+    }
+
+    return p;
+}
+
+static projXY    _win2prj(projXY p)
+// convert coordinate: window --> projected
+{
+    GLdouble mvm[16];  // modelview matrix
+    GLdouble pjm[16];  // projection matrix
+    GLdouble x,y,z;    // geo coordinate
+
+//    _glMatrixDump(GL_MODELVIEW_MATRIX);
+
+    glGetDoublev(GL_MODELVIEW_MATRIX, mvm);
+    glGetDoublev(GL_PROJECTION_MATRIX, pjm);
+
+    if (GL_TRUE == gluUnProject(p.u, p.v, 0.0, mvm, pjm, vp, &x, &y, &z)) {
+        //printf("%f %f %f \n", x, y, z);
+        p.u = x;
+        p.v = y;
+    } else {
+        printf("glUnProject() FAILED!\n");
+        exit(0);
+    }
+
+    /*
+    if (ABS(y) > 90.0 || ABS(x) > 180.0) {
+        PRINTF("ERROR: lat/lon %f/%f\n", y, x);
+
+        printf("GL_MODELVIEW_MATRIX:\n");
+        _glMatrixDump(GL_MODELVIEW_MATRIX);
+
+        printf("GL_PROJECTION_MATRIX:\n");
+        _glMatrixDump(GL_PROJECTION_MATRIX);
+    }
+    */
+
+    return p;
+}
+
+static GLvoid    _glVertex3d(guint npt, GLdouble *ppt)
+{
+    // debug
+    guint i = 0;
+    double *p = ppt;
+    for (i=0; i<npt; ++i) {
+        //GLdouble z;
+        //projUV p;
+        //p.u = *ppt++;
+        //p.v = *ppt++;
+        //z   = *ppt++;
+        p += 2;
+        *p++ = (double) (_dprio /10000.0);
+    }
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(3, GL_DOUBLE, 0, ppt);
+    glDrawArrays(GL_LINE_STRIP, 0, npt);
+    glDisableClientState(GL_VERTEX_ARRAY);
+
+
+    return;
+}
+
+static int       _DrawArrays(S57_prim *prim)
+{
+    int     i = 0;
+    int     primNbr = 0;
+    double *vertex;
+
+    prim = S57_getPrimData(prim, &primNbr, &vertex);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(3, GL_DOUBLE, 0, vertex);
+
+    for (i=0; i<primNbr; ++i) {
+        int mode  = 0;
+        int first = 0;
+        int count = 0;
+
+        S57_getPrimIdx(prim, i, &mode, &first, &count);
+
+        glDrawArrays(mode, first, count);
+        //PRINTF("i:%i mode:%i first:%i count: %i\n", i, mode, first, count);
+    }
+
+    glDisableClientState(GL_VERTEX_ARRAY);
+
+    return i;
+}
+
+static int       _fillarea(S52_obj *obj)
+{
+    S57_geo  *geoData = S52_PL_getGeo(obj);
+    //GArray  *XY      = S57_getXY(geoData);
+    S57_prim *prim    = S57_getPrimGeo(geoData);
+
+    if (NULL == prim) {
+        _tessd(tobj, obj);
+    }
+
+    _glMatrixSet(VP_PRJ);
+    _DrawArrays(S57_getPrimGeo(geoData));
+    _glMatrixDel(VP_PRJ);
+
+    return TRUE;
+}
+
+
+static int       _initPROJ()
+// get curent extend using the
+// current GL_PROJECTION & GL_MODELVIEW matrix
+{
+    projUV pc1, pc2;   // pixel center
+
+    int    c_pixel_x; // center pixel X
+    int    c_pixel_y; // center pixel Y
+    double pixel_w_m; // pixel width in meter
+    double pixel_h_m; // pixel height in meter
+    double win_w_m;   // window width in meter
+    projUV p1;
+    projUV p2;
+
+    // screen
+    /*
+     printf("screen witdth/height: %i/%i (mm)  %i/%i (pixels) \n",
+     w_mm, h_mm, w_pixel, h_pixel);
+     printf("pixel size x/y: %f/%f (mm)\n", w_pixel_mm, h_pixel_mm);
+     printf("slope: %f\n", (float)w_pixel_mm/(float)h_pixel_mm);
+     printf("window x/y: %f/%f (mm)\n", (float)492*w_pixel_mm, (float)492*h_pixel_mm);
+     */
+
+    // window
+    glGetIntegerv(GL_VIEWPORT, vp);
+    //        printf("ViewPort: %i %i %i %i (pixels)\n",
+    //       vp[0], vp[1], vp[2], vp[3]);
+
+
+    return 1;
+
+    c_pixel_x  = (int)floor((double)vp[2]/2.0);
+    c_pixel_y  = (int)floor((double)vp[3]/2.0);
+
+    // --- LAT -----------------------------
+
+    // get lat bottom screen
+    p1.u = 0.0;
+    p1.v = 0.0;
+    p1   = _win2prj(p1);
+
+    // get lat top screen
+    p2.u = (double)vp[2];
+    p2.v = (double)vp[3];
+    p2   = _win2prj(p2);
+
+    //printf("top/bot lat (meter): %f / %f\n", p2.v, p1.v);
+    //printf("delta   lat (meter): %f\n",      ABS(p1.v - p2.v));
+
+    // --- LON ------------------------------
+
+    // get window center of current GL projection
+    pc1.u = c_pixel_x;
+    pc1.v = c_pixel_y;
+    pc1   = _win2prj(pc1);
+
+    // test equator
+    //pc1.u = 0.0;
+    //pc1.v = 0.0;
+
+    // get pixel cartesian diagonal
+    // FIXME: use width or height or booth
+    pc2.u = (double)c_pixel_x + 1.0;
+    pc2.v = (double)c_pixel_y + 1.0;
+    pc2   = _win2prj(pc2);
+
+    pixel_h_m = ABS(pc1.v - pc2.v);
+    pixel_w_m = ABS(pc1.u - pc2.u);
+
+    /*
+     printf("window center XY (x/y): %i / %i\n", c_pixel_x, c_pixel_x);
+     printf("geo    center XY (u/v): %f / %f\n", pc1.u, pc1.v);
+     printf("pixel  width  (meter) : %f\n"     , pixel_w_m);
+     printf("pixel  height (meter) : %f\n"     , pixel_h_m);
+     printf("pixel w/h ratio (m)   : %f\n"     , pixel_w_m/pixel_h_m);
+     */
+
+    // --- SET -----------------------------
+
+    // set proj lat (meter)
+    pmin.v = p1.v;
+    pmax.v = p2.v;
+
+    // set proj lon (meter)
+    //win_w_m = pixel_w_m * (double)vp[2];
+    win_w_m = pixel_h_m * (double)vp[2];
+    //printf("win_w_m: %f\n", win_w_m);
+    pmin.u = pc1.u - (win_w_m / 2);
+    pmax.u = pmin.u + win_w_m;
+
+    //printf("cartesian extend l/r/t/b: %f / %f / %f / %f\n",
+    //       pmin.u, pmax.u, pmin.v, pmax.v);
+    //printf("cartesian width/height: %f/%f\n", pmin.u-pmax.u, pmin.v-pmax.v);
+
+    // --- SCALE ---------------------------
+    _scale = dotpitch_x / (pixel_w_m*1000);
+    //printf("scale: %f, pixel_w: %f\n", _scale, pixel_w_m);
+
+    return 1;
+}
+
+
+//---------------------------
+//
+// GL FEEDBACK BUFFER SECTION
+//
+//---------------------------
+
+static GLint     _fb_alloc()
+{
+    fobj.sz  += fobj.hint;
+
+    if (NULL == fobj.buf)
+        fobj.buf = g_new0(GLdouble, fobj.sz);
+    else
+        fobj.buf = g_renew(GLdouble, fobj.buf, fobj.sz);
+
+    return TRUE;
+}
+
+static GLint     _fb_init(int hint)
+// set/resize buffer before calling the real glFeedbackBuffer()
+{
+    fobj.hint = hint;
+
+    fobj.n        = 0;
+    fobj.cnt      = 0;
+    fobj.tok      = FB_EMPTY_TOK;
+    fobj.poly_n   = 0;
+    fobj.poly_cnt = 0;
+    fobj.read_tok = TRUE;
+    //fobj.nvalue   = 2;    // GL_2D
+    fobj.nvalue   = 3;    // GL_3D
+    fobj.eob      = FALSE;
+
+    return TRUE;
+}
+
+static GLint     _fb_reset()
+// set feedback buffer for rereading
+{
+    fobj.cnt      = 0;
+    fobj.read_tok = TRUE;
+    fobj.tok      = FB_EMPTY_TOK;
+    fobj.poly_n   = 0;
+    fobj.poly_cnt = 0;
+    fobj.eob      = FALSE;
+
+    return 1;
+}
+
+static GLint     _fb_flush()
+// empty feedback buffer
+{
+    fobj.tok = FB_EMPTY_TOK;
+    return 1;
+}
+
+static GLint     _fb_addLine(double *pptA, double *pptB)
+{
+    if (fobj.n+6 > fobj.sz)
+        _fb_alloc();
+
+    fobj.tok = GL_LINE_TOKEN;
+
+    fobj.buf[fobj.n++] = *pptA++;
+    fobj.buf[fobj.n++] = *pptA++;
+    fobj.buf[fobj.n++] = *pptA++;
+
+    fobj.buf[fobj.n++] = *pptB++;
+    fobj.buf[fobj.n++] = *pptB++;
+    fobj.buf[fobj.n++] = *pptB++;
+
+    if (fobj.n > fobj.sz) {
+        PRINTF("FATAL ERROR: buffer overflow\n");
+        exit(0);
+    }
+
+    return TRUE;
+}
+
+
+/*
+static int       _pushMode(GLenum newMode);
+static GLenum    _popMode(GLenum mode);
+static GLint     _fb_geo2winClip(S57_geo *geoData, S52_CmdL *cmd)
+// feedback buffer coordinate to screen transform and clip
+{
+    guint     npt     = 0;
+    GLdouble *ppt     = NULL;
+
+    // check if FB need to be filled
+    if (FB_EMPTY_TOK != fobj.tok)
+        return 1;
+
+    S57_getGeoData(geoData, 0, &npt, &ppt);
+
+    // setup feedback buffer mode
+    fobj.hint = npt;
+    _pushMode(GL_FEEDBACK);
+
+    // fill feedback buffer according to command type
+    switch (S52_PL_getCmdWord(cmd)) {
+        // LINES_T/AREAS_T
+        case S52_CMD_SIM_LN:
+        case S52_CMD_COM_LN:
+            _glMatrixSet(VP_PRJ_ZCLIP);
+            glBegin(GL_LINE_STRIP);
+                _glVertex3d(npt, ppt);
+            glEnd();
+            _glMatrixDel(VP_PRJ_ZCLIP);
+            break;
+
+        // POINT_T/AREAS_T
+        case S52_CMD_TXT_TX:
+        case S52_CMD_TXT_TE:
+        case S52_CMD_SYM_PT:
+            if (POINT_T == S57_getObjtype(geoData)) {
+                _glMatrixSet(VP_PRJ);
+                glBegin(GL_POINTS);
+                    _glVertex3d(npt, ppt);
+                glEnd();
+                _glMatrixDel(VP_PRJ);
+                break;
+            }
+
+        // AREAS_T
+        case S52_CMD_ARE_CO:
+        case S52_CMD_ARE_PA:
+            _glMatrixSet(VP_PRJ);
+            _fillarea(geoData, NULL);
+            _glMatrixDel(VP_PRJ);
+            break;
+
+       default:
+           PRINTF("ERROR: command type (%i) \n", S52_PL_getCmdWord(cmd));
+
+    }
+
+    // finish with feedback buffer mode
+    _popMode(GL_FEEDBACK);
+
+    _checkError("_fb_geo2winClip()");
+
+    return TRUE;
+}
+*/
+
+
+static GLint     _needClip(double segAbeg, double segAend,
+                           double segBbeg, double segBend)
+{
+    if (segAbeg < segBbeg) return TRUE;
+    if (segAend > segBend) return TRUE;
+
+    return FALSE;
+}
+
+static GLint     _intersec(double segAbeg, double segAend,
+                           double segBbeg, double segBend)
+// TRUE if A overlap B
+{
+    if (segAend < segBbeg) return FALSE;
+    if (segAbeg > segBend) return FALSE;
+
+    return TRUE;
+}
+
+
+static GLint     _fb_clipLine(int npt, double *ppt)
+{
+    int i = 0;
+    for (i=0; i<npt-1; ++i, ppt+=3) {
+        // check if segemnt overlap window
+        if (TRUE == _intersec(ppt[0], ppt[3], pmin.u, pmax.u) ||
+            TRUE == _intersec(ppt[1], ppt[4], pmin.v, pmax.v) )
+            _fb_addLine(ppt, ppt+3);
+    }
+
+    return TRUE;
+}
+
+static GLint     _fb_geo2winClip(S52_obj *obj)
+// coordinate to screen clip
+{
+    //guint     npt     = 0;
+    //GLdouble *ppt     = NULL;
+    S57_geo  *geoData = S52_PL_getGeo(obj);
+    //projUV  xy1, xy2;   // LL UR
+    int   i  = 0;
+    guint nr = S57_getRingNbr(geoData);
+
+
+    // check if FB need to be filled
+    if (FB_EMPTY_TOK != fobj.tok)
+        return 1;
+
+    //S57_getExt(geoData, &xy1.u, &xy1.v, &xy2.u, &xy2.v);
+
+    // check if all inside
+    //if (TRUE == _needClip(xy1.u, xy2.u, pmin.u, pmax.u) ||
+    //    TRUE == _needClip(xy1.v, xy2.v, pmin.v, pmax.v) ) {
+
+    _fb_init(100);
+    for (i=0; i<nr; ++i) {
+        guint   npt;
+        double *ppt;
+        S57_getGeoData(geoData, i, &npt, &ppt);
+        _fb_clipLine(npt, ppt);
+    }
+
+    return TRUE;
+}
+
+static GLint     _fb_getNextPoint(GLfloat *x, GLfloat *y)
+{
+    // this is the end ..
+    if (fobj.n<=fobj.cnt) {
+        //_fb_reset();
+        fobj.eob = TRUE;
+        return FALSE;
+    }
+
+    *x = fobj.buf[fobj.cnt+1];
+    *y = fobj.buf[fobj.cnt+2];
+
+    return TRUE;
+}
+
+/*
+static GLint     _fb_getNextSeg(GLfloat *x1, GLfloat *y1, GLfloat *x2, GLfloat *y2)
+// get next segment from feedback buffer
+// Doesn't assume homogenous type of token in buffer
+// return 0 --scan completed
+// return 1 --coords
+// return 2 --last poly seg or line reset
+// WARNING: not reentrant
+{
+    int offset = 0;
+    gboolean line = FALSE;
+    int ret = 1;
+
+    // only GL_2D mode --for now
+    g_assert(fobj.nvalue == 2);
+
+    // this is the end ..
+    if (fobj.n<=fobj.cnt) {
+        //_fb_reset();
+        fobj.eob = TRUE;
+        return FALSE;
+    }
+
+    // start new
+    if (TRUE == fobj.read_tok) {
+        fobj.tok = fobj.buf[fobj.cnt++];
+        //fobj.read_tok = FALSE;
+    }
+
+    // find next position
+    switch((int)fobj.tok) {
+        case GL_POLYGON_TOKEN:
+            // start of a new poly
+            if (TRUE == fobj.read_tok) {
+                fobj.read_tok = FALSE;
+                fobj.poly_n   = (int) fobj.buf[fobj.cnt++];
+                fobj.poly_cnt = 0;
+                //printf("new poly...\n");
+            }
+
+            // set next vertex
+            if (fobj.poly_cnt < fobj.poly_n-1) {
+                ++fobj.poly_cnt;
+            } else {
+                // last segment of loop
+                fobj.read_tok = TRUE;
+                offset = -(fobj.poly_n * fobj.nvalue);
+                ret = 2;
+            }
+            break;
+
+        case GL_LINE_TOKEN:
+            line = TRUE;
+            ret  = 1;
+            break;
+        case GL_LINE_RESET_TOKEN:
+            line = TRUE;
+            ret  = 2;
+            break;
+
+        case GL_POINT_TOKEN:
+            line = FALSE;
+            break;
+
+        default:
+            PRINTF("ERROR unknown feedbak buffer token (%f)\n", fobj.tok);
+            //g_return_val_if_fail(FALSE, FALSE);
+            {
+                float f = GL_POINT_TOKEN;
+                printf("GL_POINT_TOKEN      = %f \n", f);
+                f = GL_LINE_TOKEN;
+                printf("GL_LINE_TOKEN       = %f \n", f);
+                f = GL_LINE_RESET_TOKEN;
+                printf("GL_LINE_RESET_TOKEN = %f \n", f);
+                f = GL_POLYGON_TOKEN;
+                printf("GL_POLYGON_TOKEN    = %f \n", f);
+
+                exit(0);
+            }
+    }
+
+    // get vertex start
+    *x1 = fobj.buf[fobj.cnt+0];
+    *y1 = fobj.buf[fobj.cnt+1];
+
+    // move to next vertex
+    fobj.cnt += fobj.nvalue;
+
+    // get vertex end --could be first vertex of a loop
+    *x2 = fobj.buf[fobj.cnt+offset+0];
+    *y2 = fobj.buf[fobj.cnt+offset+1];
+
+    // advance past last vertex --line
+    if (TRUE == line)
+        fobj.cnt += fobj.nvalue;
+
+    //return TRUE;
+    return ret;
+}
+*/
+
+static GLint     _fb_getNextSeg(GLfloat *x1, GLfloat *y1, GLfloat *x2, GLfloat *y2)
+// get next segment from feedback buffer
+// Doesn't assume homogenous type of token in buffer
+// return 0 --scan completed
+// return 1 --coords
+// return 2 --last poly seg or line reset
+// WARNING: not reentrant
+{
+    //int offset = 0;
+    //gboolean line = FALSE;
+    int ret = 1;
+
+    // only GL_2D mode --for now
+    g_assert(fobj.nvalue == 3);
+
+    // this is the end ..
+    if (fobj.n<=fobj.cnt) {
+        //_fb_reset();
+        fobj.eob = TRUE;
+        return FALSE;
+    }
+
+
+
+    // get vertex start
+    *x1 = fobj.buf[fobj.cnt+0];
+    *y1 = fobj.buf[fobj.cnt+1];
+
+    // move to next vertex
+    fobj.cnt += fobj.nvalue;
+
+    *x2 = fobj.buf[fobj.cnt+0];
+    *y2 = fobj.buf[fobj.cnt+1];
+
+    // advance past last vertex --line
+    //if (TRUE == line)
+
+    fobj.cnt += fobj.nvalue;
+
+    //return TRUE;
+    return ret;
+}
+
+
+static GLint     _fb_getCentroid(GLfloat *x, GLfloat *y)
+// return TRUE and centroid else FALSE
+{
+    GLfloat x1, y1, x2=-1.0, y2=-1.0;
+    GLfloat ai;
+    GLfloat area;
+    GLfloat atmp = 0.0;
+    GLfloat xtmp = 0.0;
+    GLfloat ytmp = 0.0;
+    //GLfloat r = 0.0, g = 0.0, b = 0.0;
+
+    if (TRUE == fobj.eob || fobj.n<=fobj.cnt)
+        return FALSE;
+
+    //glColor3f(0.0, 1.0, 0.0);
+    //glBegin(GL_LINES);
+
+    // compute area
+    while (_fb_getNextSeg(&x1, &y1, &x2, &y2)) {
+
+        //printf("t:0x%04x ::  %6.2f %6.2f || %6.2f %6.2f \n",
+        //       (int)fobj.tok, x1, y1, x2, y2);
+        //glColor3f(r, g, 1.0);
+        //glVertex2f(x1, y1);
+        //glVertex2f(x2, y2);
+
+        // no area to compute --break loop
+        if (GL_POINT_TOKEN == fobj.tok) {
+            *x = x1;
+            *y = y1;
+            break;
+        }
+
+        ai    = x1 * y2 - x2 * y1;
+        atmp += ai;
+        xtmp += (x2 + x1) * ai;
+        ytmp += (y2 + y1) * ai;
+    }
+
+    // compute center fo area
+    if (atmp != 0.0) {
+        area = atmp / 2;
+
+        // CW = 1, CCW = 2
+        //ret (atmp >= 0.0) ? 1 : 2;
+
+        *x = xtmp / (3 * atmp);
+        *y = ytmp / (3 * atmp);
+    }
+
+    //glEnd();
+
+    //printf("_getCentroid x = %f, y = %f \n", *x, *y);
+
+    return TRUE;
+}
+
+static GLint     _fb_getNextCenterPos(S52_obj *obj, GLfloat *x, GLfloat *y)
+// get center in screen coordinate for different primitive
+{
+    int       ret   = FALSE;
+
+    //return 0;
+
+    //if (NULL == geoData)
+    //    return 0;
+
+    // send data to feedback buffer
+    _fb_geo2winClip(obj);
+
+    switch (S57_getObjtype(S52_PL_getGeo(obj))) {
+        case LINES_T: {
+            GLfloat x1,y1,x2,y2;
+            ret = _fb_getNextSeg(&x1, &y1, &x2, &y2);
+            *x = x2 - x1;
+            *y = y2 - y1;
+            break;
+        }
+
+        case POINT_T: //PRINTF(" POINT_T not computed \n");
+            break;
+
+        case AREAS_T:
+            ret = _fb_getCentroid(x, y);
+            break;
+
+        default:
+            PRINTF("ERROR: unknown object type\n");
+            exit(0);
+    }
+
+    return ret;
+}
+
+static int       _initSobj(int szfac);
+static int       _accuSobj();
+static int       _pushMode(GLenum newMode)
+// handle mutual exclusive GL rendering mode
+// newMode will become the current mode
+// previous mode will resume when this one is poped
+{
+    _checkError("start _pushMode()");
+
+    // can't handle a stack of similar mode at the moment
+    g_assert(newMode != _modes[_topMode]);
+
+    // suspend current mode
+    switch (_modes[_topMode]) {
+        case GL_RENDER:
+            // do nothing here
+            break;
+
+        case GL_SELECT:   // accumulate the number of hits
+            sobj.hits = glRenderMode(GL_RENDER);
+            _accuSobj();
+            break;
+
+        case GL_FEEDBACK: // this can't happen at the moment
+            PRINTF("ERROR: feedback mode interrupted!\n");
+            break;  
+
+        default:
+            PRINTF("ERROR: unknown GL mode\n");
+            return 0;
+    }
+
+    _modes[++_topMode] = newMode;
+
+    // start new mode
+    switch (newMode) {
+
+        case GL_RENDER:
+            // do nothing
+            break;
+
+        case GL_SELECT:
+            _initSobj(0);
+            glRenderMode(GL_SELECT);
+            glInitNames();
+            glPushName(sobj.name);
+            break;
+
+        case GL_FEEDBACK:
+            //_fb_init();
+            //glRenderMode(GL_FEEDBACK);
+            break;
+
+        default:
+            PRINTF("ERROR: unknown GL mode\n");
+            return 0;
+    }
+
+    _checkError("_pushMode()");
+
+    return 1;
+}
+
+static GLenum    _popMode(GLenum mode)
+{
+    // sanity check
+    g_assert(_topMode > 0);              // something to pop
+    g_assert(mode == _modes[_topMode]);  // poping the right thing
+
+    // handle completion of current mode
+    switch (_modes[_topMode]) {
+
+        case GL_RENDER:
+             // do nothing
+            break;
+
+        case GL_SELECT:
+            // switch off selection
+            sobj.hits = glRenderMode(GL_RENDER);
+            _accuSobj();
+            break;
+
+        case GL_FEEDBACK:
+            fobj.n = glRenderMode(GL_RENDER);
+            if (fobj.n < 0) {
+                PRINTF("ERROR feedback buffer overflow (%i)\n", fobj.n);
+                printf("fobj.sz=%i npt=%i \n", fobj.sz, fobj.hint);
+                exit(0);
+            }
+            break;
+
+        default:
+            PRINTF("ERROR: unknown GL mode\n");
+            return 0;
+    }
+
+    // pop the stack
+    _topMode--;
+
+    // resume previous mode
+    switch (_modes[_topMode]) {
+
+        case GL_RENDER:
+            glRenderMode(GL_RENDER);
+            break;
+
+        case GL_SELECT:
+            //glSelectBuffer(sobj.sz, sobj.buf);
+            glRenderMode(GL_SELECT);
+            glInitNames();
+            glPushName(sobj.name);
+            _checkError("GL_SELECT");
+            break;
+
+        case GL_FEEDBACK:
+            glRenderMode(GL_FEEDBACK);
+            break;
+
+        default:
+            PRINTF("ERROR: unknown GL mode\n");
+            return 0;
+    }
+
+    _checkError("_popMode()");
+
+    return _modes[_topMode];
+}
+
+
+//---------------------------------------
+//
+// SYMBOLOGY INSTRUCTION RENDERER SECTION
+//
+//---------------------------------------
+
+static int       _glCallList(S52_subListData *sld)
+{
+    int        i   = 0;
+    GLuint     lst = sld->startList;
+    S52_Color *col = (S52_Color *) sld->col;
+
+    for (i=0; i<sld->nSubList; ++i, ++lst) {
+        glColor4ub(col[i].R,
+                   col[i].G,
+                   col[i].B,
+                   (4 - (col[i].trans - '0'))*TRNSP_FAC);
+
+        glCallList(lst);
+        //PRINTF("list no: %i\n", lst);
+    }
+
+    _checkError("_glCallList()");
+
+    return 1;
+}
+static int       _renderEX(S52_obj *obj)
+//static S52_CmdL *_renderTEXT(S52_obj *obj)
+// render TE or TX
+// NOTE: could use later: "void glutStrokeString(void *font, char *string);"
+
+{
+    //GLfloat    x     = 0.0;
+    //GLfloat    y     = 0.0;
+    S52_Color *col   = NULL;
+    int        xoffs = 0;
+    int        yoffs = 0;
+    int        bsize = 0;
+    int        weight= 0;
+    char      *str;
+
+    //if (NULL == instruc || NULL == instruc->text)
+    //    return cmd;
+
+    //S52_PL_getTEXT(instruc->text, &col, &xoffs, &yoffs, &bsize, &weight, &str);
+    str = S52_PL_getEX(obj, &col, &xoffs, &yoffs, &bsize, &weight);
+    //printf("xoffs/yoffs/bsize/weight: %i/%i/%i/%i\n", xoffs, yoffs, bsize, weight);
+    if (NULL == str)
+        return 0;
+
+#ifdef INDEXMODE
+    glIndexi(col->idx[currentScreen]);
+#else
+    glColor4ub(col->R, col->G, col->B, 255);
+#endif
+    //_fb_geo2winClip(instruc->geoData, cmd);
+
+    //glPushAttrib(GL_LIST_BASE);
+    glPushAttrib(GL_LIST_BIT);
+    glListBase(_fontDList[weight-'4']);
+
+    _glMatrixSet(VP_PRJ);
+    //while (_fb_getNextCenterPos(instruc->geoData, cmd, &x, &y)) {
+    {
+        guint   npt;
+        double *ppt;
+        S57_getGeoData(S52_PL_getGeo(obj), 0, &npt, &ppt);
+
+        //glRasterPos2f(x /*+ xoffs*PICA*bsize*/, y /* + yoffs*PICA*bsize*/ );
+
+        // FIXME: scale offset X/Y
+        glRasterPos2d(ppt[0] /*+ xoffs*PICA*bsize*/, ppt[1] /* + yoffs*PICA*bsize*/ );
+        //glRasterPos2d(50 /*+ xoffs*PICA*bsize*/, 50 /* + yoffs*PICA*bsize*/ );
+        glCallLists(strlen(str), GL_UNSIGNED_BYTE, (GLubyte*)str);
+        //printf("(%f,%f) off %f/%f : %s \n", x, y, xoffs*PICA*bsize, yoffs*PICA*bsize, str);
+        //printf("(%f,%f) \n", ppt[0], ppt[1]);
+    }
+
+    _glMatrixDel(VP_PRJ);
+
+    glPopAttrib();
+
+    _checkError("_renderEX()");
+
+    return 1;
+}
+
+//static S52_CmdL *_renderTX(S52_obj *obj)
+// TeXt
+//{
+//    if (NULL == instruc->text)
+//        instruc->text = S52_PL_parseTX(instruc->geoData, cmd);
+//
+//    _renderTEXT(obj);
+//
+//    return cmd;
+//}
+
+//static S52_CmdL *_renderTE(S52_obj *obj)
+// TExt formatted
+//{
+//    if (NULL == instruc->text)
+//        instruc->text = S52_PL_parseTE(instruc->geoData, cmd);
+//
+//    _renderTEXT(instruc, cmd);
+//
+//    return cmd;
+//}
+
+
+
+static int       _renderSY(S52_obj *obj)
+//static S52_CmdL *_renderSY(S57_geo *geoData, S52_CmdL *cmd)
+// SYmbol
+{
+    //GLfloat  x      = 0.0;
+    //GLfloat  y      = 0.0;
+    //static int first= 0;
+
+    S57_geo *geoData= S52_PL_getGeo(obj);
+    GLdouble orient = S52_PL_getSYorient(obj);
+    S52_subListData subListData;
+
+    // filter out all but ..
+    //if (0 != S52_PL_cmpCmdParam(obj, "BOYLAT23") &&
+    //    0 != S52_PL_cmpCmdParam(obj, "BOYLAT14")) {
+    //    return TRUE;
+    //}
+    //PRINTF("BOYLAT23 found\n");
+
+    // cursor pick --fill stencil
+    if (NULL==geoData /* && NULL==cmd */ ) {
+#ifdef INDEXMODE
+        //    printf("Should do transparency _rendersy (STENCILL): %f\n",  255);
+        glIndexi(1); // TODO
+#else
+        //glColor4ub(255,0,0, 255);
+        glColor4ub(255,0,0,255);
+#endif
+        //glColor3ub(255,0,0);
+        //glLineWidth(5);
+        glPointSize(1);   // FIXME: what is a good value?
+
+        _glMatrixSet(VP_WIN);
+        glBegin(GL_LINES);
+        glVertex3d(sobj.x, sobj.y, 0);
+        glVertex3d(sobj.x+50, sobj.y+50, 0);
+        glEnd();
+        _glMatrixDel(VP_WIN);
+
+        PRINTF("mouse x: %f y: %f\n", sobj.x, sobj.y);
+
+        _checkError("_renderSY()");
+
+        return TRUE;
+    }
+
+    // error handling case or CS for wreck (point) we don't know what to draw
+    // so here we're drawing something conspic. if it ever reach the screen.
+    if (-1==orient /* || NULL==cmd */ ) {
+        guint      npt = 0;
+        GLdouble  *ppt = NULL;
+        S52_Color *c   = S52_PL_getColor("DNGHL");
+
+        S57_getGeoData(geoData, 0, &npt, &ppt);
+#ifdef INDEXMODE
+        glIndexi(c->idx[currentScreen]);
+#else
+        glColor4ub(c->R, c->G, c->B, 255);
+#endif
+        //glPointSize(3);   // FIXME: what is a good value?
+        glPointSize(1);   // FIXME: what is a good value?
+
+        _glMatrixSet(VP_PRJ);
+        glBegin(GL_POINTS);
+            //_glVertex3d(npt, ppt);
+        glEnd();
+        _glMatrixDel(VP_PRJ);
+
+        _checkError("_renderSY()");
+
+        return TRUE;
+    }
+
+    {
+        guint      npt = 0;
+        GLdouble  *ppt = NULL;
+        S57_getGeoData(geoData, 0, &npt, &ppt);
+        S52_PL_getSubListData(obj, &subListData);
+
+        //PRINTF("%s ++++++++\n", S57_getName(geoData));
+
+        //_glMatrixSet(VP_WIN);
+        _glMatrixSet(VP_PRJ);
+        //while (_fb_getNextCenterPos(geoData, cmd, &x, &y)) {
+        //PRINTF("x/y: %f/%f\n", x, y);
+        //PRINTF("x/y: %f/%f\n", ppt[0], ppt[1]);
+
+        // move screen coord origine at symbol position
+        //glTranslatef(x, y, 0.0);
+        //glLoadIdentity();
+
+        glTranslated(ppt[0], ppt[1], 0.0);
+        //glTranslated(ppt[0], ppt[1], _dprio);
+
+        //glScaled(1.0, -1.0, 1.0);
+        glScaled(0.0001, -0.0001, 1.0);
+        glRotated(orient, 0.0, 0.0, 1.0);    // rotate coord sys. on Z
+
+        //glScaled(1.0, -1.0, 1.0);
+
+        _glCallList(&subListData);
+    
+        //glLoadIdentity();
+    //}
+        //_glMatrixDel(VP_WIN);
+        _glMatrixDel(VP_PRJ);
+
+        _checkError("_renderSY()");
+    }
+
+    return TRUE;
+}
+
+static int       _renderLS_LIGHTS05(S52_obj *obj)
+//static int       _renderLS_LIGHTS05(S57_geo *geoData, S52_CmdL *cmd)
+{
+    //GLdouble pt[6] = {ppt[0], ppt[1], ppt[2], 0.0, 0.0, 0.0};
+
+    S57_geo *geoData   = S52_PL_getGeo(obj);
+    GString *orientstr = S57_getAttVal(geoData, "ORIENT");
+    GString *sectr1str = S57_getAttVal(geoData, "SECTR1");
+    double   sectr1    = (NULL==sectr1str) ? 0.0 : atof(sectr1str->str);
+    GString *sectr2str = S57_getAttVal(geoData, "SECTR2");
+    double   sectr2    = (NULL==sectr2str) ? 0.0 : atof(sectr2str->str);
+    double   leglen    = 25.0;
+    projUV   p;                  // = {ppt[0], ppt[1]};
+    double   z         = 0.0;
+
+    GLdouble *ppt = NULL;
+    guint     npt = 0;
+
+    //PRINTF("S1: %f, S2:%f\n", sectr1, sectr2);
+
+    S57_getGeoData(geoData, 0, &npt, &ppt);
+    p.u = ppt[0];
+    p.v = ppt[1];
+
+    //p = S57_geo2prj(p);
+    p = _prj2win(p);
+
+    if (TRUE == S52_getMarinerParam(S52_MAR_FULL_SECTORS)) {
+        GString  *valnmrstr = S57_getAttVal(geoData, "VALNMR");
+        if (NULL != valnmrstr) {
+            PRINTF("FIXME: compute leglen to scale (NM)\n");
+            leglen = atof(valnmrstr->str) *10;
+        }
+    }
+
+    //glDisable(GL_LINE_SMOOTH);
+
+    _glMatrixSet(VP_WIN);
+
+    if (NULL != orientstr) {
+        GLdouble o = atof(orientstr->str);
+
+        glLoadIdentity();
+        glTranslated(p.u, p.v, z);
+        glScaled(1.0, -1.0, 1.0);
+        glRotated(o+90.0, 0.0, 0.0, 1.0);
+
+        glBegin(GL_LINES);
+        glVertex3d(0.0, 0.0, 0.0);
+        glVertex3d(leglen, 0.0, 0.0);
+        glEnd();
+    }
+
+    if (NULL != sectr1str) {
+        glLoadIdentity();
+        glTranslated(p.u, p.v, z);
+        glScaled(1.0, -1.0, 1.0);
+        glRotated(sectr1+90.0, 0.0, 0.0, 1.0);
+
+        glBegin(GL_LINES);
+        glVertex3d(0.0, 0.0, 0.0);
+        glVertex3d(leglen, 0.0, 0.0);
+        glEnd();
+    }
+
+    if (NULL != sectr2str) {
+        glLoadIdentity();
+        glTranslated(p.u, p.v, z);
+        glScaled(1.0, -1.0, 1.0);
+        glRotated(sectr2+90.0, 0.0, 0.0, 1.0);
+
+        glBegin(GL_LINES);
+        glVertex3d(0.0, 0.0, 0.0);
+        glVertex3d(leglen, 0.0, 0.0);
+        glEnd();
+    }
+
+    //glEnable(GL_LINE_SMOOTH);
+
+    _glMatrixDel(VP_WIN);
+
+    return TRUE;
+}
+
+static int       _renderLS(S52_obj *obj)
+// Line Style
+{
+    S52_Color *col;
+    char       style;   // L/S/T
+    char       pen_w;
+
+    //2_PL_getLSdata(cmd, &pen_w, &style, &col);
+    S52_PL_getLSdata(obj, &pen_w, &style, &col);
+    glLineWidth(pen_w - '0');
+
+#ifdef INDEXMODE
+    glIndexi(col->idx[currentScreen]);
+#else
+    glColor4ub(col->R, col->G, col->B, 255);
+#endif
+
+    // debug
+    //glLineWidth(5);
+    //glColor3ub(255, 0, 0);
+
+    // Assuming pixel size at 0.3 mm.
+    // Note: we can draw coded depth line because
+    // pattern start is not reset a each new lines in a strip.
+    glEnable(GL_LINE_STIPPLE);
+    //glEnable(GL_LINE_SMOOTH);  // antialiase needed for line stippled
+    switch (style) {
+        // SOLD --correct
+        case 'L': glLineStipple(1, 0xFFFF); break;
+        // DASH --incorrect  (last space 1.8mm instead of 1.2mm)
+        //case 'S': glLineStipple(2, 0x9248); break;
+        // bit better !
+        case 'S': glLineStipple(3, 0x7777); break;
+        // DOTT --correct
+        case 'T': glLineStipple(1, 0xFFF0); break;
+
+        default:
+            PRINTF("ERROR: invalid line style\n");
+    }
+
+    {
+        GLdouble *ppt     = NULL;
+        guint     npt     = 0;
+        S57_geo  *geoData = S52_PL_getGeo(obj);
+
+        S57_getGeoData(geoData, 0, &npt, &ppt);
+
+        // this code is specific to CS LIGHTS05
+        if (POINT_T == S57_getObjtype(geoData))
+            _renderLS_LIGHTS05(obj);
+            //;
+        else {
+            _glMatrixSet(VP_PRJ_ZCLIP);
+                //glBegin(GL_LINE_STRIP);
+                    _glVertex3d(npt, ppt);
+                //glEnd();
+            _glMatrixDel(VP_PRJ_ZCLIP);
+        }
+    }
+
+    glDisable(GL_LINE_STIPPLE);
+
+    _checkError("_renderLS()");
+
+    return TRUE;
+}
+
+
+static int       _renderLC(S52_obj *obj)
+// Line Complex
+{
+    GLdouble symlen;
+    //GTimeVal start, end;
+    GLfloat x1,y1,x2,y2;
+    //GLuint  list = S52_PL_getDisplayList(cmd);
+    S57_geo *geoData = S52_PL_getGeo(obj);
+    S52_Obj_t ot = S57_getObjtype(geoData);
+    char       pen_w;
+    S52_subListData subListData;
+    S52_Color *c;
+
+    S52_PL_getSubListData(obj, &subListData);
+
+    c = (S52_Color*) &subListData.col;
+
+    // set pen color & size here because values might not
+    // be set via call list --short line
+    //symlen = S52_PL_getLCdata(cmd, dotpitch_x, &pen_w);
+    symlen = S52_PL_getLCdata(obj, dotpitch_x, &pen_w);
+
+#ifdef INDEXMODE
+    glIndexi(c->idx[currentScreen]);
+#else
+    glColor4ub(c->R, c->G, c->B, 255);
+#endif
+    //glLineWidth(pen_w - '0');
+    glLineWidth(1);
+
+    // FIXME: problem if feedback buffer containe line clipped
+    // via ZCLIP and then thos line are used to compute center of symbol
+    // tha area might not work! Maybe compute area from triangle!
+    // compute object to screen representation
+    //_fb_geo2winClip(geoData, cmd);
+    _fb_geo2winClip(obj);
+
+    //_glMatrixSet(VP_WIN);
+    //glPushMatrix();
+
+    while (_fb_getNextSeg(&x1, &y1, &x2, &y2)) {
+        GLdouble run    = 0.0;
+        GLdouble seglen = sqrt(pow(x1-x2, 2)  + pow(y1-y2, 2));
+        GLdouble segang = atan2(y2-y1, x2-x1) * RAD_TO_DEG;
+
+        //printf("segang: %f seglen: %f symlen:%f \n", segang, seglen, symlen);
+        //printf(">> x1: %f y1: %f \n",x1, y1);
+        //printf(">> x2: %f y2: %f \n",x2, y2);
+
+        // FIXME: compute grid offset so that the sarting symbol
+        // doesn't start at the clipped border at every redraw
+        // when scrolling
+        /*
+        if (x1 == vp[0] ||
+            y1 == vp[1] ||
+            x1 == vp[2] ||
+            y1 == vp[3]) {
+            // compute offset
+            // compute extend off screen
+            // update xy and start drawing for there
+            // so that it gets clipped at different place
+            // when scrolling
+        }
+        */
+
+        //glLoadIdentity();                    // reset origine
+
+        /*
+        if (LINES_T == ot) {
+            glTranslated(x1, y1, 0.0);           // move coord sys. at symb pos.
+            //glScaled(1.0, -1.0, 1.0);
+            glRotated(segang, 0.0, 0.0, 1.0);    // rotate coord sys. on Z
+        } else {
+            // FIXME: line of area is in reverse so rot/scale in reverse !?
+            // something is not right :/
+            //glTranslated(x1, y1, 0.0);           // move coord sys. at symb pos.
+            glTranslated(x2, y2, 0.0);           // move coord sys. at symb pos.
+            glRotated(segang+180, 0.0, 0.0, 1.0);    // rotate coord sys. on Z
+            //glScaled(1.0, -1.0, 1.0);
+        }
+        glScaled(1.0, -1.0, 1.0);
+
+        // draw symb's as long as it fit the line length
+        for (run=0.0; run<(seglen-symlen); run+=symlen) {
+            //_glCallList(list);                // draw symbol
+            _glCallList(&subListData);
+
+            glTranslated(symlen, 0.0, 0.0);  // move coord sys.
+            //printf("x:%f y:%f run:%f \n", x1, y1, run);
+        }
+        */
+        // complete the rest of the line
+        glBegin(GL_LINES);
+            //glVertex3d(0.0, 0.0, 0.0);
+            //glVertex3d(seglen-run, 0.0, 0.0);
+            glVertex3d(x1, y1, 0.0);
+            glVertex3d(x2, y2, 0.0);
+        glEnd();
+    }
+    //_glMatrixDel(VP_WIN);
+
+    //glEnable(GL_LINE_SMOOTH);
+
+/*
+    g_get_current_time(&end);
+    { // elapse time
+        glong dsec  = end.tv_sec - start.tv_sec;
+        glong dusec;
+
+        if (end.tv_usec < start.tv_usec) {
+            dsec--;
+            end.tv_usec += 1000000;
+        }
+        dusec = end.tv_usec - start.tv_usec;
+
+        PRINTF("sec = %li usec = %li\n", dsec, dusec);
+    }
+*/
+    //glPopMatrix();
+
+    _checkError("_renderLC()");
+
+    return TRUE;
+}
+
+static int       _gluPartialDisk(GLdouble inner, GLdouble outer, GLint slices,
+                           GLint loops, GLdouble start, GLdouble sweep)
+{
+    GLint     loop         = 0;
+    GLint     slice        = 0;
+    GLdouble  radius       = inner;
+    GLdouble  delta_radius = (outer - inner) / loops;;
+    GLdouble  angle        = start            * DEG_TO_RAD;
+    GLdouble  delta_angle  = (sweep / slices) * DEG_TO_RAD;
+    S57_prim *vertex       = S57_initPrim(NULL);
+
+    for (loop=0; loop<loops; ++loop) {
+        _glBegin(GL_QUAD_STRIP, vertex);
+
+        for (slice=0; slice<=slices; ++slice) {
+            struct {double x,y,z;} pt3 = {0,0,0};
+            //if (qobj->Orientation == GLU_OUTSIDE) {
+            pt3.x = (radius + delta_radius) * sin(angle);
+            pt3.y = (radius + delta_radius) * cos(angle);
+            _vertex3d(&pt3, vertex);
+            pt3.x = radius * sin(angle);
+            pt3.y = radius * cos(angle);
+            _vertex3d(&pt3, vertex);
+            //}
+            //else {
+            //    glVertex2d(radius * sin(angle), radius * cos(angle));
+            //    glVertex2d((radius + delta_radius) * sin(angle),
+            //               (radius + delta_radius) * cos(angle));
+            //}
+            angle += delta_angle;
+        }
+        _glEnd(vertex);
+        radius += delta_radius;
+    }
+    _DrawArrays(vertex);
+    S57_donePrim(vertex);
+
+    return 1;
+}
+
+static int       _renderAC_LIGHTS05(S52_obj *obj)
+// this code is specific to CS LIGHTS05
+{
+    S52_Color *col       = S52_PL_getACdata(obj);
+    S57_geo   *geoData   = S52_PL_getGeo(obj);
+    GString   *sectr1str = S57_getAttVal(geoData, "SECTR1");
+    GString   *sectr2str = S57_getAttVal(geoData, "SECTR2");
+
+    if (NULL != sectr1str && NULL != sectr2str) {
+        GLdouble *ppt = NULL;
+        guint     npt = 0;
+
+        GLdouble  sectr1    = atof(sectr1str->str);
+        GLdouble  sectr2    = atof(sectr2str->str);
+        double    sweep     = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1;
+        GString  *extradstr = S57_getAttVal(geoData, "extend_arc_radius");
+        double    radius;
+        S52_Color *black    = S52_PL_getColor("CHBLK");
+        projUV    p;
+        double    z;
+
+        S57_getGeoData(geoData, 0, &npt, &ppt);
+        p.u = ppt[0];
+        p.v = ppt[1];
+
+        //p = S57_geo2prj(p);
+        p = _prj2win(p);
+
+        if (NULL!=extradstr && 'Y'==*extradstr->str)
+            radius = 25 / dotpitch_x;    // (25 mm)
+        else
+            radius = 20 / dotpitch_x;    // (20 mm)
+
+        gluQuadricDrawStyle(qobj, GLU_FILL) ;
+
+        _glMatrixSet(VP_WIN);
+        //_glMatrixSet(VP_PRJ);
+        //glPushMatrix();
+        //glLoadIdentity();                    // reset origine
+
+        glTranslated(p.u, p.v, z);
+
+        // FIXME: 4 pixel @ 0.3mm/pixel --arc thickness depend on the pixel size
+#ifdef INDEXMODE
+        glIndexi(black->idx[currentScreen]);
+#else
+        glColor4ub(black->R, black->G, black->B, 255);
+#endif
+        //gluPartialDisk(qobj, radius, radius+8, sweep/2.0, 2,
+        //               sectr1+180, sweep);
+        _gluPartialDisk(radius, radius+8, sweep/2.0, 2,
+                       sectr1+180, sweep);
+
+#ifdef INDEXMODE
+        glIndexi(col->idx[currentScreen]);
+#else
+        glColor4ub(col->R, col->G, col->B, 255);
+#endif
+        //gluPartialDisk(qobj, radius+2, radius+6, sweep/2.0, 2,
+        //               sectr1+180, sweep);
+        _gluPartialDisk(radius+2, radius+6, sweep/2.0, 2,
+                       sectr1+180, sweep);
+
+        //glPopMatrix();
+        _glMatrixDel(VP_WIN);
+    }
+
+    return TRUE;
+}
+
+static int       _renderAC(S52_obj *obj)
+// Area Col7or (also light sector)
+{
+    S52_Color *col     = S52_PL_getACdata(obj);
+    S57_geo   *geoData = S52_PL_getGeo(obj);
+
+    //PRINTF("%s\n", S57_getName(geoData));
+
+    if (POINT_T == S57_getObjtype(geoData)) {
+        _renderAC_LIGHTS05(obj);
+        return TRUE;
+    }
+
+#ifdef INDEXMODE
+    //    printf("Should do transparency _renderAC: %f\n",  (4-col->trans)*TRNSP_FAC);
+    glIndexi(col->idx[currentScreen]);
+#else
+    glColor4ub(col->R, col->G, col->B, (4-col->trans)*TRNSP_FAC);
+#endif
+
+    // FIXME: check transparancy handling in:
+    // AC(TRFCF,3), AC(DNGHL,3), AC(ADINF,3)
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    _fillarea(obj);
+    glDisable(GL_BLEND);
+
+    _checkError("_renderAC()");
+
+    return TRUE;
+}
+
+static int       _loadStencil()
+{
+    glClear(GL_STENCIL_BUFFER_BIT);
+    //glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // debug:flush all
+    glStencilFunc(GL_ALWAYS, 0x1, 0x1);
+    glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+
+    // treate color as transparent
+    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+
+    return 1;
+}
+
+static int       _freezStencil()
+{
+    // all color to pass stencil filter
+    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+    // clip pattern pixel that lie outside of poly --clip if != 1
+    glStencilFunc(GL_EQUAL, 0x1, 0x1);
+
+    // freeze stencil state
+    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+    return 1;
+}
+
+//typedef S52_CmdL *(*renderfunc)(S52_obj *obj);
+typedef int   (*renderfunc)(S52_obj *obj);
+static int       _setStencil(S52_obj *obj, renderfunc f)
+// set filter in stencil ready for selection
+{
+#ifdef INDEXMODE
+    return 1;
+#endif
+
+    glEnable(GL_STENCIL_TEST);
+
+    _loadStencil();
+
+    // fill stencil with 1 in the shape drawn
+    //geoData);
+    f(obj);
+
+    _freezStencil();
+
+    return 1;
+}
+
+static int       _renderAP(S52_obj *obj)
+//static S52_CmdL *_renderAP(S57_geo *geoData, S52_CmdL *cmd)
+// Area Pattern
+{
+    // NOTE: S52 define pattern rotation but doesn't use that in specs.
+    //GvRect  rect;        // lat/lon
+    GLdouble x,y;          // tile pos (pixel)
+
+    //GLdouble z = 0.0;
+    GLdouble x0=0.0, y0=0.0;   // pattern alignment (origine of geo. sys in pixel)
+    GLdouble x1=0.0, y1=0.0;   // LL of region from polygone (pixel)
+    GLdouble x2=0.0, y2=0.0;   // UR of region from polygone (pixel)
+    //GLdouble xx=0.0, yy=0.0;   // dummy
+    GLdouble tw=0.0, th=0.0;   // tile width/height 1 = 01 mm
+    GLdouble dx = 0.0;         // run length offset for STG pattern
+    int      stag     = 0;     // 0-1 true/false add dx for stagged pattern
+    GLdouble offset_x = 0.0,
+             offset_y = 0.0;
+    //GLuint   list     = S52_PL_getDisplayList(obj);
+    projUV   p;
+    S52_subListData subListData;
+    //S52_Color *c;
+
+    S52_PL_getSubListData(obj, &subListData);
+
+
+
+    //if (1 != found++) return cmd;
+
+    //if (0 == list) {
+    //    PRINTF("ERROR: pattern unknown! \n");
+    //    return cmd;
+    //}
+
+    _setStencil(obj, _fillarea);
+
+    // pattern aligned to origin of geo coord
+    // FIXME: rectangular coord is used because of distortion when
+    // projecting the 'grid' in mercator
+    p.u = 0.0; //vp[2]/2;
+    p.v = 0.0; //vp[3]/2;
+    //p   = _win2prj(p);
+    p   = _prj2win(p);
+    x0  = p.u; //-vp[2]/2;
+    y0  = p.v; //-vp[3]/2;
+
+    //p.u = 0.0;
+    //p.v = 0.0;
+    //p   = _geo2prj(p);
+    //p   = _prj2win(p);
+    //x0  = p.u;
+    //y0  = p.v;
+
+    // get area's upper right corner in pixel
+    //S57_getExt(geoData, &xx, &yy, &x2, &y2);
+    p.u = x2;
+    p.v = y2;
+    //p   = S57_geo2prj(p);
+    p   = _prj2win(p);
+    //p   = _win2prj(p);
+    x2  = p.u;
+    y2  = p.v;
+    // no need to draw beyon window
+    //if (x2 > vp[2]) x2 = vp[2] + th;
+    //if (y2 > vp[3]) y2 = vp[3] + tw;
+
+    //S52_PL_getAPdata(cmd, dotpitch_x, dotpitch_y, &tw, &th, &dx);
+
+    // Reposition pattern alignment to screen origine 'grid'.
+    x1 = floor(x0 / tw) * tw;
+    offset_x = x0 - x1;
+
+    y1 = (int)(y0 / (2*th)) * 2*th;  // 2X to allow for stag
+    //y1 = (int)(y0 / (1*th)) * 1*th;  // 2X to allow for stag
+    offset_y = y0 - y1;              // ie the grid is made of 1tw x 2th cell
+
+    _glMatrixSet(VP_WIN);
+
+    // NOTE: pattern that do not fit entirely inside an area
+    // are displayed  (hence pattern are clipped) because ajacent area
+    // filled with same pattern will complete the clipped pattern.
+    // No test y+th<y2 and x+tw<x2 to check for the end of a row/collum.
+
+    //printf("offset x/y; %f/%f\n", offset_x, offset_y);
+    //printf("x0/y0: %f/%f\n", x0, y0);
+    for (y=offset_y; y<y2; y+=th) {
+        glLoadIdentity();   // reset to screen origin
+        glTranslated(offset_x + (dx*stag), y, 0.0);
+        glScaled(1.0, -1.0, 1.0);
+
+        for (x=offset_x; x<x2; x+=tw) {
+            _glCallList(&subListData);
+            glTranslated(tw, 0.0, 0.0);
+        }
+        stag = !stag;
+    }
+    _glMatrixDel(VP_WIN);
+
+    glDisable(GL_STENCIL_TEST);
+
+    _checkError("_renderAP()");
+
+    return TRUE;
+}
+static void      _processHits(int hits, GLuint buffer[], S57_geo *geoData)
+{
+    unsigned int i   = 0;
+    GLuint  *ptr     = NULL;
+    //S57_geo *crntGeo = geoData;
+
+    //PRINTF("number hits record: %d\n", hits);
+    ptr = (GLuint *) buffer;
+    for (i=0; i<hits; i++) {
+        unsigned int j = 0;
+        GLuint nnames   = *ptr;
+
+        //printf(" number of names in this hit record stack: %d \n", nnames);
+        ptr++;
+        //printf("  min win-coord z is %g;", (float) *ptr/0x7fffffff);
+        //printf("first: %d, ", *ptr);
+        ptr++;
+        //printf("  max win-coord z is %g\n", (float) *ptr/0x7fffffff);
+        //printf("last: %d\n", *ptr);
+        ptr++;
+        //printf(" name(s) in stack (bottom first): ");
+
+        if (1 < nnames) {
+            PRINTF("ERROR: selection names stack is >1 \n");
+            exit(0);
+        } else
+            S57_linkObj(geoData, g_ptr_array_index(sobj.geoV, *ptr));
+
+        for (j=0; j<nnames; j++) {
+            //printf("%d ", *ptr);
+            ptr++;
+        }
+
+        //printf("\n\n");
+    }
+}
+
+static int       _initSobj(int szfac)
+// init/grow selection buffer
+{
+    if (szfac > 1) {
+        sobj.sz *= szfac;
+        sobj.buf = g_renew(GLuint, sobj.buf, sobj.sz);
+        sobj.acc = g_renew(GLuint, sobj.acc, sobj.sz);
+    }
+
+    g_ptr_array_set_size(sobj.geoV, 0);
+    sobj.name = 0;
+    sobj.hits = 0;
+    sobj.nac  = 0;
+
+    glSelectBuffer(sobj.sz, sobj.buf);
+
+    return 1;
+}
+
+static int       _accuSobj()
+// accumulate partial result, reurn FALSE if fail
+{
+    // check buffers overflow
+    if (sobj.hits < 0) {
+        PRINTF("ERROR: selection buffer overflow, cursorpick failed\n");
+        return FALSE;
+    } else {
+        if (sobj.nac + sobj.hits*4 >= sobj.sz) {
+            PRINTF("ERROR: selection accumulation buffer overflow: %i\n",
+                   sobj.nac + sobj.hits*4);
+            return FALSE;
+        }
+    }
+
+    if (0 != sobj.hits) {
+        int i;
+
+        if (0 == *sobj.buf) {
+            PRINTF("NOTE: selection return a hit with an empty stack!\n");
+            return TRUE;
+        }
+
+        for (i=0; i<sobj.hits; i++) {
+            // stack of more then one name should occur
+            g_assert(1 == *(sobj.buf+i*4));  
+
+            sobj.acc[sobj.nac++] = *(sobj.buf+i*4+3);
+        }
+    }
+
+    return TRUE;
+}
+
+
+static int       _renderCS(S52_obj *obj)
+{
+    GString *newCS = NULL;
+
+    if (0 == S52_PL_cmpCmdParam(obj, "DEPCNT02")) {
+        PRINTF("DEPCNT02\n"); // do nothig for now --will be handled next
+        //_renderCS_DEPCNT02(obj);
+    }
+
+    if (0 == S52_PL_cmpCmdParam(obj, "LIGHTS05"))
+        PRINTF("LIGHTS05\n"); // do nothig for now --will be handled next
+
+    PRINTF("FATAL ERROR: should not get here!\n");
+    exit(0);
+
+    //newCS = S52_PL_parseCS(obj);
+    if (NULL == newCS)
+        return TRUE;
+
+    return TRUE;
+}
+
+
+//-----------------------------------
+//
+// SYMBOL CREATION SECTION
+//
+//-----------------------------------
+
+static GLint     _renderHPGL(gpointer key, gpointer value, gpointer data)
+// display list generator
+{
+    // Select pen Width unit = .32 mm.
+    // Assume: a width of 1 unit is 1 pixel.
+    // WARNING: offet might need adjustemnt since bounding box
+    // doesn't include line tickness but moment all pattern,
+    // upto PLib 3.2, use a line width of 1.
+
+    // BUG: instruction EP (Edge Polygon), AA (Arc Angle) and SC (Symbol Call)
+    //      are not used in current PLib/Chart-1, so not implemented.
+
+    // NOTE: transparency: 0=0%(opaque), 1=25%, 2=50%, 3=75%
+
+    GLdouble   scaleFac = dotpitch_x * 100;
+    GLenum     fillMode = GLU_SILHOUETTE;
+
+    // hold tesselated vertex, also accumulate GL primitive
+    S57_prim  *vertex   = S57_initPrim(NULL);
+    S52_vCmd   vcmd     = S52_VC_NEW;
+    S52_vec   *vecObj   = S52_PL_initVOCmd((S52_cmdDef*)value);
+    int        inList   = FALSE; // TRUE if we're already building a list
+    int        nlist    = 0;
+
+    // debug
+    //if (70 == _listIndex) {
+    //    PRINTF("%i\n",_listIndex);
+    //}
+    //if (0==strncmp("OBSTRN11",S52_PL_getVOname(vecObj), 8))
+    //    PRINTF("OBSTRN11 found\n");
+
+    //glNewList(_listIndex, GL_COMPILE);
+    S52_PL_setDisplayList((S52_cmdDef*)value, _listIndex);
+    //_listIndex++;
+
+    while (S52_VC_NONE != vcmd) {
+        vcmd = S52_PL_getVOCmd(vecObj);
+
+        switch (vcmd) {
+
+            case S52_VC_NONE: continue;
+
+            case S52_VC_NEW:
+                if (TRUE == inList)
+                    glEndList();
+
+                inList = TRUE;
+                glNewList(_listIndex, GL_COMPILE);
+                _listIndex++;
+                nlist++;
+                continue;
+
+            case S52_VC_SW: { // pen width
+                char pen_w = S52_PL_getVOwidth(vecObj);
+                glLineWidth(pen_w - '0');
+                glPointSize(pen_w - '0');
+                continue;
+            }
+
+            case S52_VC_PM: // poly mode PM0/PM2, fill disk when not in PM
+                fillMode = (GLU_FILL==fillMode) ? GLU_SILHOUETTE : GLU_FILL;
+                continue;
+
+            case S52_VC_CI: {  // circle --draw immediatly
+                GLdouble  radius = S52_PL_getVOradius(vecObj);
+                GArray   *vec    = S52_PL_getVOdata(vecObj);
+                GLdouble *d      = (GLdouble *)vec->data;
+                GLint     i      = 0;
+                GLint     slices = 100;
+                GLint     loops  = 2;
+                GLdouble  inner  = 0.0;            // radius
+                GLdouble  outer  = radius/scaleFac; // radius
+                GLdouble  dr     = (outer-inner) / (GLdouble) loops;
+                //GLdouble  M_PI   = 3.1415926;
+                GLdouble  da     = 2.0 * M_PI / slices;
+
+                glMatrixMode(GL_MODELVIEW);
+                glPushMatrix();
+
+                glTranslated(d[0], d[1], d[2]);
+                //gluQuadricDrawStyle(qobj, fillMode);
+                //gluDisk(qobj, inner, outer, slices, loops);
+
+                if (GLU_FILL == fillMode) {
+                    for (i = 0; i<loops; ++i) {
+                        GLdouble r2 = inner + dr;
+                        GLint    s  = 0;
+
+                        _glBegin(GL_QUAD_STRIP, vertex);
+                        for (s = 0; s <= slices; s++) {
+                            struct {double x,y,z;} pt3 = {0,0,0};
+                            GLdouble sa = 0.0;
+                            GLdouble ca = 0.0;
+                            GLdouble a  = (s == slices)? 0.0 : s * da;
+
+                            sa = sin(a);
+                            ca = cos(a);
+                            pt3.x = r2 * sa;
+                            pt3.y = r2 * ca;
+                            _vertex3d(&pt3, vertex);
+                            pt3.x = inner * sa;
+                            pt3.y = inner * ca;
+                            _vertex3d(&pt3, vertex);
+                        }
+                        _glEnd(vertex);
+                        inner = r2;
+                    }
+
+                } else {  // GLU_SILHOUETTE
+                    GLdouble a;
+                    if (inner != 0.0) {
+                        _glBegin(GL_LINE_LOOP, vertex);
+                        for (a = 0.0; a < 2.0 * M_PI; a += da) {
+                            struct {double x,y,z;} pt3 = {0,0,0};
+                            pt3.x = inner * sin(a);
+                            pt3.y = inner * cos(a);
+                            _vertex3d(&pt3, vertex);
+                        }
+                        _glEnd(vertex);
+                    }
+                    _glBegin(GL_LINE_LOOP, vertex);
+                    for (a = 0; a < 2.0 * M_PI; a += da) {
+                        struct {double x,y,z;} pt3 = {0,0,0};
+                        pt3.x = outer * sin(a);
+                        pt3.y = outer * cos(a);
+                        _vertex3d(&pt3, vertex);
+                    }
+                    _glEnd(vertex);
+                }
+
+                _DrawArrays(vertex);
+                glPopMatrix();
+
+                S57_initPrim(vertex); //reset
+
+                continue;
+            }
+
+            case S52_VC_FP: { // fill poly immediatly
+                guint     i   = 0;
+                GArray   *vec = S52_PL_getVOdata(vecObj);
+                GLdouble *d   = (GLdouble *)vec->data;
+
+                // tess
+                gluTessBeginPolygon(tobj, vertex);
+                gluTessBeginContour(tobj);
+                for (i=0; i<vec->len; ++i, d+=3) {
+                    gluTessVertex(tobj, d, d);
+                    //printf("x/y/z %f/%f/%f\n", d[0],d[1],d[2]);
+                }
+                gluTessEndContour(tobj);
+                gluTessEndPolygon(tobj);
+
+                // draw
+                glEnable(GL_BLEND);
+                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                _DrawArrays(vertex);
+                glDisable(GL_BLEND);
+
+                // clean up
+                for (i=0; i<tmpV->len; ++i)
+                    g_free(g_ptr_array_index(tmpV, i)); // free vertex malloc'd, keep array
+                g_ptr_array_set_size(tmpV, 0);
+
+                S57_initPrim(vertex); //reset
+
+                continue;
+            }
+
+            case S52_VC_PD:    // pen down
+            case S52_VC_PU: {  // pen up
+                int       i   = 0;
+                GArray   *vec = S52_PL_getVOdata(vecObj);
+                GLdouble *d   = (GLdouble *)vec->data;
+
+                if (1 == vec->len)
+                    _glBegin(GL_POINTS,     vertex);
+                else
+                    _glBegin(GL_LINE_STRIP, vertex);
+
+                for (i=0; i<vec->len; ++i, d+=3)
+                    _vertex3d(d, vertex);
+
+                _glEnd(vertex);
+
+                _DrawArrays(vertex);
+                S57_initPrim(vertex); //reset
+                continue;
+            }
+
+            case S52_VC_ST: // transparancy
+            case S52_VC_SP: // color
+            default:
+                PRINTF("ERROR: invalid vector command: (%c)\n", vcmd);
+
+        }
+    } /* while */
+
+    S57_donePrim(vertex);
+
+    // paranoia: this could happen if there is no vector command
+    // (ex only color, pen width, ..)
+    if (TRUE == inList)
+        glEndList();
+
+    // save some data for later
+    {
+        char pen_w = S52_PL_getVOwidth(vecObj);
+        if (patternList)
+            S52_PL_setAPcover((S52_cmdDef*)value, dotpitch_x, dotpitch_y, pen_w);
+        else
+            S52_PL_setLCdata((S52_cmdDef*)value, pen_w);
+    }
+
+    // debug
+    if (nlist != S52_PL_getSubDisplayListNbr((S52_cmdDef*)value)) {
+        PRINTF("FATAL ERROR: mismatch of sub-list number..exiting\n");
+        exit(0);
+    }
+
+    _checkError("_renderHPGL()");
+
+    // debuf
+
+    return 0;   // return 0 to continue BTree traversal
+    //return 1; // return 1 to stop BTree traversal
+}
+
+
+static GLint     _createSymb()
+// WARNING: this must be done from inside the main loop!
+{
+    //GLuint nDisplayList = S52_PL_getTableSz(S52_SMB_PATT)
+    //                    + S52_PL_getTableSz(S52_SMB_LINE)
+    //                    + S52_PL_getTableSz(S52_SMB_SYMB);
+    GLuint nDisplayList = S52_PL_getSubListNbr();
+    //PRINTF("number of new display list = %i \n", nDisplayList);
+
+    _listIndex = glGenLists(nDisplayList);
+    if (0 == _listIndex) {
+        PRINTF("FATAL ERROR: glGenLists() failed .. exiting\n");
+        exit(0);
+    }
+    //PRINTF("total number of display list = %i \n", _listIndex+nDisplayList);
+
+    _glMatrixSet(VP_WIN);
+
+    patternList = TRUE;
+    S52_PL_traverse(S52_SMB_PATT, _renderHPGL);
+    patternList = FALSE;
+    //PRINTF("PATT symbol finish\n");
+
+    S52_PL_traverse(S52_SMB_SYMB, _renderHPGL);
+    //PRINTF("SYMB symbol finish\n");
+
+    S52_PL_traverse(S52_SMB_LINE, _renderHPGL);
+    //PRINTF("LINE symbol finish\n");
+
+    _glMatrixDel(VP_WIN);
+
+    //_fontDList = glGenLists(256);
+    //gdkfont    = gdk_font_load(font[4]);
+    //gdk_gl_use_gdk_font(gdkfont, 0, 256, _fontDList);
+
+    _checkError("_createSymb()");
+
+    return 1;
+}
+
+static GLint     _isONscreen(S52_obj *obj)
+// return TRUE if geo intersec window
+{
+    projUV  xy1, xy2;   // LL UR
+    projXY  w1,  w2;
+
+
+    // bail out if supression is ON (display supressed)
+    if (S52_SUP_ON == S52_PL_getToggleState(obj))
+        return FALSE;
+
+    // draw light sector even if the light itself is outside
+    if (0==strncmp(S52_PL_getOBCL(obj), "LIGHTS", S52_LUP_NMLN))
+        return TRUE;
+
+    // debug
+    return 1;
+
+    // **************************************
+    // FIXME: THIS IS BROKEN FOR POINT OBJECT
+    //
+
+    S57_getExt(S52_PL_getGeo(obj), &xy1.u, &xy1.v, &xy2.u, &xy2.v);
+    xy1 = S57_geo2prj(xy1);
+    xy2 = S57_geo2prj(xy2);
+
+    w1.u = vp[0];
+    w1.v = vp[1];
+    w2.u = vp[2];
+    w2.v = vp[3];
+
+    _glMatrixSet(VP_PRJ);
+    w1 = _win2prj(w1);
+    w2 = _win2prj(w2);
+    _glMatrixDel(VP_PRJ);
+
+    if (_intersec((w1.u), (w2.u), (xy1.u), (xy2.u)) &&
+        _intersec((w1.v), (w2.v), (xy1.v), (xy2.v)) ) {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+//static void      _draw(instruction *instruc, gpointer data)
+static void      _draw(S52_obj *obj)
+{
+    //if ( NULL == cmd) {
+        //PRINTF("ERROR no 'command word' for obj:%s\n", instruc->LUP->OBCL);
+    //}
+
+    // debug
+    /*
+    if (0 == strcmp("HRBFAC", S52_PL_getOBCL(obj))) {
+        PRINTF("HRBFAC found\n");
+        //return;
+    }
+    */
+    //if (0 == strcmp("LNDARE", S52_PL_getOBCL(obj))) {
+    //    PRINTF("LNDARE found\n");
+    //    //return;
+    //}
+
+
+    if (_isONscreen(obj)) {
+        S52_Cmd cmd = S52_PL_getCmdNext(obj);
+
+        _fb_flush();
+        while (S52_CMD_NONE != cmd) {
+            switch (cmd) {
+                case S52_CMD_TXT_TX:
+                case S52_CMD_TXT_TE: _renderEX(obj); break;   // TE&TX
+                case S52_CMD_SYM_PT: _renderSY(obj); break;   // SY
+                case S52_CMD_SIM_LN: _renderLS(obj); break;   // LS
+                case S52_CMD_COM_LN: _renderLC(obj); break;   // LC
+                case S52_CMD_ARE_CO: _renderAC(obj); break;   // AC
+#ifndef INDEXMODE
+                case S52_CMD_ARE_PA: _renderAP(obj); break;   // AP
+#endif
+                //case S52_CMD_CND_SY: _renderCS(obj); break;   // CS
+
+
+                // this is already handled
+                case S52_CMD_OVR_PR: break;
+                /*
+                    if (_dprio != S52_PL_getOPprio(obj)) {
+                        g_ptr_array_add(switchPrio, obj);
+                        PRINTF("object %s has shifted from %i to %i\n",
+                               S52_PL_getOBCL(obj),
+                               _dprio,
+                               S52_PL_getOPprio(obj));
+                    }
+                    PRINTF("ERROR");
+                    exit(0);
+                    break;
+                */
+
+                case S52_CMD_NONE:
+                default:
+                    //PRINTF("ERROR: no cmd for XXX\n");
+                    break;
+
+            }
+            _fb_reset();
+            cmd = S52_PL_getCmdNext(obj);
+            //cmd = S52_PL_getCmdNext(cmd);
+        }
+    }
+
+
+    //glMatrixMode(GL_MODELVIEW);
+
+    //gtk_gl_area_swap_buffers(GTK_GL_AREA(view));
+
+    _checkError("_draw()");
+
+    return;
+}
+
+
+//---------------------------------------------
+//
+// LINE REMOVAL SECTION
+//
+//---------------------------------------------
+
+static gint      _cmpVertex(gconstpointer A, gconstpointer B)
+// compare lookup name
+{
+    // debug: to check for "allmost" overlaping vertex
+
+
+    //double *d1 = (double*) A;
+    //double lonA = *d1++;
+    //double latA = *d1;
+    //double *d2 = (double*) B;
+    //double lonB = *d2++;
+    //double latB = *d2;
+
+    /*
+    int    xa   = latA * 10000.0;
+    int    ya   = lonA * 10000.0;
+    int    xb   = latB * 10000.0;
+    int    yb   = lonB * 10000.0;
+
+    latA = (double) xa / 10000.0;
+    lonA = (double) ya / 10000.0;
+    latB = (double) xb / 10000.0;
+    lonB = (double) yb / 10000.0;
+
+    if (lonA == lonB) {
+        if (latA == latB)
+            return 0;
+
+        if (latA > latB)
+            return  1;
+        else
+            return -1;
+
+    } else {
+        if (lonA > lonB)
+            return  1;
+        else
+            return -1;
+    }
+    */
+
+    return memcmp(A, B, 2 * sizeof(GLdouble));
+
+    //turn comp;
+}
+
+/*
+static int       _loadVertex(instruction *instruc)
+// load vertex of lines (line and area object)
+// FIXME: do area's hole
+{
+    //guint i  = 0;
+    //guint nr = S57_getRingNbr(instruc->geoData);
+    static void  *value = (void*) 0;
+
+    if (NULL == _vertexSet)
+        _vertexSet = g_tree_new(_cmpVertex);
+
+    //for (i=0; i<nr; ++i) {
+        guint j;
+        guint npt;
+        pt3 *ppt;
+
+        //S57_getGeoData(instruc->geoData, i, &npt, (double**)&ppt);
+        S57_getGeoData(instruc->geoData, 0, &npt, (double**)&ppt);
+        if (0 == ++countseg)
+            PRINTF("countseg\n");
+
+        for (j=0; j<npt; ++j, ++ppt) {
+            void *tmp = g_tree_lookup(_vertexSet, ppt);
+
+            if (NULL == tmp)
+                g_tree_insert(_vertexSet, ppt, ++value);
+        }
+    //}
+
+    return 1;
+}
+*/
+/*
+static int       _computeMask(instruction *instruc)
+// eliminate line of lower priority that are masked by line
+// of higher priority by moving them outside of the view volume.
+// NOTE: GL depth test wont do since it work at the pixel level
+//       and this need to work at geometrical level because
+//       of complex line style varing width.
+{
+    static pt3  **m     = NULL;
+    static gint   nnode = 0;
+    static int m_sz = 0;
+
+    // create matrix of mask
+    if (NULL == m) {
+        nnode = g_tree_nnodes(_vertexSet);
+        m_sz  = (nnode*nnode - nnode) / 2;
+        // start form extreme case of 2 vertex only
+        PRINTF("matrix: n=%i, m_sz=%i\n", nnode, m_sz);
+        if (nnode > 1)
+            m = calloc(m_sz, sizeof(pt3*));
+        else
+            return 1;
+    }
+
+    // load matrix
+    {
+        guint i = 0;
+        guint npt;
+        pt3  *ppt;
+
+        S57_getGeoData(instruc->geoData, 0, &npt, (double**)&ppt);
+        ++countseg;
+
+        for (i=0; i<npt-1; ++i) {
+            int n      = nnode - 1;
+            int x, y;
+            int off    = 0;
+            int index1 = (int) g_tree_lookup(_vertexSet, &ppt[i]);
+            int index2 = (int) g_tree_lookup(_vertexSet, &ppt[i+1]);
+
+            if (0==index1 || 0==index2 || index1==index2) {
+                PRINTF("ERROR: index == 0 (NULL) or index1==index2\n");
+                exit(0);
+            }
+
+            if (index1 > index2)
+                x = index1-1, y = index2-1;
+            else
+                y = index1-1, x = index2-1;
+
+            // compute unique offset for all (x,y) and y<x
+            off = ((n-y) * (n-y) - (n-y)) / 2 + (n-x);
+
+            // paranoia check
+            if (off > m_sz) {
+                PRINTF("FATAL ERROR: matrix overflow!\n");
+                exit(0);
+            }
+
+            pt3 *p =  m[off];
+
+            // mask segment --put depth outsize view volume
+            if (NULL != p) {
+                //if (p->z < 15000)
+                p->z = -Z_CLIP_PLANE;
+
+                //PRINTF("MASKING off:%i  x1:%f y1:%f \n", off, p->x, p->y);
+                ++p;
+                //PRINTF("MASKING             x2:%f y2:%f \n", p->x, p->y);
+
+                //if (p->z < 15000)
+                p->z = -Z_CLIP_PLANE;
+            }
+
+            m[off] = &ppt[i];
+        }
+    }
+
+    return 1;
+}
+*/
+static int       _loadLine(S52_obj *obj)
+//static int       _loadLine(instruction *instruc)
+{
+    //S52_CmdL *cmdL = instruc->cmdList;
+    S52_Cmd cmd = S52_PL_getCmdNext(obj);
+
+    while (S52_CMD_NONE == cmd) {
+        //switch (S52_PL_getCmdWord(cmdL)) {
+        switch (cmd) {
+            case S52_CMD_SIM_LN:
+            case S52_CMD_COM_LN:
+                //if (1 == _loadV)
+                    //_loadVertex(instruc);
+                //else
+                    //_computeMask(instruc);
+                break;
+
+            // do nothing
+            case S52_CMD_TXT_TX:
+            case S52_CMD_TXT_TE:
+            case S52_CMD_SYM_PT:
+            case S52_CMD_ARE_CO:
+            case S52_CMD_ARE_PA:
+            case S52_CMD_CND_SY:
+            case S52_CMD_OVR_PR: 
+                break;
+
+            // error
+            case S52_CMD_NONE:
+            default:
+                PRINTF("ERROR no cmd for XXX\n");
+                break;
+
+        }
+        //cmdL = S52_PL_getCmdNext(cmdL);
+        cmd = S52_PL_getCmdNext(obj);
+    }
+
+    return 1;
+}
+
+//static GLint     _pick(instruction *instruc)
+static GLint     _pick(S52_obj *obj)
+// recorde name of object 'drawn' in selection mode
+{
+    g_ptr_array_add(sobj.geoV, (gpointer)obj);
+    glLoadName(sobj.name);
+    _draw(obj);
+    ++sobj.name;
+    return 1;
+}
+
+static int       _doPending(GPtrArray *L)
+// handle pending object priority switch
+{
+    while (switchPrio->len > 0) {
+        S52_diPrio   disPrioIdx = 0;
+        S52_LUPtnm   LUPtypeIdx = 0;
+        //instruction *instruc    = g_ptr_array_remove_index(switchPrio, 0);
+        S52_obj *obj = g_ptr_array_remove_index(switchPrio, 0);
+
+        if (FALSE == g_ptr_array_remove_fast(L, obj)) {
+            PRINTF("ERROR: instrction not in list\n");
+            exit(0);
+        }
+
+        // find new display priority index
+        //disPrioIdx = S52_PL_getDPRI(instruc->LUP);
+        disPrioIdx = S52_PL_getDPRI(obj);
+
+        // find new lookup type index
+        //LUPtypeIdx = S52_PL_getTNAM(instruc->LUP);
+        LUPtypeIdx = S52_PL_getTNAM(obj);
+
+        //g_ptr_array_add(instrucL[disPrioIdx][LUPtypeIdx], instruc);
+        g_ptr_array_add(instrucL[disPrioIdx][LUPtypeIdx], obj);
+
+        if (_dprio > disPrioIdx)
+            _redraw = TRUE;
+    }
+
+    return 1;
+}
+
+/*
+typedef GLint (*f)(instruction *instruc, gpointer user_data);
+static int       _forEachObject(f func, gpointer user_data)
+// call this function on every object
+{
+  int prio = S52_PRIO_NUM;
+  if(getenv("PRIO"))
+    prio = atoi(getenv("PRIO"));
+  if (prio == 0)
+    prio =  S52_PRIO_NUM;
+  
+  for (_dprio=0; _dprio<prio; ++_dprio) {
+        int i = 0;
+
+        // areas
+
+        for (i=0; i<instrucL[_dprio][S52_LUP_SYMBO]->len; ++i)
+            func(g_ptr_array_index(instrucL[_dprio][S52_LUP_SYMBO], i), NULL);
+
+        _doPending(instrucL[_dprio][S52_LUP_SYMBO]);
+
+        // lines
+        for (i=0; i<instrucL[_dprio][S52_LUP_LINES]->len; ++i)
+            func(g_ptr_array_index(instrucL[_dprio][S52_LUP_LINES], i), NULL);
+
+        _doPending(instrucL[_dprio][S52_LUP_LINES]);
+
+        // points
+        for (i=0; i<instrucL[_dprio][S52_LUP_SIMPL]->len; ++i)
+            func(g_ptr_array_index(instrucL[_dprio][S52_LUP_SIMPL], i), NULL);
+
+        _doPending(instrucL[_dprio][S52_LUP_SIMPL]);
+    }
+
+    // FIXME: can we loop here for ever
+    // if an object constantly switch layer
+    if (TRUE == _redraw) {
+        _forEachObject(func, NULL);
+        _redraw = FALSE;
+    }
+
+    return 1;
+}
+*/
+
+/*
+static S57_geo  *_doPick()
+{
+    int i = 0;
+    S57_geo *geoData = NULL;
+    S57_geo *current = NULL;
+
+    _pushMode(GL_SELECT);
+    _forEachObject((gpointer) _pick, NULL);
+    _popMode(GL_SELECT);
+
+    printf("OBJECT FOUND: %i TOTAL SEARCHED: %i\n\n",
+           sobj.nac, sobj.geoV->len);
+
+    //for (i=0; i<sobj.hits; i++) {
+    //for (i=0; i<sobj.nac; i++) {
+    for (i=sobj.nac-1; i>=0; --i) {
+        //int          idx     = _getHit(sobj.acc, i);
+        //int          idx     = *(sobj.buf+4*i+3);
+        int          idx     = *(sobj.acc + i);
+        //int          idx     = *(sobj.acc+4*i+3);
+        instruction *instruc = g_ptr_array_index(sobj.geoV, idx);
+        //S52_LUP     *LUP     = instruc->LUP;
+
+        if (NULL == geoData)
+            geoData = instruc->geoData;
+        else
+            S57_linkObj(current, instruc->geoData);
+
+        current = instruc->geoData;
+
+
+        S52_PL_dumpData();
+
+        printf("%s\t%i/%i DPRI:%i DISC:%c (%s)\n",
+               S52_PL_getOBCL(LUP),
+               idx, sobj.geoV->len,
+               S52_PL_getDPRI(obj),
+               S52_PL_getDISC(obj),
+               S52_PL_infoLUP(obj)
+              );
+
+
+        printf("\tatt name : att value\n");
+        S57_dumpData(instruc->geoData);
+        printf("\n");
+
+    }
+    return geoData;
+}
+*/
+static gint      _printVertex(gpointer key, gpointer value, gpointer data )
+// print vertex
+{
+    int i = (int) value;
+    double *d  = (double*) key;
+    double lon = *d++;
+    double lat = *d;
+
+    PRINTF("%i \t lon/lat: %f %f \n", i, lon, lat);
+
+    return 0;
+}
+
+//---------------------------
+//
+// LIB ENTRY POINT SECTION
+//
+//---------------------------
+
+
+int        S52_GL_draw(S52_obj *obj)
+// draw all
+// later redraw only dirty region
+// later redraw only Group 2 object
+// later ...
+{
+    int numBuffers[2];
+
+    // debug
+    countseg = 0;
+    //found = 0;
+
+    // RIVA 4096/4096
+    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, (int*)&numBuffers);
+    // RIVE 0 (no aux buf!)
+    glGetIntegerv(GL_AUX_BUFFERS, (int*)&numBuffers);
+    //glDrawBuffer();
+    //glViewPort();
+    //glXCreateGLXPixmap();
+    //numBuffers[1] = glXIsDirect(0, data);
+    //view = data;
+
+    if (FALSE == g_static_mutex_trylock(&sobj_mutex)) {
+        PRINTF("TRYLOCK failed\n");
+        return 1;
+    }
+
+    //glEnable(GL_LINE_SMOOTH);  // antialiase is needed for line stippled
+    //glEnable(GL_ALPHA_TEST);
+    /*
+    if (glIsEnabled(GL_ALPHA_TEST)) {
+        GLenum ret;
+
+        PRINTF("GL_ALPHA_TEST = ON \n");
+
+        glGetIntegerv(GL_ALPHA_TEST_FUNC, &ret);
+        PRINTF("GL_ALPHA_TEST_FUNC = %i \n", ret);
+
+        glGetIntegerv(GL_ALPHA_TEST_REF,  &ret);
+        PRINTF("GL_ALPHA_TEST_REF = %i \n", ret);
+    } else
+        PRINTF("GL_ALPHA_TEST = OFF \n");
+    */
+
+    glPushAttrib(GL_ENABLE_BIT);
+
+    glDisable(GL_NORMALIZE);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_DITHER);
+    glShadeModel(GL_FLAT);
+
+    // draw both side
+    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+    if (_initPROJ()) {
+
+        // FIXME: check that this work with Mesa!
+        //if (NULL != _glcontext)
+        //    gdk_gl_make_current(GTK_WIDGET(data)->window, _glcontext);
+
+        // now that the main loop and projection is up ..
+        // whe create all symbol
+#ifdef INDEXMODE
+              _createSymb();
+#else
+        if (0==_listIndex) {
+
+            _createSymb();
+
+            /*
+            _loadV = 1;    // create vertex set
+            _forEachObject(_loadLine);
+            //g_tree_foreach(_vertexSet, _printVertex, NULL);
+            //g_tree_traverse(_vertexSet, _printVertex, G_IN_ORDER, NULL);
+            //exit(0);
+            _loadV = 0;    // compute line mask
+            _forEachObject(_loadLine);
+            */
+        }
+#endif
+
+        // APP ...
+        //
+        // CULL...
+        //
+        // DRAW...
+        if (TRUE == sobj.doPick) {
+            //_doPick();
+
+            //PRINTF("doPick .. returning ..\n");
+            sobj.doPick = FALSE;
+
+        } else {
+            _draw(obj);
+            //_glMatrixSet(VP_PRJ);
+            //_forEachObject((gpointer) _draw, NULL);
+            //_glMatrixDel(VP_PRJ);
+        }
+
+        // check that we're not returning GL in a bizard state
+        g_assert(0 == _topMode);
+    }
+
+    //glDisable(GL_ALPHA_TEST);
+
+    //glDisable(GL_LINE_SMOOTH);
+
+    //glMatrixMode(GL_PROJECTION);
+    //glPopMatrix();
+
+    //glMatrixMode(GL_MODELVIEW);
+    //glPopMatrix();
+
+    glPopAttrib();
+
+    glFlush();
+
+    _checkError("S52_GL_draw()");
+
+    g_static_mutex_unlock(&sobj_mutex);
+
+#ifdef S52_USE_OSG
+    S52_OSG_done();
+    exit(0);
+#endif
+
+    // debug
+    //S52_setMarinerParam(S52_MAR_COLOR_PALETTE, found);
+    //if (++found > 4)
+    //    found = 0;
+
+    return 1;
+}
+
+int        S52_GL_objectSetup(S57_geo *geoData)
+{
+    // WARNING: this will go into a per view handling module
+    //          its definitly not related to GL
+    S52_diPrio   disPrioIdx = 0;
+    S52_LUPtnm   LUPtypeIdx = 0;
+    S52_obj     *obj        = NULL;
+
+    if (NULL == geoData) {
+        PRINTF("ERROR: no geoData object insert!!\n");
+        return 0;
+    }
+
+    obj = S52_PL_getObj(geoData);
+
+    // try to guess extend
+    if (0==strcmp(S57_getName(geoData), "M_COVR")) {
+        S57_getExt(geoData, &pmin.u, &pmin.v, &pmax.u, &pmax.v);
+        _M_COVR = obj;
+    }
+
+    g_assert(S52_PL_getFTYP(obj) == S57_getObjtype(S52_PL_getGeo(obj)));
+
+    // find display priority index
+    disPrioIdx = S52_PL_getDPRI(obj);
+
+    // find lookup type index
+    LUPtypeIdx = S52_PL_getTNAM(obj);
+    
+    g_ptr_array_add(instrucL[disPrioIdx][LUPtypeIdx], obj);
+
+    return 1;
+}
+
+S57_geo*   S52_GL_doPick(double x, double y)
+{
+    if (FALSE == g_static_mutex_trylock(&sobj_mutex)) {
+        PRINTF("TRYLOCK failed\n");
+        return 0;
+    }
+    //_initPROJ();
+    
+//#ifdef INDEXMODE
+//    _createSymb();
+//#else
+//    if (0==_listIndex) {
+//        _createSymb();
+//    }
+//#endif
+
+
+    sobj.x      = x;
+    sobj.y      = y;
+    sobj.doPick = TRUE;
+
+    //S57_geo *geoData = _doPick();
+
+    //PRINTF("doPick .. returning ..\n");
+    sobj.doPick = FALSE;
+    g_static_mutex_unlock(&sobj_mutex);
+
+    //return geoData;
+    return NULL;
+}
+
+int        S52_GL_validCtx()
+// return TRUE if current GL context support S52 rendering
+// NOTE: for now check for stencil buffer (needed to clip pattern)
+{
+    GLint r=0,g=0,b=0,a=0,s=0; // bool true if in RGBA mode
+    GLboolean m=0;
+
+    glGetBooleanv(GL_RGBA_MODE,   &m);
+    //       glGetBooleanv(GL_INDEX_MODE,   &m);
+    glGetIntegerv(GL_RED_BITS,    &r);
+    glGetIntegerv(GL_GREEN_BITS,  &g);
+    glGetIntegerv(GL_BLUE_BITS,   &b);
+    glGetIntegerv(GL_ALPHA_BITS,  &a);
+    glGetIntegerv(GL_STENCIL_BITS,&s);
+    //PRINTF("mode,r,g,b,a,s: %d %d %d %d %d %d \n",m,r,g,b,a,s);
+    // 16 bits:mode,r,g,b,a,s: 1 5 6 5 0 8
+    // 24 bits:mode,r,g,b,a,s: 1 8 8 8 0 8
+    if (s <= 0) {
+        PRINTF("ERROR no stencil buffer for pattern!\n");
+        //exit(0);
+        return 0;
+    }
+
+    // force something
+    //    _glcontext = (GdkGLContext*) 1;
+
+    //return 0; // force init code
+    return 1;
+}
+
+int        S52_GL_init(Display * dpy, Colormap colormap)
+{
+    int i = 0;
+
+    Font font;
+    _display = dpy;
+    _cmap    = colormap;
+
+    S52_PL_load(NULL);
+
+    _initGLmodule();
+
+#ifdef S52_USE_OSG
+    S52_OSG_init();
+#endif
+
+    for (i=0; i<3; ++i) {
+
+        _fontDList[i] = glGenLists(256);
+
+        font = XLoadFont(dpy, _font[i]);
+        glXUseXFont(font, 0, 256, _fontDList[i]);
+    }
+
+/*
+#ifndef INDEXMODE
+    {
+        int old = S52_getMarinerParam(S52_MAR_COLOR_PALETTE);
+
+        currentScreen = DefaultScreen(dpy);
+        printf("CurrentScreen: %d\n", currentScreen);
+
+        S52_setMarinerParam(S52_MAR_COLOR_PALETTE, 0);
+        _toRGB(dpy, colormap, S52_PL_getColorTable());
+
+        S52_setMarinerParam(S52_MAR_COLOR_PALETTE, 1);
+        _toRGB(dpy, colormap, S52_PL_getColorTable());
+
+        S52_setMarinerParam(S52_MAR_COLOR_PALETTE, 2);
+        _toRGB(dpy, colormap, S52_PL_getColorTable());
+
+        S52_setMarinerParam(S52_MAR_COLOR_PALETTE, 3);
+        _toRGB(dpy, colormap, S52_PL_getColorTable());
+
+        S52_setMarinerParam(S52_MAR_COLOR_PALETTE, 4);
+        _toRGB(dpy, colormap, S52_PL_getColorTable());
+
+        S52_setMarinerParam(S52_MAR_COLOR_PALETTE, old);
+    }
+#else
+    _toRGB(dpy, colormap, S52_PL_getColorTable());
+#endif
+*/
+    return 1;
+}
+
+int        S52_GL_done()
+{
+    int j,k;
+    GPtrArray *crnt = NULL;
+
+    //S52_OSG_done();
+
+    S52_PL_done();
+    S57_donePROJ();
+    //old_glcontext;
+    _doneGLobj();
+
+    /*
+    for (_dprio=0; _dprio<S52_PRIO_NUM; ++_dprio) {
+        for (j=0; j<S52_LUP_NUM; ++j) {
+            GPtrArray *instL = instrucL[_dprio][j];
+
+            for (k=0; k<crnt->len; ++k) {
+                instruction *instruc = g_ptr_array_index(instL, k);
+
+                // FIXME: trace this when shuting-down signal will be connected
+
+                //S52_PL_doneCSinst(instruc->CSinst);
+                //S52_PL_doneCmdList(instruc->cmdList);
+                //S52_PL_doneTXT(instruc->text);
+                //S57_doneObject(instruc->geoData);
+
+                g_free(instruc);
+            }
+
+            g_ptr_array_free(crnt, FALSE);
+        }
+    }
+    */
+
+    return 1;
+}
+
+
+//---------------------------
+//
+// MAIN SECTION
+//
+//---------------------------
+
+
+#ifdef S52_GL_TEST
+int        S52_GL_start(char *catalogPath, int checkConfigFile)
+{
+    int i;
+    GtkGLArea *glarea;
+    Extent *ext;
+    double dx,dy;
+    double latPix,lonPix;
+    double pix_w,pix_h;
+    int cellNum = 0;
+    char *catPath = NULL;
+
+    if (checkConfigFile)
+        catPath = _getGonfig(CATALOG);
+
+    if (catPath)
+        cellNum = S57_loadCell(catPath);
+    else
+        cellNum = S57_loadCell(catalogPath);
+
+    _linkObj2cmd();
+    ext = S57_getExtent(0);
+
+    dx = ext->ELON - ext->WLON;
+    dy = ext->NLAT - ext->SLAT;
+
+    // fixme: assume a spherical earth --for the moment.
+    // Also since mercator preserve angle
+    // assume that MercatorPixel(TM) are round!
+    //if (dx<dy){
+
+    pix_h  = 500.0;
+    latPix = dy / pix_h;
+    lonPix = latPix;
+    pix_w  = dx / lonPix;
+
+    /*
+     }else{
+     pix_w  = 1000.0;
+     lonPix = dx / pix_w;
+     latPix = lonPix;
+     pix_h  = dy / latPix;
+     }
+     */
+
+
+
+//   _initGtkFmaps((int)pix_w,(int)pix_h);
+//   _initGtkFmaps((int)1000,(int)500);
+
+    glarea = &fmaps->glarea;
+    // not an OpenGL widget
+    g_return_val_if_fail(
+                         gtk_gl_area_make_current(GTK_GL_AREA(glarea)),FALSE);
+
+    // symb creation need some sort scaling
+    // also test for pattern initialisation
+    _createSymb();
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+
+
+    //gluOrtho2D( ext->WLON, ext->ELON, ext->SLAT, ext->NLAT);
+
+    //glMatrixMode(GL_MODELVIEW);
+    //glLoadIdentity();
+
+    //   glViewport(0,0,1000, 500);
+    //glViewport(0,0,(int)pix_w, (int)pix_h);
+
+    //   glDrawBuffer(GL_BACK);
+    //   glDrawBuffer(GL_FRONT);
+
+
+    //   glClearColor(0,0,100,0); 		// BLUE
+    glClearColor(0,0,0.7,1); 		// BLUE
+
+    glClear(GL_COLOR_BUFFER_BIT);
+
+
+    // draw here ...
+
+
+    glFlush();
+    glFinish();
+
+    printf("CTRL-C to exit ... \n");
+    gtk_main();
+    printf("exiting ... \n");
+
+    return 1;
+}
+
+int main(int argc, char** argv)
+{
+    _GL_init();
+
+    S52_GL_start(argv[1], FALSE);
+
+    //   gtk_main();
+
+    S52_GL_done();
+
+    return 1;
+}
+#endif
+
+

Added: packages/openev/branches/upstream/current/contrib/S52/S52GL.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52GL.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52GL.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,53 @@
+// S52GL.h: display S57 data using S52 symbology and OpenGL.
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC.
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S52GL_H_
+#define _S52GL_H_
+
+#include "S52PL.h"          // S52_LUP
+#include "S57data.h"        // S57_geo
+#include <X11/Xlib.h>       // Display, Colormap
+
+// initialises the dpy and cmap. If cmap is 0, a new one is created.
+extern int      S52_GL_validCtx();
+extern int      S52_GL_init(Display * dpy, Colormap cmap);
+// register S57 object for rendering
+//extern int      S52_GL_objectSetup(S52_LUP *LUP, S57_geo *geoData, void *instruc);
+extern int      S52_GL_objectSetup(S57_geo *geoData);
+// render object
+extern int      S52_GL_draw(S52_obj *obj);
+// next S52_GL_draw call will do cursor pick instead of rendering
+extern S57_geo* S52_GL_doPick(double x, double y);
+// flush objects, clean up mem
+extern int      S52_GL_done();
+
+
+// Christian stuff
+// determine which priorities to draw(should use enumerationxo). 
+extern void     S52_setPrio(int prio, int state);
+
+// apply changes being set by marinerparams
+extern void     S52_changeLUP();
+
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,389 @@
+// S52OSG.cpp: translate and export S52/S57 to OSG (OpenSceneGraph)
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC.
+    Copyright (C) 2004 Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "S52OSG.cpp.h"
+
+/*
+#include "S52OSG.h"
+#include "S52utils.h"
+//#undef glVertexPointer(size, type, stride, ptr)
+
+#include <osg/Group>
+//#include <osg/Node>
+#include <osg/PositionAttitudeTransform>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Drawable>
+#include <osg/Array>
+//#include <osg/ref_ptr>
+#include <osg/PrimitiveSet>
+#include <osg/StateSet>
+#include <osg/StateAttribute>
+
+#include <osgDB/WriteFile>
+
+#include <osgProducer/Viewer>
+
+#include <osg/MatrixTransform>
+
+#include <osg/Depth>
+
+#include <osg/Projection>
+
+#include <string.h>
+
+#include <glib.h>  // g_ptr_array()
+*/
+
+// nodes
+static osg::Group     *_root     = NULL;
+static osg::Group     *_symb     = NULL;
+static osg::Geode     *_geode    = NULL;
+static osg::Geode     *_geodeSym = NULL;
+static osg::Geometry  *_geo      = NULL;
+static osg::Geometry  *_geoSym   = NULL;
+
+static osg::MatrixTransform *_transform = NULL;
+//static osg::PositionAttitudeTransform *_transform = NULL;
+static osg::MatrixTransform *_rootnode  = NULL;
+//static osg::Group *_rootnode  = NULL;
+
+// helper
+static osg::Vec3Array *_coords = NULL;
+static osg::Vec3Array *_coordsSym = NULL;
+
+//typedef GLdouble  _cood
+static struct    _vertex { GLdouble x,y,z; } *_vertex;
+static GLsizei   _vertexCount = 0;
+
+static osg::Vec4Array *_color = NULL;
+
+static int        _inList    = FALSE;   // TRUE if w're loading a call list
+static GPtrArray *_geoArray  = NULL;
+static GLsizei    _rangeList = 0;
+static GLuint     _baseList  = 0;
+
+//static osg::Node     *node  = NULL;
+//osg::ref_ptr<osg::Vec3Array> coords;
+
+//static osgProducer::Viewer viewer;
+
+int    S52_OSG_init() /*fold00*/
+{
+    // tilt the scene so the default eye position is looking down on the model.
+    _rootnode = new osg::MatrixTransform();
+    //_rootnode = new osg::Group();
+    //_rootnode->setMatrix(osg::Matrix::rotate(osg::inDegrees(30.0f),1.0f,0.0f,0.0f));
+    //_rootnode->setMatrix(osg::Matrix::scale(0.01f, 0.01f, 1.0f));
+    //_rootnode->setMatrix(osg::Matrix::scale(10.0f, 10.0f, 1.0f));
+    _rootnode->setMatrix(osg::Matrix::translate(68.5f, -48.5f, 10.0f));
+    _rootnode->postMult(osg::Matrix::scale(100.0, 100.0, 1.0));
+
+    _root  = new osg::Group();
+    _root->ref();  // (!)
+    _rootnode->addChild(_root);
+
+    osg::Depth* depth = new osg::Depth;
+    depth->setFunction(osg::Depth::ALWAYS);
+
+    _geode = new osg::Geode();
+    _geode->ref();
+    osg::StateSet *bin1 = new osg::StateSet;
+    bin1->setRenderBinDetails(1, "RenderBin");
+    //bin1->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+    //bin1->setAttribute(depth);
+    _geode->setStateSet(bin1);
+    _root->addChild(_geode);
+
+    _symb = new osg::Group();
+    _symb->ref();
+
+    osg::StateSet *bin2 = new osg::StateSet;
+    bin2->setRenderBinDetails(2, "RenderBin");
+    //bin2->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+    bin2->setAttribute(depth);
+    _symb->setStateSet(bin2);
+
+    _rootnode->addChild(_symb);
+
+    //_geodeSym = new osg::Geode();
+    //_geodeSym->ref();
+    //_root->addChild(_geodeSym);
+
+
+    return 1;
+}
+
+void   _setNorth() /*FOLD00*/
+{
+    osg::Geode* geode = new osg::Geode();
+    osg::StateSet* stateset = geode->getOrCreateStateSet();
+    //stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
+    stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
+    stateset->setRenderBinDetails(9, "RenderBin");
+    osg::Vec3 position(50.0f,50.0f,0.0f);
+
+    osg::Geometry *geoSym = (osg::Geometry*) g_ptr_array_index(_geoArray, 105);
+    _color->push_back(osg::Vec4(255.0, 0.0, 0.0, 255.0));
+    geoSym->setColorArray(_color);
+    geoSym->setColorBinding(osg::Geometry::BIND_OVERALL);
+    //_geodeSym = new osg::Geode();
+    //_geodeSym->addDrawable(geoSym);
+    geode->addDrawable(geoSym);
+
+    // create the hud.
+    osg::MatrixTransform* modelview_abs = new osg::MatrixTransform;
+    modelview_abs->setReferenceFrame(osg::Transform::RELATIVE_TO_ABSOLUTE);
+    modelview_abs->setMatrix(osg::Matrix::identity());
+    //modelview_abs->addChild(_geodeSym);
+    modelview_abs->addChild(geode);
+
+    osg::Projection* projection = new osg::Projection;
+    projection->setMatrix(osg::Matrix::ortho2D(0,1024,0,768));
+    projection->addChild(modelview_abs);
+
+    _symb->addChild(projection);
+}
+
+int    S52_OSG_done() /*FOLD00*/
+{
+    osgProducer::Viewer viewer;
+
+    std::string *fname = new std::string("test.osg");
+    //_setNorth();
+
+    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
+    viewer.setSceneData(_rootnode);
+
+    viewer.realize();
+
+    while (!viewer.done()) {
+        viewer.sync();
+        viewer.update();
+        viewer.frame();
+    }
+
+    //if (!osgDB::writeNodeFile(*_geode, *fname))
+    if (!osgDB::writeNodeFile(*_rootnode, *fname))
+    //if (!osgDB::writeNodeFile(*_root, *fname))
+        printf("write failed\n");
+
+    return 1;
+}
+
+void   S52_OSG_glVertexPointer(GLint size, GLenum type, /*FOLD00*/
+                               GLsizei stride, const GLvoid *ptr)
+{
+    _vertex      = (struct _vertex*) ptr;
+    _vertexCount = 0;
+
+    // normal
+    osg::Vec3Array *normals = new osg::Vec3Array();
+    normals->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
+
+    if (TRUE == _inList) {
+        _geoSym   = new osg::Geometry();
+        //_geoSym->ref();
+
+        _coordsSym = new osg::Vec3Array();
+        //_coordsSym->ref();
+        _geoSym->setVertexArray(_coordsSym);
+
+        _geoSym->setNormalArray(normals);
+        _geoSym->setNormalBinding(osg::Geometry::BIND_OVERALL);
+
+    } else {
+        _geo   = new osg::Geometry();
+        _geo->ref();
+
+        _coords = new osg::Vec3Array();
+        _coords->ref();
+        _geo->setVertexArray(_coords);
+
+        _geo->setNormalArray(normals);
+        _geo->setNormalBinding(osg::Geometry::BIND_OVERALL);
+    }
+
+    // this will draw all symb
+    //_geode->addDrawable(_geo);
+
+}
+
+void   S52_OSG_glDrawArrays(GLenum mode, GLint first, GLsizei count) /*fold00*/
+{
+    int n = first + count;
+
+    osg::DrawArrays *osgDrawArrays =
+        new osg::DrawArrays(mode, first, count);
+
+
+    // don't set color if building list
+    // skip the rest --will be picked up later
+    if (TRUE == _inList) {
+        _coordsSym->resize(n);
+
+        for (int i=first; i<n; i++) {
+            (*_coordsSym)[i].set(_vertex[i].x, _vertex[i].y, _vertex[i].z);
+            //(*_coordsSym)[i][0] = _vertex[i].x;
+            //(*_coordsSym)[i][1] = _vertex[i].y;
+            //(*_coordsSym)[i][2] = _vertex[i].z;
+        }
+        _geoSym->addPrimitiveSet(osgDrawArrays);
+        //_geodeSym->addDrawable(_geoSym);
+
+    } else {
+        _coords->resize(n);
+
+        for (int i=first; i<n; i++) {
+            (*_coords)[i][0] = _vertex[i].x;
+            (*_coords)[i][1] = _vertex[i].y;
+            (*_coords)[i][2] = _vertex[i].z;
+        }
+        _geo->addPrimitiveSet(osgDrawArrays);
+        _geo->setColorArray(_color);
+        _geo->setColorBinding(osg::Geometry::BIND_OVERALL);
+
+        _geode->addDrawable(_geo);
+    }
+}
+
+
+void   S52_OSG_glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) /*fold00*/
+{
+    //osg::Vec4Array *color = new osg::Vec4Array();
+    _color = new osg::Vec4Array();
+    //_color->ref();
+    _color->push_back(osg::Vec4(red/255.0, green/255.0, blue/255.0, alpha/255.0));
+    //PRINTF(" %f %f %f %f \n", red/255.0, green/255.0, blue/255.0, alpha/255.0);
+}
+
+
+void   S52_OSG_glNewList(GLuint list, GLenum mode) /*fold00*/
+{
+    _inList = TRUE;
+    //_listNo = list;
+}
+
+void   S52_OSG_glEndList() /*fold00*/
+{
+    //osg::Geode geodeSym;
+    //_geoSym.clone(geodeSym);
+
+    _inList = FALSE;
+
+    g_ptr_array_add(_geoArray, (gpointer) _geoSym);
+}
+
+void   S52_OSG_glCallList(GLuint list) /*FOLD00*/
+{
+    GLuint idx = list - _baseList;
+
+    g_assert(_geoArray->len > idx);
+
+    PRINTF("listNo: %i\n", idx);
+
+    osg::Geometry *geoSym = (osg::Geometry*) g_ptr_array_index(_geoArray, idx);
+    if (NULL == geoSym) {
+        PRINTF("ERROR: no display list at %i\n", idx);
+        return;
+    }
+
+    geoSym->setColorArray(_color);
+    geoSym->setColorBinding(osg::Geometry::BIND_OVERALL);
+
+
+    //osg::Geode *geode = new osg::Geode();
+    //geode->addDrawable(geoSym);
+    //_transform->addChild(geode);
+
+    _geodeSym->addDrawable(geoSym);
+    _transform->addChild(_geodeSym);
+}
+
+GLuint S52_OSG_glGenLists(GLsizei range, GLuint base) /*fold00*/
+{
+    if (NULL == _geoArray) {
+        _rangeList = range;
+        _baseList  = base;
+        _geoArray  = g_ptr_array_new();
+    } else {
+        printf("to much gen list");
+        exit(0);
+    }
+
+    return base;
+}
+
+void   S52_OSG_glTranslated(GLdouble x, GLdouble y, GLdouble z) /*fold00*/
+{
+    _transform = new osg::MatrixTransform();
+    _transform->setMatrix(osg::Matrix::translate(x, y, z));
+
+    //_root->addChild(_transform);
+
+
+    _geodeSym = new osg::Geode();
+    _transform->addChild(_geodeSym);
+
+    //osg::Group *symbGrp = new osg::Group();
+    //symbGrp->addChild(_transform);
+
+    _symb->addChild(_transform);
+}
+
+void   S52_OSG_glScaled(GLdouble x, GLdouble y, GLdouble z) /*fold00*/
+{
+    //osg::MatrixTransform *transform = new osg::MatrixTransform();
+    //_transform->setMatrix(osg::Matrix::scale(x, y, z));
+
+    //_geodeSym = new osg::Geode();
+    //transform->addChild(_geodeSym);
+
+    //_transform->addChild(transform);
+
+
+
+    //_transform->setMatrix(osg::Matrix::scale(x, y, z));
+    //_transform->postMult(osg::Matrix::scale(x, y, z));
+    _transform->preMult(osg::Matrix::scale(x, y, z));
+    //_transform *= osg::Matrix::scale(x, y, z);
+}
+
+void   S52_OSG_glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) /*fold00*/
+{
+    //_transform->setMatrix(osg::Matrix::rotate(angle, x, y, z));
+    //_transform->postMult(osg::Matrix::rotate(angle, x, y, z));
+    //_transform->preMult(osg::Matrix::rotate(angle, x, y, z));
+}
+
+
+//========================= S C R A P ================================
+    // set line width
+    /*
+    if (GL_LINE_STRIP == mode) {
+        osg::StateSet* lineStateSet = new osg::StateSet;
+        lineStateSet->setMode(mode, osg::StateAttribute::ON);
+
+        _geo->setStateSet(lineStateSet);
+    }
+    */

Added: packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52OSG.cpp.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,52 @@
+// S52OSG.cpp.h: precompile header S52OSG.cpp
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC.
+    Copyright (C) 2004 Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "S52OSG.h"
+#include "S52utils.h"
+//#undef glVertexPointer(size, type, stride, ptr)
+
+#include <osg/Group>
+//#include <osg/Node>
+#include <osg/PositionAttitudeTransform>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Drawable>
+#include <osg/Array>
+//#include <osg/ref_ptr>
+#include <osg/PrimitiveSet>
+#include <osg/StateSet>
+#include <osg/StateAttribute>
+
+#include <osgDB/WriteFile>
+
+#include <osgProducer/Viewer>
+
+#include <osg/MatrixTransform>
+
+#include <osg/Depth>
+
+#include <osg/Projection>
+
+#include <string.h>
+
+#include <glib.h>  // g_ptr_array()

Added: packages/openev/branches/upstream/current/contrib/S52/S52OSG.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52OSG.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52OSG.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,89 @@
+// S52OSG.h: translate and export S52/S57 to OSG (OpenSceneGraph)
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC.
+    Copyright (C) 2004 Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef _S52OSG_H_
+#define _S52OSG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <GL/gl.h>
+
+#define     glVertexPointer(size, type, stride, ptr)  \
+            glVertexPointer(size, type, stride, ptr), \
+    S52_OSG_glVertexPointer(size, type, stride, ptr);
+
+#define     glDrawArrays(mode, first, count)   \
+            glDrawArrays(mode, first, count),  \
+    S52_OSG_glDrawArrays(mode, first, count);
+
+#define     glColor4ub(red, green, blue, alpha)   \
+            glColor4ub(red, green, blue, alpha),  \
+    S52_OSG_glColor4ub(red, green, blue, alpha);
+
+#define     glNewList(_listIndex, GL_COMPILE)  \
+            glNewList(_listIndex, GL_COMPILE), \
+    S52_OSG_glNewList(_listIndex, GL_COMPILE);
+
+#define     glEndList()  \
+            glEndList(), \
+    S52_OSG_glEndList();
+
+#define     glCallList(list)  \
+            glCallList(list), \
+    S52_OSG_glCallList(list);
+
+#define     glGenLists(range) \
+    S52_OSG_glGenLists(range, glGenLists(range));
+
+#define     glTranslated(x, y, z)  \
+            glTranslated(x, y, z), \
+    S52_OSG_glTranslated(x, y, z);
+
+#define     glScaled(x, y, z)  \
+            glScaled(x, y, z), \
+    S52_OSG_glScaled(x, y, z);
+
+#define     glRotated(angle, x, y, z)  \
+            glRotated(angle, x, y, z), \
+    S52_OSG_glRotated(angle, x, y, z);
+
+    extern int    S52_OSG_init();
+    extern int    S52_OSG_done();
+    extern void   S52_OSG_glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr);
+    extern void   S52_OSG_glDrawArrays(GLenum mode, GLint first, GLsizei count );
+    extern void   S52_OSG_glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);
+    extern void   S52_OSG_glNewList(GLuint list, GLenum mode);
+    extern void   S52_OSG_glEndList();
+    extern void   S52_OSG_glCallList(GLuint list);
+    extern GLuint S52_OSG_glGenLists(GLsizei range, GLuint base);
+    extern void   S52_OSG_glTranslated(GLdouble x, GLdouble y, GLdouble z);
+    extern void   S52_OSG_glScaled(GLdouble x, GLdouble y, GLdouble z);
+    extern void   S52_OSG_glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_S52OSG_H_

Added: packages/openev/branches/upstream/current/contrib/S52/S52PL.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52PL.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52PL.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>502 Bad Gateway</title>
+</head><body>
+<h1>Bad Gateway</h1>
+<p>The proxy server received an invalid
+response from an upstream server.<br />
+</p>
+</body></html>

Added: packages/openev/branches/upstream/current/contrib/S52/S52PL.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52PL.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52PL.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,289 @@
+// S52PL.h: interface to S52 Presentation Library Parser
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S52PLIB_H_
+#define _S52PLIB_H_
+
+#include "S52type.h"     // S52_Obj_t
+#include "S57data.h"     // S57_geo
+#include <glib.h>        // GString, GData, GArray
+
+#define S52_LUP_NMLN   6 // lookup name lenght
+
+// S52 lookup table name (fifth letter)
+typedef enum _S52_LUPtnm {
+    S52_LUP_PLAIN =  0,  //'N', // areas  --PLAIN_BOUNDARIES
+    S52_LUP_SYMBO =  1,  //'O', // areas  --SYMBOLIZED_BOUNDARIES
+    S52_LUP_LINES =  2,  //'S', // lines  --LINES
+    S52_LUP_SIMPL =  3,  //'L', // points --SIMPLIFIED
+    S52_LUP_PAPER =  4,  //'R', // points --PAPER_CHART
+    //S52_LUP_NUM   =  0 , // unknown LUP (META)
+    S52_LUP_NUM   =  5   // number of lookup name
+} S52_LUPtnm;
+
+// S52 symbology table name
+typedef enum _S52_SMBtblName {
+    S52_SMB_LINE = 0,   // Complex Linestyle --line
+    S52_SMB_PATT,       // Pattern           --area
+    S52_SMB_SYMB,       // Symbol            --point
+    S52_SMB_COND,       // Conditional       --point,line,area
+    S52_SMB_NUM         // number of tables
+} S52_SMBtblName;
+
+// S52 Display Priority
+typedef enum _S52_diPrio {
+    S52_PRIO_NODATA = 0, //'0',    // no data fill area pattern
+    S52_PRIO_GROUP1 = 1, //'1',    // S57 group 1 filled areas
+    S52_PRIO_AREA_1 = 2, //'2',    // superimposed areas
+    S52_PRIO_AREA_2 = 3, //'3',    // superimposed areas also water features
+    S52_PRIO_SYM_PT = 4, //'4',    // point symbol also land features
+    S52_PRIO_SYM_LN = 5, //'5',    // line symbol also restricted areas
+    S52_PRIO_SYM_AR = 6, //'6',    // area symbol also traffic areas
+    S52_PRIO_ROUTES = 7, //'7',    // routeing lines
+    S52_PRIO_HAZRDS = 8, //'8',    // hazards
+    S52_PRIO_MARINR = 9, //'9',    // VRM & EBL, own ship
+    S52_PRIO_NUM    = 10,      // number of priority levels
+    S52_PRIO_NOPRIO = '-'    // use default prio (CS)
+} S52_diPrio;
+
+// RADAR Priority
+typedef enum _RadPrio {
+    RAD_OVER = 'O',      // presentation on top of RADAR
+    RAD_SUPP = 'S',      // presentation suppressed by RADAR
+    RAD_NUM  =  2,
+    RAD_NPPR = '-'      // use default prio (CS)
+} S52_RadPrio;
+
+// display categorie type
+typedef enum S52_DisCat {
+    DISPLAYBASE      = 'D',      //
+    STANDARD         = 'S',      //
+    OTHER            = '0',      //
+    MARINERS_STANDARD,           // value not defined
+    MARINERS_OTHER,              // value not defined
+    DISP_CAT_NUM,                // value not defined
+    NO_DISP_CAT      = '-'       // use default categorie (CS)
+} S52_DisCat;
+
+// Command Word
+typedef enum S52_Cmd {
+    S52_CMD_NONE,       // no rule type (init)
+    S52_CMD_TXT_TX,     // TX --SHOWTEXT (formated)
+    S52_CMD_TXT_TE,     // TE --SHOWTEXT
+    S52_CMD_SYM_PT,     // SY --SHOWPOINT
+    S52_CMD_SIM_LN,     // LS --SHOWLINE
+    S52_CMD_COM_LN,     // LC --SHOWLINE
+    S52_CMD_ARE_CO,     // AC --SHOWAREA`
+    S52_CMD_ARE_PA,     // AP --SHOWAREA
+    S52_CMD_CND_SY,     // CS --CALLSYMPROC
+
+    S52_CMD_OVR_PR      // OVERRIDE PRIORITY (not in S52specs).
+                        // Used 8 char to passe data from S52CS ('-' = field not used):
+                        // 0   -S52_diPrio,
+                        // 1   -S52_RadPrio,
+                        // 2   -S52_DisCat,
+                        // 3-7 -viewing group,
+} S52_Cmd;
+
+// color definition
+typedef struct S52_Color {
+    char     colName[5];
+    float    x;
+    float    y;
+    float    L;
+    guchar   R;
+    guchar   G;
+    guchar   B;
+
+    //--- not S52 field --------
+    int      idx;
+    guchar   i;      // index of this color in array,
+    guchar   trans;  // place holder --may vary
+} S52_Color;
+
+// display list sub list for color switch
+#define MAX_SUBLIST 10  // SCALEB10 need to switch color (2 colors)
+//struct _S52_subListData {
+
+typedef struct S52_subListData {
+    // can't tell before hand if trans will
+    char  col[sizeof(S52_Color) * MAX_SUBLIST];
+    guint startList;
+    guint nSubList;
+} S52_subListData;               // be the same for all color C
+
+// Vector Command (a la HPGL)
+typedef enum S52_vCmd {
+    S52_VC_NONE = 0,    // initial / no (more) command
+    S52_VC_NEW  = 'X',  // start new sub-list
+
+    // second character
+    S52_VC_ST = 'T',    // transparency
+    S52_VC_SW = 'W',    // width
+    S52_VC_PU = 'U',    // pen up
+    S52_VC_PD = 'D',    // pen down
+    S52_VC_CI = 'I',    // circle
+    S52_VC_AA = 'A',    // arc
+    S52_VC_SC = 'C',    // call symbol
+
+    S52_VC_PM = 'M',    // polygone mode
+
+    // first charater
+    S52_VC__P = 'P',    // check first character
+    S52_VC_SP = 'S',    // color
+    S52_VC_EP = 'E',    // outline polygone
+    S52_VC_FP = 'F'     // fill polygone
+} S52_vCmd;
+
+// display supression flag
+typedef enum S52_objSup {
+    S52_SUP_OFF  = 0,    // initial object not supressed
+    S52_SUP_ON   = 1,    // display of object is supressed
+    S52_SUP_ERR  = 2     // object not found or not toggled (displaybase)
+
+} S52_objSup;
+
+typedef struct _S52_CmdL   S52_CmdL;
+typedef struct _S52_cmdDef S52_cmdDef;
+typedef struct _S52_LUP    S52_LUP;
+typedef struct _S52_Text   S52_Text;
+typedef struct _S52_vec    S52_vec;
+typedef struct _S52_obj    S52_obj;
+
+// load/init presentation library
+extern int        S52_PL_load(char *PLib);
+// free presentation library
+extern int        S52_PL_done();
+
+// get RGB from color name, for the currently selected color table
+extern S52_Color *S52_PL_getColor(char *colorName);
+// get currently selected color table
+extern GArray    *S52_PL_getColorTable();
+// return color at index, for the currently selected color table
+extern S52_Color *S52_PL_getColorAt(guchar index);
+
+// get a rasterising rules for this S57 object
+//extern S52_LUP   *S52_PL_getLUP(const char * objectName, S57_geo *geoData);
+//extern S52_LUP   *S52_PL_getLUP(S57_geo *geoData);
+extern S52_obj   *S52_PL_getObj(S57_geo *geoData);
+extern S57_geo   *S52_PL_getGeo(S52_obj *obj);
+
+// get LUP name
+extern char      *S52_PL_getOBCL(S52_obj *obj);
+// get addressed object TYPe
+extern S52_Obj_t  S52_PL_getFTYP(S52_obj *obj);
+// get Display PRIority
+extern S52_diPrio S52_PL_getDPRI(S52_obj *obj);
+// get DISplay Category
+extern S52_DisCat S52_PL_getDISC(S52_obj *obj);
+// get LUP table name
+extern S52_LUPtnm S52_PL_getTNAM(S52_obj *obj);
+// return plain text info for this type (TNAM) of lookup
+extern char      *S52_PL_infoLUP(S52_obj *obj);
+// get Condition Symbology instruction
+//extern GString   *S52_PL_getCSinst(S52_LUP *LUP);
+//extern S52_CmdL  *S52_PL_getCSinst(S52_LUP *LUP);
+//extern int        S52_PL_doneCSinst(GString *CSinst);
+
+// command word list handling
+// get next command word in the list
+extern S52_Cmd    S52_PL_getCmdNext(S52_obj *obj);
+// get current command word
+extern S52_Cmd    S52_PL_getCmdWord(S52_obj *obj);
+// compare name to parameter of current command word
+extern int        S52_PL_cmpCmdParam(S52_obj *obj, char *name);
+
+// set (save) display list name (OpenGL)
+extern int        S52_PL_setDisplayList(S52_cmdDef *def, guint listIndex);
+// debug: return the number of sublist for this command
+extern int        S52_PL_getSubDisplayListNbr(S52_cmdDef *def);
+// get display list name (OpenGL)
+extern guint      S52_PL_getDisplayList(S52_obj *obj);
+
+// init vector commands parser
+extern S52_vec   *S52_PL_initVOCmd(S52_cmdDef *def);
+// get next vector command, width in ASCII (1 pixel=0.32 mm)
+extern S52_vCmd   S52_PL_getVOCmd(S52_vec *vecObj);
+// get vextor for this command
+extern S57_prim  *S52_PL_getVOprim(S52_vec *vecObj);
+// get pen width
+extern char       S52_PL_getVOwidth(S52_vec *vecObj);
+// get disk radius
+extern double     S52_PL_getVOradius(S52_vec *vecObj);
+// get vextex array
+extern GArray    *S52_PL_getVOdata(S52_vec *vecObj);
+// get name --debug
+extern char      *S52_PL_getVOname(S52_vec *vecObj);
+
+
+// return symbol orientation [0..360[
+extern double     S52_PL_getSYorient(S52_obj *obj);
+// get Line Style data, width in ASCII (1 pixel=0.32 mm)
+extern int        S52_PL_getLSdata(S52_obj *obj, char *pen_w, char *style, S52_Color **color);
+// set Line Complex data, width in ASCII (1 pixel=0.32 mm)
+extern int        S52_PL_setLCdata(S52_cmdDef *def, char pen_w);
+// get Line Complex data, width in ASCII (1 pixel=0.32 mm), return symbol lenght
+extern double     S52_PL_getLCdata(S52_obj *obj, double dotpitch_x, char *pen_w);
+// get Area Color data
+extern S52_Color *S52_PL_getACdata(S52_obj *obj);
+// get Area Pattern data
+extern int        S52_PL_getAPdata(S52_obj *obj, double dotpitch_x, double dotpitch_y,
+                                   double *tw, double *th, double *dx);
+// set Area Pattern cover data
+extern int        S52_PL_setAPcover(S52_cmdDef *def, double dotpitch_x, double dotpitch_y, char pen_w);
+// get symbol offset
+//extern int        S52_PL_getSymOff(S52_cmdDef* def, int patt, int *off_x, int *off_y);
+// traverse a symbology table calling 'callback' for each entree
+extern gint       S52_PL_traverse(S52_SMBtblName tableNm, GTraverseFunc callBack);
+// return the number of entree in a symbology table
+//extern gint       S52_PL_getTableSz(S52_SMBtblName tableNm);
+
+// return total number of sub-vector
+extern guint      S52_PL_getSubListData(S52_obj *obj, S52_subListData *subListData);
+extern guint      S52_PL_getSubListNbr();
+
+// text parser
+//extern S52_Text  *S52_PL_parseTX(S57_geo *geoData, S52_CmdL *cmd);
+//extern S52_Text  *S52_PL_parseTE(S57_geo *geoData, S52_CmdL *cmd);
+//extern gint       S52_PL_getTEXT(S52_Text  *text, S52_Color **col,
+extern char      *S52_PL_getEX(S52_obj *obj, S52_Color **col,
+                               int *xoffs, int *yoffs, int *bsize, int *weight);
+//extern gint       S52_PL_doneTXT(S52_Text *text);
+
+// parse/compute/expand cond. symb.
+//extern GString   *S52_PL_parseCS(S52_obj *obj);
+
+// compare symbology instruction againt name (0==match)
+//extern int        S52_PL_cmpCSname(S52_obj *obj, char *name);
+// get new display priority
+extern S52_diPrio S52_PL_getOPprio(S52_obj *obj);
+
+// toggle display supression of this type of object
+extern S52_objSup S52_PL_toggleObjType(S52_obj *obj);
+// toggle display suppression of this class of  object
+extern S52_objSup S52_PL_toggleObjClass(char *className);
+// get display state for this type of object
+extern S52_objSup S52_PL_getToggleState(S52_obj *obj);
+
+#endif // _S52PL_H_

Added: packages/openev/branches/upstream/current/contrib/S52/S52glxsimple.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52glxsimple.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52glxsimple.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,191 @@
+// S52glxsimple.c: simple S52 driver using only GLX and OGR.
+//
+// Inspired from glxsimple.c
+//
+//
+
+#include "ogr_api.h"    // OGR*
+
+#include "S52.h"
+#include "S52utils.h"   // PRINTF()
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+#include <stdio.h>
+#include <stdlib.h>       // exit()
+
+
+static int _dblBuf[] = {
+    GLX_RGBA,
+    //GLX_DEPTH_SIZE,     4,
+    GLX_DOUBLEBUFFER,
+    GLX_RED_SIZE,       3,
+    GLX_GREEN_SIZE,     3,
+    GLX_BLUE_SIZE,      3,
+    GLX_STENCIL_SIZE,   1,
+    None
+};
+
+static Display     *_dpy;
+static Window       _win;
+static XVisualInfo *_vi;
+static GLXContext   _cx;
+
+static int _ogr_loadLayer_cb(const char *layername, void *ogrLayer)
+{
+    S52_loadLayer(layername, ogrLayer);
+
+    return 1;
+}
+
+static int _loadCell(const char *filename)
+{
+    S52_loadCell(filename, _ogr_loadLayer_cb);
+
+    return 1;
+}
+
+
+static int _setupData(int loadData)
+{
+    // setup env. var for OGR/S57
+    char *env = g_getenv("OGR_S57_OPTIONS");
+    
+    if (!env ||
+        NULL == strstr(env, "UPDATES:ON")        ||
+        NULL == strstr(env, "LNAM_REFS:ON")      ||
+        NULL == strstr(env, "SPLIT_MULTIPOINT:ON"))
+    {
+#ifdef SOLARIS
+      putenv("OGR_S57_OPTIONS=LNAM_REFS:ON,UPDATES:ON,SPLIT_MULTIPOINT:ON");
+#else
+      setenv("OGR_S57_OPTIONS", "LNAM_REFS:ON,UPDATES:ON,SPLIT_MULTIPOINT:ON", 1);
+#endif
+    }
+
+
+    valueBuf chartPath = {'\0'};
+    if (0 == S52_getConfig(CONF_CHART, &chartPath)) {
+        printf("  .. exiting !\n");
+        exit(0);
+    } else {
+        _loadCell(chartPath);
+    }
+
+    return 1;
+}
+
+static int _initX()
+{
+    Colormap cmap;
+    XSetWindowAttributes swa;
+
+    // create an X colormap since probably not using default visual
+    cmap = XCreateColormap(_dpy, RootWindow(_dpy, _vi->screen),
+                           _vi->visual, AllocNone);
+    swa.colormap     = cmap;
+    swa.border_pixel = 0;
+    swa.event_mask   = ExposureMask | ButtonPressMask | StructureNotifyMask;
+    // create an X window with the selected visual
+    _win = XCreateWindow(_dpy, RootWindow(_dpy, _vi->screen),
+                        0, 0, 493, 493, 0, _vi->depth,
+                        InputOutput, _vi->visual,
+                        CWBorderPixel | CWColormap | CWEventMask, &swa);
+    XSetStandardProperties(_dpy, _win, "S52glxsimple", "S52glxsimple",
+                           //None, argv, argc, NULL);
+                           None, NULL, 0, NULL);
+
+
+    // request the X window to be displayed on the screen
+    XMapWindow(_dpy, _win);
+
+    return TRUE;
+}
+
+static int _initGLX()
+{
+    int             dummy;
+
+    // make sure OpenGL's GLX extension supported
+    if (!glXQueryExtension(_dpy, &dummy, &dummy)) {
+        perror("X server has no OpenGL GLX extension");
+        exit(0);
+    }
+
+    // find an appropriate visual
+    // find an OpenGL-capable RGB visual with depth buffer
+    _vi = glXChooseVisual(_dpy, DefaultScreen(_dpy), _dblBuf);
+    if (_vi == NULL) {
+        PRINTF("ERROR: no RGBA visual");
+        exit(0);
+    }
+
+    if (_vi->class != TrueColor) {
+        perror("TrueColor visual required for this program");
+        exit(0);
+    }
+
+    // create an OpenGL rendering context
+    _cx = glXCreateContext(_dpy, _vi,
+                          None,      // no sharing of display lists
+                          GL_TRUE);  // direct rendering if possible
+    if (_cx == NULL) {
+        perror("could not create GLX rendering context");
+        exit(0);
+    }
+
+    _initX();
+
+    return TRUE;
+}
+
+int main(int argc, char* argv[])
+{
+    // open a connection to the X server
+    _dpy = XOpenDisplay(NULL);
+    if (_dpy == NULL) {
+        perror("could not open display");
+        exit(0);
+    }
+
+    _initGLX();
+
+    // bind the rendering context to the window
+    glXMakeCurrent(_dpy, _win, _cx);
+
+    S52_GL_init(_dpy, 0);
+    _setupData(1);
+
+    if (!S52_GL_validCtx()) {
+      printf("not a proper context. Exiting ...");
+      exit(1);
+    }
+
+    // main loop
+    while (1) {
+        XEvent event;
+
+        do {
+            XNextEvent(_dpy, &event);
+            switch (event.type) {
+                case ConfigureNotify:
+                    ;// glViewport(0, 0, event.xconfigure.width, event.xconfigure.height);
+            }
+        } while (XPending(_dpy));
+
+        glClearColor(0,0,0,1);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        S52_draw();
+
+        glXSwapBuffers(_dpy, _win);
+        //glXMakeCurrent(_dpy, _win, _cx);
+        //glFlush();
+    }
+
+    return 1;
+}
+
+
+

Added: packages/openev/branches/upstream/current/contrib/S52/S52raz-3.2.rle
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52raz-3.2.rle	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52raz-3.2.rle	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,16123 @@
+; S52raz-3.2.rle: rasterazing rule
+;		
+;
+;
+COLS   21CS00002NILDAY_BRIGHT
+CCIE   30NODTA0.28000.310045.00grey
+CCIE   32CURSR0.50000.400032.00orange
+CCIE   30CHBLK0.28000.31000.00black
+CCIE   30CHGRD0.28000.310025.00grey
+CCIE   30CHGRF0.28000.310045.00grey
+CCIE   29CHRED0.48000.300025.00red
+CCIE   31CHGRN0.30000.520060.00green
+CCIE   32CHYLW0.41000.470070.00yellow
+CCIE   33CHMGD0.30000.170020.00magenta
+CCIE   33CHMGF0.28000.240048.00magenta
+CCIE   31CHBRN0.42000.450030.00brown
+CCIE   31CHWHT0.28000.310080.00white
+CCIE   32SCLBR0.50000.400032.00orange
+CCIE   32CHCOR0.50000.400032.00orange
+CCIE   29LITRD0.48000.300025.00red
+CCIE   31LITGN0.30000.520060.00green
+CCIE   32LITYW0.41000.470070.00yellow
+CCIE   33ISDNG0.30000.170020.00magenta
+CCIE   29DNGHL0.48000.300025.00red
+CCIE   33TRFCD0.30000.170020.00magenta
+CCIE   33TRFCF0.28000.240048.00magenta
+CCIE   31LANDA0.36000.400049.00brown
+CCIE   31LANDF0.45000.450015.00brown
+CCIE   30CSTLN0.28000.310010.00grey
+CCIE   30SNDG10.28000.310025.00grey
+CCIE   30SNDG20.28000.31000.00black
+CCIE   30DEPSC0.28000.310010.00grey
+CCIE   30DEPCN0.28000.310025.00grey
+CCIE   31DEPDW0.28000.310080.00white
+CCIE   35DEPMD0.27000.300065.00pale_blue
+CCIE   36DEPMS0.24000.260055.00light_blue
+CCIE   37DEPVS0.22000.240045.00medium_blue
+CCIE   38DEPIT0.28000.360040.00yellow-green
+CCIE   31RADHI0.30000.520060.00green
+CCIE   31RADLO0.30000.520020.00green
+CCIE   36ARPAT0.26000.420030.00blue-green
+CCIE   32NINFO0.50000.400032.00orange
+CCIE   30RESBL0.18000.150022.00blue
+CCIE   32ADINF0.41000.470035.00yellow
+CCIE   30RESGR0.28000.310025.00grey
+CCIE   30SHIPS0.28000.31000.00black
+CCIE   30PSTRK0.28000.31000.00black
+CCIE   30SYTRK0.28000.310025.00grey
+CCIE   29PLRTE0.58000.350018.00red
+CCIE   32APLRT0.50000.400032.00orange
+CCIE   30UINFD0.28000.31000.00black
+CCIE   30UINFF0.28000.310025.00grey
+CCIE   31UIBCK0.28000.310080.00white
+CCIE   37UIAFD0.22000.240045.00medium_blue
+CCIE   29UINFR0.48000.300025.00red
+CCIE   31UINFG0.30000.520060.00green
+CCIE   32UINFO0.50000.400032.00orange
+CCIE   30UINFB0.18000.150022.00blue
+CCIE   33UINFM0.30000.170020.00magenta
+CCIE   30UIBDR0.28000.310025.00grey
+CCIE   31UIAFF0.36000.400049.00brown
+CCIE   30OUTLW0.28000.31000.00black
+CCIE   31OUTLL0.36000.400049.00brown
+CCIE   30RES010.28000.310045.00grey
+CCIE   30RES020.28000.310045.00grey
+CCIE   30RES030.28000.310045.00grey
+CCIE   31BKAJ10.28000.31000.000black
+CCIE   30BKAJ20.28000.31001.600grey
+****    0
+0001    500003
+COLS   24CS00003NILDAY_BLACKBACK
+CCIE   30NODTA0.28000.310025.00grey
+CCIE   32CURSR0.50000.400028.00orange
+CCIE   30CHBLK0.28000.310045.00grey
+CCIE   30CHGRD0.28000.310045.00grey
+CCIE   30CHGRF0.28000.310025.00grey
+CCIE   29CHRED0.48000.300025.00red
+CCIE   31CHGRN0.30000.520060.00green
+CCIE   32CHYLW0.41000.470070.00yellow
+CCIE   33CHMGD0.28000.240053.00magenta
+CCIE   33CHMGF0.30000.170015.00magenta
+CCIE   31CHBRN0.42000.450025.00brown
+CCIE   31CHWHT0.28000.310080.00white
+CCIE   32SCLBR0.50000.400028.00orange
+CCIE   32CHCOR0.50000.400028.00orange
+CCIE   29LITRD0.48000.300025.00red
+CCIE   31LITGN0.30000.520060.00green
+CCIE   32LITYW0.41000.470070.00yellow
+CCIE   33ISDNG0.28000.240053.00magenta
+CCIE   29DNGHL0.48000.300025.00red
+CCIE   33TRFCD0.28000.240053.00magenta
+CCIE   33TRFCF0.30000.170015.00magenta
+CCIE   31LANDA0.36000.400020.00brown
+CCIE   31LANDF0.45000.450045.00brown
+CCIE   30CSTLN0.28000.310045.00grey
+CCIE   30SNDG10.28000.310025.00grey
+CCIE   31SNDG20.28000.310080.00white
+CCIE   30DEPSC0.28000.310045.00grey
+CCIE   30DEPCN0.28000.310025.00grey
+CCIE   30DEPDW0.28000.31000.00black
+CCIE   34DEPMD0.27000.30002.00dark_blue
+CCIE   36DEPMS0.24000.26008.00medium_blue
+CCIE   36DEPVS0.22000.240014.00light_blue
+CCIE   38DEPIT0.26000.360014.00yellow-green
+CCIE   31RADHI0.30000.520060.00green
+CCIE   31RADLO0.30000.520020.00green
+CCIE   36ARPAT0.26000.420050.00blue-green
+CCIE   32NINFO0.50000.400028.00orange
+CCIE   30RESBL0.18000.150022.00blue
+CCIE   32ADINF0.41000.470035.00yellow
+CCIE   30RESGR0.28000.310025.00grey
+CCIE   31SHIPS0.28000.310080.00white
+CCIE   31PSTRK0.28000.310080.00white
+CCIE   30SYTRK0.28000.310025.00grey
+CCIE   29PLRTE0.58000.350018.00red
+CCIE   32APLRT0.50000.400028.00orange
+CCIE   31UINFD0.28000.310080.00white
+CCIE   30UINFF0.28000.310025.00grey
+CCIE   30UIBCK0.28000.31000.00black
+CCIE   36UIAFD0.22000.240014.00light_blue
+CCIE   29UINFR0.48000.300025.00red
+CCIE   31UINFG0.30000.520060.00green
+CCIE   32UINFO0.50000.400028.00orange
+CCIE   30UINFB0.18000.150022.00blue
+CCIE   33UINFM0.30000.170015.00magenta
+CCIE   30UIBDR0.28000.310045.00grey
+CCIE   31UIAFF0.36000.400020.00brown
+CCIE   30OUTLW0.28000.31000.00black
+CCIE   31OUTLL0.36000.400020.00brown
+CCIE   30RES010.28000.310025.00grey
+CCIE   30RES020.28000.310025.00grey
+CCIE   30RES030.28000.310025.00grey
+CCIE   31BKAJ10.28000.31000.000black
+CCIE   30BKAJ20.28000.31001.600grey
+****    0
+0001    500004
+COLS   24CS00004NILDAY_WHITEBACK
+CCIE   31NODTA0.28000.310031.500grey
+CCIE   33CURSR0.50000.400022.400orange
+CCIE   31CHBLK0.28000.31000.000black
+CCIE   31CHGRD0.28000.310017.500grey
+CCIE   31CHGRF0.28000.310031.500grey
+CCIE   30CHRED0.48000.300017.500red
+CCIE   32CHGRN0.30000.520042.000green
+CCIE   33CHYLW0.41000.470049.000yellow
+CCIE   34CHMGD0.30000.170014.000magenta
+CCIE   34CHMGF0.28000.240033.600magenta
+CCIE   32CHBRN0.42000.450021.000brown
+CCIE   32CHWHT0.28000.310056.000white
+CCIE   33SCLBR0.50000.400022.400orange
+CCIE   33CHCOR0.50000.400022.400orange
+CCIE   30LITRD0.48000.300017.500red
+CCIE   32LITGN0.30000.520042.000green
+CCIE   33LITYW0.41000.470049.000yellow
+CCIE   34ISDNG0.30000.170014.000magenta
+CCIE   30DNGHL0.48000.300017.500red
+CCIE   34TRFCD0.30000.170014.000magenta
+CCIE   34TRFCF0.28000.240033.600magenta
+CCIE   32LANDA0.36000.400034.300brown
+CCIE   32LANDF0.45000.450010.500brown
+CCIE   30CSTLN0.28000.31007.000grey
+CCIE   31SNDG10.28000.310017.500grey
+CCIE   31SNDG20.28000.31000.000black
+CCIE   30DEPSC0.28000.31007.000grey
+CCIE   31DEPCN0.28000.310017.500grey
+CCIE   32DEPDW0.28000.310056.000white
+CCIE   36DEPMD0.27000.300045.500pale_blue
+CCIE   37DEPMS0.24000.260038.500light_blue
+CCIE   38DEPVS0.22000.240031.500medium_blue
+CCIE   39DEPIT0.28000.360028.000yellow-green
+CCIE   32RADHI0.30000.520042.000green
+CCIE   32RADLO0.30000.520014.000green
+CCIE   37ARPAT0.26000.420021.000blue-green
+CCIE   33NINFO0.50000.400022.400orange
+CCIE   31RESBL0.18000.150015.400blue
+CCIE   33ADINF0.41000.470024.500yellow
+CCIE   31RESGR0.28000.310017.500grey
+CCIE   31SHIPS0.28000.31000.000black
+CCIE   31PSTRK0.28000.31000.000black
+CCIE   31SYTRK0.28000.310017.500grey
+CCIE   30PLRTE0.58000.350018.000red
+CCIE   33APLRT0.50000.400022.400orange
+CCIE   31UINFD0.28000.31000.000black
+CCIE   31UINFF0.28000.310017.500grey
+CCIE   32UIBCK0.28000.310056.000white
+CCIE   38UIAFD0.22000.240031.500medium_blue
+CCIE   30UINFR0.48000.300017.500red
+CCIE   32UINFG0.30000.520042.000green
+CCIE   33UINFO0.50000.400022.400orange
+CCIE   31UINFB0.18000.150015.400blue
+CCIE   34UINFM0.30000.170014.000magenta
+CCIE   31UIBDR0.28000.310017.500grey
+CCIE   32UIAFF0.36000.400034.300brown
+CCIE   30OUTLW0.28000.31000.00black
+CCIE   32OUTLL0.36000.400034.300brown
+CCIE   31RES010.28000.310031.500grey
+CCIE   31RES020.28000.310031.500grey
+CCIE   31RES030.28000.310031.500grey
+CCIE   32BKAJ10.28000.31000.0000black
+CCIE   31BKAJ20.28000.31001.1200grey
+****    0
+0001    500005
+COLS   15CS00005NILDUSK
+CCIE   30NODTA0.28000.31002.250grey
+CCIE   32CURSR0.50000.39002.520orange
+CCIE   30CHBLK0.28000.31004.050grey
+CCIE   30CHGRD0.28000.31004.050grey
+CCIE   30CHGRF0.28000.31002.250grey
+CCIE   29CHRED0.48000.30002.250red
+CCIE   31CHGRN0.30000.52005.400green
+CCIE   32CHYLW0.41000.47006.300yellow
+CCIE   33CHMGD0.28000.24004.770magenta
+CCIE   34CHMGF0.30000.17001.3500magenta
+CCIE   31CHBRN0.42000.45002.250brown
+CCIE   31CHWHT0.28000.31007.200white
+CCIE   32SCLBR0.50000.39002.520orange
+CCIE   32CHCOR0.50000.39002.520orange
+CCIE   29LITRD0.48000.30002.250red
+CCIE   31LITGN0.30000.52005.400green
+CCIE   32LITYW0.41000.47006.300yellow
+CCIE   33ISDNG0.28000.24004.770magenta
+CCIE   29DNGHL0.48000.30002.250red
+CCIE   33TRFCD0.28000.24004.770magenta
+CCIE   34TRFCF0.30000.17001.3500magenta
+CCIE   31LANDA0.36000.40001.800brown
+CCIE   31LANDF0.45000.45004.050brown
+CCIE   30CSTLN0.28000.31004.050grey
+CCIE   31SNDG10.28000.31002.2500grey
+CCIE   31SNDG20.28000.31007.200white
+CCIE   30DEPSC0.28000.31004.050grey
+CCIE   30DEPCN0.28000.31002.250grey
+CCIE   31DEPDW0.28000.31000.000black
+CCIE   35DEPMD0.27000.30000.180dark_blue
+CCIE   37DEPMS0.24000.26000.720medium_blue
+CCIE   36DEPVS0.22000.24001.260light_blue
+CCIE   38DEPIT0.26000.36001.260yellow-green
+CCIE   31RADHI0.30000.52005.400green
+CCIE   31RADLO0.30000.52001.800green
+CCIE   36ARPAT0.26000.42004.500blue-green
+CCIE   32NINFO0.50000.39002.520orange
+CCIE   30RESBL0.18000.15001.980blue
+CCIE   32ADINF0.41000.47003.150yellow
+CCIE   30RESGR0.28000.31002.250grey
+CCIE   31SHIPS0.28000.31007.200white
+CCIE   31PSTRK0.28000.31007.200white
+CCIE   31SYTRK0.28000.31002.2500grey
+CCIE   29PLRTE0.58000.35001.620red
+CCIE   32APLRT0.50000.39002.520orange
+CCIE   31UINFD0.28000.31007.200white
+CCIE   31UINFF0.28000.31002.2500grey
+CCIE   31UIBCK0.28000.31000.000black
+CCIE   36UIAFD0.22000.24001.260light_blue
+CCIE   29UINFR0.48000.30002.250red
+CCIE   31UINFG0.30000.52005.400green
+CCIE   32UINFO0.50000.39002.520orange
+CCIE   30UINFB0.18000.15001.980blue
+CCIE   34UINFM0.30000.17001.3500magenta
+CCIE   30UIBDR0.28000.31004.050grey
+CCIE   31UIAFF0.36000.40001.800brown
+CCIE   30OUTLW0.28000.31000.00black
+CCIE   31OUTLL0.36000.40001.800brown
+CCIE   30RES010.28000.31002.250grey
+CCIE   30RES020.28000.31002.250grey
+CCIE   30RES030.28000.31002.250grey
+CCIE   31BKAJ10.28000.31000.000black
+CCIE   30BKAJ20.28000.31000.144grey
+****    0
+0001    500006
+COLS   16CS00006NILNIGHT
+CCIE   30NODTA0.28000.31000.00black
+CCIE   31CURSR0.50000.40001.20orange
+CCIE   29CHBLK0.28000.31001.20grey
+CCIE   29CHGRD0.28000.31001.20grey
+CCIE   29CHGRF0.28000.31000.30grey
+CCIE   28CHRED0.58000.35001.00red
+CCIE   30CHGRN0.34000.54001.00green
+CCIE   31CHYLW0.43000.46001.20yellow
+CCIE   32CHMGD0.30000.17001.10magenta
+CCIE   32CHMGF0.30000.17001.10magenta
+CCIE   30CHBRN0.42000.45000.15brown
+CCIE   30CHWHT0.28000.31001.74white
+CCIE   31SCLBR0.50000.40001.20orange
+CCIE   31CHCOR0.50000.40001.20orange
+CCIE   28LITRD0.58000.35001.00red
+CCIE   30LITGN0.34000.54001.00green
+CCIE   31LITYW0.43000.46001.20yellow
+CCIE   32ISDNG0.30000.17001.10magenta
+CCIE   28DNGHL0.58000.35001.00red
+CCIE   32TRFCD0.30000.17001.40magenta
+CCIE   32TRFCF0.30000.17001.10magenta
+CCIE   30LANDA0.36000.36000.10brown
+CCIE   30LANDF0.45000.45000.30brown
+CCIE   30CSTLN0.28000.31001.74white
+CCIE   29SNDG10.28000.31001.20grey
+CCIE   30SNDG20.28000.31002.46white
+CCIE   30DEPSC0.28000.31001.74white
+CCIE   29DEPCN0.28000.31001.20grey
+CCIE   30DEPDW0.28000.31000.00black
+CCIE   30DEPMD0.28000.31000.00black
+CCIE   34DEPMS0.16000.08000.04dark-blue
+CCIE   34DEPVS0.16000.08000.04dark-blue
+CCIE   37DEPIT0.28000.36000.10yellow-green
+CCIE   30RADHI0.34000.54001.00green
+CCIE   30RADLO0.34000.54000.20green
+CCIE   35ARPAT0.26000.42000.80blue-green
+CCIE   31NINFO0.50000.40001.20orange
+CCIE   29RESBL0.18000.13001.20blue
+CCIE   31ADINF0.43000.46001.20yellow
+CCIE   29RESGR0.28000.31000.30grey
+CCIE   30SHIPS0.28000.31001.74white
+CCIE   30PSTRK0.28000.31001.74white
+CCIE   29SYTRK0.28000.31001.20grey
+CCIE   28PLRTE0.58000.35001.30red
+CCIE   31APLRT0.50000.40001.20orange
+CCIE   29UINFD0.28000.31002.46grey
+CCIE   30UINFF0.28000.31001.20white
+CCIE   30UIBCK0.28000.31000.00black
+CCIE   34UIAFD0.16000.08000.04dark-blue
+CCIE   28UINFR0.58000.35001.00red
+CCIE   30UINFG0.34000.54001.00green
+CCIE   31UINFO0.50000.40001.20orange
+CCIE   29UINFB0.18000.13001.20blue
+CCIE   32UINFM0.30000.17001.10magenta
+CCIE   29UIBDR0.28000.31001.20grey
+CCIE   30UIAFF0.36000.36000.10brown
+CCIE   30OUTLW0.28000.31000.00black
+CCIE   30OUTLL0.36000.36000.10brown
+CCIE   30RES010.28000.31000.00black
+CCIE   30RES020.28000.31000.00black
+CCIE   30RES030.28000.31000.00black
+CCIE   30BKAJ10.28000.31000.00black
+CCIE   29BKAJ20.28000.31000.05grey
+****    0
+0001    500007
+LUPT   40LU00007NIL######A00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   30AP(QUESMRK1);LS(DASH,1,CHMGD)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500008
+LUPT   40LU00008NILACHAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(ACHARE51);LS(DASH,2,CHMGF);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500009
+LUPT   40LU00009NILACHAREA00003SPLAIN_BOUNDARIES
+ATTC    8CATACH8
+INST   43SY(ACHARE02);LS(DASH,2,CHMGF);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500010
+LUPT   40LU00010NILACHBRTA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   78SY(ACHBRT07);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29);LS(DASH,2,CHMGF)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500011
+LUPT   40LU00011NILADMAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500012
+LUPT   40LU00012NILAIRAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30AP(AIRARE02);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    500013
+LUPT   40LU00013NILAIRAREA00002SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   40AC(LANDA);AP(AIRARE02);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500014
+LUPT   40LU00014NILBERTHSA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   61SY(BRTHNO01);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500015
+LUPT   40LU00015NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    1
+INST  108TX(OBJNAM,3,1,2,'15110',1,0,CHBLK,21);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500016
+LUPT   40LU00016NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    8CATBRG2
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500017
+LUPT   40LU00017NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    8CATBRG3
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500018
+LUPT   40LU00018NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    8CATBRG4
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500019
+LUPT   40LU00019NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    8CATBRG5
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500020
+LUPT   40LU00020NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    8CATBRG7
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500021
+LUPT   40LU00021NILBRIDGEA00008OPLAIN_BOUNDARIES
+ATTC    8CATBRG8
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500022
+LUPT   40LU00022NILBUAAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   65AC(CHBRN);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26);LS(SOLD,1,LANDF)
+DISC    9STANDARD
+LUCM    622240
+****    0
+0001    500023
+LUPT   40LU00023NILBUISGLA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500024
+LUPT   40LU00024NILBUISGLA00004SPLAIN_BOUNDARIES
+ATTC   17FUNCTN33CONVIS1
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500025
+LUPT   40LU00025NILBUISGLA00004SPLAIN_BOUNDARIES
+ATTC    9FUNCTN33
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500026
+LUPT   40LU00026NILBUISGLA00004SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500027
+LUPT   40LU00027NILCANALSA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500028
+LUPT   40LU00028NILCANALSA00002SPLAIN_BOUNDARIES
+ATTC    7CONDTN
+INST   27AC(DEPVS);LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500029
+LUPT   40LU00029NILCAUSWYA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,CSTLN)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500030
+LUPT   40LU00030NILCAUSWYA00005SPLAIN_BOUNDARIES
+ATTC    8WATLEV4
+INST   27AC(DEPIT);LS(DASH,2,CSTLN)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500031
+LUPT   40LU00031NILCBLAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(CBLARE51);LS(DASH,2,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500032
+LUPT   40LU00032NILCHKPNTA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13SY(POSGEN04)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500033
+LUPT   40LU00033NILCONVYRA00008OPLAIN_BOUNDARIES
+ATTC    1
+INST   70TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11);LS(SOLD,3,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500034
+LUPT   40LU00034NILCONVYRA00008OPLAIN_BOUNDARIES
+ATTC    8CONRAD1
+INST   83SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11);LS(SOLD,3,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500035
+LUPT   40LU00035NILCONVYRA00008OPLAIN_BOUNDARIES
+ATTC    8CONRAD3
+INST   83SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11);LS(SOLD,3,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500036
+LUPT   40LU00036NILCONZNEA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500037
+LUPT   40LU00037NILCOSAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636010
+****    0
+0001    500038
+LUPT   40LU00038NILCRANESA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500039
+LUPT   40LU00039NILCRANESA00004SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500040
+LUPT   40LU00040NILCTNAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(CTNARE51);LS(DASH,2,TRFCD)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500041
+LUPT   40LU00041NILCTSAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(INFARE51);LS(DASH,1,CHMGF)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    500042
+LUPT   40LU00042NILCUSZNEA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHGRF)
+DISC    6OTHER
+LUCM    636020
+****    0
+0001    500043
+LUPT   40LU00043NILDAMCONA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500044
+LUPT   40LU00044NILDAMCONA00006SPLAIN_BOUNDARIES
+ATTC    8CATDAM3
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500045
+LUPT   40LU00045NILDEPAREA00001SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(DEPARE01)
+DISC   12DISPLAYBASE
+LUCM    613030
+****    0
+0001    500046
+LUPT   40LU00046NILDEPAREA00001SPLAIN_BOUNDARIES
+ATTC   16DRVAL1?DRVAL2?
+INST   40AC(NODTA);AP(PRTSUR01);LS(SOLD,2,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    613030
+****    0
+0001    500047
+LUPT   40LU00047NILDMPGRDA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(INFARE51);LS(DASH,1,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626240
+****    0
+0001    500048
+LUPT   40LU00048NILDMPGRDA00003SPLAIN_BOUNDARIES
+ATTC    8CATDPG5
+INST   30LS(DASH,1,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626240
+****    0
+0001    500049
+LUPT   40LU00049NILDOCAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   65AC(DEPVS);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500050
+LUPT   40LU00050NILDOCAREA00002SPLAIN_BOUNDARIES
+ATTC    7CONDTN
+INST   65AC(DEPVS);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26);LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500051
+LUPT   40LU00051NILDRGAREA00001SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(DEPARE01)
+DISC   12DISPLAYBASE
+LUCM    613030
+****    0
+0001    500052
+LUPT   40LU00052NILDRYDOCA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(LANDA);LS(SOLD,1,CSTLN)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500053
+LUPT   40LU00053NILDWRTPTA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   56SY(TSLDEF51);SY(DWRTPT51);LS(DASH,3,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500054
+LUPT   40LU00054NILDWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   63SY(TSSLPT51,ORIENT);SY(DWRTPT51);LS(DASH,3,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500055
+LUPT   40LU00055NILDWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   63SY(TSSLPT51,ORIENT);SY(DWRTPT51);LS(DASH,3,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500056
+LUPT   40LU00056NILDWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   63SY(TSSLPT51,ORIENT);SY(DWRTPT51);LS(DASH,3,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500057
+LUPT   40LU00057NILDWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   63SY(DWRUTE51,ORIENT);SY(DWRTPT51);LS(DASH,3,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500058
+LUPT   40LU00058NILDYKCONA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500059
+LUPT   40LU00059NILEXEZNEA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500060
+LUPT   40LU00060NILFAIRWYA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30LS(DASH,1,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500061
+LUPT   40LU00061NILFAIRWYA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   50SY(FAIRWY51,ORIENT);LS(DASH,1,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500062
+LUPT   40LU00062NILFAIRWYA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   50SY(FAIRWY51,ORIENT);LS(DASH,1,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500063
+LUPT   40LU00063NILFAIRWYA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   50SY(FAIRWY51,ORIENT);LS(DASH,1,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500064
+LUPT   40LU00064NILFAIRWYA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   50SY(FAIRWY52,ORIENT);LS(DASH,1,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500065
+LUPT   40LU00065NILFERYRTA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(FRYARE51);LS(DASH,2,CHMGD)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500066
+LUPT   40LU00066NILFERYRTA00003SPLAIN_BOUNDARIES
+ATTC    8CATFRY2
+INST   30SY(FRYARE52);LS(DASH,2,CHBLK)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500067
+LUPT   40LU00067NILFLODOCA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500068
+LUPT   40LU00068NILFORSTCA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500069
+LUPT   40LU00069NILFORSTCA00004SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500070
+LUPT   40LU00070NILFRPAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636020
+****    0
+0001    500071
+LUPT   40LU00071NILFSHFACA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30AP(FSHHAV02);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500072
+LUPT   40LU00072NILFSHFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATFIF1
+INST   30AP(FSHFAC03);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500073
+LUPT   40LU00073NILFSHFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATFIF2
+INST   30AP(FSHFAC04);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500074
+LUPT   40LU00074NILFSHFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATFIF3
+INST   30AP(FSHFAC04);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500075
+LUPT   40LU00075NILFSHFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATFIF4
+INST   30AP(FSHFAC04);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500076
+LUPT   40LU00076NILFSHGRDA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(FSHGRD01);LS(DASH,2,CHGRF)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    500077
+LUPT   40LU00077NILFSHZNEA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636040
+****    0
+0001    500078
+LUPT   40LU00078NILGATCONA00008SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500079
+LUPT   40LU00079NILGRIDRNA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    632460
+****    0
+0001    500080
+LUPT   40LU00080NILHRBAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    636020
+****    0
+0001    500081
+LUPT   40LU00081NILHRBFACA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500082
+LUPT   40LU00082NILHRBFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATHAF1
+INST   13SY(ROLROL01)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500083
+LUPT   40LU00083NILHRBFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATHAF4
+INST   13SY(HRBFAC09)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500084
+LUPT   40LU00084NILHRBFACA00004SPLAIN_BOUNDARIES
+ATTC    8CATHAF5
+INST   13SY(SMCFAC02)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500085
+LUPT   40LU00085NILHULKESA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500086
+LUPT   40LU00086NILICEAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   40AC(NODTA);AP(ICEARE04);LS(DASH,1,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500087
+LUPT   40LU00087NILICNAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(INFARE51);LS(DASH,1,CHMGF);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    500088
+LUPT   40LU00088NILISTZNEA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(ITZARE51);LS(DASH,1,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500089
+LUPT   40LU00089NILLAKAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500090
+LUPT   40LU00090NILLNDAREA00001SPLAIN_BOUNDARIES
+ATTC    1
+INST   10AC(LANDA)
+DISC   12DISPLAYBASE
+LUCM    612010
+****    0
+0001    500091
+LUPT   40LU00091NILLNDMRKA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500092
+LUPT   40LU00092NILLNDMRKA00004SPLAIN_BOUNDARIES
+ATTC   26CATLMK17FUNCTN33CONVIS1
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500093
+LUPT   40LU00093NILLNDMRKA00004SPLAIN_BOUNDARIES
+ATTC   18CATLMK17FUNCTN33
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500094
+LUPT   40LU00094NILLNDMRKA00004SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500095
+LUPT   40LU00095NILLNDRGNA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500096
+LUPT   40LU00096NILLNDRGNA00003SPLAIN_BOUNDARIES
+ATTC    8CATLND2
+INST   13AP(MARSHES1)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500097
+LUPT   40LU00097NILLNDRGNA00003SPLAIN_BOUNDARIES
+ATTC    9CATLND12
+INST   13AP(MARSHES1)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500098
+LUPT   40LU00098NILLOCMAGA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(LOCMAG51);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500099
+LUPT   40LU00099NILLOGPONA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500100
+LUPT   40LU00100NILLOKBSNA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500101
+LUPT   40LU00101NILM_ACCYA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500102
+LUPT   40LU00102NILM_COVRA00001SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(DATCVR01)
+DISC    6OTHER
+LUCM    631040
+****    0
+0001    500103
+LUPT   40LU00103NILM_CSCLA00001SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(DATCVR01)
+DISC    6OTHER
+LUCM    631040
+****    0
+0001    500104
+LUPT   40LU00104NILM_HOPAA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500105
+LUPT   40LU00105NILM_NPUBA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500106
+LUPT   40LU00106NILM_NSYSA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13LC(MARSYS51)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500107
+LUPT   40LU00107NILM_NSYSA00004SPLAIN_BOUNDARIES
+ATTC   15MARSYS1ORIENT
+INST   37SY(DIRBOYA1,ORIENT);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500108
+LUPT   40LU00108NILM_NSYSA00004SPLAIN_BOUNDARIES
+ATTC   15MARSYS2ORIENT
+INST   37SY(DIRBOYB1,ORIENT);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500109
+LUPT   40LU00109NILM_NSYSA00004SPLAIN_BOUNDARIES
+ATTC    7ORIENT
+INST   37SY(DIRBOY01,ORIENT);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500110
+LUPT   40LU00110NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30AP(NODATA03);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500111
+LUPT   40LU00111NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    8CATZOC1
+INST   30AP(DQUALA11);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500112
+LUPT   40LU00112NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    8CATZOC2
+INST   30AP(DQUALA21);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500113
+LUPT   40LU00113NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    8CATZOC3
+INST   30AP(DQUALB01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500114
+LUPT   40LU00114NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    8CATZOC4
+INST   30AP(DQUALC01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500115
+LUPT   40LU00115NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    8CATZOC5
+INST   30AP(DQUALD01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500116
+LUPT   40LU00116NILM_QUALA00004SPLAIN_BOUNDARIES
+ATTC    8CATZOC6
+INST   30AP(DQUALU01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500117
+LUPT   40LU00117NILM_SDATA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500118
+LUPT   40LU00118NILM_SRELA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500119
+LUPT   40LU00119NILM_VDATA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500120
+LUPT   40LU00120NILMAGVARA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13SY(MAGVAR51)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500121
+LUPT   40LU00121NILMARCULA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   43AP(MARCUL02);LS(DASH,1,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    500122
+LUPT   40LU00122NILMIPAREA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(CTYARE51);LS(DASH,2,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500123
+LUPT   40LU00123NILMORFACA00006SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500124
+LUPT   40LU00124NILOBSTRNA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500125
+LUPT   40LU00125NILOBSTRNA00004SPLAIN_BOUNDARIES
+ATTC    8CATOBS6
+INST   43CS(OBSTRN04);AP(FOULAR01);LS(DOTT,2,CHBLK)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500126
+LUPT   40LU00126NILOBSTRNA00004SPLAIN_BOUNDARIES
+ATTC    8CATOBS7
+INST   30SY(FOULGND1);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500127
+LUPT   40LU00127NILOFSPLFA00005OPLAIN_BOUNDARIES
+ATTC    1
+INST   78AC(CHBRN);LS(SOLD,4,CSTLN);TE('Prod %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500128
+LUPT   40LU00128NILOSPAREA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(CTYARE51);LS(DASH,2,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500129
+LUPT   40LU00129NILPILBOPA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(PILBOP02);LS(DASH,2,TRFCF)
+DISC    9STANDARD
+LUCM    628010
+****    0
+0001    500130
+LUPT   40LU00130NILPIPAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(INFARE51);LS(DASH,2,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500131
+LUPT   40LU00131NILPIPAREA00003SPLAIN_BOUNDARIES
+ATTC    8CATPIP2
+INST   43SY(INFARE51);LS(DASH,2,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500132
+LUPT   40LU00132NILPIPAREA00003SPLAIN_BOUNDARIES
+ATTC    8CATPIP3
+INST   43SY(INFARE51);LS(DASH,2,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500133
+LUPT   40LU00133NILPIPAREA00003SPLAIN_BOUNDARIES
+ATTC    8PRODCT3
+INST   43SY(INFARE51);LS(DASH,2,CHGRD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500134
+LUPT   40LU00134NILPONTONA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500135
+LUPT   40LU00135NILPRCAREA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(PRCARE51);LS(DASH,2,TRFCD);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500136
+LUPT   40LU00136NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500137
+LUPT   40LU00137NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC   16CATPRA5CONVIS1
+INST   30SY(RFNERY11);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500138
+LUPT   40LU00138NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC   16CATPRA8CONVIS1
+INST   30SY(TNKFRM11);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500139
+LUPT   40LU00139NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC   16CATPRA9CONVIS1
+INST   30SY(WNDFRM61);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500140
+LUPT   40LU00140NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC    8CATPRA1
+INST   30SY(QUARRY01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500141
+LUPT   40LU00141NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC    8CATPRA5
+INST   30SY(RFNERY01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500142
+LUPT   40LU00142NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC    8CATPRA6
+INST   30SY(TMBYRD01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500143
+LUPT   40LU00143NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC    8CATPRA8
+INST   30SY(TNKFRM01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500144
+LUPT   40LU00144NILPRDAREA00004SPLAIN_BOUNDARIES
+ATTC    8CATPRA9
+INST   30SY(WNDFRM51);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500145
+LUPT   40LU00145NILPYLONSA00008SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500146
+LUPT   40LU00146NILRADRNGA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,TRFCF)
+DISC    9STANDARD
+LUCM    625040
+****    0
+0001    500147
+LUPT   40LU00147NILRAPIDSA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   10AC(CHGRD)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500148
+LUPT   40LU00148NILRCTLPTA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13SY(RTLDEF51)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500149
+LUPT   40LU00149NILRCTLPTA00004SPLAIN_BOUNDARIES
+ATTC    7ORIENT
+INST   20SY(RCTLPT52,ORIENT)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500150
+LUPT   40LU00150NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(RECDEF51);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500151
+LUPT   40LU00151NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC1
+INST   91SY(RECTRC58,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500152
+LUPT   40LU00152NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC2
+INST   91SY(RECTRC58,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500153
+LUPT   40LU00153NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC3
+INST   91SY(RECTRC58,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500154
+LUPT   40LU00154NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC4
+INST   91SY(RECTRC56,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500155
+LUPT   40LU00155NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC1
+INST   91SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500156
+LUPT   40LU00156NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC2
+INST   91SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500157
+LUPT   40LU00157NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC3
+INST   91SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500158
+LUPT   40LU00158NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC4
+INST   91SY(RECTRC55,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500159
+LUPT   40LU00159NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   91SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500160
+LUPT   40LU00160NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   91SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500161
+LUPT   40LU00161NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   91SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500162
+LUPT   40LU00162NILRECTRCA00006SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   91SY(RECTRC55,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500163
+LUPT   40LU00163NILRESAREA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(RESARE02)
+DISC    9STANDARD
+LUCM    626010
+****    0
+0001    500164
+LUPT   40LU00164NILRIVERSA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500165
+LUPT   40LU00165NILROADWYA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(LANDA);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500166
+LUPT   40LU00166NILRUNWAYA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   10AC(CHBRN)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    500167
+LUPT   40LU00167NILRUNWAYA00005SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500168
+LUPT   40LU00168NILSBDAREA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500169
+LUPT   40LU00169NILSBDAREA00003SPLAIN_BOUNDARIES
+ATTC   15WATLEV3NATSUR
+INST   55TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500170
+LUPT   40LU00170NILSBDAREA00003SPLAIN_BOUNDARIES
+ATTC   16WATLEV4NATSUR9
+INST   30AP(RCKLDG01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500171
+LUPT   40LU00171NILSBDAREA00003SPLAIN_BOUNDARIES
+ATTC   17WATLEV4NATSUR11
+INST   30AP(RCKLDG01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500172
+LUPT   40LU00172NILSBDAREA00003SPLAIN_BOUNDARIES
+ATTC   17WATLEV4NATSUR14
+INST   30AP(RCKLDG01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500173
+LUPT   40LU00173NILSBDAREA00003SPLAIN_BOUNDARIES
+ATTC   15WATLEV4NATSUR
+INST   55TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500174
+LUPT   40LU00174NILSEAAREA00003SPLAIN_BOUNDARIES
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500175
+LUPT   40LU00175NILSILTNKA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500176
+LUPT   40LU00176NILSILTNKA00004SPLAIN_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500177
+LUPT   40LU00177NILSLCONSA00007OPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(SLCONS03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500178
+LUPT   40LU00178NILSLOGRDA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500179
+LUPT   40LU00179NILSLOGRDA00003SPLAIN_BOUNDARIES
+ATTC    8CATSLO6
+INST   10AC(CHGRD)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500180
+LUPT   40LU00180NILSMCFACA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   40AC(CHBRN);SY(SMCFAC02);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    638210
+****    0
+0001    500181
+LUPT   40LU00181NILSNDWAVA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30AP(SNDWAV01);LS(DASH,2,CHGRD)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500182
+LUPT   40LU00182NILSPLAREA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(CTYARE51);LS(DASH,1,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500183
+LUPT   40LU00183NILSUBTLNA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   43SY(CTYARE51);LS(DASH,1,CHMGD);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500184
+LUPT   40LU00184NILSWPAREA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   88SY(SWPARE51);TE('swept to %5.1lf','DRVAL1',1,2,2,'15110',0,1,CHBLK,27);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    623030
+****    0
+0001    500185
+LUPT   40LU00185NILT_HMONA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDEHT01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500186
+LUPT   40LU00186NILT_NHMNA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDEHT01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500187
+LUPT   40LU00187NILT_TIMSA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDEHT01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500188
+LUPT   40LU00188NILTS_FEBA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(CURDEF01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500189
+LUPT   40LU00189NILTS_FEBA00004SPLAIN_BOUNDARIES
+ATTC   15CAT_TS1ORIENT
+INST   73SY(FLDSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500190
+LUPT   40LU00190NILTS_FEBA00004SPLAIN_BOUNDARIES
+ATTC   15CAT_TS2ORIENT
+INST   73SY(EBBSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500191
+LUPT   40LU00191NILTS_FEBA00004SPLAIN_BOUNDARIES
+ATTC   15CAT_TS3ORIENT
+INST   73SY(CURENT01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500192
+LUPT   40LU00192NILTS_PADA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDSTR01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500193
+LUPT   40LU00193NILTS_PNHA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDSTR01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500194
+LUPT   40LU00194NILTS_PRHA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDSTR01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500195
+LUPT   40LU00195NILTS_TISA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TIDSTR01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500196
+LUPT   40LU00196NILTESAREA00002SPLAIN_BOUNDARIES
+ATTC    1
+INST   30LS(DASH,2,CHGRF);CS(RESTRN01)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500197
+LUPT   40LU00197NILTIDEWYA00007SPLAIN_BOUNDARIES
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,25)
+DISC    6OTHER
+LUCM    632070
+****    0
+0001    500198
+LUPT   40LU00198NILTSEZNEA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   12AC(TRFCF,3)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500199
+LUPT   40LU00199NILTSSCRSA00006SPLAIN_BOUNDARIES
+ATTC    1
+INST   39AP(TSSJCT02);SY(TSSCRS51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500200
+LUPT   40LU00200NILTSSLPTA00006SPLAIN_BOUNDARIES
+ATTC    1
+INST   65SY(CTNARE51);TX(INFORM,1,1,2,'15110',0,-2,CHBLK,24);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500201
+LUPT   40LU00201NILTSSLPTA00006SPLAIN_BOUNDARIES
+ATTC    7ORIENT
+INST   33SY(TSSLPT51,ORIENT);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500202
+LUPT   40LU00202NILTSSRONA00006SPLAIN_BOUNDARIES
+ATTC    1
+INST   26SY(TSSRON51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500203
+LUPT   40LU00203NILTUNNELA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500204
+LUPT   40LU00204NILTUNNELA00004SPLAIN_BOUNDARIES
+ATTC    8BURDEP0
+INST   27AC(DEPVS);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500205
+LUPT   40LU00205NILTWRTPTA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(TWRDEF51);LS(DASH,4,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500206
+LUPT   40LU00206NILTWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   37SY(TWRTPT53,ORIENT);LS(DASH,4,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500207
+LUPT   40LU00207NILTWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   37SY(TWRTPT53,ORIENT);LS(DASH,4,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500208
+LUPT   40LU00208NILTWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   37SY(TWRTPT53,ORIENT);LS(DASH,4,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500209
+LUPT   40LU00209NILTWRTPTA00004SPLAIN_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   37SY(TWRTPT52,ORIENT);LS(DASH,4,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500210
+LUPT   40LU00210NILUNSAREA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST   40AC(NODTA);AP(NODATA03);LS(SOLD,2,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    611050
+****    0
+0001    500211
+LUPT   40LU00211NILVEGATNA00000SPLAIN_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500212
+LUPT   40LU00212NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    8CATVEG7
+INST   30AP(VEGATN04);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500213
+LUPT   40LU00213NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG21
+INST   30AP(VEGATN04);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500214
+LUPT   40LU00214NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    8CATVEG3
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500215
+LUPT   40LU00215NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    8CATVEG4
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500216
+LUPT   40LU00216NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    8CATVEG5
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500217
+LUPT   40LU00217NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    8CATVEG6
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500218
+LUPT   40LU00218NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG13
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500219
+LUPT   40LU00219NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG14
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500220
+LUPT   40LU00220NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG15
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500221
+LUPT   40LU00221NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG16
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500222
+LUPT   40LU00222NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG17
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500223
+LUPT   40LU00223NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG18
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500224
+LUPT   40LU00224NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG19
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500225
+LUPT   40LU00225NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG20
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500226
+LUPT   40LU00226NILVEGATNA00003SPLAIN_BOUNDARIES
+ATTC    9CATVEG22
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500227
+LUPT   40LU00227NILWATTURA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(WATTUR02);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633040
+****    0
+0001    500228
+LUPT   40LU00228NILWEDKLPA00003OPLAIN_BOUNDARIES
+ATTC    1
+INST   30SY(WEDKLP03);LS(DASH,1,CHGRF)
+DISC    6OTHER
+LUCM    634020
+****    0
+0001    500229
+LUPT   40LU00229NILWRECKSA00004SPLAIN_BOUNDARIES
+ATTC    1
+INST   13CS(WRECKS02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500230
+LUPT   40LU00230NILWRECKSA00004SPLAIN_BOUNDARIES
+ATTC    8CATWRK3
+INST   17LS(DASH,1,CHBLK)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500231
+LUPT   40LU00231NILdnghltA00008SPLAIN_BOUNDARIES
+ATTC    1
+INST   29AC(DNGHL,3);LS(SOLD,3,DNGHL)
+DISC   18MARINERS STANDARD
+LUCM    653010
+****    0
+0001    500232
+LUPT   40LU00232NILmarfeaA00008SPLAIN_BOUNDARIES
+ATTC    1
+INST   85AC(ADINF,3);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,50);LS(SOLD,2,NINFO);LS(SOLD,1,CHBLK);
+DISC   18MARINERS STANDARD
+LUCM    653050
+****    0
+0001    500233
+LUPT   40LU00233NILmnufeaA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,ADINF)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    500234
+LUPT   40LU00234NIL$AREASA00005SPLAIN_BOUNDARIES
+ATTC    1
+INST   13AP(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500235
+LUPT   40LU00235NIL$AREASA00005SPLAIN_BOUNDARIES
+ATTC   15$SCODECHCRDEL1
+INST   26SY(CHCRDEL1);LC(CHCRDEL1)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500236
+LUPT   40LU00236NIL$AREASA00005SPLAIN_BOUNDARIES
+ATTC   15$SCODECHCRID01
+INST   26SY(CHCRID01);LC(CHCRID01)
+DISC    9STANDARD
+LUCM    623030
+****    0
+0001    500237
+LUPT   40LU00237NIL$AREASA00003OPLAIN_BOUNDARIES
+ATTC   15$SCODEOVERSC01
+INST   13AP(OVERSC01)
+DISC    9STANDARD
+LUCM    621030
+****    0
+0001    500238
+LUPT   40LU00238NIL$AREASA00005SPLAIN_BOUNDARIES
+ATTC   15$SCODEQUESMRK1
+INST   30AP(QUESMRK1);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500239
+LUPT   40LU00239NIL$AREASA00003SPLAIN_BOUNDARIES
+ATTC   15$SCODEDIAMOND1
+INST   13AP(DIAMOND1)
+DISC    9STANDARD
+LUCM    623010
+****    0
+0001    500240
+LUPT   40LU00240NIL$AREASA00002OPLAIN_BOUNDARIES
+ATTC   17$SCODEBACKGROUND
+INST   27AC(NODTA);LS(SOLD,2,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500241
+LUPT   40LU00241NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX1
+INST   62AC(DEPDW);LS(SOLD,1,CHGRD);TX('1',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500242
+LUPT   40LU00242NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX2
+INST   62AC(NODTA);LS(SOLD,1,CHGRD);TX('2',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500243
+LUPT   40LU00243NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX3
+INST   62AC(DEPVS);LS(SOLD,1,CHGRD);TX('3',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500244
+LUPT   40LU00244NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX4
+INST   62AC(NODTA);LS(SOLD,1,CHGRD);TX('4',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500245
+LUPT   40LU00245NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX5
+INST   62AC(DEPVS);LS(SOLD,1,CHGRD);TX('5',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500246
+LUPT   40LU00246NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX6
+INST   62AC(LANDA);LS(SOLD,1,CHGRD);TX('6',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500247
+LUPT   40LU00247NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX7
+INST   62AC(DEPDW);LS(SOLD,1,CHGRD);TX('7',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500248
+LUPT   40LU00248NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX8
+INST   62AC(DEPDW);LS(SOLD,1,CHGRD);TX('8',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500249
+LUPT   40LU00249NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   11$SCODEBOX9
+INST   62AC(NODTA);LS(SOLD,1,CHGRD);TX('9',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500250
+LUPT   40LU00250NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX10
+INST   63AC(DEPDW);LS(SOLD,1,CHGRD);TX('10',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500251
+LUPT   40LU00251NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX11
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('11',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500252
+LUPT   40LU00252NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX12
+INST   63AC(NODTA);LS(SOLD,1,CHGRD);TX('12',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500253
+LUPT   40LU00253NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX13
+INST   63AC(DEPDW);LS(SOLD,1,CHGRD);TX('13',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500254
+LUPT   40LU00254NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX14
+INST   63AC(LANDA);LS(SOLD,1,CHGRD);TX('14',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500255
+LUPT   40LU00255NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX15
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('15',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500256
+LUPT   40LU00256NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX16
+INST   63AC(NODTA);LS(SOLD,1,CHGRD);TX('16',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500257
+LUPT   40LU00257NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX17
+INST   63AC(LANDA);LS(SOLD,1,CHGRD);TX('17',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500258
+LUPT   40LU00258NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX18
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('18',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500259
+LUPT   40LU00259NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX19
+INST   63AC(DEPDW);LS(SOLD,1,CHGRD);TX('19',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500260
+LUPT   40LU00260NIL$AREASA00005OPLAIN_BOUNDARIES
+ATTC   12$SCODEBOX20
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('20',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500261
+LUPT   40LU00261NIL$TEXTSA00007OPLAIN_BOUNDARIES
+ATTC    1
+INST   59TX($TXSTR,$JUSTH=3,$JUSTV=1,$SPACE=2,'15110',0,0,CHBLK,27)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500262
+LUPT   45LU00262NIL######A00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30AP(QUESMRK1);LS(DASH,1,CHMGD)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500263
+LUPT   45LU00263NILACHAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(ACHARE51);LC(ACHARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500264
+LUPT   45LU00264NILACHAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATACH8
+INST   43SY(ACHARE02);LS(DASH,2,CHMGF);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500265
+LUPT   45LU00265NILACHBRTA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   78SY(ACHBRT07);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29);LS(DASH,2,CHMGF)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500266
+LUPT   45LU00266NILADMAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500267
+LUPT   45LU00267NILAIRAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30AP(AIRARE02);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    500268
+LUPT   45LU00268NILAIRAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   40AC(LANDA);AP(AIRARE02);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500269
+LUPT   45LU00269NILBERTHSA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   61SY(BRTHNO01);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500270
+LUPT   45LU00270NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST  108TX(OBJNAM,3,1,2,'15110',1,0,CHBLK,21);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500271
+LUPT   45LU00271NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CATBRG2
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500272
+LUPT   45LU00272NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CATBRG3
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500273
+LUPT   45LU00273NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CATBRG4
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500274
+LUPT   45LU00274NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CATBRG5
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500275
+LUPT   45LU00275NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CATBRG7
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500276
+LUPT   45LU00276NILBRIDGEA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CATBRG8
+INST  142SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11);LS(SOLD,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500277
+LUPT   45LU00277NILBUAAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   65AC(CHBRN);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26);LS(SOLD,1,LANDF)
+DISC    9STANDARD
+LUCM    622240
+****    0
+0001    500278
+LUPT   45LU00278NILBUISGLA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500279
+LUPT   45LU00279NILBUISGLA00004SSYMBOLIZED_BOUNDARIES
+ATTC   17FUNCTN33CONVIS1
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500280
+LUPT   45LU00280NILBUISGLA00004SSYMBOLIZED_BOUNDARIES
+ATTC    9FUNCTN33
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500281
+LUPT   45LU00281NILBUISGLA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500282
+LUPT   45LU00282NILCANALSA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500283
+LUPT   45LU00283NILCANALSA00002SSYMBOLIZED_BOUNDARIES
+ATTC    7CONDTN
+INST   27AC(DEPVS);LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500284
+LUPT   45LU00284NILCAUSWYA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,CSTLN)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500285
+LUPT   45LU00285NILCAUSWYA00005SSYMBOLIZED_BOUNDARIES
+ATTC    8WATLEV4
+INST   27AC(DEPIT);LS(DASH,2,CSTLN)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500286
+LUPT   45LU00286NILCBLAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(CBLARE51);LC(CBLARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500287
+LUPT   45LU00287NILCHKPNTA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13SY(POSGEN04)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500288
+LUPT   45LU00288NILCONVYRA00008OSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   70TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11);LS(SOLD,3,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500289
+LUPT   45LU00289NILCONVYRA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CONRAD1
+INST   83SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11);LS(SOLD,3,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500290
+LUPT   45LU00290NILCONVYRA00008OSYMBOLIZED_BOUNDARIES
+ATTC    8CONRAD3
+INST   83SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11);LS(SOLD,3,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500291
+LUPT   45LU00291NILCONZNEA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500292
+LUPT   45LU00292NILCOSAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636010
+****    0
+0001    500293
+LUPT   45LU00293NILCRANESA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500294
+LUPT   45LU00294NILCRANESA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500295
+LUPT   45LU00295NILCTNAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(CTNARE51);LC(CTNARE51)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500296
+LUPT   45LU00296NILCTSAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(INFARE51);LC(CTYARE51)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    500297
+LUPT   45LU00297NILCUSZNEA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHGRF)
+DISC    6OTHER
+LUCM    636020
+****    0
+0001    500298
+LUPT   45LU00298NILDAMCONA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500299
+LUPT   45LU00299NILDAMCONA00006SSYMBOLIZED_BOUNDARIES
+ATTC    8CATDAM3
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500300
+LUPT   45LU00300NILDEPAREA00001SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(DEPARE01)
+DISC   12DISPLAYBASE
+LUCM    613030
+****    0
+0001    500301
+LUPT   45LU00301NILDEPAREA00001SSYMBOLIZED_BOUNDARIES
+ATTC   16DRVAL1?DRVAL2?
+INST   40AC(NODTA);AP(PRTSUR01);LS(SOLD,2,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    613030
+****    0
+0001    500302
+LUPT   45LU00302NILDMPGRDA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(INFARE51);LC(CTYARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626240
+****    0
+0001    500303
+LUPT   45LU00303NILDMPGRDA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATDPG5
+INST   26LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626240
+****    0
+0001    500304
+LUPT   45LU00304NILDOCAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   65AC(DEPVS);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500305
+LUPT   45LU00305NILDOCAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    7CONDTN
+INST   65AC(DEPVS);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26);LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500306
+LUPT   45LU00306NILDRGAREA00001SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(DEPARE01)
+DISC   12DISPLAYBASE
+LUCM    613030
+****    0
+0001    500307
+LUPT   45LU00307NILDRYDOCA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(LANDA);LS(SOLD,1,CSTLN)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500308
+LUPT   45LU00308NILDWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   52SY(TSLDEF51);SY(DWRTPT51);LC(DWRUTE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500309
+LUPT   45LU00309NILDWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   59SY(TSSLPT51,ORIENT);SY(DWRTPT51);LC(DWRUTE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500310
+LUPT   45LU00310NILDWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   59SY(TSSLPT51,ORIENT);SY(DWRTPT51);LC(DWRUTE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500311
+LUPT   45LU00311NILDWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   59SY(TSSLPT51,ORIENT);SY(DWRTPT51);LC(DWRUTE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500312
+LUPT   45LU00312NILDWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   59SY(DWRUTE51,ORIENT);SY(DWRTPT51);LC(DWRUTE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500313
+LUPT   45LU00313NILDYKCONA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500314
+LUPT   45LU00314NILEXEZNEA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500315
+LUPT   45LU00315NILFAIRWYA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500316
+LUPT   45LU00316NILFAIRWYA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   46SY(FAIRWY51,ORIENT);LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500317
+LUPT   45LU00317NILFAIRWYA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   46SY(FAIRWY51,ORIENT);LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500318
+LUPT   45LU00318NILFAIRWYA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   46SY(FAIRWY51,ORIENT);LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500319
+LUPT   45LU00319NILFAIRWYA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   46SY(FAIRWY52,ORIENT);LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500320
+LUPT   45LU00320NILFERYRTA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(FRYARE51);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500321
+LUPT   45LU00321NILFERYRTA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATFRY2
+INST   26SY(FRYARE52);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500322
+LUPT   45LU00322NILFLODOCA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500323
+LUPT   45LU00323NILFORSTCA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500324
+LUPT   45LU00324NILFORSTCA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500325
+LUPT   45LU00325NILFRPAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636020
+****    0
+0001    500326
+LUPT   45LU00326NILFSHFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(FSHFAC02);LC(NAVARE51)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500327
+LUPT   45LU00327NILFSHFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATFIF1
+INST   26SY(FSHFAC03);LC(NAVARE51)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500328
+LUPT   45LU00328NILFSHFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATFIF2
+INST   26SY(FSHFAC02);LC(NAVARE51)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500329
+LUPT   45LU00329NILFSHFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATFIF3
+INST   26SY(FSHFAC02);LC(NAVARE51)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500330
+LUPT   45LU00330NILFSHFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATFIF4
+INST   26SY(FSHFAC02);LC(NAVARE51)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500331
+LUPT   45LU00331NILFSHGRDA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30SY(FSHGRD01);LS(DASH,2,CHGRF)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    500332
+LUPT   45LU00332NILFSHZNEA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    6OTHER
+LUCM    636040
+****    0
+0001    500333
+LUPT   45LU00333NILGATCONA00008SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500334
+LUPT   45LU00334NILGRIDRNA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    632460
+****    0
+0001    500335
+LUPT   45LU00335NILHRBAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13LC(NAVARE51)
+DISC    6OTHER
+LUCM    636020
+****    0
+0001    500336
+LUPT   45LU00336NILHRBFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500337
+LUPT   45LU00337NILHRBFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATHAF1
+INST   13SY(ROLROL01)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500338
+LUPT   45LU00338NILHRBFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATHAF4
+INST   13SY(HRBFAC09)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500339
+LUPT   45LU00339NILHRBFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATHAF5
+INST   13SY(SMCFAC02)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500340
+LUPT   45LU00340NILHULKESA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500341
+LUPT   45LU00341NILICEAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   40AC(NODTA);AP(ICEARE04);LS(DASH,1,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500342
+LUPT   45LU00342NILICNAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(INFARE51);LC(CTYARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    500343
+LUPT   45LU00343NILISTZNEA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(ITZARE51);LC(RESARE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500344
+LUPT   45LU00344NILLAKAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500345
+LUPT   45LU00345NILLNDAREA00001SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   10AC(LANDA)
+DISC   12DISPLAYBASE
+LUCM    612010
+****    0
+0001    500346
+LUPT   45LU00346NILLNDMRKA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500347
+LUPT   45LU00347NILLNDMRKA00004SSYMBOLIZED_BOUNDARIES
+ATTC   26CATLMK17FUNCTN33CONVIS1
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500348
+LUPT   45LU00348NILLNDMRKA00004SSYMBOLIZED_BOUNDARIES
+ATTC   18CATLMK17FUNCTN33
+INST   65AC(CHBRN);TX(OBJNAM,1,2,2,'15110',0,0,CHBLK,26);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500349
+LUPT   45LU00349NILLNDMRKA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500350
+LUPT   45LU00350NILLNDRGNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500351
+LUPT   45LU00351NILLNDRGNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATLND2
+INST   13AP(MARSHES1)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500352
+LUPT   45LU00352NILLNDRGNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATLND12
+INST   13AP(MARSHES1)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500353
+LUPT   45LU00353NILLOCMAGA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(LOCMAG51);LC(NAVARE51)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500354
+LUPT   45LU00354NILLOGPONA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500355
+LUPT   45LU00355NILLOKBSNA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500356
+LUPT   45LU00356NILM_ACCYA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500357
+LUPT   45LU00357NILM_COVRA00001SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(DATCVR01)
+DISC    6OTHER
+LUCM    631040
+****    0
+0001    500358
+LUPT   45LU00358NILM_CSCLA00001SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(DATCVR01)
+DISC    6OTHER
+LUCM    631040
+****    0
+0001    500359
+LUPT   45LU00359NILM_HOPAA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500360
+LUPT   45LU00360NILM_NPUBA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500361
+LUPT   45LU00361NILM_NSYSA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13LC(MARSYS51)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500362
+LUPT   45LU00362NILM_NSYSA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15MARSYS1ORIENT
+INST   33SY(DIRBOYA1,ORIENT);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500363
+LUPT   45LU00363NILM_NSYSA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15MARSYS2ORIENT
+INST   33SY(DIRBOYB1,ORIENT);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500364
+LUPT   45LU00364NILM_NSYSA00004SSYMBOLIZED_BOUNDARIES
+ATTC    7ORIENT
+INST   33SY(DIRBOY01,ORIENT);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500365
+LUPT   45LU00365NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30AP(NODATA03);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500366
+LUPT   45LU00366NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATZOC1
+INST   30AP(DQUALA11);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500367
+LUPT   45LU00367NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATZOC2
+INST   30AP(DQUALA21);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500368
+LUPT   45LU00368NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATZOC3
+INST   30AP(DQUALB01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500369
+LUPT   45LU00369NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATZOC4
+INST   30AP(DQUALC01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500370
+LUPT   45LU00370NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATZOC5
+INST   30AP(DQUALD01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500371
+LUPT   45LU00371NILM_QUALA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATZOC6
+INST   30AP(DQUALU01);LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    631010
+****    0
+0001    500372
+LUPT   45LU00372NILM_SDATA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500373
+LUPT   45LU00373NILM_SRELA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500374
+LUPT   45LU00374NILM_VDATA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500375
+LUPT   45LU00375NILMAGVARA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13SY(MAGVAR51)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500376
+LUPT   45LU00376NILMARCULA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39AP(MARCUL02);LC(NAVARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    500377
+LUPT   45LU00377NILMIPAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(CTYARE51);LC(CTYARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500378
+LUPT   45LU00378NILMORFACA00006SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500379
+LUPT   45LU00379NILOBSTRNA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500380
+LUPT   45LU00380NILOBSTRNA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATOBS6
+INST   43CS(OBSTRN04);AP(FOULAR01);LS(DOTT,2,CHBLK)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500381
+LUPT   45LU00381NILOBSTRNA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATOBS7
+INST   26SY(FOULGND1);LC(NAVARE51)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500382
+LUPT   45LU00382NILOFSPLFA00005OSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   78AC(CHBRN);LS(SOLD,4,CSTLN);TE('Prod %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500383
+LUPT   45LU00383NILOSPAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(CTYARE51);LC(CTYARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500384
+LUPT   45LU00384NILPILBOPA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(PILBOP02);LC(CTYARE51)
+DISC    9STANDARD
+LUCM    628010
+****    0
+0001    500385
+LUPT   45LU00385NILPIPAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(INFARE51);LC(PIPARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500386
+LUPT   45LU00386NILPIPAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPIP2
+INST   39SY(INFARE51);LC(PIPARE61);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500387
+LUPT   45LU00387NILPIPAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPIP3
+INST   39SY(INFARE51);LC(PIPARE61);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500388
+LUPT   45LU00388NILPIPAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8PRODCT3
+INST   39SY(INFARE51);LC(PIPARE61);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500389
+LUPT   45LU00389NILPONTONA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500390
+LUPT   45LU00390NILPRCAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   52AP(TSSJCT02);SY(PRCARE51);LC(PRCARE51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500391
+LUPT   45LU00391NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500392
+LUPT   45LU00392NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC   16CATPRA5CONVIS1
+INST   30SY(RFNERY11);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500393
+LUPT   45LU00393NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC   16CATPRA8CONVIS1
+INST   30SY(TNKFRM11);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500394
+LUPT   45LU00394NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC   16CATPRA9CONVIS1
+INST   30SY(WNDFRM61);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500395
+LUPT   45LU00395NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPRA1
+INST   30SY(QUARRY01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500396
+LUPT   45LU00396NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPRA5
+INST   30SY(RFNERY01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500397
+LUPT   45LU00397NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPRA6
+INST   30SY(TMBYRD01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500398
+LUPT   45LU00398NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPRA8
+INST   30SY(TNKFRM01);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500399
+LUPT   45LU00399NILPRDAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATPRA9
+INST   30SY(WNDFRM51);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500400
+LUPT   45LU00400NILPYLONSA00008SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500401
+LUPT   45LU00401NILRADRNGA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,TRFCF)
+DISC    9STANDARD
+LUCM    625040
+****    0
+0001    500402
+LUPT   45LU00402NILRAPIDSA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   10AC(CHGRD)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500403
+LUPT   45LU00403NILRCTLPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13SY(RTLDEF51)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500404
+LUPT   45LU00404NILRCTLPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC    7ORIENT
+INST   20SY(RCTLPT52,ORIENT)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500405
+LUPT   45LU00405NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(RECDEF51);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500406
+LUPT   45LU00406NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC1
+INST   87SY(RECTRC58,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500407
+LUPT   45LU00407NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC2
+INST   87SY(RECTRC58,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500408
+LUPT   45LU00408NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC3
+INST   87SY(RECTRC58,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500409
+LUPT   45LU00409NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK1TRAFIC4
+INST   87SY(RECTRC56,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500410
+LUPT   45LU00410NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC1
+INST   87SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500411
+LUPT   45LU00411NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC2
+INST   87SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500412
+LUPT   45LU00412NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC3
+INST   87SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500413
+LUPT   45LU00413NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   23ORIENTCATTRK2TRAFIC4
+INST   87SY(RECTRC55,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500414
+LUPT   45LU00414NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   87SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500415
+LUPT   45LU00415NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   87SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500416
+LUPT   45LU00416NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   87SY(RECTRC57,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500417
+LUPT   45LU00417NILRECTRCA00006SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   87SY(RECTRC55,ORIENT);TE('%03.0lf deg','ORIENT',3,2,2,'15110',4,0,CHBLK,11);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500418
+LUPT   45LU00418NILRESAREA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(RESARE02)
+DISC    9STANDARD
+LUCM    626010
+****    0
+0001    500419
+LUPT   45LU00419NILRIVERSA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(DEPVS);LS(SOLD,1,CHBLK)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500420
+LUPT   45LU00420NILROADWYA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(LANDA);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500421
+LUPT   45LU00421NILRUNWAYA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   10AC(CHBRN)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    500422
+LUPT   45LU00422NILRUNWAYA00005SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500423
+LUPT   45LU00423NILSBDAREA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500424
+LUPT   45LU00424NILSBDAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC   15WATLEV3NATSUR
+INST   55TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500425
+LUPT   45LU00425NILSBDAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC   16WATLEV4NATSUR9
+INST   30AP(RCKLDG01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500426
+LUPT   45LU00426NILSBDAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC   17WATLEV4NATSUR11
+INST   30AP(RCKLDG01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500427
+LUPT   45LU00427NILSBDAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC   17WATLEV4NATSUR14
+INST   30AP(RCKLDG01);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500428
+LUPT   45LU00428NILSBDAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC   15WATLEV4NATSUR
+INST   55TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500429
+LUPT   45LU00429NILSEAAREA00003SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500430
+LUPT   45LU00430NILSILTNKA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   27AC(CHBRN);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500431
+LUPT   45LU00431NILSILTNKA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CONVIS1
+INST   27AC(CHBRN);LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500432
+LUPT   45LU00432NILSLCONSA00007OSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(SLCONS03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500433
+LUPT   45LU00433NILSLOGRDA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500434
+LUPT   45LU00434NILSLOGRDA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATSLO6
+INST   27AC(CHGRD);LS(SOLD,1,CHBLK)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500435
+LUPT   45LU00435NILSMCFACA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   40AC(CHBRN);SY(SMCFAC02);LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    638210
+****    0
+0001    500436
+LUPT   45LU00436NILSNDWAVA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26AP(SNDWAV01);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500437
+LUPT   45LU00437NILSPLAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(CTYARE51);LC(CTYARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500438
+LUPT   45LU00438NILSUBTLNA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39SY(CTYARE51);LC(CTYARE51);CS(RESTRN01)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500439
+LUPT   45LU00439NILSWPAREA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   84SY(SWPARE51);TE('swept to %5.1lf','DRVAL1',1,2,2,'15110',0,1,CHBLK,27);LC(NAVARE51)
+DISC    9STANDARD
+LUCM    623030
+****    0
+0001    500440
+LUPT   45LU00440NILT_HMONA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDEHT01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500441
+LUPT   45LU00441NILT_NHMNA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDEHT01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500442
+LUPT   45LU00442NILT_TIMSA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDEHT01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500443
+LUPT   45LU00443NILTS_FEBA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(CURDEF01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500444
+LUPT   45LU00444NILTS_FEBA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15CAT_TS1ORIENT
+INST   73SY(FLDSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500445
+LUPT   45LU00445NILTS_FEBA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15CAT_TS2ORIENT
+INST   73SY(EBBSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500446
+LUPT   45LU00446NILTS_FEBA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15CAT_TS3ORIENT
+INST   73SY(CURENT01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500447
+LUPT   45LU00447NILTS_PADA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDSTR01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500448
+LUPT   45LU00448NILTS_PNHA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDSTR01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500449
+LUPT   45LU00449NILTS_PRHA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDSTR01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500450
+LUPT   45LU00450NILTS_TISA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TIDSTR01);LC(TIDINF51)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500451
+LUPT   45LU00451NILTESAREA00002SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30LS(DASH,2,CHGRF);CS(RESTRN01)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500452
+LUPT   45LU00452NILTIDEWYA00007SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,25)
+DISC    6OTHER
+LUCM    632070
+****    0
+0001    500453
+LUPT   45LU00453NILTSEZNEA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   12AC(TRFCF,3)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500454
+LUPT   45LU00454NILTSSCRSA00006SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39AP(TSSJCT02);SY(TSSCRS51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500455
+LUPT   45LU00455NILTSSLPTA00006SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   78AP(TSSJCT02);SY(CTNARE51);TX(INFORM,1,1,2,'15110',0,-2,CHBLK,24);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500456
+LUPT   45LU00456NILTSSLPTA00006SSYMBOLIZED_BOUNDARIES
+ATTC    7ORIENT
+INST   33SY(TSSLPT51,ORIENT);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500457
+LUPT   45LU00457NILTSSRONA00006SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   39AP(TSSJCT02);SY(TSSRON51);CS(RESTRN01)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500458
+LUPT   45LU00458NILTUNNELA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500459
+LUPT   45LU00459NILTUNNELA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8BURDEP0
+INST   27AC(DEPVS);LS(DASH,1,CHBLK)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500460
+LUPT   45LU00460NILTWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   26SY(TWRDEF51);LC(CTYARE51)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500461
+LUPT   45LU00461NILTWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC1
+INST   33SY(TWRTPT53,ORIENT);LC(CTYARE51)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500462
+LUPT   45LU00462NILTWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC2
+INST   33SY(TWRTPT53,ORIENT);LC(CTYARE51)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500463
+LUPT   45LU00463NILTWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC3
+INST   33SY(TWRTPT53,ORIENT);LC(CTYARE51)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500464
+LUPT   45LU00464NILTWRTPTA00004SSYMBOLIZED_BOUNDARIES
+ATTC   15ORIENTTRAFIC4
+INST   33SY(TWRTPT52,ORIENT);LC(CTYARE51)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500465
+LUPT   45LU00465NILUNSAREA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   40AC(NODTA);AP(NODATA03);LS(SOLD,2,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    611050
+****    0
+0001    500466
+LUPT   45LU00466NILVEGATNA00000SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500467
+LUPT   45LU00467NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATVEG7
+INST   30AP(VEGATN04);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500468
+LUPT   45LU00468NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG21
+INST   30AP(VEGATN04);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500469
+LUPT   45LU00469NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATVEG3
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500470
+LUPT   45LU00470NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATVEG4
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500471
+LUPT   45LU00471NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATVEG5
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500472
+LUPT   45LU00472NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    8CATVEG6
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500473
+LUPT   45LU00473NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG13
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500474
+LUPT   45LU00474NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG14
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500475
+LUPT   45LU00475NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG15
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500476
+LUPT   45LU00476NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG16
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500477
+LUPT   45LU00477NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG17
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500478
+LUPT   45LU00478NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG18
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500479
+LUPT   45LU00479NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG19
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500480
+LUPT   45LU00480NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG20
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500481
+LUPT   45LU00481NILVEGATNA00003SSYMBOLIZED_BOUNDARIES
+ATTC    9CATVEG22
+INST   30AP(VEGATN03);LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500482
+LUPT   45LU00482NILWATTURA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30SY(WATTUR02);LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    633040
+****    0
+0001    500483
+LUPT   45LU00483NILWEDKLPA00003OSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   30SY(WEDKLP03);LS(DASH,1,CHGRF)
+DISC    6OTHER
+LUCM    634020
+****    0
+0001    500484
+LUPT   45LU00484NILWRECKSA00004SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13CS(WRECKS02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500485
+LUPT   45LU00485NILWRECKSA00004SSYMBOLIZED_BOUNDARIES
+ATTC    8CATWRK3
+INST   13LC(NAVARE51)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500486
+LUPT   45LU00486NILdnghltA00008SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   29AC(DNGHL,3);LS(SOLD,3,DNGHL)
+DISC   18MARINERS STANDARD
+LUCM    653010
+****    0
+0001    500487
+LUPT   45LU00487NILmarfeaA00008SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   85AC(ADINF,3);TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,50);LS(SOLD,2,NINFO);LS(SOLD,1,CHBLK);
+DISC   18MARINERS STANDARD
+LUCM    653050
+****    0
+0001    500488
+LUPT   45LU00488NILmnufeaA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   17LS(DASH,2,ADINF)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    500489
+LUPT   45LU00489NIL$AREASA00005SSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   13AP(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500490
+LUPT   45LU00490NIL$AREASA00005SSYMBOLIZED_BOUNDARIES
+ATTC   15$SCODECHCRDEL1
+INST   26SY(CHCRDEL1);LC(CHCRDEL1)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500491
+LUPT   45LU00491NIL$AREASA00005SSYMBOLIZED_BOUNDARIES
+ATTC   15$SCODECHCRID01
+INST   26SY(CHCRID01);LC(CHCRID01)
+DISC    9STANDARD
+LUCM    623030
+****    0
+0001    500492
+LUPT   45LU00492NIL$AREASA00003OSYMBOLIZED_BOUNDARIES
+ATTC   15$SCODEOVERSC01
+INST   13AP(OVERSC01)
+DISC    9STANDARD
+LUCM    621030
+****    0
+0001    500493
+LUPT   45LU00493NIL$AREASA00005SSYMBOLIZED_BOUNDARIES
+ATTC   15$SCODEQUESMRK1
+INST   30AP(QUESMRK1);LS(DASH,1,CHGRD)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500494
+LUPT   45LU00494NIL$AREASA00003SSYMBOLIZED_BOUNDARIES
+ATTC   15$SCODEDIAMOND1
+INST   13AP(DIAMOND1)
+DISC    9STANDARD
+LUCM    623010
+****    0
+0001    500495
+LUPT   45LU00495NIL$AREASA00002OSYMBOLIZED_BOUNDARIES
+ATTC   17$SCODEBACKGROUND
+INST   27AC(NODTA);LS(SOLD,2,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500496
+LUPT   45LU00496NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX1
+INST   62AC(DEPDW);LS(SOLD,1,CHGRD);TX('1',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500497
+LUPT   45LU00497NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX2
+INST   62AC(NODTA);LS(SOLD,1,CHGRD);TX('2',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500498
+LUPT   45LU00498NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX3
+INST   62AC(DEPVS);LS(SOLD,1,CHGRD);TX('3',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500499
+LUPT   45LU00499NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX4
+INST   62AC(NODTA);LS(SOLD,1,CHGRD);TX('4',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500500
+LUPT   45LU00500NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX5
+INST   62AC(DEPVS);LS(SOLD,1,CHGRD);TX('5',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500501
+LUPT   45LU00501NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX6
+INST   62AC(LANDA);LS(SOLD,1,CHGRD);TX('6',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500502
+LUPT   45LU00502NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX7
+INST   62AC(DEPDW);LS(SOLD,1,CHGRD);TX('7',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500503
+LUPT   45LU00503NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX8
+INST   62AC(DEPDW);LS(SOLD,1,CHGRD);TX('8',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500504
+LUPT   45LU00504NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   11$SCODEBOX9
+INST   62AC(NODTA);LS(SOLD,1,CHGRD);TX('9',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500505
+LUPT   45LU00505NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX10
+INST   63AC(DEPDW);LS(SOLD,1,CHGRD);TX('10',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500506
+LUPT   45LU00506NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX11
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('11',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500507
+LUPT   45LU00507NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX12
+INST   63AC(NODTA);LS(SOLD,1,CHGRD);TX('12',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500508
+LUPT   45LU00508NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX13
+INST   63AC(DEPDW);LS(SOLD,1,CHGRD);TX('13',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500509
+LUPT   45LU00509NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX14
+INST   63AC(LANDA);LS(SOLD,1,CHGRD);TX('14',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500510
+LUPT   45LU00510NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX15
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('15',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500511
+LUPT   45LU00511NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX16
+INST   63AC(NODTA);LS(SOLD,1,CHGRD);TX('16',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500512
+LUPT   45LU00512NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX17
+INST   63AC(LANDA);LS(SOLD,1,CHGRD);TX('17',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500513
+LUPT   45LU00513NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX18
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('18',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500514
+LUPT   45LU00514NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX19
+INST   63AC(DEPDW);LS(SOLD,1,CHGRD);TX('19',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500515
+LUPT   45LU00515NIL$AREASA00005OSYMBOLIZED_BOUNDARIES
+ATTC   12$SCODEBOX20
+INST   63AC(DEPVS);LS(SOLD,1,CHGRD);TX('20',2,2,3,'15110',3,2,CHBLK,30)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500516
+LUPT   45LU00516NIL$TEXTSA00007OSYMBOLIZED_BOUNDARIES
+ATTC    1
+INST   59TX($TXSTR,$JUSTH=3,$JUSTV=1,$SPACE=2,'15110',0,0,CHBLK,27)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500517
+LUPT   29LU00517NIL######L00005OLINES
+ATTC    1
+INST   13LC(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500518
+LUPT   29LU00518NILBERTHSL00005OLINES
+ATTC    1
+INST   78LS(SOLD,3,CHGRD);SY(BRTHNO01);TE('No %s','OBJNAM',1,2,2,'15110',0,0,CHBLK,29)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500519
+LUPT   29LU00519NILBRIDGEL00008OLINES
+ATTC    1
+INST  108LS(SOLD,5,CHGRD);TX(OBJNAM,3,1,2,'15110',1,0,CHBLK,21);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500520
+LUPT   29LU00520NILBRIDGEL00008OLINES
+ATTC    8CATBRG2
+INST  142LS(SOLD,5,CHGRD);SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500521
+LUPT   29LU00521NILBRIDGEL00008OLINES
+ATTC    8CATBRG3
+INST  142LS(SOLD,5,CHGRD);SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500522
+LUPT   29LU00522NILBRIDGEL00008OLINES
+ATTC    8CATBRG4
+INST  142LS(SOLD,5,CHGRD);SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500523
+LUPT   29LU00523NILBRIDGEL00008OLINES
+ATTC    8CATBRG5
+INST  142LS(SOLD,5,CHGRD);SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500524
+LUPT   29LU00524NILBRIDGEL00008OLINES
+ATTC    8CATBRG7
+INST  142LS(SOLD,5,CHGRD);SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500525
+LUPT   29LU00525NILBRIDGEL00008OLINES
+ATTC    8CATBRG8
+INST  142LS(SOLD,5,CHGRD);SY(BRIDGE01);TE('clr cl %4.1lf','VERCCL',3,1,2,'15110',1,0,CHBLK,11);TE('clr op %4.1lf','VERCOP',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500526
+LUPT   29LU00526NILCANALSL00002OLINES
+ATTC    1
+INST   17LS(SOLD,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612420
+****    0
+0001    500527
+LUPT   29LU00527NILCAUSWYL00005OLINES
+ATTC    1
+INST   17LS(SOLD,3,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500528
+LUPT   29LU00528NILCAUSWYL00005OLINES
+ATTC    8WATLEV4
+INST   17LS(DASH,3,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500529
+LUPT   29LU00529NILCBLOHDL00008OLINES
+ATTC    1
+INST   17LS(DASH,4,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500530
+LUPT   29LU00530NILCBLOHDL00008OLINES
+ATTC   15CONRAD1VERCSA
+INST   86LS(DASH,4,CHGRD);SY(RACNSP01);TE('sf clr %4.1lf','VERCSA',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500531
+LUPT   29LU00531NILCBLOHDL00008OLINES
+ATTC   15CONRAD3VERCSA
+INST   86LS(DASH,4,CHGRD);SY(RACNSP01);TE('sf clr %4.1lf','VERCSA',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500532
+LUPT   29LU00532NILCBLOHDL00008OLINES
+ATTC   15CONRAD1VERCLR
+INST   83LS(DASH,4,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500533
+LUPT   29LU00533NILCBLOHDL00008OLINES
+ATTC   15CONRAD3VERCLR
+INST   83LS(DASH,4,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500534
+LUPT   29LU00534NILCBLOHDL00008OLINES
+ATTC    8CONRAD1
+INST   30LS(DASH,4,CHGRD);SY(RACNSP01)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500535
+LUPT   29LU00535NILCBLOHDL00008OLINES
+ATTC    8CONRAD3
+INST   30LS(DASH,4,CHGRD);SY(RACNSP01)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500536
+LUPT   29LU00536NILCBLOHDL00008OLINES
+ATTC    7VERCSA
+INST   73LS(DASH,4,CHGRD);TE('sf clr %4.1lf','VERCSA',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500537
+LUPT   29LU00537NILCBLOHDL00008OLINES
+ATTC    7VERCLR
+INST   73LS(DASH,4,CHGRD);TE('sf clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500538
+LUPT   29LU00538NILCBLSUBL00003OLINES
+ATTC    1
+INST   13LC(CBLSUB06)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500539
+LUPT   29LU00539NILCBLSUBL00006OLINES
+ATTC    8CATCBL6
+INST   17LS(DASH,1,CHMGD)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500540
+LUPT   29LU00540NILCOALNEL00007OLINES
+ATTC    1
+INST   13CS(QUAPOS01)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500541
+LUPT   29LU00541NILCOALNEL00007OLINES
+ATTC    8CATCOA6
+INST   17LS(DASH,1,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500542
+LUPT   29LU00542NILCOALNEL00007OLINES
+ATTC    8CATCOA7
+INST   17LS(DASH,1,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500543
+LUPT   29LU00543NILCOALNEL00007OLINES
+ATTC    8CATCOA8
+INST   17LS(DASH,1,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500544
+LUPT   29LU00544NILCOALNEL00007OLINES
+ATTC    9CATCOA10
+INST   17LS(DASH,1,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500545
+LUPT   29LU00545NILCONVYRL00008OLINES
+ATTC    1
+INST   70LS(DASH,4,CHGRD);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500546
+LUPT   29LU00546NILCONVYRL00008OLINES
+ATTC   16CATCON1CONRAD1
+INST   83LS(DASH,4,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500547
+LUPT   29LU00547NILCONVYRL00008OLINES
+ATTC   16CATCON1CONRAD3
+INST   83LS(DASH,4,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500548
+LUPT   29LU00548NILCONVYRL00008OLINES
+ATTC   16CATCON2CONRAD1
+INST   83LS(SOLD,3,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500549
+LUPT   29LU00549NILCONVYRL00008OLINES
+ATTC   16CATCON2CONRAD3
+INST   83LS(SOLD,3,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500550
+LUPT   29LU00550NILCONVYRL00008OLINES
+ATTC    8CATCON1
+INST   70LS(DASH,4,CHGRD);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500551
+LUPT   29LU00551NILCONVYRL00008OLINES
+ATTC    8CATCON2
+INST   70LS(SOLD,3,CHGRD);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500552
+LUPT   29LU00552NILCONVYRL00008OLINES
+ATTC    8CONRAD1
+INST   83LS(DASH,4,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500553
+LUPT   29LU00553NILCONVYRL00008OLINES
+ATTC    8CONRAD3
+INST   83LS(DASH,4,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,0,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500554
+LUPT   29LU00554NILDAMCONL00006OLINES
+ATTC    1
+INST   17LS(SOLD,4,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500555
+LUPT   29LU00555NILDAMCONL00006OLINES
+ATTC    8CATDAM3
+INST   17LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500556
+LUPT   29LU00556NILDEPAREL00003OLINES
+ATTC    1
+INST   13CS(DEPCNT02)
+DISC    6OTHER
+LUCM    633020
+****    0
+0001    500557
+LUPT   29LU00557NILDEPCNTL00005OLINES
+ATTC    1
+INST   13CS(DEPCNT02)
+DISC    6OTHER
+LUCM    633020
+****    0
+0001    500558
+LUPT   29LU00558NILDWRTCLL00006OLINES
+ATTC    1
+INST   68LC(DWLDEF01);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500559
+LUPT   29LU00559NILDWRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC1
+INST   68LC(DWRTCL08);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500560
+LUPT   29LU00560NILDWRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC2
+INST   68LC(DWRTCL08);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500561
+LUPT   29LU00561NILDWRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC3
+INST   68LC(DWRTCL08);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500562
+LUPT   29LU00562NILDWRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC4
+INST   68LC(DWRTCL06);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500563
+LUPT   29LU00563NILDWRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC1
+INST   68LC(DWRTCL07);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500564
+LUPT   29LU00564NILDWRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC2
+INST   68LC(DWRTCL07);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500565
+LUPT   29LU00565NILDWRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC3
+INST   68LC(DWRTCL07);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500566
+LUPT   29LU00566NILDWRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC4
+INST   68LC(DWRTCL05);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500567
+LUPT   29LU00567NILDWRTCLL00006OLINES
+ATTC    8TRAFIC1
+INST   68LC(DWRTCL07);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500568
+LUPT   29LU00568NILDWRTCLL00006OLINES
+ATTC    8TRAFIC2
+INST   68LC(DWRTCL07);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500569
+LUPT   29LU00569NILDWRTCLL00006OLINES
+ATTC    8TRAFIC3
+INST   68LC(DWRTCL07);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500570
+LUPT   29LU00570NILDWRTCLL00006OLINES
+ATTC    8TRAFIC4
+INST   68LC(DWRTCL05);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500571
+LUPT   29LU00571NILDYKCONL00004OLINES
+ATTC    1
+INST   17LS(SOLD,3,LANDF)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500572
+LUPT   29LU00572NILDYKCONL00004OLINES
+ATTC    8CONRAD1
+INST   17LS(SOLD,2,CHBLK)
+DISC    9STANDARD
+LUCM    622210
+****    0
+0001    500573
+LUPT   29LU00573NILFERYRTL00004OLINES
+ATTC    1
+INST   13LC(FERYRT02)
+DISC    9STANDARD
+LUCM    625030
+****    0
+0001    500574
+LUPT   29LU00574NILFERYRTL00004OLINES
+ATTC    8CATFRY1
+INST   13LC(FERYRT01)
+DISC    9STANDARD
+LUCM    625030
+****    0
+0001    500575
+LUPT   29LU00575NILFERYRTL00004OLINES
+ATTC    8CATFRY2
+INST   13LC(FERYRT02)
+DISC    9STANDARD
+LUCM    625030
+****    0
+0001    500576
+LUPT   29LU00576NILFLODOCL00005OLINES
+ATTC    1
+INST   17LS(SOLD,3,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500577
+LUPT   29LU00577NILFNCLNEL00003OLINES
+ATTC    1
+INST   17LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500578
+LUPT   29LU00578NILFNCLNEL00003OLINES
+ATTC    8CONVIS1
+INST   17LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500579
+LUPT   29LU00579NILFORSTCL00004OLINES
+ATTC    1
+INST   17LS(SOLD,3,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500580
+LUPT   29LU00580NILFSHFACL00004OLINES
+ATTC    1
+INST   17LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500581
+LUPT   29LU00581NILFSHFACL00004OLINES
+ATTC    8CATFIF1
+INST   13LC(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500582
+LUPT   29LU00582NILGATCONL00008OLINES
+ATTC    1
+INST   17LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500583
+LUPT   29LU00583NILGATCONL00008OLINES
+ATTC    8CATGAT2
+INST   17LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500584
+LUPT   29LU00584NILGATCONL00008OLINES
+ATTC    8CATGAT3
+INST   30LS(SOLD,2,CSTLN);SY(GATCON04)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500585
+LUPT   29LU00585NILGATCONL00008OLINES
+ATTC    8CATGAT4
+INST   30LS(SOLD,2,CSTLN);SY(GATCON03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500586
+LUPT   29LU00586NILGATCONL00008OLINES
+ATTC    8CATGAT5
+INST   17LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500587
+LUPT   29LU00587NILLNDAREL00008OLINES
+ATTC    1
+INST   13CS(QUAPOS01)
+DISC   12DISPLAYBASE
+LUCM    612010
+****    0
+0001    500588
+LUPT   29LU00588NILLNDELVL00004OLINES
+ATTC    1
+INST   17LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500589
+LUPT   29LU00589NILLNDMRKL00004OLINES
+ATTC    1
+INST   17LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500590
+LUPT   29LU00590NILLNDMRKL00004OLINES
+ATTC    8CONVIS1
+INST   17LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500591
+LUPT   29LU00591NILLOCMAGL00004SLINES
+ATTC    1
+INST   30LS(DASH,1,CHMGF);SY(LOCMAG01)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500592
+LUPT   29LU00592NILM_SRELL00000SLINES
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500593
+LUPT   29LU00593NILMAGVARL00004OLINES
+ATTC    1
+INST   81LS(SOLD,2,CHMGF);SY(MAGVAR51);TE('varn %s','VALMAG',3,1,2,'15110',1,-1,CHBLK,27)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500594
+LUPT   29LU00594NILMARCULL00004OLINES
+ATTC    1
+INST   17LS(DASH,2,CHGRF)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    500595
+LUPT   29LU00595NILMORFACL00006OLINES
+ATTC    1
+INST   17LS(SOLD,2,CSTLN)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500596
+LUPT   29LU00596NILMORFACL00006OLINES
+ATTC    8CATMOR4
+INST   17LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500597
+LUPT   29LU00597NILMORFACL00006OLINES
+ATTC    8CATMOR6
+INST   17LS(DASH,1,CHMGF)
+DISC   12DISPLAYBASE
+LUCM    614010
+****    0
+0001    500598
+LUPT   29LU00598NILNAVLNEL00004OLINES
+ATTC    1
+INST   72LS(DASH,1,CHGRD);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625010
+****    0
+0001    500599
+LUPT   29LU00599NILOBSTRNL00004OLINES
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500600
+LUPT   29LU00600NILOBSTRNL00004OLINES
+ATTC    8CATOBS8
+INST   17LS(DASH,1,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500601
+LUPT   29LU00601NILOILBARL00004OLINES
+ATTC    1
+INST   17LS(DASH,1,CHBLK)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500602
+LUPT   29LU00602NILPIPOHDL00008OLINES
+ATTC    1
+INST   71LS(SOLD,3,CHGRD);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500603
+LUPT   29LU00603NILPIPOHDL00008OLINES
+ATTC    8CONRAD1
+INST   84LS(SOLD,3,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500604
+LUPT   29LU00604NILPIPOHDL00008OLINES
+ATTC    8CONRAD3
+INST   84LS(SOLD,3,CHGRD);SY(RACNSP01);TE('clr %4.1lf','VERCLR',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500605
+LUPT   29LU00605NILPIPSOLL00006OLINES
+ATTC    1
+INST   13LC(PIPSOL05)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500606
+LUPT   29LU00606NILPIPSOLL00006OLINES
+ATTC    8PRODCT3
+INST   13LC(PIPSOL06)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500607
+LUPT   29LU00607NILPIPSOLL00006OLINES
+ATTC    8CATPIP2
+INST   13LC(PIPSOL06)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500608
+LUPT   29LU00608NILPIPSOLL00006OLINES
+ATTC    8CATPIP3
+INST   13LC(PIPSOL06)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500609
+LUPT   29LU00609NILPIPSOLL00006OLINES
+ATTC    8CATPIP4
+INST   13LC(PIPSOL06)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500610
+LUPT   29LU00610NILPIPSOLL00006OLINES
+ATTC    8CATPIP5
+INST   13LC(PIPSOL06)
+DISC    6OTHER
+LUCM    634070
+****    0
+0001    500611
+LUPT   29LU00611NILPONTONL00005OLINES
+ATTC    1
+INST   17LS(SOLD,2,CSTLN)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500612
+LUPT   29LU00612NILRADLNEL00006OLINES
+ATTC    1
+INST   72LS(DASH,2,TRFCD);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625040
+****    0
+0001    500613
+LUPT   29LU00613NILRAILWYL00004OLINES
+ATTC    1
+INST   17LS(SOLD,2,LANDF)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500614
+LUPT   29LU00614NILRAPIDSL00003OLINES
+ATTC    1
+INST   17LS(SOLD,3,CHGRD)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500615
+LUPT   29LU00615NILRCRTCLL00006OLINES
+ATTC    1
+INST   68LC(RCRDEF01);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500616
+LUPT   29LU00616NILRCRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC1
+INST   68LC(RCRTCL04);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500617
+LUPT   29LU00617NILRCRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC2
+INST   68LC(RCRTCL04);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500618
+LUPT   29LU00618NILRCRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC3
+INST   68LC(RCRTCL04);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500619
+LUPT   29LU00619NILRCRTCLL00006OLINES
+ATTC   16CATTRK1TRAFIC4
+INST   68LC(RCRTCL03);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500620
+LUPT   29LU00620NILRCRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC1
+INST   68LC(RCRTCL02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500621
+LUPT   29LU00621NILRCRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC2
+INST   68LC(RCRTCL02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500622
+LUPT   29LU00622NILRCRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC3
+INST   68LC(RCRTCL02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500623
+LUPT   29LU00623NILRCRTCLL00006OLINES
+ATTC   16CATTRK2TRAFIC4
+INST   68LC(RCRTCL01);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500624
+LUPT   29LU00624NILRCRTCLL00006OLINES
+ATTC    8TRAFIC1
+INST   68LC(RCRTCL02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500625
+LUPT   29LU00625NILRCRTCLL00006OLINES
+ATTC    8TRAFIC2
+INST   68LC(RCRTCL02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500626
+LUPT   29LU00626NILRCRTCLL00006OLINES
+ATTC    8TRAFIC3
+INST   68LC(RCRTCL02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500627
+LUPT   29LU00627NILRCRTCLL00006OLINES
+ATTC    8TRAFIC4
+INST   68LC(RCRTCL01);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500628
+LUPT   29LU00628NILRDOCALL00006OLINES
+ATTC    1
+INST   79LS(DASH,1,TRFCD);SY(RCLDEF01);TE('No %s','OBJNAM',3,2,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500629
+LUPT   29LU00629NILRDOCALL00006OLINES
+ATTC   15TRAFIC1ORIENT
+INST  134LS(DASH,1,TRFCD);SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500630
+LUPT   29LU00630NILRDOCALL00006OLINES
+ATTC   15TRAFIC2ORIENT
+INST  134LS(DASH,1,TRFCD);SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500631
+LUPT   29LU00631NILRDOCALL00006OLINES
+ATTC   15TRAFIC3ORIENT
+INST  134LS(DASH,1,TRFCD);SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500632
+LUPT   29LU00632NILRDOCALL00006OLINES
+ATTC   15TRAFIC4ORIENT
+INST  134LS(DASH,1,TRFCD);SY(RDOCAL03,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500633
+LUPT   29LU00633NILRECTRCL00006OLINES
+ATTC    1
+INST   68LC(RECDEF02);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500634
+LUPT   29LU00634NILRECTRCL00006OLINES
+ATTC   16CATTRK1TRAFIC1
+INST   68LC(RECTRC12);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500635
+LUPT   29LU00635NILRECTRCL00006OLINES
+ATTC   16CATTRK1TRAFIC2
+INST   68LC(RECTRC12);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500636
+LUPT   29LU00636NILRECTRCL00006OLINES
+ATTC   16CATTRK1TRAFIC3
+INST   68LC(RECTRC12);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500637
+LUPT   29LU00637NILRECTRCL00006OLINES
+ATTC   16CATTRK1TRAFIC4
+INST   68LC(RECTRC10);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500638
+LUPT   29LU00638NILRECTRCL00006OLINES
+ATTC   16CATTRK2TRAFIC1
+INST   68LC(RECTRC11);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500639
+LUPT   29LU00639NILRECTRCL00006OLINES
+ATTC   16CATTRK2TRAFIC2
+INST   68LC(RECTRC11);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500640
+LUPT   29LU00640NILRECTRCL00006OLINES
+ATTC   16CATTRK2TRAFIC3
+INST   68LC(RECTRC11);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500641
+LUPT   29LU00641NILRECTRCL00006OLINES
+ATTC   16CATTRK2TRAFIC4
+INST   68LC(RECTRC09);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500642
+LUPT   29LU00642NILRECTRCL00006OLINES
+ATTC    8TRAFIC1
+INST   68LC(RECTRC11);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500643
+LUPT   29LU00643NILRECTRCL00006OLINES
+ATTC    8TRAFIC2
+INST   68LC(RECTRC11);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500644
+LUPT   29LU00644NILRECTRCL00006OLINES
+ATTC    8TRAFIC3
+INST   68LC(RECTRC11);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500645
+LUPT   29LU00645NILRECTRCL00006OLINES
+ATTC    8TRAFIC4
+INST   68LC(RECTRC09);TE('%03.0lf deg','ORIENT',3,1,2,'15110',1,-1,CHBLK,11)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    500646
+LUPT   29LU00646NILRIVERSL00002OLINES
+ATTC    1
+INST   17LS(SOLD,1,CHBLK)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500647
+LUPT   29LU00647NILROADWYL00004OLINES
+ATTC    1
+INST   17LS(SOLD,2,LANDF)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500648
+LUPT   29LU00648NILRUNWAYL00006OLINES
+ATTC    1
+INST   17LS(SOLD,3,LANDF)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    500649
+LUPT   29LU00649NILSBDAREL00004OLINES
+ATTC    1
+INST   55LS(SOLD,1,CHGRD);TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500650
+LUPT   29LU00650NILSLCONSL00007OLINES
+ATTC    1
+INST   13CS(SLCONS03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500651
+LUPT   29LU00651NILSLOTOPL00004OLINES
+ATTC    1
+INST   17LS(SOLD,1,LANDF)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500652
+LUPT   29LU00652NILSLOTOPL00004OLINES
+ATTC   16CATSLO2CONRAD1
+INST   17LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622210
+****    0
+0001    500653
+LUPT   29LU00653NILSLOTOPL00004OLINES
+ATTC   16CATSLO6CONRAD1
+INST   17LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622210
+****    0
+0001    500654
+LUPT   29LU00654NILSLOTOPL00004OLINES
+ATTC   16CATSLO6CONVIS1
+INST   17LS(SOLD,1,CHBLK)
+DISC    9STANDARD
+LUCM    622210
+****    0
+0001    500655
+LUPT   29LU00655NILSLOTOPL00004OLINES
+ATTC    8CATSLO2
+INST   17LS(SOLD,1,CHGRD)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500656
+LUPT   29LU00656NILSLOTOPL00004OLINES
+ATTC    8CATSLO6
+INST   17LS(SOLD,1,CHGRD)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500657
+LUPT   29LU00657NILSNDWAVL00004OLINES
+ATTC    1
+INST   30LS(DASH,2,CHGRD);SY(SNDWAV02)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500658
+LUPT   29LU00658NILSTSLNEL00003OLINES
+ATTC    1
+INST   17LS(DASH,1,CHGRF)
+DISC    6OTHER
+LUCM    636050
+****    0
+0001    500659
+LUPT   29LU00659NILTIDEWYL00003OLINES
+ATTC    1
+INST   17LS(SOLD,1,CHGRF)
+DISC    6OTHER
+LUCM    632070
+****    0
+0001    500660
+LUPT   29LU00660NILTSELNEL00008OLINES
+ATTC    1
+INST   17LS(SOLD,6,TRFCF)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500661
+LUPT   29LU00661NILTSSBNDL00007OLINES
+ATTC    1
+INST   17LS(DASH,4,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500662
+LUPT   29LU00662NILTUNNELL00004OLINES
+ATTC    1
+INST   17LS(DASH,1,CHGRD)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500663
+LUPT   29LU00663NILTUNNELL00004OLINES
+ATTC    8BURDEP0
+INST   17LS(DASH,2,CHBLK)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500664
+LUPT   29LU00664NILVEGATNL00003OLINES
+ATTC    1
+INST   17LS(DASH,1,LANDF)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    500665
+LUPT   29LU00665NILWATFALL00003OLINES
+ATTC    1
+INST   17LS(SOLD,3,CHGRF)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500666
+LUPT   29LU00666NILWATFALL00003OLINES
+ATTC    8CONVIS1
+INST   17LS(SOLD,3,CHWHT)
+DISC    6OTHER
+LUCM    632050
+****    0
+0001    500667
+LUPT   29LU00667NILWATTURL00004OLINES
+ATTC    1
+INST   30LS(DASH,1,CHGRD);SY(WATTUR02)
+DISC    6OTHER
+LUCM    633040
+****    0
+0001    500668
+LUPT   29LU00668NILclrlinL00009OLINES
+ATTC    1
+INST   13CS(CLRLIN01)
+DISC   15MARINERS OTHER
+LUCM    653020
+****    0
+0001    500669
+LUPT   29LU00669NILeblineL00009OLINES
+ATTC    1
+INST   13CS(VRMEBL01)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    500670
+LUPT   29LU00670NILleglinL00008OLINES
+ATTC    1
+INST   13CS(LEGLIN02)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    500671
+LUPT   29LU00671NILmarfeaL00008OLINES
+ATTC    1
+INST   55LS(SOLD,2,NINFO);TX(OBJNAM,3,3,2,'15110',0,1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    653050
+****    0
+0001    500672
+LUPT   29LU00672NILmnufeaL00005OLINES
+ATTC    1
+INST   17LS(SOLD,1,ADINF)
+DISC   15MARINERS OTHER
+LUCM    655010
+****    0
+0001    500673
+LUPT   29LU00673NILpastrkL00003OLINES
+ATTC    1
+INST   13CS(PASTRK01)
+DISC   18MARINERS STANDARD
+LUCM    652430
+****    0
+0001    500674
+LUPT   29LU00674NILposlinL00003OLINES
+ATTC    1
+INST   56LS(SOLD,1,NINFO);TX(loctim,3,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662020
+****    0
+0001    500675
+LUPT   29LU00675NILposlinL00003OLINES
+ATTC    8transf2
+INST   93LS(SOLD,1,NINFO);TX(loctim,3,1,2,'15110',0,-1,CHBLK,50);TX('TPL',3,3,2,'15110',0,1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662020
+****    0
+0001    500676
+LUPT   29LU00676NILrngrngL00009OLINES
+ATTC    1
+INST   17LS(SOLD,1,CURSR)
+DISC   15MARINERS OTHER
+LUCM    661030
+****    0
+0001    500677
+LUPT   29LU00677NILvrmarkL00009OLINES
+ATTC    1
+INST   13CS(VRMEBL01)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    500678
+LUPT   29LU00678NILwholinL00008OLINES
+ATTC    1
+INST   94LS(SOLD,2,NINFO);TX(loctim,3,3,2,'15110',0,1,CHBLK,50);TX(usrmrk,3,1,2,'15110',0,-1,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    652010
+****    0
+0001    500679
+LUPT   29LU00679NIL$LINESL00005SLINES
+ATTC    1
+INST   13LC(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500680
+LUPT   29LU00680NIL$LINESL00003OLINES
+ATTC   15$SCODESCLBDY51
+INST   13LC(SCLBDY51)
+DISC    9STANDARD
+LUCM    621030
+****    0
+0001    500681
+LUPT   29LU00681NIL$LINESL00005SLINES
+ATTC   15$SCODEQUESMRK1
+INST   13LC(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500682
+LUPT   29LU00682NIL$LINESL00006OLINES
+ATTC   15$SCODECHCRID01
+INST   13LC(CHCRID01)
+DISC    6OTHER
+LUCM    633020
+****    0
+0001    500683
+LUPT   29LU00683NIL$LINESL00006OLINES
+ATTC   15$SCODECHCRDEL1
+INST   13LC(CHCRDEL1)
+DISC    6OTHER
+LUCM    633020
+****    0
+0001    500684
+LUPT   29LU00684NIL$LINESL00009OLINES
+ATTC   15$SCODEUNITMTR1
+INST   13LC(UNITMTR1)
+DISC   12DISPLAYBASE
+LUCM    611080
+****    0
+0001    500685
+LUPT   29LU00685NIL$LINESL00003OLINES
+ATTC   15$SCODEHODATA01
+INST   13LC(HODATA01)
+DISC   12DISPLAYBASE
+LUCM    611060
+****    0
+0001    500686
+LUPT   29LU00686NIL$LINESL00005OLINES
+ATTC   15$SCODEGRIDLINE
+INST   17LS(SOLD,1,CHGRD)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500687
+LUPT   29LU00687NIL$LINESL00002SLINES
+ATTC   15$SCODETIDINF51
+INST   13LC(TIDINF51)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500688
+LUPT   29LU00688NIL$LINESL00005OLINES
+ATTC   15$SCODELOWACC41
+INST   13LC(LOWACC41)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500689
+LUPT   29LU00689NIL$LINESL00000OLINES
+ATTC   13$SCODEDANGER
+INST   17LS(DOTT,2,CHBLK)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500690
+LUPT   29LU00690NIL$LINESL00000OLINES
+ATTC   13$SCODEOBSTRN
+INST   17LS(DASH,2,CHGRD)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500691
+LUPT   29LU00691NIL$LINESL00004SLINES
+ATTC   15$SCODEMARSYS51
+INST   13LC(MARSYS51)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    500692
+LUPT   29LU00692NIL$LINESL00009OLINES
+ATTC   10$SCODEAIS
+INST   17LS(SOLD,2,ARPAT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    500693
+LUPT   29LU00693NIL$LINESL00009OLINES
+ATTC   14$SCODEAISHEAD
+INST   17LS(SOLD,1,ARPAT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    500694
+LUPT   29LU00694NIL$LINESL00009OLINES
+ATTC   11$SCODEARPA
+INST   17LS(SOLD,2,ARPAT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    500695
+LUPT   29LU00695NIL$LINESL00009OLINES
+ATTC   15$SCODEERBLNB01
+INST   13LC(ERBLNB01)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    500696
+LUPT   29LU00696NIL$LINESL00009OLINES
+ATTC   15$SCODEERBLNA01
+INST   13LC(ERBLNA01)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    500697
+LUPT   29LU00697NIL$LINESL00009OLINES
+ATTC   12$SCODEHEDNG
+INST   17LS(SOLD,1,SHIPS)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    500698
+LUPT   29LU00698NIL$LINESL00009OLINES
+ATTC   10$SCODESOG
+INST   17LS(SOLD,1,SHIPS)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    500699
+LUPT   29LU00699NIL$LINESL00003OLINES
+ATTC   12$SCODEPSTRK
+INST   17LS(SOLD,2,PSTRK)
+DISC   18MARINERS STANDARD
+LUCM    652430
+****    0
+0001    500700
+LUPT   29LU00700NIL$LINESL00003OLINES
+ATTC   12$SCODESYTRK
+INST   17LS(SOLD,1,SYTRK)
+DISC   18MARINERS STANDARD
+LUCM    652460
+****    0
+0001    500701
+LUPT   29LU00701NIL$LINESL00009OLINES
+ATTC   10$SCODEERB
+INST   17LS(DASH,2,NINFO)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    500702
+LUPT   29LU00702NIL$LINESL00005OLINES
+ATTC   15$SCODEPLNRTE03
+INST   13LC(PLNRTE03)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    500703
+LUPT   29LU00703NIL$LINESL00005OLINES
+ATTC   13$SCODEALPLRT
+INST   17LS(DOTT,2,APLRT)
+DISC   18MARINERS STANDARD
+LUCM    652210
+****    0
+0001    500704
+LUPT   29LU00704NIL$LINESL00009OLINES
+ATTC   13$SCODECLRLIN
+INST   17LS(SOLD,2,NINFO)
+DISC   18MARINERS STANDARD
+LUCM    653020
+****    0
+0001    500705
+LUPT   29LU00705NIL$LINESL00005OLINES
+ATTC   12$SCODELINE1
+INST   17LS(SOLD,3,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500706
+LUPT   29LU00706NIL$LINESL00005OLINES
+ATTC   12$SCODELINE2
+INST   17LS(SOLD,3,RESBL)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500707
+LUPT   29LU00707NIL$LINESL00005OLINES
+ATTC   12$SCODELINE3
+INST   17LS(SOLD,3,DEPSC)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500708
+LUPT   29LU00708NIL$LINESL00005OLINES
+ATTC   12$SCODELINE4
+INST   17LS(SOLD,3,RADLO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500709
+LUPT   29LU00709NIL$LINESL00005OLINES
+ATTC   12$SCODELINE5
+INST   17LS(SOLD,3,NINFO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500710
+LUPT   29LU00710NIL$LINESL00005OLINES
+ATTC   12$SCODELINE6
+INST   17LS(SOLD,3,RADLO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500711
+LUPT   29LU00711NIL$LINESL00005OLINES
+ATTC   12$SCODELINE7
+INST   17LS(SOLD,3,RESBL)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500712
+LUPT   29LU00712NIL$LINESL00005OLINES
+ATTC   12$SCODELINE8
+INST   17LS(SOLD,3,NINFO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500713
+LUPT   29LU00713NIL$LINESL00005OLINES
+ATTC   12$SCODELINE9
+INST   17LS(SOLD,3,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500714
+LUPT   29LU00714NIL$LINESL00005OLINES
+ATTC   13$SCODELINE10
+INST   17LS(SOLD,3,DEPSC)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500715
+LUPT   29LU00715NIL$LINESL00005OLINES
+ATTC   13$SCODELINE11
+INST   17LS(SOLD,3,TRFCD)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500716
+LUPT   29LU00716NIL$LINESL00005OLINES
+ATTC   13$SCODELINE12
+INST   17LS(SOLD,3,ADINF)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500717
+LUPT   29LU00717NIL$LINESL00005OLINES
+ATTC   13$SCODELINE13
+INST   17LS(SOLD,3,RADLO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500718
+LUPT   29LU00718NIL$LINESL00005OLINES
+ATTC   13$SCODELINE14
+INST   17LS(SOLD,3,NINFO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500719
+LUPT   29LU00719NIL$LINESL00005OLINES
+ATTC   13$SCODELINE15
+INST   17LS(SOLD,3,ADINF)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500720
+LUPT   29LU00720NIL$LINESL00005OLINES
+ATTC   13$SCODELINE16
+INST   17LS(SOLD,3,NINFO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500721
+LUPT   29LU00721NIL$LINESL00005OLINES
+ATTC   13$SCODELINE17
+INST   17LS(SOLD,3,DEPSC)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500722
+LUPT   29LU00722NIL$LINESL00005OLINES
+ATTC   13$SCODELINE18
+INST   17LS(SOLD,3,RADLO)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500723
+LUPT   29LU00723NIL$LINESL00005OLINES
+ATTC   13$SCODELINE19
+INST   17LS(SOLD,3,ADINF)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500724
+LUPT   29LU00724NIL$LINESL00005OLINES
+ATTC   13$SCODELINE20
+INST   17LS(SOLD,3,RESBL)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500725
+LUPT   29LU00725NIL$TEXTSL00008OLINES
+ATTC    1
+INST   59TX($TXSTR,$JUSTH=3,$JUSTV=1,$SPACE=2,'15110',0,0,CHBLK,27)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    500726
+LUPT   34LU00726NIL######P00005OSIMPLIFIED
+ATTC    1
+INST   13SY(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    500727
+LUPT   34LU00727NILACHAREP00006OSIMPLIFIED
+ATTC    1
+INST   13SY(ACHARE02)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500728
+LUPT   34LU00728NILACHBRTP00005OSIMPLIFIED
+ATTC    1
+INST   61SY(ACHBRT07);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    500729
+LUPT   34LU00729NILAIRAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(AIRARE02)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    500730
+LUPT   34LU00730NILBCNCARP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BCNDEF13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500731
+LUPT   34LU00731NILBCNCARP00008OSIMPLIFIED
+ATTC    8CATCAM4
+INST   63SY(BCNCAR04);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500732
+LUPT   34LU00732NILBCNCARP00008OSIMPLIFIED
+ATTC    8CATCAM3
+INST   63SY(BCNCAR03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500733
+LUPT   34LU00733NILBCNCARP00008OSIMPLIFIED
+ATTC    8CATCAM2
+INST   63SY(BCNCAR02);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500734
+LUPT   34LU00734NILBCNCARP00008OSIMPLIFIED
+ATTC    8CATCAM1
+INST   63SY(BCNCAR01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500735
+LUPT   34LU00735NILBCNISDP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BCNISD21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500736
+LUPT   34LU00736NILBCNLATP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BCNDEF13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500737
+LUPT   34LU00737NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR3,4,3BCNSHP1
+INST   63SY(BCNLAT21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500738
+LUPT   34LU00738NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR3,4,3BCNSHP2
+INST   63SY(BCNLAT21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500739
+LUPT   34LU00739NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR3,4,3BCNSHP7
+INST   63SY(BCNLAT21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500740
+LUPT   34LU00740NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP1
+INST   63SY(BCNLAT22);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500741
+LUPT   34LU00741NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP2
+INST   63SY(BCNLAT22);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500742
+LUPT   34LU00742NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP7
+INST   63SY(BCNLAT22);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500743
+LUPT   34LU00743NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR3,4,3BCNSHP3
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500744
+LUPT   34LU00744NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR3,4,3BCNSHP4
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500745
+LUPT   34LU00745NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR3,4,3BCNSHP5
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500746
+LUPT   34LU00746NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP3
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500747
+LUPT   34LU00747NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP4
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500748
+LUPT   34LU00748NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP5
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500749
+LUPT   34LU00749NILBCNLATP00008OSIMPLIFIED
+ATTC   20COLOUR4,3,4BCNSHP6
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500750
+LUPT   34LU00750NILBCNLATP00008OSIMPLIFIED
+ATTC   12COLOUR3,4,3
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500751
+LUPT   34LU00751NILBCNLATP00008OSIMPLIFIED
+ATTC   12COLOUR4,3,4
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500752
+LUPT   34LU00752NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR3BCNSHP1
+INST   63SY(BCNLAT21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500753
+LUPT   34LU00753NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR3BCNSHP2
+INST   63SY(BCNLAT21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500754
+LUPT   34LU00754NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR3BCNSHP7
+INST   63SY(BCNLAT21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500755
+LUPT   34LU00755NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR4BCNSHP1
+INST   63SY(BCNLAT22);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500756
+LUPT   34LU00756NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR4BCNSHP2
+INST   63SY(BCNLAT22);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500757
+LUPT   34LU00757NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR4BCNSHP7
+INST   63SY(BCNLAT22);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500758
+LUPT   34LU00758NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR3BCNSHP3
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500759
+LUPT   34LU00759NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR3BCNSHP4
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500760
+LUPT   34LU00760NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR3BCNSHP5
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500761
+LUPT   34LU00761NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR4BCNSHP3
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500762
+LUPT   34LU00762NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR4BCNSHP4
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500763
+LUPT   34LU00763NILBCNLATP00008OSIMPLIFIED
+ATTC   16COLOUR4BCNSHP5
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500764
+LUPT   34LU00764NILBCNLATP00008OSIMPLIFIED
+ATTC    8COLOUR3
+INST   63SY(BCNLAT15);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500765
+LUPT   34LU00765NILBCNLATP00008OSIMPLIFIED
+ATTC    8COLOUR4
+INST   63SY(BCNLAT16);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500766
+LUPT   34LU00766NILBCNSAWP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BCNSAW13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500767
+LUPT   34LU00767NILBCNSAWP00008OSIMPLIFIED
+ATTC    8BCNSHP1
+INST   63SY(BCNSAW21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500768
+LUPT   34LU00768NILBCNSAWP00008OSIMPLIFIED
+ATTC    8BCNSHP2
+INST   63SY(BCNSAW21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500769
+LUPT   34LU00769NILBCNSAWP00008OSIMPLIFIED
+ATTC    8BCNSHP7
+INST   63SY(BCNSAW21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500770
+LUPT   34LU00770NILBCNSAWP00008OSIMPLIFIED
+ATTC    8BCNSHP3
+INST   63SY(BCNSAW13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500771
+LUPT   34LU00771NILBCNSAWP00008OSIMPLIFIED
+ATTC    8BCNSHP4
+INST   63SY(BCNSAW13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500772
+LUPT   34LU00772NILBCNSAWP00008OSIMPLIFIED
+ATTC    8BCNSHP5
+INST   63SY(BCNSAW13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500773
+LUPT   34LU00773NILBCNSPPP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BCNSPP21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500774
+LUPT   34LU00774NILBCNSPPP00008OSIMPLIFIED
+ATTC   16BCNSHP6CONVIS1
+INST   63SY(CAIRNS11);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500775
+LUPT   34LU00775NILBCNSPPP00008OSIMPLIFIED
+ATTC    9CATSPM18
+INST   63SY(NOTBRD11);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500776
+LUPT   34LU00776NILBCNSPPP00008OSIMPLIFIED
+ATTC    9CATSPM44
+INST   63SY(BCNSPP13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500777
+LUPT   34LU00777NILBCNSPPP00008OSIMPLIFIED
+ATTC    9CATSPM52
+INST   63SY(BCNDEF13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500778
+LUPT   34LU00778NILBCNSPPP00008OSIMPLIFIED
+ATTC    8BCNSHP1
+INST   63SY(BCNSPP21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500779
+LUPT   34LU00779NILBCNSPPP00008OSIMPLIFIED
+ATTC    8BCNSHP3
+INST   63SY(BCNSPP13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500780
+LUPT   34LU00780NILBCNSPPP00008OSIMPLIFIED
+ATTC    8BCNSHP4
+INST   63SY(BCNSPP13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500781
+LUPT   34LU00781NILBCNSPPP00008OSIMPLIFIED
+ATTC    8BCNSHP5
+INST   63SY(BCNSPP13);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500782
+LUPT   34LU00782NILBCNSPPP00008OSIMPLIFIED
+ATTC    8BCNSHP6
+INST   63SY(CAIRNS01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500783
+LUPT   34LU00783NILBCNSPPP00008OSIMPLIFIED
+ATTC    8BCNSHP7
+INST   63SY(BCNSPP21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    500784
+LUPT   34LU00784NILBERTHSP00003OSIMPLIFIED
+ATTC    1
+INST   61SY(BRTHNO01);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500785
+LUPT   34LU00785NILBOYCARP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BOYDEF03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500786
+LUPT   34LU00786NILBOYCARP00008OSIMPLIFIED
+ATTC    8CATCAM4
+INST   63SY(BOYCAR04);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500787
+LUPT   34LU00787NILBOYCARP00008OSIMPLIFIED
+ATTC    8CATCAM3
+INST   63SY(BOYCAR03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500788
+LUPT   34LU00788NILBOYCARP00008OSIMPLIFIED
+ATTC    8CATCAM2
+INST   63SY(BOYCAR02);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500789
+LUPT   34LU00789NILBOYCARP00008OSIMPLIFIED
+ATTC    8CATCAM1
+INST   63SY(BOYCAR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500790
+LUPT   34LU00790NILBOYINBP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BOYMOR11);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500791
+LUPT   34LU00791NILBOYISDP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BOYISD12);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500792
+LUPT   34LU00792NILBOYLATP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BOYDEF03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500793
+LUPT   34LU00793NILBOYLATP00008OSIMPLIFIED
+ATTC   20BOYSHP1COLOUR3,4,3
+INST   63SY(BOYLAT14);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500794
+LUPT   34LU00794NILBOYLATP00008OSIMPLIFIED
+ATTC   20BOYSHP1COLOUR4,3,4
+INST   63SY(BOYLAT13);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500795
+LUPT   34LU00795NILBOYLATP00008OSIMPLIFIED
+ATTC   20BOYSHP2COLOUR3,4,3
+INST   63SY(BOYLAT24);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500796
+LUPT   34LU00796NILBOYLATP00008OSIMPLIFIED
+ATTC   20BOYSHP2COLOUR4,3,4
+INST   63SY(BOYLAT23);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500797
+LUPT   34LU00797NILBOYLATP00008OSIMPLIFIED
+ATTC   20CATLAM3COLOUR3,4,3
+INST   63SY(BOYLAT24);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500798
+LUPT   34LU00798NILBOYLATP00008OSIMPLIFIED
+ATTC   20CATLAM3COLOUR4,3,4
+INST   63SY(BOYLAT23);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500799
+LUPT   34LU00799NILBOYLATP00008OSIMPLIFIED
+ATTC   20CATLAM4COLOUR3,4,3
+INST   63SY(BOYLAT14);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500800
+LUPT   34LU00800NILBOYLATP00008OSIMPLIFIED
+ATTC   20CATLAM4COLOUR4,3,4
+INST   63SY(BOYLAT13);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500801
+LUPT   34LU00801NILBOYLATP00008OSIMPLIFIED
+ATTC   16BOYSHP1COLOUR3
+INST   63SY(BOYLAT14);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500802
+LUPT   34LU00802NILBOYLATP00008OSIMPLIFIED
+ATTC   16BOYSHP1COLOUR4
+INST   63SY(BOYLAT13);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500803
+LUPT   34LU00803NILBOYLATP00008OSIMPLIFIED
+ATTC   16BOYSHP2COLOUR3
+INST   63SY(BOYLAT24);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500804
+LUPT   34LU00804NILBOYLATP00008OSIMPLIFIED
+ATTC   16BOYSHP2COLOUR4
+INST   63SY(BOYLAT23);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500805
+LUPT   34LU00805NILBOYLATP00008OSIMPLIFIED
+ATTC   16CATLAM1COLOUR3
+INST   63SY(BOYLAT24);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500806
+LUPT   34LU00806NILBOYLATP00008OSIMPLIFIED
+ATTC   16CATLAM1COLOUR4
+INST   63SY(BOYLAT23);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500807
+LUPT   34LU00807NILBOYLATP00008OSIMPLIFIED
+ATTC   16CATLAM2COLOUR3
+INST   63SY(BOYLAT14);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500808
+LUPT   34LU00808NILBOYLATP00008OSIMPLIFIED
+ATTC   16CATLAM2COLOUR4
+INST   63SY(BOYLAT13);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500809
+LUPT   34LU00809NILBOYSAWP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BOYSAW12);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500810
+LUPT   34LU00810NILBOYSPPP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(BOYSPP11);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500811
+LUPT   34LU00811NILBOYSPPP00008OSIMPLIFIED
+ATTC   17CATSPM19BOYSHP1
+INST   63SY(BOYSPP15);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500812
+LUPT   34LU00812NILBOYSPPP00008OSIMPLIFIED
+ATTC   17CATSPM19BOYSHP2
+INST   63SY(BOYSPP25);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500813
+LUPT   34LU00813NILBOYSPPP00008OSIMPLIFIED
+ATTC    8CATSPM9
+INST   63SY(BOYSUP02);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500814
+LUPT   34LU00814NILBOYSPPP00008OSIMPLIFIED
+ATTC    9CATSPM15
+INST   63SY(BOYSUP02);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500815
+LUPT   34LU00815NILBOYSPPP00008OSIMPLIFIED
+ATTC    9CATSPM52
+INST   63SY(BOYDEF03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500816
+LUPT   34LU00816NILBOYSPPP00008OSIMPLIFIED
+ATTC    8BOYSHP7
+INST   63SY(BOYSUP02);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500817
+LUPT   34LU00817NILBRIDGEP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500818
+LUPT   34LU00818NILBUAAREP00003OSIMPLIFIED
+ATTC    1
+INST   51SY(BUAARE02);TX(OBJNAM,3,2,2,'15110',1,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    622240
+****    0
+0001    500819
+LUPT   34LU00819NILBUISGLP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(BUISGL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500820
+LUPT   34LU00820NILBUISGLP00006OSIMPLIFIED
+ATTC   24FUNCTN33CONVIS1OBJNAM
+INST   51SY(POSGEN03);TX(OBJNAM,3,2,2,'15110',1,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500821
+LUPT   34LU00821NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500822
+LUPT   34LU00822NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500823
+LUPT   34LU00823NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN22CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500824
+LUPT   34LU00824NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN23CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500825
+LUPT   34LU00825NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN24CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500826
+LUPT   34LU00826NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN25CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500827
+LUPT   34LU00827NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN26CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500828
+LUPT   34LU00828NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN27CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500829
+LUPT   34LU00829NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN33CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500830
+LUPT   34LU00830NILBUISGLP00006OSIMPLIFIED
+ATTC   17FUNCTN35CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500831
+LUPT   34LU00831NILBUISGLP00004OSIMPLIFIED
+ATTC   16FUNCTN33OBJNAM
+INST   51SY(POSGEN03);TX(OBJNAM,3,2,2,'15110',1,0,CHBLK,26)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500832
+LUPT   34LU00832NILBUISGLP00006OSIMPLIFIED
+ATTC    8CONVIS1
+INST   13SY(BUISGL11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500833
+LUPT   34LU00833NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN20
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500834
+LUPT   34LU00834NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN21
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500835
+LUPT   34LU00835NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN22
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500836
+LUPT   34LU00836NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN23
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500837
+LUPT   34LU00837NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN24
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500838
+LUPT   34LU00838NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN25
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500839
+LUPT   34LU00839NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN26
+INST   13SY(BUIREL05)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500840
+LUPT   34LU00840NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN27
+INST   13SY(BUIREL05)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500841
+LUPT   34LU00841NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN33
+INST   13SY(POSGEN03)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500842
+LUPT   34LU00842NILBUISGLP00004OSIMPLIFIED
+ATTC    9FUNCTN35
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500843
+LUPT   34LU00843NILCGUSTAP00007OSIMPLIFIED
+ATTC    1
+INST   13SY(CGUSTA02)
+DISC    6OTHER
+LUCM    638030
+****    0
+0001    500844
+LUPT   34LU00844NILCHKPNTP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500845
+LUPT   34LU00845NILCRANESP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CRANES01)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500846
+LUPT   34LU00846NILCTNAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    500847
+LUPT   34LU00847NILCTRPNTP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(POSGEN04)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    500848
+LUPT   34LU00848NILCTSAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    500849
+LUPT   34LU00849NILCURENTP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500850
+LUPT   34LU00850NILCURENTP00005OSIMPLIFIED
+ATTC   14ORIENTCURVEL
+INST   73SY(CURENT01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500851
+LUPT   34LU00851NILCURENTP00005OSIMPLIFIED
+ATTC    7ORIENT
+INST   20SY(CURENT01,ORIENT)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    500852
+LUPT   34LU00852NILDAMCONP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500853
+LUPT   34LU00853NILDAMCONP00004OSIMPLIFIED
+ATTC    8CATDAM3
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500854
+LUPT   34LU00854NILDAYMARP00007OSIMPLIFIED
+ATTC    1
+INST   63SY(DAYSQR01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    500855
+LUPT   34LU00855NILDAYMARP00007OSIMPLIFIED
+ATTC    9TOPSHP19
+INST   63SY(DAYSQR01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    500856
+LUPT   34LU00856NILDAYMARP00007OSIMPLIFIED
+ATTC    9TOPSHP20
+INST   63SY(DAYSQR01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    500857
+LUPT   34LU00857NILDAYMARP00007OSIMPLIFIED
+ATTC    9TOPSHP21
+INST   63SY(DAYSQR01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    500858
+LUPT   34LU00858NILDAYMARP00007OSIMPLIFIED
+ATTC    9TOPSHP24
+INST   63SY(DAYTRI01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    500859
+LUPT   34LU00859NILDAYMARP00007OSIMPLIFIED
+ATTC    9TOPSHP25
+INST   63SY(DAYTRI05);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    500860
+LUPT   34LU00860NILDISMARP00007OSIMPLIFIED
+ATTC    1
+INST   51SY(DISMAR03);TX(INFORM,2,1,2,'15110',2,0,CHBLK,21)
+DISC    6OTHER
+LUCM    632430
+****    0
+0001    500861
+LUPT   34LU00861NILDISMARP00007OSIMPLIFIED
+ATTC    8CATDIS1
+INST   51SY(DISMAR04);TX(INFORM,2,1,2,'15110',2,0,CHBLK,21)
+DISC    6OTHER
+LUCM    632430
+****    0
+0001    500862
+LUPT   34LU00862NILDMPGRDP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626240
+****    0
+0001    500863
+LUPT   34LU00863NILFOGSIGP00006OSIMPLIFIED
+ATTC    1
+INST   13SY(FOGSIG01)
+DISC    9STANDARD
+LUCM    627080
+****    0
+0001    500864
+LUPT   34LU00864NILFORSTCP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(FORSTC01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500865
+LUPT   34LU00865NILFORSTCP00004OSIMPLIFIED
+ATTC    8CONVIS1
+INST   13SY(FORSTC11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500866
+LUPT   34LU00866NILFSHFACP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(FSHHAV01)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500867
+LUPT   34LU00867NILFSHFACP00004OSIMPLIFIED
+ATTC    8CATFIF1
+INST   13SY(FSHFAC03)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500868
+LUPT   34LU00868NILFSHFACP00004OSIMPLIFIED
+ATTC    8CATFIF2
+INST   13SY(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500869
+LUPT   34LU00869NILFSHFACP00004OSIMPLIFIED
+ATTC    8CATFIF3
+INST   13SY(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500870
+LUPT   34LU00870NILFSHFACP00004OSIMPLIFIED
+ATTC    8CATFIF4
+INST   13SY(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    500871
+LUPT   34LU00871NILGATCONP00008OSIMPLIFIED
+ATTC    1
+INST   13SY(GATCON04)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500872
+LUPT   34LU00872NILGATCONP00008OSIMPLIFIED
+ATTC    8CATGAT2
+INST   13SY(GATCON04)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    500873
+LUPT   34LU00873NILGATCONP00008OSIMPLIFIED
+ATTC    8CATGAT3
+INST   13SY(GATCON04)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500874
+LUPT   34LU00874NILGATCONP00008OSIMPLIFIED
+ATTC    8CATGAT4
+INST   13SY(GATCON03)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500875
+LUPT   34LU00875NILGRIDRNP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500876
+LUPT   34LU00876NILHRBFACP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500877
+LUPT   34LU00877NILHRBFACP00004OSIMPLIFIED
+ATTC    8CATHAF1
+INST   13SY(ROLROL01)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500878
+LUPT   34LU00878NILHRBFACP00004OSIMPLIFIED
+ATTC    8CATHAF4
+INST   13SY(HRBFAC09)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500879
+LUPT   34LU00879NILHRBFACP00004OSIMPLIFIED
+ATTC    8CATHAF5
+INST   13SY(SMCFAC02)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    500880
+LUPT   34LU00880NILHULKESP00005OSIMPLIFIED
+ATTC    1
+INST   13SY(HULKES01)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500881
+LUPT   34LU00881NILICNAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    500882
+LUPT   34LU00882NILLIGHTSP00008OSIMPLIFIED
+ATTC    1
+INST   13CS(LIGHTS05)
+DISC    9STANDARD
+LUCM    627070
+****    0
+0001    500883
+LUPT   34LU00883NILLITFLTP00008OSIMPLIFIED
+ATTC    1
+INST   63SY(LITFLT02);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500884
+LUPT   34LU00884NILLITVESP00008OSIMPLIFIED
+ATTC    1
+INST   64SY(LITVES02);TE('LtV %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617011
+****    0
+0001    500885
+LUPT   34LU00885NILLNDAREP00004OSIMPLIFIED
+ATTC    1
+INST   26SY(LNDARE01);CS(QUAPOS01)
+DISC   12DISPLAYBASE
+LUCM    612010
+****    0
+0001    500886
+LUPT   34LU00886NILLNDELVP00004OSIMPLIFIED
+ATTC    1
+INST   52SY(POSGEN04);TX(ELEVAT,3,2,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500887
+LUPT   34LU00887NILLNDMRKP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(POSGEN01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500888
+LUPT   34LU00888NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK17FUNCTN33CONVIS1
+INST   52SY(TOWERS03);TX(OBJNAM,3,2,2,'15110',1,-1,CHBLK,26)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500889
+LUPT   34LU00889NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK15FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500890
+LUPT   34LU00890NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK15FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500891
+LUPT   34LU00891NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK17FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500892
+LUPT   34LU00892NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK17FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500893
+LUPT   34LU00893NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK20FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500894
+LUPT   34LU00894NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK20FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500895
+LUPT   34LU00895NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK20FUNCTN26CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500896
+LUPT   34LU00896NILLNDMRKP00006OSIMPLIFIED
+ATTC   26CATLMK20FUNCTN27CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500897
+LUPT   34LU00897NILLNDMRKP00004OSIMPLIFIED
+ATTC   18CATLMK17FUNCTN33
+INST   52SY(TOWERS01);TX(OBJNAM,3,2,2,'15110',1,-1,CHBLK,26)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500898
+LUPT   34LU00898NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK1CONVIS1
+INST   13SY(CAIRNS11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500899
+LUPT   34LU00899NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK3CONVIS1
+INST   13SY(CHIMNY11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500900
+LUPT   34LU00900NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK4CONVIS1
+INST   13SY(DSHAER11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500901
+LUPT   34LU00901NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK5CONVIS1
+INST   13SY(FLGSTF01)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500902
+LUPT   34LU00902NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK6CONVIS1
+INST   13SY(FLASTK11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500903
+LUPT   34LU00903NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK7CONVIS1
+INST   13SY(MSTCON14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500904
+LUPT   34LU00904NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK8CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500905
+LUPT   34LU00905NILLNDMRKP00006OSIMPLIFIED
+ATTC   16CATLMK9CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500906
+LUPT   34LU00906NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK10CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500907
+LUPT   34LU00907NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK12CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500908
+LUPT   34LU00908NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK13CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500909
+LUPT   34LU00909NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK15CONVIS1
+INST   13SY(DOMES011)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500910
+LUPT   34LU00910NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK16CONVIS1
+INST   13SY(RASCAN11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500911
+LUPT   34LU00911NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK17CONVIS1
+INST   13SY(TOWERS03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500912
+LUPT   34LU00912NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK18CONVIS1
+INST   13SY(WNDMIL12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500913
+LUPT   34LU00913NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK19CONVIS1
+INST   13SY(WIMCON11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500914
+LUPT   34LU00914NILLNDMRKP00006OSIMPLIFIED
+ATTC   17CATLMK20CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500915
+LUPT   34LU00915NILLNDMRKP00004OSIMPLIFIED
+ATTC   18CATLMK15FUNCTN20
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500916
+LUPT   34LU00916NILLNDMRKP00004OSIMPLIFIED
+ATTC   18CATLMK17FUNCTN20
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500917
+LUPT   34LU00917NILLNDMRKP00004OSIMPLIFIED
+ATTC   18CATLMK20FUNCTN20
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500918
+LUPT   34LU00918NILLNDMRKP00004OSIMPLIFIED
+ATTC    8CATLMK3
+INST   13SY(CHIMNY01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500919
+LUPT   34LU00919NILLNDMRKP00004OSIMPLIFIED
+ATTC    8CATLMK6
+INST   13SY(FLASTK01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500920
+LUPT   34LU00920NILLNDMRKP00004OSIMPLIFIED
+ATTC    8CATLMK7
+INST   13SY(MSTCON04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500921
+LUPT   34LU00921NILLNDMRKP00004OSIMPLIFIED
+ATTC    9CATLMK15
+INST   13SY(DOMES001)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500922
+LUPT   34LU00922NILLNDMRKP00004OSIMPLIFIED
+ATTC    9CATLMK16
+INST   13SY(RASCAN01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500923
+LUPT   34LU00923NILLNDMRKP00004OSIMPLIFIED
+ATTC    9CATLMK17
+INST   13SY(TOWERS01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500924
+LUPT   34LU00924NILLNDMRKP00006OSIMPLIFIED
+ATTC    8CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500925
+LUPT   34LU00925NILLNDRGNP00004OSIMPLIFIED
+ATTC    1
+INST   52SY(POSGEN04);TX(OBJNAM,1,2,2,'15110',0,-1,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500926
+LUPT   34LU00926NILLOCMAGP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(LOCMAG01)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500927
+LUPT   34LU00927NILLOGPONP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500928
+LUPT   34LU00928NILM_NPUBP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    6OTHER
+LUCM    631020
+****    0
+0001    500929
+LUPT   34LU00929NILMAGVARP00004OSIMPLIFIED
+ATTC    1
+INST   52SY(MAGVAR01);TX(VALMAG,3,1,2,'15110',1,-1,CHBLK,27)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    500930
+LUPT   34LU00930NILMARCULP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(MARCUL02)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    500931
+LUPT   34LU00931NILMIPAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500932
+LUPT   34LU00932NILMORFACP00006OSIMPLIFIED
+ATTC    1
+INST   13SY(MORFAC03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500933
+LUPT   34LU00933NILMORFACP00006OSIMPLIFIED
+ATTC    8CATMOR1
+INST   13SY(MORFAC03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500934
+LUPT   34LU00934NILMORFACP00006OSIMPLIFIED
+ATTC    8CATMOR2
+INST   13SY(MORFAC04)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500935
+LUPT   34LU00935NILMORFACP00006OSIMPLIFIED
+ATTC    8CATMOR3
+INST   13SY(PILPNT02)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    500936
+LUPT   34LU00936NILMORFACP00006OSIMPLIFIED
+ATTC    8CATMOR5
+INST   13SY(PILPNT02)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500937
+LUPT   34LU00937NILMORFACP00008OSIMPLIFIED
+ATTC    8CATMOR7
+INST   13SY(BOYMOR11)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    500938
+LUPT   34LU00938NILOBSTRNP00004OSIMPLIFIED
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500939
+LUPT   34LU00939NILOBSTRNP00004OSIMPLIFIED
+ATTC    8CATOBS7
+INST   13SY(FOULGND1)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500940
+LUPT   34LU00940NILOBSTRNP00004OSIMPLIFIED
+ATTC    8CATOBS9
+INST   13SY(ACHARE02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    500941
+LUPT   34LU00941NILOFSPLFP00005OSIMPLIFIED
+ATTC    1
+INST   64SY(OFSPLF01);TE('Prod %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500942
+LUPT   34LU00942NILPILBOPP00006OSIMPLIFIED
+ATTC    1
+INST   63SY(PILBOP02);TE('Plt %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    628010
+****    0
+0001    500943
+LUPT   34LU00943NILPILPNTP00005OSIMPLIFIED
+ATTC    1
+INST   13SY(PILPNT02)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500944
+LUPT   34LU00944NILPIPAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    500945
+LUPT   34LU00945NILPIPSOLP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500946
+LUPT   34LU00946NILPRCAREP00005OSIMPLIFIED
+ATTC    1
+INST   13SY(PRCARE12)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    500947
+LUPT   34LU00947NILPRDAREP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500948
+LUPT   34LU00948NILPRDAREP00004OSIMPLIFIED
+ATTC   16CATPRA5CONVIS1
+INST   13SY(FLASTK11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500949
+LUPT   34LU00949NILPRDAREP00004OSIMPLIFIED
+ATTC   16CATPRA8CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500950
+LUPT   34LU00950NILPRDAREP00004OSIMPLIFIED
+ATTC   16CATPRA9CONVIS1
+INST   13SY(WIMCON11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500951
+LUPT   34LU00951NILPRDAREP00003OSIMPLIFIED
+ATTC    8CATPRA1
+INST   13SY(PRDINS02)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500952
+LUPT   34LU00952NILPRDAREP00003OSIMPLIFIED
+ATTC    8CATPRA5
+INST   13SY(FLASTK01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500953
+LUPT   34LU00953NILPRDAREP00003OSIMPLIFIED
+ATTC    8CATPRA6
+INST   13SY(TMBYRD01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500954
+LUPT   34LU00954NILPRDAREP00003OSIMPLIFIED
+ATTC    8CATPRA8
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500955
+LUPT   34LU00955NILPRDAREP00003OSIMPLIFIED
+ATTC    8CATPRA9
+INST   13SY(WIMCON01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    500956
+LUPT   34LU00956NILPYLONSP00008OSIMPLIFIED
+ATTC    1
+INST   13SY(POSGEN03)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    500957
+LUPT   34LU00957NILRADRFLP00006OSIMPLIFIED
+ATTC    1
+INST   13SY(RADRFL03)
+DISC    9STANDARD
+LUCM    627230
+****    0
+0001    500958
+LUPT   34LU00958NILRADSTAP00005OSIMPLIFIED
+ATTC    1
+INST   13SY(POSGEN01)
+DISC    6OTHER
+LUCM    638010
+****    0
+0001    500959
+LUPT   34LU00959NILRADSTAP00005OSIMPLIFIED
+ATTC    8CATRAS2
+INST   61SY(RDOSTA02);TE('ch %s','COMCHA',3,1,2,'15110',0,0,CHBLK,11)
+DISC    6OTHER
+LUCM    638010
+****    0
+0001    500960
+LUPT   34LU00960NILRAPIDSP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500961
+LUPT   34LU00961NILRCTLPTP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(RTLDEF51)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500962
+LUPT   34LU00962NILRCTLPTP00004OSIMPLIFIED
+ATTC    7ORIENT
+INST   20SY(RCTLPT52,ORIENT)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    500963
+LUPT   34LU00963NILRDOCALP00006OSIMPLIFIED
+ATTC    1
+INST   62SY(RCLDEF01);TE('No %s','OBJNAM',3,2,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500964
+LUPT   34LU00964NILRDOCALP00006OSIMPLIFIED
+ATTC   15TRAFIC1ORIENT
+INST  117SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500965
+LUPT   34LU00965NILRDOCALP00006OSIMPLIFIED
+ATTC   15TRAFIC2ORIENT
+INST  117SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500966
+LUPT   34LU00966NILRDOCALP00006OSIMPLIFIED
+ATTC   15TRAFIC3ORIENT
+INST  117SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500967
+LUPT   34LU00967NILRDOCALP00006OSIMPLIFIED
+ATTC   15TRAFIC4ORIENT
+INST  117SY(RDOCAL03,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    500968
+LUPT   34LU00968NILRDOSTAP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(RDOSTA02)
+DISC    6OTHER
+LUCM    638010
+****    0
+0001    500969
+LUPT   34LU00969NILRETRFLP00006OSIMPLIFIED
+ATTC    1
+INST   13SY(RETRFL02)
+DISC    9STANDARD
+LUCM    627080
+****    0
+0001    500970
+LUPT   34LU00970NILROADWYP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500971
+LUPT   34LU00971NILRSCSTAP00007OSIMPLIFIED
+ATTC    1
+INST   13SY(RSCSTA02)
+DISC    6OTHER
+LUCM    638030
+****    0
+0001    500972
+LUPT   34LU00972NILRTPBCNP00006OSIMPLIFIED
+ATTC    1
+INST   13SY(RTPBCN02)
+DISC    9STANDARD
+LUCM    627210
+****    0
+0001    500973
+LUPT   34LU00973NILRUNWAYP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500974
+LUPT   34LU00974NILSBDAREP00004OSIMPLIFIED
+ATTC    1
+INST   38TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    500975
+LUPT   34LU00975NILSEAAREP00003SSIMPLIFIED
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    500976
+LUPT   34LU00976NILSILTNKP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500977
+LUPT   34LU00977NILSILTNKP00004OSIMPLIFIED
+ATTC   16CATSIL1CONVIS1
+INST   13SY(SILBUI11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500978
+LUPT   34LU00978NILSILTNKP00004OSIMPLIFIED
+ATTC   16CATSIL2CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500979
+LUPT   34LU00979NILSILTNKP00004OSIMPLIFIED
+ATTC   16CATSIL3CONVIS1
+INST   13SY(TOWERS03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500980
+LUPT   34LU00980NILSILTNKP00004OSIMPLIFIED
+ATTC   16CATSIL4CONVIS1
+INST   13SY(TOWERS12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500981
+LUPT   34LU00981NILSILTNKP00004OSIMPLIFIED
+ATTC    8CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500982
+LUPT   34LU00982NILSILTNKP00004OSIMPLIFIED
+ATTC    8CATSIL1
+INST   13SY(SILBUI01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500983
+LUPT   34LU00983NILSILTNKP00004OSIMPLIFIED
+ATTC    8CATSIL2
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500984
+LUPT   34LU00984NILSILTNKP00004OSIMPLIFIED
+ATTC    8CATSIL3
+INST   13SY(TOWERS01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500985
+LUPT   34LU00985NILSILTNKP00004OSIMPLIFIED
+ATTC    8CATSIL4
+INST   13SY(TOWERS02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    500986
+LUPT   34LU00986NILSISTATP00007OSIMPLIFIED
+ATTC    1
+INST   13SY(SISTAT02)
+DISC    9STANDARD
+LUCM    628020
+****    0
+0001    500987
+LUPT   34LU00987NILSISTAWP00007OSIMPLIFIED
+ATTC    1
+INST   13SY(SISTAT02)
+DISC    9STANDARD
+LUCM    628020
+****    0
+0001    500988
+LUPT   34LU00988NILSLCONSP00008OSIMPLIFIED
+ATTC    1
+INST   26SY(MORFAC03);CS(SLCONS03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    500989
+LUPT   34LU00989NILSLOGRDP00003SSIMPLIFIED
+ATTC    1
+INST   13SY(HILTOP01)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500990
+LUPT   34LU00990NILSLOTOPP00003SSIMPLIFIED
+ATTC    1
+INST   13SY(HILTOP01)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    500991
+LUPT   34LU00991NILSLOTOPP00003SSIMPLIFIED
+ATTC    8CONVIS1
+INST   13SY(HILTOP11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    500992
+LUPT   34LU00992NILSMCFACP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    500993
+LUPT   34LU00993NILSNDWAVP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(SNDWAV02)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    500994
+LUPT   34LU00994NILSOUNDGP00006OSIMPLIFIED
+ATTC    1
+INST   13CS(SOUNDG02)
+DISC    6OTHER
+LUCM    633010
+****    0
+0001    500995
+LUPT   34LU00995NILSPLAREP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    500996
+LUPT   34LU00996NILSPRINGP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(SPRING02)
+DISC    6OTHER
+LUCM    634020
+****    0
+0001    500997
+LUPT   34LU00997NILT_HMONP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDEHT01)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500998
+LUPT   34LU00998NILT_NHMNP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDEHT01)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    500999
+LUPT   34LU00999NILT_TIMSP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDEHT01)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    501000
+LUPT   34LU01000NILTS_FEBP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(CURDEF01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501001
+LUPT   34LU01001NILTS_FEBP00004OSIMPLIFIED
+ATTC   15CAT_TS1ORIENT
+INST   73SY(FLDSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501002
+LUPT   34LU01002NILTS_FEBP00004OSIMPLIFIED
+ATTC   15CAT_TS2ORIENT
+INST   73SY(EBBSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501003
+LUPT   34LU01003NILTS_FEBP00004OSIMPLIFIED
+ATTC   15CAT_TS3ORIENT
+INST   73SY(CURENT01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501004
+LUPT   34LU01004NILTS_PADP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501005
+LUPT   34LU01005NILTS_PNHP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501006
+LUPT   34LU01006NILTS_PRHP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501007
+LUPT   34LU01007NILTS_TISP00004OSIMPLIFIED
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501008
+LUPT   34LU01008NILTOPMARP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501009
+LUPT   34LU01009NILTUNNELP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501010
+LUPT   34LU01010NILUWTROCP00004OSIMPLIFIED
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501011
+LUPT   34LU01011NILVEGATNP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501012
+LUPT   34LU01012NILWATFALP00000SSIMPLIFIED
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501013
+LUPT   34LU01013NILWATTURP00003OSIMPLIFIED
+ATTC    1
+INST   13SY(WATTUR02)
+DISC    6OTHER
+LUCM    633040
+****    0
+0001    501014
+LUPT   34LU01014NILWEDKLPP00003OSIMPLIFIED
+ATTC    1
+INST   13SY(WEDKLP03)
+DISC    6OTHER
+LUCM    634020
+****    0
+0001    501015
+LUPT   34LU01015NILWRECKSP00004OSIMPLIFIED
+ATTC    1
+INST   13CS(WRECKS02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501016
+LUPT   34LU01016NILWRECKSP00004OSIMPLIFIED
+ATTC    8CATWRK3
+INST   13SY(FOULGND1)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501017
+LUPT   34LU01017NILcursorP00008OSIMPLIFIED
+ATTC    1
+INST   13SY(CURSRA01)
+DISC   12DISPLAYBASE
+LUCM    611010
+****    0
+0001    501018
+LUPT   34LU01018NILcursorP00008OSIMPLIFIED
+ATTC    8cursty2
+INST   13SY(CURSRB01)
+DISC   15MARINERS OTHER
+LUCM    661040
+****    0
+0001    501019
+LUPT   34LU01019NILdnghltP00008OSIMPLIFIED
+ATTC    1
+INST   13SY(DNGHILIT)
+DISC   18MARINERS STANDARD
+LUCM    653010
+****    0
+0001    501020
+LUPT   34LU01020NILeventsP00008OSIMPLIFIED
+ATTC    1
+INST   51SY(EVENTS02);TX(OBJNAM,3,2,3,'15110',1,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    652410
+****    0
+0001    501021
+LUPT   34LU01021NILmarfeaP00008OSIMPLIFIED
+ATTC    1
+INST   52SY(CHINFO09);TX(OBJNAM,3,1,3,'15110',1,-1,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653050
+****    0
+0001    501022
+LUPT   34LU01022NILmarnotP00008OSIMPLIFIED
+ATTC    1
+INST   51SY(CHINFO09);TX(usrmrk,3,1,2,'15110',0,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653040
+****    0
+0001    501023
+LUPT   34LU01023NILmarnotP00008OSIMPLIFIED
+ATTC    8catnot1
+INST   51SY(CHINFO08);TX(usrmrk,3,1,2,'15110',0,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653030
+****    0
+0001    501024
+LUPT   34LU01024NILmarnotP00008OSIMPLIFIED
+ATTC    8catnot2
+INST   51SY(CHINFO09);TX(usrmrk,3,1,2,'15110',0,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653040
+****    0
+0001    501025
+LUPT   34LU01025NILmnufeaP00005OSIMPLIFIED
+ATTC    1
+INST   13SY(CHINFO10)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    501026
+LUPT   34LU01026NILmnufeaP00005OSIMPLIFIED
+ATTC    8catnot1
+INST   13SY(CHINFO10)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    501027
+LUPT   34LU01027NILmnufeaP00005OSIMPLIFIED
+ATTC    8catnot2
+INST   13SY(CHINFO11)
+DISC   18MARINERS STANDARD
+LUCM    655020
+****    0
+0001    501028
+LUPT   34LU01028NILownshpP00009OSIMPLIFIED
+ATTC    1
+INST   13CS(OWNSHP02)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501029
+LUPT   34LU01029NILplnposP00005OSIMPLIFIED
+ATTC    1
+INST   72SY(PLNPOS01);SY(PLNPOS02,ORIENT);TX(plndat,1,2,2,'15110',4,3,CHBLK,50);
+DISC   18MARINERS STANDARD
+LUCM    652030
+****    0
+0001    501030
+LUPT   34LU01030NILpositnP00005OSIMPLIFIED
+ATTC    1
+INST   52SY(POSITN02);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501031
+LUPT   34LU01031NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth1
+INST   89SY(POSITN02);TX('DR',2,3,2,'15110',-1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501032
+LUPT   34LU01032NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth2
+INST   89SY(POSITN02);TX('EP',2,3,2,'15110',-1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501033
+LUPT   34LU01033NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth3
+INST   87SY(POSITN02);TX('V',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501034
+LUPT   34LU01034NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth4
+INST   87SY(POSITN02);TX('A',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501035
+LUPT   34LU01035NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth5
+INST   87SY(POSITN02);TX('R',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501036
+LUPT   34LU01036NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth6
+INST   87SY(POSITN02);TX('D',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501037
+LUPT   34LU01037NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth7
+INST   87SY(POSITN02);TX('G',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501038
+LUPT   34LU01038NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth8
+INST   88SY(POSITN02);TX('Gl',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501039
+LUPT   34LU01039NILpositnP00005OSIMPLIFIED
+ATTC    8pfmeth9
+INST   87SY(POSITN02);TX('L',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501040
+LUPT   34LU01040NILpositnP00005OSIMPLIFIED
+ATTC    9pfmeth10
+INST   87SY(POSITN02);TX('M',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501041
+LUPT   34LU01041NILpositnP00005OSIMPLIFIED
+ATTC    9pfmeth11
+INST   87SY(POSITN02);TX('O',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501042
+LUPT   34LU01042NILpositnP00005OSIMPLIFIED
+ATTC    9pfmeth12
+INST   87SY(POSITN02);TX('T',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501043
+LUPT   34LU01043NILpositnP00005OSIMPLIFIED
+ATTC    9pfmeth13
+INST   88SY(POSITN02);TX('dG',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501044
+LUPT   34LU01044NILpositnP00005OSIMPLIFIED
+ATTC    9pfmeth14
+INST   89SY(POSITN02);TX('dGl',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501045
+LUPT   34LU01045NILpositnP00005OSIMPLIFIED
+ATTC    9pfmeth15
+INST   88SY(POSITN02);TX('dO',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501046
+LUPT   34LU01046NILrefpntP00007OSIMPLIFIED
+ATTC    1
+INST   13SY(REFPNT02)
+DISC   15MARINERS OTHER
+LUCM    661050
+****    0
+0001    501047
+LUPT   34LU01047NILtidcurP00007OSIMPLIFIED
+ATTC    1
+INST  111SY(TIDCUR01,ORIENT);SY(TIDCUR03);TX(curstr,2,3,2,'15110',-1,2,CHBLK,50);TX(loctim,3,1,2,'15110',1,-2,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501048
+LUPT   34LU01048NILtidcurP00007OSIMPLIFIED
+ATTC    8catcur1
+INST  147SY(TIDCUR01,ORIENT);SY(TIDCUR03);TX('P',2,3,2,'15110',-4,2,CHBLK,50);TX(curstr,2,3,2,'15110',-1,2,CHBLK,50);TX(loctim,3,1,2,'15110',1,-2,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501049
+LUPT   34LU01049NILtidcurP00007OSIMPLIFIED
+ATTC    8catcur2
+INST  147SY(TIDCUR02,ORIENT);SY(TIDCUR03);TX('A',2,3,2,'15110',-4,2,CHBLK,50);TX(curstr,2,3,2,'15110',-1,2,CHBLK,50);TX(loctim,3,1,2,'15110',1,-2,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501050
+LUPT   34LU01050NILvesselP00009OSIMPLIFIED
+ATTC    1
+INST   13CS(VESSEL01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501051
+LUPT   34LU01051NILwaypntP00008OSIMPLIFIED
+ATTC    1
+INST   52SY(WAYPNT11);TX(OBJNAM,3,1,3,'15110',1,-1,APLRT,50)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501052
+LUPT   34LU01052NILwaypntP00008OSIMPLIFIED
+ATTC    8select1
+INST   52SY(WAYPNT11);TX(OBJNAM,3,1,3,'15110',1,-1,CHBLK,50)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501053
+LUPT   34LU01053NILwaypntP00008OSIMPLIFIED
+ATTC    8select2
+INST   52SY(WAYPNT03);TX(OBJNAM,3,1,3,'15110',1,-1,APLRT,50)
+DISC   18MARINERS STANDARD
+LUCM    652210
+****    0
+0001    501054
+LUPT   34LU01054NIL$CSYMBP00005OSIMPLIFIED
+ATTC    1
+INST   13SY(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    501055
+LUPT   34LU01055NIL$CSYMBP00005SSIMPLIFIED
+ATTC   15$SCODEBRIDGE01
+INST   13SY(BRIDGE01)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    501056
+LUPT   34LU01056NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODESCALEB10
+INST   13SY(SCALEB10)
+DISC   12DISPLAYBASE
+LUCM    611030
+****    0
+0001    501057
+LUPT   34LU01057NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODESCALEB11
+INST   13SY(SCALEB11)
+DISC   12DISPLAYBASE
+LUCM    611030
+****    0
+0001    501058
+LUPT   34LU01058NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODENORTHAR1
+INST   13SY(NORTHAR1)
+DISC   12DISPLAYBASE
+LUCM    611040
+****    0
+0001    501059
+LUPT   34LU01059NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEUNITFTH1
+INST   13SY(UNITFTH1)
+DISC   12DISPLAYBASE
+LUCM    611000
+****    0
+0001    501060
+LUPT   34LU01060NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEUNITMTR1
+INST   13SY(UNITMTR1)
+DISC   12DISPLAYBASE
+LUCM    611000
+****    0
+0001    501061
+LUPT   34LU01061NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODECURSRA01
+INST   13SY(CURSRA01)
+DISC   12DISPLAYBASE
+LUCM    611010
+****    0
+0001    501062
+LUPT   34LU01062NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODECURSRB01
+INST   13SY(CURSRB01)
+DISC   12DISPLAYBASE
+LUCM    611010
+****    0
+0001    501063
+LUPT   34LU01063NIL$CSYMBP00007OSIMPLIFIED
+ATTC   15$SCODEREFPNT02
+INST   13SY(REFPNT02)
+DISC   15MARINERS OTHER
+LUCM    661050
+****    0
+0001    501064
+LUPT   34LU01064NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODECHINFO09
+INST   13SY(CHINFO09)
+DISC   18MARINERS STANDARD
+LUCM    653040
+****    0
+0001    501065
+LUPT   34LU01065NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODECHINFO08
+INST   13SY(CHINFO08)
+DISC   18MARINERS STANDARD
+LUCM    653030
+****    0
+0001    501066
+LUPT   34LU01066NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODECHINFO11
+INST   13SY(CHINFO11)
+DISC   18MARINERS STANDARD
+LUCM    655020
+****    0
+0001    501067
+LUPT   34LU01067NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODECHINFO10
+INST   13SY(CHINFO10)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    501068
+LUPT   34LU01068NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODELOWACC01
+INST   13SY(LOWACC01)
+DISC    9STANDARD
+LUCM    621000
+****    0
+0001    501069
+LUPT   34LU01069NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEINFORM01
+INST   13SY(INFORM01)
+DISC    6OTHER
+LUCM    631030
+****    0
+0001    501070
+LUPT   34LU01070NIL$CSYMBP00007OSIMPLIFIED
+ATTC   15$SCODEQUAPOS01
+INST   13SY(QUAPOS01)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501071
+LUPT   34LU01071NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODECHCRID01
+INST   13SY(CHCRID01)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    501072
+LUPT   34LU01072NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODECHCRDEL1
+INST   13SY(CHCRDEL1)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    501073
+LUPT   34LU01073NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODETREPNT04
+INST   13SY(TREPNT04)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    501074
+LUPT   34LU01074NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODETREPNT05
+INST   13SY(TREPNT05)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    501075
+LUPT   34LU01075NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODETOWERS15
+INST   13SY(TOWERS15)
+DISC    9STANDARD
+LUCM    622200
+****    0
+0001    501076
+LUPT   34LU01076NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODETOWERS05
+INST   13SY(TOWERS05)
+DISC    6OTHER
+LUCM    632200
+****    0
+0001    501077
+LUPT   34LU01077NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEOBSTRN11
+INST   13SY(OBSTRN11)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501078
+LUPT   34LU01078NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEOBSTRN01
+INST   13SY(OBSTRN01)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501079
+LUPT   34LU01079NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEOBSTRN02
+INST   13SY(OBSTRN02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501080
+LUPT   34LU01080NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEOBSTRN03
+INST   13SY(OBSTRN03)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501081
+LUPT   34LU01081NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEDANGER01
+INST   13SY(DANGER01)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501082
+LUPT   34LU01082NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEDANGER02
+INST   13SY(DANGER02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501083
+LUPT   34LU01083NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEDANGER03
+INST   13SY(DANGER03)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501084
+LUPT   34LU01084NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODEQUARRY01
+INST   13SY(QUARRY01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501085
+LUPT   34LU01085NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODERFNERY01
+INST   13SY(RFNERY01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501086
+LUPT   34LU01086NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODERFNERY11
+INST   13SY(RFNERY11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501087
+LUPT   34LU01087NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODETNKFRM01
+INST   13SY(TNKFRM01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501088
+LUPT   34LU01088NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODETNKFRM11
+INST   13SY(TNKFRM11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501089
+LUPT   34LU01089NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODEWNDFRM51
+INST   13SY(WNDFRM51)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501090
+LUPT   34LU01090NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODEWNDFRM61
+INST   13SY(WNDFRM61)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501091
+LUPT   34LU01091NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODETMBYRD01
+INST   13SY(TMBYRD01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501092
+LUPT   34LU01092NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEISODGR01
+INST   13SY(ISODGR01)
+DISC   12DISPLAYBASE
+LUCM    614010
+****    0
+0001    501093
+LUPT   34LU01093NIL$CSYMBP00006SSIMPLIFIED
+ATTC   15$SCODERECTRC55
+INST   13SY(RECTRC55)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501094
+LUPT   34LU01094NIL$CSYMBP00006SSIMPLIFIED
+ATTC   15$SCODERECTRC56
+INST   13SY(RECTRC56)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501095
+LUPT   34LU01095NIL$CSYMBP00006SSIMPLIFIED
+ATTC   15$SCODERECTRC57
+INST   13SY(RECTRC57)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501096
+LUPT   34LU01096NIL$CSYMBP00006SSIMPLIFIED
+ATTC   15$SCODERECTRC58
+INST   13SY(RECTRC58)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501097
+LUPT   34LU01097NIL$CSYMBP00006SSIMPLIFIED
+ATTC   15$SCODERECDEF51
+INST   13SY(RECDEF51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501098
+LUPT   34LU01098NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODEDIRBOY01
+INST   13SY(DIRBOY01)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    501099
+LUPT   34LU01099NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODEDIRBOYA1
+INST   13SY(DIRBOYA1)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    501100
+LUPT   34LU01100NIL$CSYMBP00004SSIMPLIFIED
+ATTC   15$SCODEDIRBOYB1
+INST   13SY(DIRBOYB1)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    501101
+LUPT   34LU01101NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODERACNSP01
+INST   13SY(RACNSP01)
+DISC    9STANDARD
+LUCM    622210
+****    0
+0001    501102
+LUPT   34LU01102NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODECHKSYM01
+INST   13SY(CHKSYM01)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501103
+LUPT   34LU01103NIL$CSYMBP00005SSIMPLIFIED
+ATTC   15$SCODEACHRES61
+INST   13SY(ACHRES61)
+DISC    9STANDARD
+LUCM    626010
+****    0
+0001    501104
+LUPT   34LU01104NIL$CSYMBP00005SSIMPLIFIED
+ATTC   15$SCODEACHRES71
+INST   13SY(ACHRES71)
+DISC    9STANDARD
+LUCM    626010
+****    0
+0001    501105
+LUPT   34LU01105NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEAISONE01
+INST   13SY(AISONE01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501106
+LUPT   34LU01106NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEAISSIX01
+INST   13SY(AISSIX01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501107
+LUPT   34LU01107NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEAISDEF01
+INST   13SY(AISDEF01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501108
+LUPT   34LU01108NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEAISSLP01
+INST   20SY(AISSLP01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501109
+LUPT   34LU01109NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEAISVES01
+INST   20SY(AISVES01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501110
+LUPT   34LU01110NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEARPATG01
+INST   13SY(ARPATG01)
+DISC   18MARINERS STANDARD
+LUCM    654010
+****    0
+0001    501111
+LUPT   34LU01111NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEARPONE01
+INST   20SY(ARPONE01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654010
+****    0
+0001    501112
+LUPT   34LU01112NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEARPSIX01
+INST   20SY(ARPSIX01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654010
+****    0
+0001    501113
+LUPT   34LU01113NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEVECWTR01
+INST   13SY(VECWTR01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501114
+LUPT   34LU01114NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEVECGND01
+INST   13SY(VECGND01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501115
+LUPT   34LU01115NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEVECGND21
+INST   13SY(VECGND21)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501116
+LUPT   34LU01116NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEVECWTR21
+INST   20SY(VECWTR21,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501117
+LUPT   34LU01117NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEOWNSHP01
+INST   13SY(OWNSHP01)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501118
+LUPT   34LU01118NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEOWNSHP05
+INST   20SY(OWNSHP05,ORIENT)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501119
+LUPT   34LU01119NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEOSPONE02
+INST   13SY(OSPONE02)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501120
+LUPT   34LU01120NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEOSPSIX02
+INST   13SY(OSPSIX02)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501121
+LUPT   34LU01121NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODEPOSITN02
+INST   13SY(POSITN02)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501122
+LUPT   34LU01122NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEEVENTS02
+INST   13SY(EVENTS02)
+DISC   18MARINERS STANDARD
+LUCM    652410
+****    0
+0001    501123
+LUPT   34LU01123NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODECLRLIN01
+INST   20SY(CLRLIN01,ORIENT)
+DISC   15MARINERS OTHER
+LUCM    653020
+****    0
+0001    501124
+LUPT   34LU01124NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEDNGHILIT
+INST   14SY(DNGHILIT))
+DISC   18MARINERS STANDARD
+LUCM    653010
+****    0
+0001    501125
+LUPT   34LU01125NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEWAYPNT03
+INST   13SY(WAYPNT03)
+DISC   18MARINERS STANDARD
+LUCM    652210
+****    0
+0001    501126
+LUPT   34LU01126NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODEPLNPOS01
+INST   13SY(PLNPOS01)
+DISC   18MARINERS STANDARD
+LUCM    652030
+****    0
+0001    501127
+LUPT   34LU01127NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEPLNSPD04
+INST   13SY(PLNSPD04)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501128
+LUPT   34LU01128NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEWAYPNT11
+INST   13SY(WAYPNT11)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501129
+LUPT   34LU01129NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEWAYPNT01
+INST   13SY(WAYPNT01)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501130
+LUPT   34LU01130NIL$CSYMBP00007OSIMPLIFIED
+ATTC   15$SCODETIDCUR01
+INST   20SY(TIDCUR01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501131
+LUPT   34LU01131NIL$CSYMBP00007OSIMPLIFIED
+ATTC   15$SCODETIDCUR02
+INST   20SY(TIDCUR02,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501132
+LUPT   34LU01132NIL$CSYMBP00007OSIMPLIFIED
+ATTC   15$SCODETIDCUR03
+INST   13SY(TIDCUR03)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501133
+LUPT   34LU01133NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEEBLVRM11
+INST   13SY(EBLVRM11)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    501134
+LUPT   34LU01134NIL$CSYMBP00009OSIMPLIFIED
+ATTC   15$SCODEERBLTIK1
+INST   20SY(ERBLTIK1,ORIENT)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    501135
+LUPT   34LU01135NIL$CSYMBP00008OSIMPLIFIED
+ATTC   15$SCODEPLNSPD03
+INST   13SY(PLNSPD03)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501136
+LUPT   34LU01136NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEUWTROC04
+INST   13SY(UWTROC04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501137
+LUPT   34LU01137NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEUWTROC03
+INST   13SY(UWTROC03)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501138
+LUPT   34LU01138NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEWRECKS01
+INST   13SY(WRECKS01)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501139
+LUPT   34LU01139NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEWRECKS04
+INST   13SY(WRECKS04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501140
+LUPT   34LU01140NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEWRECKS05
+INST   13SY(WRECKS05)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501141
+LUPT   34LU01141NIL$CSYMBP00004OSIMPLIFIED
+ATTC   15$SCODEQUESMRK1
+INST   13SY(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    501142
+LUPT   34LU01142NIL$CSYMBP00005OSIMPLIFIED
+ATTC   15$SCODEBLKADJ01
+INST   13SY(BLKADJ01)
+DISC    9STANDARD
+LUCM    621000
+****    0
+0001    501143
+LUPT   34LU01143NIL$TEXTSP00008OSIMPLIFIED
+ATTC    1
+INST   59TX($TXSTR,$JUSTH=3,$JUSTV=1,$SPACE=2,'15110',0,0,CHBLK,27)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    501144
+LUPT   35LU01144NIL######P00005OPAPER_CHART
+ATTC    1
+INST   13SY(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    501145
+LUPT   35LU01145NILACHAREP00006OPAPER_CHART
+ATTC    1
+INST   13SY(ACHARE02)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    501146
+LUPT   35LU01146NILACHBRTP00005OPAPER_CHART
+ATTC    1
+INST   61SY(ACHBRT07);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29)
+DISC    9STANDARD
+LUCM    626220
+****    0
+0001    501147
+LUPT   35LU01147NILAIRAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(AIRARE02)
+DISC    6OTHER
+LUCM    632240
+****    0
+0001    501148
+LUPT   35LU01148NILBCNCARP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BCNGEN03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501149
+LUPT   35LU01149NILBCNCARP00008OPAPER_CHART
+ATTC    8BCNSHP1
+INST   63SY(BCNSTK02);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501150
+LUPT   35LU01150NILBCNCARP00008OPAPER_CHART
+ATTC    8BCNSHP3
+INST   63SY(BCNTOW01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501151
+LUPT   35LU01151NILBCNCARP00008OPAPER_CHART
+ATTC    8BCNSHP4
+INST   63SY(BCNLTC01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501152
+LUPT   35LU01152NILBCNCARP00008OPAPER_CHART
+ATTC    8BCNSHP5
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501153
+LUPT   35LU01153NILBCNCARP00008OPAPER_CHART
+ATTC    8BCNSHP7
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501154
+LUPT   35LU01154NILBCNISDP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BCNGEN03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501155
+LUPT   35LU01155NILBCNISDP00008OPAPER_CHART
+ATTC    8BCNSHP1
+INST   63SY(BCNSTK02);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501156
+LUPT   35LU01156NILBCNISDP00008OPAPER_CHART
+ATTC    8BCNSHP3
+INST   63SY(BCNTOW01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501157
+LUPT   35LU01157NILBCNISDP00008OPAPER_CHART
+ATTC    8BCNSHP4
+INST   63SY(BCNLTC01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501158
+LUPT   35LU01158NILBCNISDP00008OPAPER_CHART
+ATTC    8BCNSHP5
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501159
+LUPT   35LU01159NILBCNISDP00008OPAPER_CHART
+ATTC    8BCNSHP7
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501160
+LUPT   35LU01160NILBCNLATP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BCNGEN03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501161
+LUPT   35LU01161NILBCNLATP00008OPAPER_CHART
+ATTC   16BCNSHP2CATLAM1
+INST   63SY(PRICKE03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501162
+LUPT   35LU01162NILBCNLATP00008OPAPER_CHART
+ATTC   16BCNSHP2CATLAM2
+INST   63SY(PRICKE04);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501163
+LUPT   35LU01163NILBCNLATP00008OPAPER_CHART
+ATTC   16BCNSHP6CONVIS1
+INST   63SY(CAIRNS11);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501164
+LUPT   35LU01164NILBCNLATP00008OPAPER_CHART
+ATTC    8BCNSHP1
+INST   63SY(BCNSTK02);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501165
+LUPT   35LU01165NILBCNLATP00008OPAPER_CHART
+ATTC    8BCNSHP3
+INST   63SY(BCNTOW01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501166
+LUPT   35LU01166NILBCNLATP00008OPAPER_CHART
+ATTC    8BCNSHP4
+INST   63SY(BCNLTC01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501167
+LUPT   35LU01167NILBCNLATP00008OPAPER_CHART
+ATTC    8BCNSHP5
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501168
+LUPT   35LU01168NILBCNLATP00008OPAPER_CHART
+ATTC    8BCNSHP6
+INST   63SY(CAIRNS01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501169
+LUPT   35LU01169NILBCNLATP00008OPAPER_CHART
+ATTC    8BCNSHP7
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501170
+LUPT   35LU01170NILBCNSAWP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BCNGEN03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501171
+LUPT   35LU01171NILBCNSAWP00008OPAPER_CHART
+ATTC    8BCNSHP1
+INST   63SY(BCNSTK02);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501172
+LUPT   35LU01172NILBCNSAWP00008OPAPER_CHART
+ATTC    8BCNSHP3
+INST   63SY(BCNTOW01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501173
+LUPT   35LU01173NILBCNSAWP00008OPAPER_CHART
+ATTC    8BCNSHP4
+INST   63SY(BCNLTC01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501174
+LUPT   35LU01174NILBCNSAWP00008OPAPER_CHART
+ATTC    8BCNSHP5
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501175
+LUPT   35LU01175NILBCNSAWP00008OPAPER_CHART
+ATTC    8BCNSHP7
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501176
+LUPT   35LU01176NILBCNSPPP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BCNGEN03);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501177
+LUPT   35LU01177NILBCNSPPP00008OPAPER_CHART
+ATTC   16BCNSHP6CONVIS1
+INST   63SY(CAIRNS11);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501178
+LUPT   35LU01178NILBCNSPPP00008OPAPER_CHART
+ATTC    9CATSPM18
+INST   63SY(NOTBRD11);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501179
+LUPT   35LU01179NILBCNSPPP00008OPAPER_CHART
+ATTC    9CATSPM44
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501180
+LUPT   35LU01180NILBCNSPPP00008OPAPER_CHART
+ATTC    8BCNSHP1
+INST   63SY(BCNSTK02);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501181
+LUPT   35LU01181NILBCNSPPP00008OPAPER_CHART
+ATTC    8BCNSHP3
+INST   63SY(BCNTOW01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501182
+LUPT   35LU01182NILBCNSPPP00008OPAPER_CHART
+ATTC    8BCNSHP4
+INST   63SY(BCNLTC01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501183
+LUPT   35LU01183NILBCNSPPP00008OPAPER_CHART
+ATTC    8BCNSHP5
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501184
+LUPT   35LU01184NILBCNSPPP00008OPAPER_CHART
+ATTC    8BCNSHP6
+INST   63SY(CAIRNS01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501185
+LUPT   35LU01185NILBCNSPPP00008OPAPER_CHART
+ATTC    8BCNSHP7
+INST   63SY(BCNGEN01);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-2,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617020
+****    0
+0001    501186
+LUPT   35LU01186NILBERTHSP00003OPAPER_CHART
+ATTC    1
+INST   61SY(BRTHNO01);TE('No %s','OBJNAM',3,1,2,'15110',1,0,CHBLK,29)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    501187
+LUPT   35LU01187NILBOYCARP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BOYGEN03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501188
+LUPT   35LU01188NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP1
+INST   63SY(BOYCON01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501189
+LUPT   35LU01189NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP2
+INST   63SY(BOYCAN01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501190
+LUPT   35LU01190NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP3
+INST   63SY(BOYSPH01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501191
+LUPT   35LU01191NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP4
+INST   63SY(BOYPIL01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501192
+LUPT   35LU01192NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP5
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501193
+LUPT   35LU01193NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP6
+INST   63SY(BOYBAR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501194
+LUPT   35LU01194NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP7
+INST   63SY(BOYSUP01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501195
+LUPT   35LU01195NILBOYCARP00008OPAPER_CHART
+ATTC    8BOYSHP8
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501196
+LUPT   35LU01196NILBOYINBP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BOYINB01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501197
+LUPT   35LU01197NILBOYISDP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BOYGEN03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501198
+LUPT   35LU01198NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP1
+INST   63SY(BOYCON01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501199
+LUPT   35LU01199NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP2
+INST   63SY(BOYCAN01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501200
+LUPT   35LU01200NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP3
+INST   63SY(BOYSPH01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501201
+LUPT   35LU01201NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP4
+INST   63SY(BOYPIL01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501202
+LUPT   35LU01202NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP5
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501203
+LUPT   35LU01203NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP6
+INST   63SY(BOYBAR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501204
+LUPT   35LU01204NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP7
+INST   63SY(BOYSUP01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501205
+LUPT   35LU01205NILBOYISDP00008OPAPER_CHART
+ATTC    8BOYSHP8
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501206
+LUPT   35LU01206NILBOYLATP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BOYGEN03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501207
+LUPT   35LU01207NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP1
+INST   63SY(BOYCON01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501208
+LUPT   35LU01208NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP2
+INST   63SY(BOYCAN01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501209
+LUPT   35LU01209NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP3
+INST   63SY(BOYSPH01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501210
+LUPT   35LU01210NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP4
+INST   63SY(BOYPIL01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501211
+LUPT   35LU01211NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP5
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501212
+LUPT   35LU01212NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP6
+INST   63SY(BOYBAR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501213
+LUPT   35LU01213NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP7
+INST   63SY(BOYSUP01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501214
+LUPT   35LU01214NILBOYLATP00008OPAPER_CHART
+ATTC    8BOYSHP8
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501215
+LUPT   35LU01215NILBOYSAWP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BOYGEN03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501216
+LUPT   35LU01216NILBOYSAWP00008OPAPER_CHART
+ATTC    8BOYSHP3
+INST   63SY(BOYSPH01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501217
+LUPT   35LU01217NILBOYSAWP00008OPAPER_CHART
+ATTC    8BOYSHP4
+INST   63SY(BOYPIL01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501218
+LUPT   35LU01218NILBOYSAWP00008OPAPER_CHART
+ATTC    8BOYSHP5
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501219
+LUPT   35LU01219NILBOYSAWP00008OPAPER_CHART
+ATTC    8BOYSHP6
+INST   63SY(BOYBAR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501220
+LUPT   35LU01220NILBOYSAWP00008OPAPER_CHART
+ATTC    8BOYSHP7
+INST   63SY(BOYSUP01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501221
+LUPT   35LU01221NILBOYSAWP00008OPAPER_CHART
+ATTC    8BOYSHP8
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501222
+LUPT   35LU01222NILBOYSPPP00008OPAPER_CHART
+ATTC    1
+INST   63SY(BOYGEN03);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501223
+LUPT   35LU01223NILBOYSPPP00008OPAPER_CHART
+ATTC    8CATSPM9
+INST   63SY(BOYSUP01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501224
+LUPT   35LU01224NILBOYSPPP00008OPAPER_CHART
+ATTC    9CATSPM15
+INST   63SY(BOYSUP03);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501225
+LUPT   35LU01225NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP1
+INST   63SY(BOYCON01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501226
+LUPT   35LU01226NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP2
+INST   63SY(BOYCAN01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501227
+LUPT   35LU01227NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP3
+INST   63SY(BOYSPH01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501228
+LUPT   35LU01228NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP4
+INST   63SY(BOYPIL01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501229
+LUPT   35LU01229NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP5
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501230
+LUPT   35LU01230NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP6
+INST   63SY(BOYBAR01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501231
+LUPT   35LU01231NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP7
+INST   63SY(BOYSUP01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501232
+LUPT   35LU01232NILBOYSPPP00008OPAPER_CHART
+ATTC    8BOYSHP8
+INST   63SY(BOYSPR01);TE('by %s','OBJNAM',2,1,2,'15110',-2,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501233
+LUPT   35LU01233NILBRIDGEP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501234
+LUPT   35LU01234NILBUAAREP00003OPAPER_CHART
+ATTC    1
+INST   51SY(BUAARE02);TX(OBJNAM,3,2,2,'15110',1,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    622240
+****    0
+0001    501235
+LUPT   35LU01235NILBUISGLP00004OPAPER_CHART
+ATTC    1
+INST   13SY(BUISGL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501236
+LUPT   35LU01236NILBUISGLP00006OPAPER_CHART
+ATTC   24FUNCTN33CONVIS1OBJNAM
+INST   51SY(POSGEN03);TX(OBJNAM,3,2,2,'15110',1,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501237
+LUPT   35LU01237NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501238
+LUPT   35LU01238NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501239
+LUPT   35LU01239NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN22CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501240
+LUPT   35LU01240NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN23CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501241
+LUPT   35LU01241NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN24CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501242
+LUPT   35LU01242NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN25CONVIS1
+INST   13SY(BUIREL14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501243
+LUPT   35LU01243NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN26CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501244
+LUPT   35LU01244NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN27CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501245
+LUPT   35LU01245NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN33CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501246
+LUPT   35LU01246NILBUISGLP00006OPAPER_CHART
+ATTC   17FUNCTN35CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501247
+LUPT   35LU01247NILBUISGLP00004OPAPER_CHART
+ATTC   16FUNCTN33OBJNAM
+INST   51SY(POSGEN03);TX(OBJNAM,3,2,2,'15110',1,0,CHBLK,26)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501248
+LUPT   35LU01248NILBUISGLP00006OPAPER_CHART
+ATTC    8CONVIS1
+INST   13SY(BUISGL11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501249
+LUPT   35LU01249NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN20
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501250
+LUPT   35LU01250NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN21
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501251
+LUPT   35LU01251NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN22
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501252
+LUPT   35LU01252NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN23
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501253
+LUPT   35LU01253NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN24
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501254
+LUPT   35LU01254NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN25
+INST   13SY(BUIREL04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501255
+LUPT   35LU01255NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN26
+INST   13SY(BUIREL05)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501256
+LUPT   35LU01256NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN27
+INST   13SY(BUIREL05)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501257
+LUPT   35LU01257NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN33
+INST   13SY(POSGEN03)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501258
+LUPT   35LU01258NILBUISGLP00004OPAPER_CHART
+ATTC    9FUNCTN35
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501259
+LUPT   35LU01259NILCGUSTAP00007OPAPER_CHART
+ATTC    1
+INST   13SY(CGUSTA02)
+DISC    6OTHER
+LUCM    638030
+****    0
+0001    501260
+LUPT   35LU01260NILCHKPNTP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501261
+LUPT   35LU01261NILCRANESP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CRANES01)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    501262
+LUPT   35LU01262NILCTNAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    626050
+****    0
+0001    501263
+LUPT   35LU01263NILCTRPNTP00004OPAPER_CHART
+ATTC    1
+INST   13SY(POSGEN04)
+DISC    6OTHER
+LUCM    632250
+****    0
+0001    501264
+LUPT   35LU01264NILCTSAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    501265
+LUPT   35LU01265NILCURENTP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501266
+LUPT   35LU01266NILCURENTP00005OPAPER_CHART
+ATTC   14ORIENTCURVEL
+INST   73SY(CURENT01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501267
+LUPT   35LU01267NILCURENTP00005OPAPER_CHART
+ATTC    7ORIENT
+INST   20SY(CURENT01,ORIENT)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501268
+LUPT   35LU01268NILDAMCONP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501269
+LUPT   35LU01269NILDAMCONP00004OPAPER_CHART
+ATTC    8CATDAM3
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    501270
+LUPT   35LU01270NILDAYMARP00007OPAPER_CHART
+ATTC    1
+INST   63SY(DAYSQR21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    501271
+LUPT   35LU01271NILDAYMARP00007OPAPER_CHART
+ATTC    9TOPSHP19
+INST   63SY(DAYSQR21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    501272
+LUPT   35LU01272NILDAYMARP00007OPAPER_CHART
+ATTC    9TOPSHP20
+INST   63SY(DAYSQR21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    501273
+LUPT   35LU01273NILDAYMARP00007OPAPER_CHART
+ATTC    9TOPSHP21
+INST   63SY(DAYSQR21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    501274
+LUPT   35LU01274NILDAYMARP00007OPAPER_CHART
+ATTC    9TOPSHP24
+INST   63SY(DAYTRI21);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    501275
+LUPT   35LU01275NILDAYMARP00007OPAPER_CHART
+ATTC    9TOPSHP25
+INST   63SY(DAYTRI25);TE('bn %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    627025
+****    0
+0001    501276
+LUPT   35LU01276NILDISMARP00007OPAPER_CHART
+ATTC    1
+INST   51SY(DISMAR03);TX(INFORM,2,1,2,'15110',2,0,CHBLK,21)
+DISC    6OTHER
+LUCM    632430
+****    0
+0001    501277
+LUPT   35LU01277NILDISMARP00007OPAPER_CHART
+ATTC    8CATDIS1
+INST   51SY(DISMAR04);TX(INFORM,2,1,2,'15110',2,0,CHBLK,21)
+DISC    6OTHER
+LUCM    632430
+****    0
+0001    501278
+LUPT   35LU01278NILDMPGRDP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626240
+****    0
+0001    501279
+LUPT   35LU01279NILFOGSIGP00006OPAPER_CHART
+ATTC    1
+INST   13SY(FOGSIG01)
+DISC    9STANDARD
+LUCM    627080
+****    0
+0001    501280
+LUPT   35LU01280NILFORSTCP00004OPAPER_CHART
+ATTC    1
+INST   13SY(FORSTC01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501281
+LUPT   35LU01281NILFORSTCP00004OPAPER_CHART
+ATTC    8CONVIS1
+INST   13SY(FORSTC11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501282
+LUPT   35LU01282NILFSHFACP00004OPAPER_CHART
+ATTC    1
+INST   13SY(FSHHAV01)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    501283
+LUPT   35LU01283NILFSHFACP00004OPAPER_CHART
+ATTC    8CATFIF1
+INST   13SY(FSHFAC03)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    501284
+LUPT   35LU01284NILFSHFACP00004OPAPER_CHART
+ATTC    8CATFIF2
+INST   13SY(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    501285
+LUPT   35LU01285NILFSHFACP00004OPAPER_CHART
+ATTC    8CATFIF3
+INST   13SY(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    501286
+LUPT   35LU01286NILFSHFACP00004OPAPER_CHART
+ATTC    8CATFIF4
+INST   13SY(FSHFAC02)
+DISC    6OTHER
+LUCM    634040
+****    0
+0001    501287
+LUPT   35LU01287NILGATCONP00008OPAPER_CHART
+ATTC    1
+INST   13SY(GATCON04)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    501288
+LUPT   35LU01288NILGATCONP00008OPAPER_CHART
+ATTC    8CATGAT2
+INST   13SY(GATCON04)
+DISC    9STANDARD
+LUCM    622010
+****    0
+0001    501289
+LUPT   35LU01289NILGATCONP00008OPAPER_CHART
+ATTC    8CATGAT3
+INST   13SY(GATCON04)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    501290
+LUPT   35LU01290NILGATCONP00008OPAPER_CHART
+ATTC    8CATGAT4
+INST   13SY(GATCON03)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    501291
+LUPT   35LU01291NILGRIDRNP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501292
+LUPT   35LU01292NILHRBFACP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    501293
+LUPT   35LU01293NILHRBFACP00004OPAPER_CHART
+ATTC    8CATHAF1
+INST   13SY(ROLROL01)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    501294
+LUPT   35LU01294NILHRBFACP00004OPAPER_CHART
+ATTC    8CATHAF4
+INST   13SY(HRBFAC09)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    501295
+LUPT   35LU01295NILHRBFACP00004OPAPER_CHART
+ATTC    8CATHAF5
+INST   13SY(SMCFAC02)
+DISC    6OTHER
+LUCM    632410
+****    0
+0001    501296
+LUPT   35LU01296NILHULKESP00005OPAPER_CHART
+ATTC    1
+INST   13SY(HULKES01)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501297
+LUPT   35LU01297NILICNAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626250
+****    0
+0001    501298
+LUPT   35LU01298NILLIGHTSP00008OPAPER_CHART
+ATTC    1
+INST   13CS(LIGHTS05)
+DISC    9STANDARD
+LUCM    627070
+****    0
+0001    501299
+LUPT   35LU01299NILLITFLTP00008OPAPER_CHART
+ATTC    1
+INST   63SY(LITFLT01);TE('by %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501300
+LUPT   35LU01300NILLITVESP00008OPAPER_CHART
+ATTC    1
+INST   64SY(LITVES01);TE('LtV %s','OBJNAM',2,1,2,'15110',-1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    617011
+****    0
+0001    501301
+LUPT   35LU01301NILLNDAREP00004OPAPER_CHART
+ATTC    1
+INST   26SY(LNDARE01);CS(QUAPOS01)
+DISC   12DISPLAYBASE
+LUCM    612010
+****    0
+0001    501302
+LUPT   35LU01302NILLNDELVP00004OPAPER_CHART
+ATTC    1
+INST   52SY(POSGEN04);TX(ELEVAT,3,2,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    501303
+LUPT   35LU01303NILLNDMRKP00004OPAPER_CHART
+ATTC    1
+INST   13SY(POSGEN01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501304
+LUPT   35LU01304NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK17FUNCTN33CONVIS1
+INST   52SY(TOWERS03);TX(OBJNAM,3,2,2,'15110',1,-1,CHBLK,26)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501305
+LUPT   35LU01305NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK15FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501306
+LUPT   35LU01306NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK15FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501307
+LUPT   35LU01307NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK17FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501308
+LUPT   35LU01308NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK17FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501309
+LUPT   35LU01309NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK20FUNCTN20CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501310
+LUPT   35LU01310NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK20FUNCTN21CONVIS1
+INST   13SY(BUIREL13)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501311
+LUPT   35LU01311NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK20FUNCTN26CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501312
+LUPT   35LU01312NILLNDMRKP00006OPAPER_CHART
+ATTC   26CATLMK20FUNCTN27CONVIS1
+INST   13SY(BUIREL15)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501313
+LUPT   35LU01313NILLNDMRKP00004OPAPER_CHART
+ATTC   18CATLMK17FUNCTN33
+INST   52SY(TOWERS01);TX(OBJNAM,3,2,2,'15110',1,-1,CHBLK,26)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501314
+LUPT   35LU01314NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK1CONVIS1
+INST   13SY(CAIRNS11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501315
+LUPT   35LU01315NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK3CONVIS1
+INST   13SY(CHIMNY11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501316
+LUPT   35LU01316NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK4CONVIS1
+INST   13SY(DSHAER11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501317
+LUPT   35LU01317NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK5CONVIS1
+INST   13SY(FLGSTF01)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501318
+LUPT   35LU01318NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK6CONVIS1
+INST   13SY(FLASTK11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501319
+LUPT   35LU01319NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK7CONVIS1
+INST   13SY(MSTCON14)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501320
+LUPT   35LU01320NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK8CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501321
+LUPT   35LU01321NILLNDMRKP00006OPAPER_CHART
+ATTC   16CATLMK9CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501322
+LUPT   35LU01322NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK10CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501323
+LUPT   35LU01323NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK12CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501324
+LUPT   35LU01324NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK13CONVIS1
+INST   13SY(MONUMT12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501325
+LUPT   35LU01325NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK15CONVIS1
+INST   13SY(DOMES011)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501326
+LUPT   35LU01326NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK16CONVIS1
+INST   13SY(RASCAN11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501327
+LUPT   35LU01327NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK17CONVIS1
+INST   13SY(TOWERS03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501328
+LUPT   35LU01328NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK18CONVIS1
+INST   13SY(WNDMIL12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501329
+LUPT   35LU01329NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK19CONVIS1
+INST   13SY(WIMCON11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501330
+LUPT   35LU01330NILLNDMRKP00006OPAPER_CHART
+ATTC   17CATLMK20CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501331
+LUPT   35LU01331NILLNDMRKP00004OPAPER_CHART
+ATTC   18CATLMK20FUNCTN20
+INST   13SY(BUIREL01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501332
+LUPT   35LU01332NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK1
+INST   13SY(CAIRNS01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501333
+LUPT   35LU01333NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK3
+INST   13SY(CHIMNY01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501334
+LUPT   35LU01334NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK4
+INST   13SY(DSHAER01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501335
+LUPT   35LU01335NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK5
+INST   13SY(FLGSTF01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501336
+LUPT   35LU01336NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK6
+INST   13SY(FLASTK01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501337
+LUPT   35LU01337NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK7
+INST   13SY(MSTCON04)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501338
+LUPT   35LU01338NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK8
+INST   13SY(POSGEN03)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501339
+LUPT   35LU01339NILLNDMRKP00004OPAPER_CHART
+ATTC    8CATLMK9
+INST   13SY(MONUMT02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501340
+LUPT   35LU01340NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK10
+INST   13SY(MONUMT02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501341
+LUPT   35LU01341NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK12
+INST   13SY(MONUMT02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501342
+LUPT   35LU01342NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK13
+INST   13SY(MONUMT02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501343
+LUPT   35LU01343NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK15
+INST   13SY(DOMES001)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501344
+LUPT   35LU01344NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK16
+INST   13SY(RASCAN01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501345
+LUPT   35LU01345NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK17
+INST   13SY(TOWERS01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501346
+LUPT   35LU01346NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK18
+INST   13SY(WNDMIL02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501347
+LUPT   35LU01347NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK19
+INST   13SY(WIMCON01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501348
+LUPT   35LU01348NILLNDMRKP00004OPAPER_CHART
+ATTC    9CATLMK20
+INST   13SY(POSGEN01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501349
+LUPT   35LU01349NILLNDMRKP00006OPAPER_CHART
+ATTC    8CONVIS1
+INST   13SY(POSGEN03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501350
+LUPT   35LU01350NILLNDRGNP00004OPAPER_CHART
+ATTC    1
+INST   52SY(POSGEN04);TX(OBJNAM,1,2,2,'15110',0,-1,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    501351
+LUPT   35LU01351NILLOCMAGP00004OPAPER_CHART
+ATTC    1
+INST   13SY(LOCMAG01)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    501352
+LUPT   35LU01352NILLOGPONP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501353
+LUPT   35LU01353NILM_NPUBP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    6OTHER
+LUCM    631020
+****    0
+0001    501354
+LUPT   35LU01354NILMAGVARP00004OPAPER_CHART
+ATTC    1
+INST   52SY(MAGVAR01);TX(VALMAG,3,1,2,'15110',1,-1,CHBLK,27)
+DISC    6OTHER
+LUCM    631080
+****    0
+0001    501355
+LUPT   35LU01355NILMARCULP00004OPAPER_CHART
+ATTC    1
+INST   13SY(MARCUL02)
+DISC    9STANDARD
+LUCM    626210
+****    0
+0001    501356
+LUPT   35LU01356NILMIPAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    501357
+LUPT   35LU01357NILMORFACP00006OPAPER_CHART
+ATTC    1
+INST   13SY(MORFAC03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501358
+LUPT   35LU01358NILMORFACP00008OPAPER_CHART
+ATTC   16CATMOR7BOYSHP3
+INST   13SY(BOYMOR01)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501359
+LUPT   35LU01359NILMORFACP00008OPAPER_CHART
+ATTC   16CATMOR7BOYSHP6
+INST   13SY(BOYMOR03)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501360
+LUPT   35LU01360NILMORFACP00006OPAPER_CHART
+ATTC    8CATMOR1
+INST   13SY(MORFAC03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501361
+LUPT   35LU01361NILMORFACP00006OPAPER_CHART
+ATTC    8CATMOR2
+INST   13SY(MORFAC04)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501362
+LUPT   35LU01362NILMORFACP00006OPAPER_CHART
+ATTC    8CATMOR3
+INST   13SY(PILPNT02)
+DISC    6OTHER
+LUCM    632440
+****    0
+0001    501363
+LUPT   35LU01363NILMORFACP00006OPAPER_CHART
+ATTC    8CATMOR5
+INST   13SY(PILPNT02)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501364
+LUPT   35LU01364NILMORFACP00008OPAPER_CHART
+ATTC    8CATMOR7
+INST   13SY(BOYMOR11)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501365
+LUPT   35LU01365NILOBSTRNP00004OPAPER_CHART
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501366
+LUPT   35LU01366NILOBSTRNP00004OPAPER_CHART
+ATTC    8CATOBS7
+INST   13SY(FOULGND1)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501367
+LUPT   35LU01367NILOBSTRNP00004OPAPER_CHART
+ATTC    8CATOBS9
+INST   13SY(ACHARE02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501368
+LUPT   35LU01368NILOFSPLFP00005OPAPER_CHART
+ATTC    1
+INST   64SY(OFSPLF01);TE('Prod %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    501369
+LUPT   35LU01369NILPILBOPP00006OPAPER_CHART
+ATTC    1
+INST   63SY(PILBOP02);TE('Plt %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21)
+DISC    9STANDARD
+LUCM    628010
+****    0
+0001    501370
+LUPT   35LU01370NILPILPNTP00005OPAPER_CHART
+ATTC    1
+INST   13SY(PILPNT02)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501371
+LUPT   35LU01371NILPIPAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO07)
+DISC    9STANDARD
+LUCM    626230
+****    0
+0001    501372
+LUPT   35LU01372NILPIPSOLP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501373
+LUPT   35LU01373NILPRCAREP00005OPAPER_CHART
+ATTC    1
+INST   13SY(PRCARE12)
+DISC   12DISPLAYBASE
+LUCM    615010
+****    0
+0001    501374
+LUPT   35LU01374NILPRDAREP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501375
+LUPT   35LU01375NILPRDAREP00004OPAPER_CHART
+ATTC   16CATPRA5CONVIS1
+INST   13SY(FLASTK11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501376
+LUPT   35LU01376NILPRDAREP00004OPAPER_CHART
+ATTC   16CATPRA8CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501377
+LUPT   35LU01377NILPRDAREP00004OPAPER_CHART
+ATTC   16CATPRA9CONVIS1
+INST   13SY(WIMCON11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501378
+LUPT   35LU01378NILPRDAREP00003OPAPER_CHART
+ATTC    8CATPRA1
+INST   13SY(PRDINS02)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501379
+LUPT   35LU01379NILPRDAREP00003OPAPER_CHART
+ATTC    8CATPRA5
+INST   13SY(FLASTK01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501380
+LUPT   35LU01380NILPRDAREP00003OPAPER_CHART
+ATTC    8CATPRA6
+INST   13SY(TMBYRD01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501381
+LUPT   35LU01381NILPRDAREP00003OPAPER_CHART
+ATTC    8CATPRA8
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501382
+LUPT   35LU01382NILPRDAREP00003OPAPER_CHART
+ATTC    8CATPRA9
+INST   13SY(WIMCON01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501383
+LUPT   35LU01383NILPYLONSP00008OPAPER_CHART
+ATTC    1
+INST   13SY(POSGEN03)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    501384
+LUPT   35LU01384NILRADRFLP00006OPAPER_CHART
+ATTC    1
+INST   13SY(RADRFL03)
+DISC    9STANDARD
+LUCM    627230
+****    0
+0001    501385
+LUPT   35LU01385NILRADSTAP00005OPAPER_CHART
+ATTC    1
+INST   13SY(POSGEN01)
+DISC    6OTHER
+LUCM    638010
+****    0
+0001    501386
+LUPT   35LU01386NILRADSTAP00005OPAPER_CHART
+ATTC    8CATRAS2
+INST   61SY(RDOSTA02);TE('ch %s','COMCHA',3,1,2,'15110',0,0,CHBLK,11)
+DISC    6OTHER
+LUCM    638010
+****    0
+0001    501387
+LUPT   35LU01387NILRAPIDSP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501388
+LUPT   35LU01388NILRCTLPTP00004OPAPER_CHART
+ATTC    1
+INST   13SY(RTLDEF51)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    501389
+LUPT   35LU01389NILRCTLPTP00004OPAPER_CHART
+ATTC    7ORIENT
+INST   20SY(RCTLPT52,ORIENT)
+DISC   12DISPLAYBASE
+LUCM    615020
+****    0
+0001    501390
+LUPT   35LU01390NILRDOCALP00006OPAPER_CHART
+ATTC    1
+INST   62SY(RCLDEF01);TE('No %s','OBJNAM',3,2,2,'15110',1,-1,CHBLK,21)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    501391
+LUPT   35LU01391NILRDOCALP00006OPAPER_CHART
+ATTC   15TRAFIC1ORIENT
+INST  117SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    501392
+LUPT   35LU01392NILRDOCALP00006OPAPER_CHART
+ATTC   15TRAFIC2ORIENT
+INST  117SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    501393
+LUPT   35LU01393NILRDOCALP00006OPAPER_CHART
+ATTC   15TRAFIC3ORIENT
+INST  117SY(RDOCAL02,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    501394
+LUPT   35LU01394NILRDOCALP00006OPAPER_CHART
+ATTC   15TRAFIC4ORIENT
+INST  117SY(RDOCAL03,ORIENT);TE('No %s','OBJNAM',3,1,2,'15110',1,-1,CHBLK,21);TE('ch %s','COMCHA',3,1,2,'15110',1,1,CHBLK,11)
+DISC   12DISPLAYBASE
+LUCM    615060
+****    0
+0001    501395
+LUPT   35LU01395NILRDOSTAP00004OPAPER_CHART
+ATTC    1
+INST   13SY(RDOSTA02)
+DISC    6OTHER
+LUCM    638010
+****    0
+0001    501396
+LUPT   35LU01396NILRETRFLP00006OPAPER_CHART
+ATTC    1
+INST   13SY(RETRFL01)
+DISC    9STANDARD
+LUCM    627080
+****    0
+0001    501397
+LUPT   35LU01397NILROADWYP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501398
+LUPT   35LU01398NILRSCSTAP00007OPAPER_CHART
+ATTC    1
+INST   13SY(RSCSTA02)
+DISC    6OTHER
+LUCM    638030
+****    0
+0001    501399
+LUPT   35LU01399NILRTPBCNP00006OPAPER_CHART
+ATTC    1
+INST   13SY(RTPBCN02)
+DISC    9STANDARD
+LUCM    627210
+****    0
+0001    501400
+LUPT   35LU01400NILRUNWAYP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501401
+LUPT   35LU01401NILSBDAREP00004OPAPER_CHART
+ATTC    1
+INST   38TX(NATSUR,1,2,2,'15110',0,0,CHBLK,25)
+DISC    6OTHER
+LUCM    634010
+****    0
+0001    501402
+LUPT   35LU01402NILSEAAREP00003SPAPER_CHART
+ATTC    1
+INST   38TX(OBJNAM,1,2,3,'15110',0,0,CHBLK,26)
+DISC    9STANDARD
+LUCM    621060
+****    0
+0001    501403
+LUPT   35LU01403NILSILTNKP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501404
+LUPT   35LU01404NILSILTNKP00004OPAPER_CHART
+ATTC   16CATSIL1CONVIS1
+INST   13SY(SILBUI11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501405
+LUPT   35LU01405NILSILTNKP00004OPAPER_CHART
+ATTC   16CATSIL2CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501406
+LUPT   35LU01406NILSILTNKP00004OPAPER_CHART
+ATTC   16CATSIL3CONVIS1
+INST   13SY(TOWERS03)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501407
+LUPT   35LU01407NILSILTNKP00004OPAPER_CHART
+ATTC   16CATSIL4CONVIS1
+INST   13SY(TOWERS12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501408
+LUPT   35LU01408NILSILTNKP00004OPAPER_CHART
+ATTC    8CONVIS1
+INST   13SY(TNKCON12)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501409
+LUPT   35LU01409NILSILTNKP00004OPAPER_CHART
+ATTC    8CATSIL1
+INST   13SY(SILBUI01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501410
+LUPT   35LU01410NILSILTNKP00004OPAPER_CHART
+ATTC    8CATSIL2
+INST   13SY(TNKCON02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501411
+LUPT   35LU01411NILSILTNKP00004OPAPER_CHART
+ATTC    8CATSIL3
+INST   13SY(TOWERS01)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501412
+LUPT   35LU01412NILSILTNKP00004OPAPER_CHART
+ATTC    8CATSIL4
+INST   13SY(TOWERS02)
+DISC    6OTHER
+LUCM    632220
+****    0
+0001    501413
+LUPT   35LU01413NILSISTATP00007OPAPER_CHART
+ATTC    1
+INST   13SY(SISTAT02)
+DISC    9STANDARD
+LUCM    628020
+****    0
+0001    501414
+LUPT   35LU01414NILSISTAWP00007OPAPER_CHART
+ATTC    1
+INST   13SY(SISTAT02)
+DISC    9STANDARD
+LUCM    628020
+****    0
+0001    501415
+LUPT   35LU01415NILSLCONSP00008OPAPER_CHART
+ATTC    1
+INST   26SY(MORFAC03);CS(SLCONS03)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501416
+LUPT   35LU01416NILSLOGRDP00003SPAPER_CHART
+ATTC    1
+INST   13SY(HILTOP01)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    501417
+LUPT   35LU01417NILSLOTOPP00003SPAPER_CHART
+ATTC    1
+INST   13SY(HILTOP01)
+DISC    6OTHER
+LUCM    632010
+****    0
+0001    501418
+LUPT   35LU01418NILSLOTOPP00003SPAPER_CHART
+ATTC    8CONVIS1
+INST   13SY(HILTOP11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501419
+LUPT   35LU01419NILSMCFACP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501420
+LUPT   35LU01420NILSNDWAVP00004OPAPER_CHART
+ATTC    1
+INST   13SY(SNDWAV02)
+DISC    9STANDARD
+LUCM    624010
+****    0
+0001    501421
+LUPT   35LU01421NILSOUNDGP00006OPAPER_CHART
+ATTC    1
+INST   13CS(SOUNDG02)
+DISC    6OTHER
+LUCM    633010
+****    0
+0001    501422
+LUPT   35LU01422NILSPLAREP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO06)
+DISC    9STANDARD
+LUCM    626040
+****    0
+0001    501423
+LUPT   35LU01423NILSPRINGP00004OPAPER_CHART
+ATTC    1
+INST   13SY(SPRING02)
+DISC    6OTHER
+LUCM    634020
+****    0
+0001    501424
+LUPT   35LU01424NILT_HMONP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDEHT01)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    501425
+LUPT   35LU01425NILT_NHMNP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDEHT01)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    501426
+LUPT   35LU01426NILT_TIMSP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDEHT01)
+DISC    6OTHER
+LUCM    633050
+****    0
+0001    501427
+LUPT   35LU01427NILTS_FEBP00004OPAPER_CHART
+ATTC    1
+INST   13SY(CURDEF01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501428
+LUPT   35LU01428NILTS_FEBP00004OPAPER_CHART
+ATTC   15CAT_TS1ORIENT
+INST   73SY(FLDSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501429
+LUPT   35LU01429NILTS_FEBP00004OPAPER_CHART
+ATTC   15CAT_TS2ORIENT
+INST   73SY(EBBSTR01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501430
+LUPT   35LU01430NILTS_FEBP00004OPAPER_CHART
+ATTC   15CAT_TS3ORIENT
+INST   73SY(CURENT01,ORIENT);TE('%4.1lf kn','CURVEL',3,1,2,'15110',1,-1,CHBLK,31)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501431
+LUPT   35LU01431NILTS_PADP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501432
+LUPT   35LU01432NILTS_PNHP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501433
+LUPT   35LU01433NILTS_PRHP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501434
+LUPT   35LU01434NILTS_TISP00004OPAPER_CHART
+ATTC    1
+INST   13SY(TIDSTR01)
+DISC    6OTHER
+LUCM    633060
+****    0
+0001    501435
+LUPT   35LU01435NILTOPMARP00006OPAPER_CHART
+ATTC    1
+INST   13CS(TOPMAR01)
+DISC    9STANDARD
+LUCM    627050
+****    0
+0001    501436
+LUPT   35LU01436NILTUNNELP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501437
+LUPT   35LU01437NILUWTROCP00004OPAPER_CHART
+ATTC    1
+INST   13CS(OBSTRN04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501438
+LUPT   35LU01438NILVEGATNP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501439
+LUPT   35LU01439NILWATFALP00000SPAPER_CHART
+ATTC    1
+INST    1
+DISC    1
+LUCM    1
+****    0
+0001    501440
+LUPT   35LU01440NILWATTURP00003OPAPER_CHART
+ATTC    1
+INST   13SY(WATTUR02)
+DISC    6OTHER
+LUCM    633040
+****    0
+0001    501441
+LUPT   35LU01441NILWEDKLPP00003OPAPER_CHART
+ATTC    1
+INST   13SY(WEDKLP03)
+DISC    6OTHER
+LUCM    634020
+****    0
+0001    501442
+LUPT   35LU01442NILWRECKSP00004OPAPER_CHART
+ATTC    1
+INST   13CS(WRECKS02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501443
+LUPT   35LU01443NILWRECKSP00004OPAPER_CHART
+ATTC    8CATWRK3
+INST   13SY(FOULGND1)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501444
+LUPT   35LU01444NILcursorP00008OPAPER_CHART
+ATTC    1
+INST   13SY(CURSRA01)
+DISC   12DISPLAYBASE
+LUCM    611010
+****    0
+0001    501445
+LUPT   35LU01445NILcursorP00008OPAPER_CHART
+ATTC    8cursty2
+INST   13SY(CURSRB01)
+DISC   15MARINERS OTHER
+LUCM    661040
+****    0
+0001    501446
+LUPT   35LU01446NILdnghltP00008OPAPER_CHART
+ATTC    1
+INST   13SY(DNGHILIT)
+DISC   18MARINERS STANDARD
+LUCM    653010
+****    0
+0001    501447
+LUPT   35LU01447NILeventsP00008OPAPER_CHART
+ATTC    1
+INST   51SY(EVENTS02);TX(OBJNAM,3,2,3,'15110',1,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    652410
+****    0
+0001    501448
+LUPT   35LU01448NILmarfeaP00008OPAPER_CHART
+ATTC    1
+INST   52SY(CHINFO09);TX(OBJNAM,3,1,3,'15110',1,-1,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653050
+****    0
+0001    501449
+LUPT   35LU01449NILmarnotP00008OPAPER_CHART
+ATTC    1
+INST   51SY(CHINFO09);TX(usrmrk,3,1,2,'15110',0,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653040
+****    0
+0001    501450
+LUPT   35LU01450NILmarnotP00008OPAPER_CHART
+ATTC    8catnot1
+INST   51SY(CHINFO08);TX(usrmrk,3,1,2,'15110',0,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653030
+****    0
+0001    501451
+LUPT   35LU01451NILmarnotP00008OPAPER_CHART
+ATTC    8catnot2
+INST   51SY(CHINFO09);TX(usrmrk,3,1,2,'15110',0,0,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653040
+****    0
+0001    501452
+LUPT   35LU01452NILmnufeaP00005OPAPER_CHART
+ATTC    1
+INST   13SY(CHINFO10)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    501453
+LUPT   35LU01453NILmnufeaP00005OPAPER_CHART
+ATTC    8catnot1
+INST   13SY(CHINFO10)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    501454
+LUPT   35LU01454NILmnufeaP00005OPAPER_CHART
+ATTC    8catnot2
+INST   13SY(CHINFO11)
+DISC   18MARINERS STANDARD
+LUCM    655020
+****    0
+0001    501455
+LUPT   35LU01455NILownshpP00009OPAPER_CHART
+ATTC    1
+INST   13CS(OWNSHP02)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501456
+LUPT   35LU01456NILplnposP00005OPAPER_CHART
+ATTC    1
+INST   72SY(PLNPOS01);SY(PLNPOS02,ORIENT);TX(plndat,1,2,2,'15110',4,3,CHBLK,50);
+DISC   18MARINERS STANDARD
+LUCM    652030
+****    0
+0001    501457
+LUPT   35LU01457NILpositnP00005OPAPER_CHART
+ATTC    1
+INST   52SY(POSITN02);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501458
+LUPT   35LU01458NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth1
+INST   89SY(POSITN02);TX('DR',2,3,2,'15110',-1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501459
+LUPT   35LU01459NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth2
+INST   89SY(POSITN02);TX('EP',2,3,2,'15110',-1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501460
+LUPT   35LU01460NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth3
+INST   87SY(POSITN02);TX('V',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501461
+LUPT   35LU01461NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth4
+INST   87SY(POSITN02);TX('A',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501462
+LUPT   35LU01462NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth5
+INST   87SY(POSITN02);TX('R',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501463
+LUPT   35LU01463NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth6
+INST   87SY(POSITN02);TX('D',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501464
+LUPT   35LU01464NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth7
+INST   87SY(POSITN02);TX('G',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501465
+LUPT   35LU01465NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth8
+INST   88SY(POSITN02);TX('Gl',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501466
+LUPT   35LU01466NILpositnP00005OPAPER_CHART
+ATTC    8pfmeth9
+INST   87SY(POSITN02);TX('L',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501467
+LUPT   35LU01467NILpositnP00005OPAPER_CHART
+ATTC    9pfmeth10
+INST   87SY(POSITN02);TX('M',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501468
+LUPT   35LU01468NILpositnP00005OPAPER_CHART
+ATTC    9pfmeth11
+INST   87SY(POSITN02);TX('O',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501469
+LUPT   35LU01469NILpositnP00005OPAPER_CHART
+ATTC    9pfmeth12
+INST   87SY(POSITN02);TX('T',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501470
+LUPT   35LU01470NILpositnP00005OPAPER_CHART
+ATTC    9pfmeth13
+INST   88SY(POSITN02);TX('dG',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501471
+LUPT   35LU01471NILpositnP00005OPAPER_CHART
+ATTC    9pfmeth14
+INST   89SY(POSITN02);TX('dGl',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501472
+LUPT   35LU01472NILpositnP00005OPAPER_CHART
+ATTC    9pfmeth15
+INST   88SY(POSITN02);TX('dO',3,3,2,'15110',1,1,CHBLK,50);TX(loctim,1,1,2,'15110',0,-1,CHBLK,50)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501473
+LUPT   35LU01473NILrefpntP00007OPAPER_CHART
+ATTC    1
+INST   13SY(REFPNT02)
+DISC   15MARINERS OTHER
+LUCM    661050
+****    0
+0001    501474
+LUPT   35LU01474NILtidcurP00007OPAPER_CHART
+ATTC    1
+INST  111SY(TIDCUR01,ORIENT);SY(TIDCUR03);TX(curstr,2,3,2,'15110',-1,2,CHBLK,50);TX(loctim,3,1,2,'15110',1,-2,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501475
+LUPT   35LU01475NILtidcurP00007OPAPER_CHART
+ATTC    8catcur1
+INST  147SY(TIDCUR01,ORIENT);SY(TIDCUR03);TX('P',2,3,2,'15110',-4,2,CHBLK,50);TX(curstr,2,3,2,'15110',-1,2,CHBLK,50);TX(loctim,3,1,2,'15110',1,-2,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501476
+LUPT   35LU01476NILtidcurP00007OPAPER_CHART
+ATTC    8catcur2
+INST  147SY(TIDCUR02,ORIENT);SY(TIDCUR03);TX('A',2,3,2,'15110',-4,2,CHBLK,50);TX(curstr,2,3,2,'15110',-1,2,CHBLK,50);TX(loctim,3,1,2,'15110',1,-2,CHBLK,50)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501477
+LUPT   35LU01477NILvesselP00009OPAPER_CHART
+ATTC    1
+INST   13CS(VESSEL01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501478
+LUPT   35LU01478NILwaypntP00008OPAPER_CHART
+ATTC    1
+INST   52SY(WAYPNT11);TX(OBJNAM,3,1,3,'15110',1,-1,APLRT,50)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501479
+LUPT   35LU01479NILwaypntP00008OPAPER_CHART
+ATTC    8select1
+INST   52SY(WAYPNT11);TX(OBJNAM,3,1,3,'15110',1,-1,CHBLK,50)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501480
+LUPT   35LU01480NILwaypntP00008OPAPER_CHART
+ATTC    8select2
+INST   52SY(WAYPNT03);TX(OBJNAM,3,1,3,'15110',1,-1,APLRT,50)
+DISC   18MARINERS STANDARD
+LUCM    652210
+****    0
+0001    501481
+LUPT   35LU01481NIL$CSYMBP00005OPAPER_CHART
+ATTC    1
+INST   13SY(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    501482
+LUPT   35LU01482NIL$CSYMBP00005SPAPER_CHART
+ATTC   15$SCODEBRIDGE01
+INST   13SY(BRIDGE01)
+DISC   12DISPLAYBASE
+LUCM    612210
+****    0
+0001    501483
+LUPT   35LU01483NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODESCALEB10
+INST   13SY(SCALEB10)
+DISC   12DISPLAYBASE
+LUCM    611030
+****    0
+0001    501484
+LUPT   35LU01484NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODESCALEB11
+INST   13SY(SCALEB11)
+DISC   12DISPLAYBASE
+LUCM    611030
+****    0
+0001    501485
+LUPT   35LU01485NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODENORTHAR1
+INST   13SY(NORTHAR1)
+DISC   12DISPLAYBASE
+LUCM    611040
+****    0
+0001    501486
+LUPT   35LU01486NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEUNITFTH1
+INST   13SY(UNITFTH1)
+DISC   12DISPLAYBASE
+LUCM    611000
+****    0
+0001    501487
+LUPT   35LU01487NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEUNITMTR1
+INST   13SY(UNITMTR1)
+DISC   12DISPLAYBASE
+LUCM    611000
+****    0
+0001    501488
+LUPT   35LU01488NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODECURSRA01
+INST   13SY(CURSRA01)
+DISC   12DISPLAYBASE
+LUCM    611010
+****    0
+0001    501489
+LUPT   35LU01489NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODECURSRB01
+INST   13SY(CURSRB01)
+DISC   12DISPLAYBASE
+LUCM    611010
+****    0
+0001    501490
+LUPT   35LU01490NIL$CSYMBP00007OPAPER_CHART
+ATTC   15$SCODEREFPNT02
+INST   13SY(REFPNT02)
+DISC   15MARINERS OTHER
+LUCM    661050
+****    0
+0001    501491
+LUPT   35LU01491NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODECHINFO09
+INST   13SY(CHINFO09)
+DISC   18MARINERS STANDARD
+LUCM    653040
+****    0
+0001    501492
+LUPT   35LU01492NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODECHINFO08
+INST   13SY(CHINFO08)
+DISC   18MARINERS STANDARD
+LUCM    653030
+****    0
+0001    501493
+LUPT   35LU01493NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODECHINFO11
+INST   13SY(CHINFO11)
+DISC   18MARINERS STANDARD
+LUCM    655020
+****    0
+0001    501494
+LUPT   35LU01494NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODECHINFO10
+INST   13SY(CHINFO10)
+DISC   18MARINERS STANDARD
+LUCM    655010
+****    0
+0001    501495
+LUPT   35LU01495NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODELOWACC01
+INST   13SY(LOWACC01)
+DISC    9STANDARD
+LUCM    621000
+****    0
+0001    501496
+LUPT   35LU01496NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEINFORM01
+INST   13SY(INFORM01)
+DISC    6OTHER
+LUCM    631030
+****    0
+0001    501497
+LUPT   35LU01497NIL$CSYMBP00007OPAPER_CHART
+ATTC   15$SCODEQUAPOS01
+INST   13SY(QUAPOS01)
+DISC   12DISPLAYBASE
+LUCM    612410
+****    0
+0001    501498
+LUPT   35LU01498NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODECHCRID01
+INST   13SY(CHCRID01)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    501499
+LUPT   35LU01499NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODECHCRDEL1
+INST   13SY(CHCRDEL1)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    501500
+LUPT   35LU01500NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODETREPNT04
+INST   13SY(TREPNT04)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    501501
+LUPT   35LU01501NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODETREPNT05
+INST   13SY(TREPNT05)
+DISC    6OTHER
+LUCM    632030
+****    0
+0001    501502
+LUPT   35LU01502NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODETOWERS15
+INST   13SY(TOWERS15)
+DISC    9STANDARD
+LUCM    622200
+****    0
+0001    501503
+LUPT   35LU01503NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODETOWERS05
+INST   13SY(TOWERS05)
+DISC    6OTHER
+LUCM    632200
+****    0
+0001    501504
+LUPT   35LU01504NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEOBSTRN11
+INST   13SY(OBSTRN11)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501505
+LUPT   35LU01505NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEOBSTRN01
+INST   13SY(OBSTRN01)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501506
+LUPT   35LU01506NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEOBSTRN02
+INST   13SY(OBSTRN02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501507
+LUPT   35LU01507NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEOBSTRN03
+INST   13SY(OBSTRN03)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501508
+LUPT   35LU01508NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEDANGER01
+INST   13SY(DANGER01)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501509
+LUPT   35LU01509NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEDANGER02
+INST   13SY(DANGER02)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501510
+LUPT   35LU01510NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEDANGER03
+INST   13SY(DANGER03)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501511
+LUPT   35LU01511NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODEQUARRY01
+INST   13SY(QUARRY01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501512
+LUPT   35LU01512NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODERFNERY01
+INST   13SY(RFNERY01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501513
+LUPT   35LU01513NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODERFNERY11
+INST   13SY(RFNERY11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501514
+LUPT   35LU01514NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODETNKFRM01
+INST   13SY(TNKFRM01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501515
+LUPT   35LU01515NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODETNKFRM11
+INST   13SY(TNKFRM11)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501516
+LUPT   35LU01516NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODEWNDFRM51
+INST   13SY(WNDFRM51)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501517
+LUPT   35LU01517NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODEWNDFRM61
+INST   13SY(WNDFRM61)
+DISC    9STANDARD
+LUCM    622220
+****    0
+0001    501518
+LUPT   35LU01518NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODETMBYRD01
+INST   13SY(TMBYRD01)
+DISC    6OTHER
+LUCM    632270
+****    0
+0001    501519
+LUPT   35LU01519NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEISODGR01
+INST   13SY(ISODGR01)
+DISC   12DISPLAYBASE
+LUCM    614010
+****    0
+0001    501520
+LUPT   35LU01520NIL$CSYMBP00006SPAPER_CHART
+ATTC   15$SCODERECTRC55
+INST   13SY(RECTRC55)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501521
+LUPT   35LU01521NIL$CSYMBP00006SPAPER_CHART
+ATTC   15$SCODERECTRC56
+INST   13SY(RECTRC56)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501522
+LUPT   35LU01522NIL$CSYMBP00006SPAPER_CHART
+ATTC   15$SCODERECTRC57
+INST   13SY(RECTRC57)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501523
+LUPT   35LU01523NIL$CSYMBP00006SPAPER_CHART
+ATTC   15$SCODERECTRC58
+INST   13SY(RECTRC58)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501524
+LUPT   35LU01524NIL$CSYMBP00006SPAPER_CHART
+ATTC   15$SCODERECDEF51
+INST   13SY(RECDEF51)
+DISC    9STANDARD
+LUCM    625020
+****    0
+0001    501525
+LUPT   35LU01525NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODEDIRBOY01
+INST   13SY(DIRBOY01)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    501526
+LUPT   35LU01526NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODEDIRBOYA1
+INST   13SY(DIRBOYA1)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    501527
+LUPT   35LU01527NIL$CSYMBP00004SPAPER_CHART
+ATTC   15$SCODEDIRBOYB1
+INST   13SY(DIRBOYB1)
+DISC    9STANDARD
+LUCM    627040
+****    0
+0001    501528
+LUPT   35LU01528NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODERACNSP01
+INST   13SY(RACNSP01)
+DISC    9STANDARD
+LUCM    622210
+****    0
+0001    501529
+LUPT   35LU01529NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODECHKSYM01
+INST   13SY(CHKSYM01)
+DISC   12DISPLAYBASE
+LUCM    617010
+****    0
+0001    501530
+LUPT   35LU01530NIL$CSYMBP00005SPAPER_CHART
+ATTC   15$SCODEACHRES61
+INST   13SY(ACHRES61)
+DISC    9STANDARD
+LUCM    626010
+****    0
+0001    501531
+LUPT   35LU01531NIL$CSYMBP00005SPAPER_CHART
+ATTC   15$SCODEACHRES71
+INST   13SY(ACHRES71)
+DISC    9STANDARD
+LUCM    626010
+****    0
+0001    501532
+LUPT   35LU01532NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEAISONE01
+INST   13SY(AISONE01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501533
+LUPT   35LU01533NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEAISSIX01
+INST   13SY(AISSIX01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501534
+LUPT   35LU01534NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEAISDEF01
+INST   13SY(AISDEF01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501535
+LUPT   35LU01535NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEAISSLP01
+INST   20SY(AISSLP01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501536
+LUPT   35LU01536NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEAISVES01
+INST   20SY(AISVES01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501537
+LUPT   35LU01537NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEARPATG01
+INST   13SY(ARPATG01)
+DISC   18MARINERS STANDARD
+LUCM    654010
+****    0
+0001    501538
+LUPT   35LU01538NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEARPONE01
+INST   20SY(ARPONE01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654010
+****    0
+0001    501539
+LUPT   35LU01539NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEARPSIX01
+INST   20SY(ARPSIX01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654010
+****    0
+0001    501540
+LUPT   35LU01540NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEVECWTR01
+INST   13SY(VECWTR01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501541
+LUPT   35LU01541NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEVECGND01
+INST   13SY(VECGND01)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501542
+LUPT   35LU01542NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEVECGND21
+INST   13SY(VECGND21)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501543
+LUPT   35LU01543NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEVECWTR21
+INST   20SY(VECWTR21,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    654030
+****    0
+0001    501544
+LUPT   35LU01544NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEOWNSHP01
+INST   13SY(OWNSHP01)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501545
+LUPT   35LU01545NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEOWNSHP05
+INST   20SY(OWNSHP05,ORIENT)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501546
+LUPT   35LU01546NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEOSPONE02
+INST   13SY(OSPONE02)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501547
+LUPT   35LU01547NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEOSPSIX02
+INST   13SY(OSPSIX02)
+DISC   12DISPLAYBASE
+LUCM    642010
+****    0
+0001    501548
+LUPT   35LU01548NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODEPOSITN02
+INST   13SY(POSITN02)
+DISC   15MARINERS OTHER
+LUCM    662010
+****    0
+0001    501549
+LUPT   35LU01549NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEEVENTS02
+INST   13SY(EVENTS02)
+DISC   18MARINERS STANDARD
+LUCM    652410
+****    0
+0001    501550
+LUPT   35LU01550NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODECLRLIN01
+INST   20SY(CLRLIN01,ORIENT)
+DISC   15MARINERS OTHER
+LUCM    653020
+****    0
+0001    501551
+LUPT   35LU01551NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEDNGHILIT
+INST   14SY(DNGHILIT))
+DISC   18MARINERS STANDARD
+LUCM    653010
+****    0
+0001    501552
+LUPT   35LU01552NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEWAYPNT03
+INST   13SY(WAYPNT03)
+DISC   18MARINERS STANDARD
+LUCM    652210
+****    0
+0001    501553
+LUPT   35LU01553NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODEPLNPOS01
+INST   13SY(PLNPOS01)
+DISC   18MARINERS STANDARD
+LUCM    652030
+****    0
+0001    501554
+LUPT   35LU01554NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEPLNSPD04
+INST   13SY(PLNSPD04)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501555
+LUPT   35LU01555NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEWAYPNT11
+INST   13SY(WAYPNT11)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501556
+LUPT   35LU01556NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEWAYPNT01
+INST   13SY(WAYPNT01)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501557
+LUPT   35LU01557NIL$CSYMBP00007OPAPER_CHART
+ATTC   15$SCODETIDCUR01
+INST   20SY(TIDCUR01,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501558
+LUPT   35LU01558NIL$CSYMBP00007OPAPER_CHART
+ATTC   15$SCODETIDCUR02
+INST   20SY(TIDCUR02,ORIENT)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501559
+LUPT   35LU01559NIL$CSYMBP00007OPAPER_CHART
+ATTC   15$SCODETIDCUR03
+INST   13SY(TIDCUR03)
+DISC   18MARINERS STANDARD
+LUCM    653080
+****    0
+0001    501560
+LUPT   35LU01560NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEEBLVRM11
+INST   13SY(EBLVRM11)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    501561
+LUPT   35LU01561NIL$CSYMBP00009OPAPER_CHART
+ATTC   15$SCODEERBLTIK1
+INST   20SY(ERBLTIK1,ORIENT)
+DISC   15MARINERS OTHER
+LUCM    661010
+****    0
+0001    501562
+LUPT   35LU01562NIL$CSYMBP00008OPAPER_CHART
+ATTC   15$SCODEPLNSPD03
+INST   13SY(PLNSPD03)
+DISC   12DISPLAYBASE
+LUCM    642210
+****    0
+0001    501563
+LUPT   35LU01563NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEUWTROC04
+INST   13SY(UWTROC04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501564
+LUPT   35LU01564NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEUWTROC03
+INST   13SY(UWTROC03)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501565
+LUPT   35LU01565NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEWRECKS01
+INST   13SY(WRECKS01)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501566
+LUPT   35LU01566NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEWRECKS04
+INST   13SY(WRECKS04)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501567
+LUPT   35LU01567NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEWRECKS05
+INST   13SY(WRECKS05)
+DISC    6OTHER
+LUCM    634050
+****    0
+0001    501568
+LUPT   35LU01568NIL$CSYMBP00004OPAPER_CHART
+ATTC   15$SCODEQUESMRK1
+INST   13SY(QUESMRK1)
+DISC    9STANDARD
+LUCM    621010
+****    0
+0001    501569
+LUPT   35LU01569NIL$CSYMBP00005OPAPER_CHART
+ATTC   15$SCODEBLKADJ01
+INST   13SY(BLKADJ01)
+DISC    9STANDARD
+LUCM    621000
+****    0
+0001    501570
+LUPT   35LU01570NIL$TEXTSP00008OPAPER_CHART
+ATTC    1
+INST   59TX($TXSTR,$JUSTH=3,$JUSTV=1,$SPACE=2,'15110',0,0,CHBLK,27)
+DISC   12DISPLAYBASE
+LUCM    610000
+****    0
+0001    501571
+LNST   10LS03346NIL
+LIND   38ACHARE51001080082003030005030030600568
+LXPO   30boundary of an anchorage area
+LCRF    6ACHMGD
+LVCT   32SPA;SW1;PU1429,568;PD1429,1070;
+LVCT   31SPA;SW1;PU1273,717;PD1577,717;
+LVCT   55SPA;SW1;PU1226,967;PD1332,1071;PD1530,1071;PD1628,970;
+LVCT   29SPA;SW1;PU306,815;PD906,815;
+LVCT   31SPA;SW1;PU2732,815;PD3336,815;
+LVCT   42SPA;SW1;PU2868,813;PD3030,975;PD3199,807;
+LVCT   31SPA;SW1;PU1928,814;PD2528,814;
+LVCT   42SPA;SW1;PU2068,812;PD2233,976;PD2397,812;
+LVCT   39SPA;SW1;PU390,812;PD546,974;PD713,813;
+****    0
+0001    501572
+LNST   10LS03354NIL
+LIND   38ACHRES51001080082002729005030044600572
+LXPO   64boundary of an area where anchoring is prohibited or restricted
+LCRF    6ACHMGD
+LVCT   32SPA;SW1;PU1208,572;PD1208,1074;
+LVCT   31SPA;SW1;PU1052,721;PD1356,721;
+LVCT   55SPA;SW1;PU1005,971;PD1111,1075;PD1309,1075;PD1407,974;
+LVCT   31SPA;SW1;PU1418,640;PD987,1071;
+LVCT   31SPA;SW1;PU2248,809;PD2552,809;
+LVCT   31SPA;SW1;PU2404,809;PD2404,959;
+LVCT   29SPA;SW1;PU446,810;PD747,810;
+LVCT   31SPA;SW1;PU2874,810;PD3175,810;
+LVCT   31SPA;SW1;PU3030,810;PD3030,971;
+LVCT   31SPA;SW1;PU1655,812;PD1957,812;
+LVCT   31SPA;SW1;PU1808,812;PD1808,979;
+LVCT   29SPA;SW1;PU595,812;PD595,970;
+****    0
+0001    501573
+LNST   10LS03194NIL
+LIND   38ADMARE01019040146800304001500210501470
+LXPO   22jurisdiction boundary
+LCRF    6ACHGRD
+LVCT   33SPA;SW2;PU2105,1470;PD2409,1470;
+LVCT   33SPA;SW2;PU2261,1470;PD2261,1620;
+****    0
+0001    501574
+LNST   10LS03347NIL
+LIND   38CBLARE51000940083402739005000029500637
+LXPO   35boundary of a submarine cable area
+LCRF    6ACHMGD
+LVCT   29SPA;SW1;PU295,840;PD895,840;
+LVCT   31SPA;SW1;PU2430,840;PD3034,840;
+LVCT   43SPA;SW1;PU2566,843;PD2727,1004;PD2897,836;
+LVCT   31SPA;SW1;PU1631,844;PD2231,844;
+LVCT   43SPA;SW1;PU1767,843;PD1933,1008;PD2097,843;
+LVCT   39SPA;SW1;PU436,842;PD592,997;PD757,835;
+LVCT   54SPA;SW1;PU1280,637;PD1130,837;PD1330,937;PD1180,1137;
+****    0
+0001    501575
+LNST   10LS02012NIL
+LIND   38CBLSUB06004480127402293005000069201050
+LXPO   16submarine cable
+LCRF    6ACHMGD
+LVCT   57SPA;SW1;PU2935,1050;PD2785,1250;PD2985,1350;PD2835,1550;
+LVCT   57SPA;SW1;PU1390,1263;PD1541,1161;PD1839,1365;PD1994,1263;
+LVCT   55SPA;SW1;PU692,1264;PD847,1164;PD1148,1371;PD1292,1264;
+LVCT   57SPA;SW1;PU2076,1249;PD2228,1149;PD2527,1352;PD2686,1249;
+****    0
+0001    501576
+LNST   10LS03432NIL
+LIND   38CHCRDEL1046360265000799008040517902251
+LXPO   46this line has been deleted by a manual update
+LCRF    6ACHCOR
+LVCT   33SPA;SW1;PU5179,3055;PD5978,2251;
+****    0
+0001    501577
+LNST   10LS03359NIL
+LIND   38CHCRID01044830265000300003000542402498
+LXPO   36this line has been manually updated
+LCRF    6ACHCOR
+LVCT   27SPA;SW1;PU5574,2648;CI150;
+****    0
+0001    501578
+LNST   10LS03418NIL
+LIND   38CTNARE51001040083003030005180029800640
+LXPO   41boundary of area with a specific caution
+LCRF    6ACHMGD
+LVCT   29SPA;SW1;PU298,822;PD898,822;
+LVCT   31SPA;SW1;PU2724,822;PD3328,822;
+LVCT   42SPA;SW1;PU2890,826;PD3051,987;PD3221,819;
+LVCT   31SPA;SW1;PU1920,829;PD2520,829;
+LVCT   42SPA;SW1;PU2056,828;PD2222,993;PD2386,828;
+LVCT   39SPA;SW1;PU432,831;PD588,986;PD753,824;
+LVCT   26SPA;SW1;PU1415,899;CI259;
+LVCT   31SPA;SW1;PU1411,725;PD1411,950;
+LVCT   33SPA;SW1;PU1395,1043;PD1450,1043;
+****    0
+0001    501579
+LNST   10LS03417NIL
+LIND   38CTYARE51012980081200600001640150700812
+LXPO   46boundary of area to be navigated with caution
+LCRF    6ACHMGD
+LVCT   31SPA;SW1;PU1507,814;PD2107,814;
+LVCT   42SPA;SW1;PU1647,812;PD1812,976;PD1976,812;
+****    0
+0001    501580
+LNST   10LS03208NIL
+LIND   38DWLDEF01014490141502550005260159601192
+LXPO   63deep water route centreline, direction not defined in the data
+LCRF    6ATRFCD
+LVCT   93SPA;SW1;PU3522,1270;PD3522,1574;PD3626,1574;PD3726,1529;PD3726,1313;PD3623,1270;PD3522,1270;
+LVCT   69SPA;SW1;PU3847,1266;PD3923,1575;PD3994,1266;PD4080,1571;PD4146,1266;
+LVCT   33SPA;SW1;PU1596,1415;PD1921,1415;
+LVCT   33SPA;SW1;PU3002,1415;PD3327,1415;
+LVCT   45SPA;SW1;PU2269,1570;PD2064,1415;PD2269,1260;
+LVCT   33SPA;SW1;PU3618,1718;PD4022,1718;
+LVCT  309SPA;SW1;PU2353,1288;PD2366,1260;PD2379,1235;PD2396,1218;PD2413,1201;PD2439,1192;PD2462,1194;PD2492,1203;PD2520,1209;PD2543,1243;PD2561,1262;PD2573,1284;PD2573,1309;PD2561,1333;PD2548,1354;PD2528,1378;PD2505,1403;PD2484,1429;PD2471,1448;PD2460,1465;PD2458,1487;PD2458,1510;PD2458,1527;PD2460,1549;PD2460,1551;
+LVCT   33SPA;SW2;PU2439,1635;PD2492,1635;
+LVCT   45SPA;SW1;PU2653,1565;PD2858,1410;PD2653,1255;
+****    0
+0001    501581
+LNST   10LS02015NIL
+LIND   38DWRTCL05014490141502550004580159601260
+LXPO   62two-way deep water route centreline, not based on fixed marks
+LCRF    6ATRFCD
+LVCT   93SPA;SW1;PU3522,1270;PD3522,1574;PD3626,1574;PD3726,1529;PD3726,1313;PD3623,1270;PD3522,1270;
+LVCT   69SPA;SW1;PU3847,1266;PD3923,1575;PD3994,1266;PD4080,1571;PD4146,1266;
+LVCT   33SPA;SW1;PU1596,1415;PD1921,1415;
+LVCT   33SPA;SW1;PU2299,1415;PD2624,1415;
+LVCT   33SPA;SW1;PU3002,1415;PD3327,1415;
+LVCT   45SPA;SW1;PU2299,1570;PD2094,1415;PD2299,1260;
+LVCT   33SPA;SW1;PU3618,1718;PD4022,1718;
+LVCT   45SPA;SW1;PU2624,1570;PD2829,1415;PD2624,1260;
+****    0
+0001    501582
+LNST   10LS02016NIL
+LIND   38DWRTCL06014490141502550004580159601260
+LXPO   58two-way deep water route centreline, based on fixed marks
+LCRF    6ATRFCD
+LVCT   93SPA;SW1;PU3522,1270;PD3522,1574;PD3626,1574;PD3726,1529;PD3726,1313;PD3623,1270;PD3522,1270;
+LVCT   69SPA;SW1;PU3847,1266;PD3923,1575;PD3994,1266;PD4080,1571;PD4146,1266;
+LVCT   33SPA;SW1;PU3618,1718;PD4022,1718;
+LVCT   33SPA;SW1;PU1596,1415;PD3327,1415;
+LVCT   45SPA;SW1;PU2299,1570;PD2094,1415;PD2299,1260;
+LVCT   45SPA;SW1;PU2624,1570;PD2829,1415;PD2624,1260;
+****    0
+0001    501583
+LNST   10LS02017NIL
+LIND   38DWRTCL07014490141501860004580159601269
+LXPO   62one-way deep water route centreline, not based on fixed marks
+LCRF    6ATRFCD
+LVCT   69SPA;SW1;PU3157,1275;PD3233,1584;PD3304,1275;PD3390,1584;PD3456,1275;
+LVCT   33SPA;SW1;PU1596,1415;PD1921,1415;
+LVCT   33SPA;SW1;PU2312,1415;PD2637,1415;
+LVCT   33SPA;SW1;PU2928,1727;PD3332,1727;
+LVCT   45SPA;SW1;PU1934,1579;PD2139,1424;PD1934,1269;
+LVCT   93SPA;SW1;PU2832,1279;PD2832,1583;PD2936,1583;PD3036,1538;PD3036,1322;PD2933,1279;PD2832,1279;
+****    0
+0001    501584
+LNST   10LS02018NIL
+LIND   38DWRTCL08014490141501860004580159601269
+LXPO   58one-way deep water route centreline, based on fixed-marks
+LCRF    6ATRFCD
+LVCT   69SPA;SW1;PU3157,1275;PD3233,1584;PD3304,1275;PD3390,1580;PD3456,1275;
+LVCT   33SPA;SW1;PU1596,1415;PD2637,1415;
+LVCT   33SPA;SW1;PU2928,1727;PD3332,1727;
+LVCT   45SPA;SW1;PU1934,1579;PD2139,1424;PD1934,1269;
+LVCT   93SPA;SW1;PU2832,1279;PD2832,1583;PD2936,1583;PD3036,1538;PD3036,1322;PD2933,1279;PD2832,1279;
+****    0
+0001    501585
+LNST   10LS03352NIL
+LIND   38DWRUTE51001040083003030003090029800743
+LXPO   31boundary of a deep water route
+LCRF    6ATRFCD
+LVCT   29SPA;SW1;PU298,822;PD898,822;
+LVCT   31SPA;SW1;PU2724,822;PD3328,822;
+LVCT   42SPA;SW1;PU2890,826;PD3051,987;PD3221,819;
+LVCT   31SPA;SW1;PU1920,818;PD2520,818;
+LVCT   42SPA;SW1;PU2056,828;PD2222,993;PD2386,828;
+LVCT   39SPA;SW1;PU432,831;PD588,986;PD753,824;
+LVCT   89SPA;SW1;PU1101,747;PD1101,1051;PD1205,1051;PD1305,1006;PD1305,790;PD1202,747;PD1101,747;
+LVCT   66SPA;SW1;PU1426,743;PD1502,1052;PD1573,743;PD1659,1048;PD1725,743;
+****    0
+0001    501586
+LNST   10LS03381NIL
+LIND   38ENTRES51001380080702729005780044600522
+LXPO   60boundary of an area where entry is prohibited or restricted
+LCRF    6ACHMGD
+LVCT   31SPA;SW1;PU2248,809;PD2552,809;
+LVCT   31SPA;SW1;PU2404,809;PD2404,959;
+LVCT   29SPA;SW1;PU446,810;PD747,810;
+LVCT   31SPA;SW1;PU2874,810;PD3175,810;
+LVCT   31SPA;SW1;PU3030,810;PD3030,971;
+LVCT   31SPA;SW1;PU1655,812;PD1957,812;
+LVCT   31SPA;SW1;PU1808,812;PD1808,979;
+LVCT   29SPA;SW1;PU595,812;PD595,970;
+LVCT   26SPA;SW1;PU1237,811;CI289;
+LVCT   31SPA;SW1;PU1085,818;PD1396,818;
+****    0
+0001    501587
+LNST   10LS03414NIL
+LIND   38ERBLNA01012440155002204000000145901555
+LXPO   30electronic bearing line, dash
+LCRF    6ANINFO
+LVCT   33SPA;SW2;PU1459,1555;PD2463,1555;
+LVCT   33SPA;SW2;PU2664,1555;PD3663,1555;
+****    0
+0001    501588
+LNST   10LS03415NIL
+LIND   38ERBLNB01012490155201407000030145901552
+LXPO   34electronic bearing line, dash dot
+LCRF    6ANINFO
+LVCT   33SPA;SW2;PU1459,1555;PD2463,1555;
+LVCT   33SPA;SW2;PU2668,1552;PD2866,1552;
+****    0
+0001    501589
+LNST   10LS02019NIL
+LIND   38FERYRT01001870085301999001600038700774
+LXPO   12ferry route
+LCRF    6ACHMGD
+LVCT   84SPA;SW1;PU1374,890;PD1275,934;PD869,933;PD869,774;PD1275,774;PD1374,819;PD1374,890;
+LVCT   31SPA;SW1;PU1602,853;PD1894,853;
+LVCT   29SPA;SW1;PU697,853;PD387,853;
+LVCT   31SPA;SW1;PU2094,853;PD2386,853;
+****    0
+0001    501590
+LNST   10LS02020NIL
+LIND   38FERYRT02001870085301999001600038700774
+LXPO   18cable ferry route
+LCRF    6ACHBLK
+LVCT   84SPA;SW1;PU1374,890;PD1275,934;PD869,933;PD869,774;PD1275,774;PD1374,819;PD1374,890;
+LVCT   31SPA;SW1;PU1602,853;PD1894,853;
+LVCT   29SPA;SW1;PU697,853;PD387,853;
+LVCT   31SPA;SW1;PU2094,853;PD2386,853;
+****    0
+0001    501591
+LNST   10LS02021NIL
+LIND   38FSHFAC02006870077400202001530068700621
+LXPO   15fishing stakes
+LCRF    6ACHGRD
+LVCT   39SPA;SW1;PU889,621;PD889,774;PD687,774;
+****    0
+0001    501592
+LNST   10LS03355NIL
+LIND   38FSHRES51001080082002691003930044600689
+LXPO   74boundary of an area where trawling or fishing is prohibited or restricted
+LCRF    6ACHMGD
+LVCT   31SPA;SW1;PU2248,809;PD2552,809;
+LVCT   31SPA;SW1;PU2404,809;PD2404,959;
+LVCT   29SPA;SW1;PU446,810;PD747,810;
+LVCT   31SPA;SW1;PU2836,810;PD3137,810;
+LVCT   31SPA;SW1;PU2992,810;PD2992,971;
+LVCT   31SPA;SW1;PU1655,812;PD1957,812;
+LVCT   31SPA;SW1;PU1808,812;PD1808,979;
+LVCT   29SPA;SW1;PU595,812;PD595,970;
+LVCT  252SPA;SW1;PU895,973;PD1013,874;PD1063,819;PD1121,781;PD1182,758;PD1239,741;PD1302,741;PD1355,747;PD1419,769;PD1477,795;PD1529,834;PD1560,874;PD1521,910;PD1484,943;PD1438,966;PD1377,989;PD1316,1001;PD1247,1001;PD1182,989;PD1118,966;PD1063,929;PD1025,898;
+LVCT   29SPA;SW1;PU997,863;PD914,755;
+LVCT   30SPA;SW1;PU1023,897;PD996,867;
+LVCT   32SPA;SW1;PU1499,689;PD1106,1082;
+****    0
+0001    501593
+LNST   10LS03345NIL
+LIND   38HODATA01007870063400601003000078800635
+LXPO   32boundary marking end of HO data
+LCRF    6ACHGRD
+LVCT   30SPA;SW1;PU788,935;PD1088,635;
+LVCT   30SPA;SW1;PU789,636;PD1389,636;
+****    0
+0001    501594
+LNST   10LS03424NIL
+LIND   38LOWACC01053050265000130000790547602607
+LXPO   43safety contour of low accuracy in position
+LCRF    6ADEPSC
+LVCT   69SPA;SW1;PU5476,2607;PD5476,2686;PD5606,2686;PD5606,2607;PD5476,2607;
+****    0
+0001    501595
+LNST   10LS03425NIL
+LIND   38LOWACC11053050265000130000790547602607
+LXPO   36contour of low accuracy in position
+LCRF    6ADEPCN
+LVCT   69SPA;SW1;PU5476,2607;PD5476,2686;PD5606,2686;PD5606,2607;PD5476,2607;
+****    0
+0001    501596
+LNST   10LS03426NIL
+LIND   38LOWACC21053050265000130000790547602607
+LXPO   64coastline or shoreline construction of low accuracy in position
+LCRF    6ACSTLN
+LVCT   69SPA;SW1;PU5476,2607;PD5476,2686;PD5606,2686;PD5606,2607;PD5476,2607;
+****    0
+0001    501597
+LNST   10LS03427NIL
+LIND   38LOWACC31053050265000130000790547602607
+LXPO   47area of wrecks or obstructions of low accuracy
+LCRF    6ACHGRD
+LVCT   69SPA;SW1;PU5476,2607;PD5476,2686;PD5606,2686;PD5606,2607;PD5476,2607;
+****    0
+0001    501598
+LNST   10LS03428NIL
+LIND   38LOWACC41053050265000130000790547602607
+LXPO   52danger line of low accuracy surrounding a foul area
+LCRF    6ACHBLK
+LVCT   69SPA;SW1;PU5476,2607;PD5476,2686;PD5606,2686;PD5606,2607;PD5476,2607;
+****    0
+0001    501599
+LNST   10LS03379NIL
+LIND   38MARSYS51001120084302558003200010900681
+LXPO   72boundary between IALA-A and IALA-B systems of lateral buoys and beacons
+LCRF    6ACHGRD
+LVCT   31SPA;SW1;PU1633,846;PD1925,846;
+LVCT   31SPA;SW1;PU2375,846;PD2667,846;
+LVCT   31SPA;SW1;PU1415,846;PD1109,846;
+LVCT   39SPA;SW1;PU743,982;PD843,681;PD945,982;
+LVCT   29SPA;SW1;PU783,863;PD909,863;
+LVCT   55SPA;SW1;PU2181,700;PD2082,700;PD2082,1001;PD2181,1001;
+LVCT   31SPA;SW1;PU2082,852;PD2181,852;
+LVCT   31SPA;SW1;PU2224,730;PD2224,829;
+LVCT   31SPA;SW1;PU2223,879;PD2223,978;
+LVCT   31SPA;SW1;PU2181,700;PD2224,730;
+LVCT   32SPA;SW1;PU2181,1001;PD2224,968;
+LVCT   42SPA;SW1;PU2224,826;PD2178,856;PD2221,882;
+LVCT   29SPA;SW1;PU618,846;PD314,846;
+LVCT   29SPA;SW1;PU109,849;PD120,843;
+****    0
+0001    501600
+LNST   10LS03380NIL
+LIND   38NAVARE51012980081200600001640150700812
+LXPO   75boundary of a navigation feature such as a fairway, magnetic anomaly, etc.
+LCRF    6ACHGRD
+LVCT   31SPA;SW1;PU1507,814;PD2107,814;
+LVCT   42SPA;SW1;PU1647,812;PD1812,976;PD1976,812;
+****    0
+0001    501601
+LNST   10LS03348NIL
+LIND   38PIPARE51000930080503030002210029800772
+LXPO   74boundary of a submarine pipeline area with potentially dangerous contents
+LCRF    6ACHMGD
+LVCT   25SPA;SW1;PU1619,867;CI95;
+LVCT   31SPA;SW1;PU1125,867;PD1522,867;
+LVCT   29SPA;SW1;PU298,822;PD898,822;
+LVCT   31SPA;SW1;PU2724,822;PD3328,822;
+LVCT   42SPA;SW1;PU2890,826;PD3051,987;PD3221,819;
+LVCT   31SPA;SW1;PU1920,829;PD2520,829;
+LVCT   42SPA;SW1;PU2056,828;PD2222,993;PD2386,828;
+LVCT   39SPA;SW1;PU432,831;PD588,986;PD753,824;
+****    0
+0001    501602
+LNST   10LS03419NIL
+LIND   38PIPARE61000930080503030002210029800772
+LXPO   76boundary of a submarine pipeline area with generally non-dangerous contents
+LCRF    6ACHGRD
+LVCT   25SPA;SW1;PU1619,867;CI95;
+LVCT   31SPA;SW1;PU1125,867;PD1522,867;
+LVCT   29SPA;SW1;PU298,822;PD898,822;
+LVCT   31SPA;SW1;PU2724,822;PD3328,822;
+LVCT   42SPA;SW1;PU2890,826;PD3051,987;PD3221,819;
+LVCT   31SPA;SW1;PU1920,829;PD2520,829;
+LVCT   42SPA;SW1;PU2056,828;PD2222,993;PD2386,828;
+LVCT   39SPA;SW1;PU432,831;PD588,986;PD753,824;
+****    0
+0001    501603
+LNST   10LS02025NIL
+LIND   38PIPSOL05017290219900589001900208702104
+LXPO   40oil, gas pipeline, submerged or on land
+LCRF    6ACHMGD
+LVCT   26SPA;SW1;PU2581,2199;CI95;
+LVCT   33SPA;SW1;PU2087,2199;PD2484,2199;
+****    0
+0001    501604
+LNST   10LS02026NIL
+LIND   38PIPSOL06017310219700512001140208702140
+LXPO   28water pipeline, sewer, etc.
+LCRF    6ACHGRD
+LVCT   33SPA;SW1;PU2087,2197;PD2485,2197;
+LVCT   26SPA;SW1;PU2542,2197;CI57;
+****    0
+0001    501605
+LNST   10LS03651NIL
+LIND   38PLNRTE03016030143400172001720173801343
+LXPO   27planned route for own ship
+LCRF    6APLRTE
+LVCT   41SPA;SW1;ST0;PU1824,1429;PM0;CI86;PM2;FP;
+****    0
+0001    501606
+LNST   10LS03353NIL
+LIND   38PRCARE51001040083002869005030029800595
+LXPO   33boundary of a precautionary area
+LCRF    6ACHMGD
+LVCT   29SPA;SW1;PU298,822;PD898,822;
+LVCT   31SPA;SW1;PU2563,817;PD3167,817;
+LVCT   42SPA;SW1;PU2729,821;PD2890,982;PD3060,814;
+LVCT   31SPA;SW1;PU1759,824;PD2359,824;
+LVCT   42SPA;SW1;PU1895,823;PD2061,988;PD2225,823;
+LVCT   39SPA;SW1;PU432,831;PD588,986;PD753,824;
+LVCT   56SPA;SW1;PU1138,1098;PD1504,1098;PD1313,595;PD1141,1098;
+LVCT   33SPA;SW1;PU1291,1027;PD1346,1027;
+LVCT   31SPA;SW1;PU1315,971;PD1315,773;
+****    0
+0001    501607
+LNST   10LS02028NIL
+LIND   38QUESMRK1005000149801456004430070001227
+LXPO  113object which is not sufficiently described to be symbolized, or for which no symbol exists in the symbol library
+LCRF    6ACHMGD
+LVCT  309SPA;SW1;PU1568,1323;PD1581,1295;PD1594,1270;PD1611,1253;PD1628,1236;PD1654,1227;PD1677,1229;PD1707,1238;PD1735,1244;PD1758,1278;PD1776,1297;PD1788,1319;PD1788,1344;PD1776,1368;PD1763,1389;PD1743,1413;PD1720,1438;PD1699,1464;PD1686,1483;PD1675,1500;PD1673,1522;PD1673,1545;PD1673,1562;PD1675,1584;PD1675,1586;
+LVCT   33SPA;SW2;PU1654,1670;PD1707,1670;
+LVCT   32SPA;SW2;PU700,1498;PD1000,1498;
+LVCT   33SPA;SW2;PU1200,1498;PD1500,1498;
+LVCT   33SPA;SW2;PU1856,1498;PD2156,1498;
+****    0
+0001    501608
+LNST   10LS03195NIL
+LIND   38RCRDEF01016070147002323005370160101204
+LXPO   60regulated recommended route centreline, details not defined
+LCRF    6ATRFCD
+LVCT   33SPA;SW1;PU2105,1470;PD2409,1470;
+LVCT   33SPA;SW1;PU3110,1470;PD3422,1470;
+LVCT   33SPA;SW1;PU3608,1470;PD3912,1470;
+LVCT   33SPA;SW1;PU1607,1470;PD1907,1470;
+LVCT   45SPA;SW1;PU1753,1573;PD1601,1657;PD1768,1741;
+LVCT   45SPA;SW1;PU3771,1204;PD3924,1288;PD3782,1375;
+LVCT   33SPA;SW1;PU3106,1294;PD3316,1294;
+LVCT   33SPA;SW1;PU3408,1294;PD3610,1294;
+LVCT   33SPA;SW1;PU3709,1290;PD3915,1290;
+LVCT   33SPA;SW1;PU1608,1655;PD1807,1655;
+LVCT   33SPA;SW1;PU1902,1655;PD2108,1655;
+LVCT   33SPA;SW1;PU2207,1655;PD2410,1655;
+LVCT  309SPA;SW1;PU2609,1310;PD2622,1282;PD2635,1257;PD2652,1240;PD2669,1223;PD2695,1214;PD2718,1216;PD2748,1225;PD2776,1231;PD2799,1265;PD2817,1284;PD2829,1306;PD2829,1331;PD2817,1355;PD2804,1376;PD2784,1400;PD2761,1425;PD2740,1451;PD2727,1470;PD2716,1487;PD2714,1509;PD2714,1532;PD2714,1549;PD2716,1571;PD2716,1573;
+LVCT   33SPA;SW1;PU2695,1656;PD2745,1656;
+****    0
+0001    501609
+LNST   10LS02239NIL
+LIND   38RCRTCL01016070147002305005230160701209
+LXPO   73regulated two-way recommended route centreline, not based on fixed marks
+LCRF    6ATRFCD
+LVCT   33SPA;SW1;PU2608,1470;PD2912,1470;
+LVCT   33SPA;SW1;PU2105,1470;PD2409,1470;
+LVCT   33SPA;SW1;PU3110,1470;PD3422,1470;
+LVCT   33SPA;SW1;PU3608,1470;PD3912,1470;
+LVCT   33SPA;SW1;PU1607,1470;PD1907,1470;
+LVCT   45SPA;SW1;PU2152,1564;PD2000,1648;PD2167,1732;
+LVCT   45SPA;SW1;PU3355,1209;PD3508,1293;PD3366,1380;
+LVCT   33SPA;SW1;PU2690,1299;PD2900,1299;
+LVCT   33SPA;SW1;PU2992,1299;PD3194,1299;
+LVCT   33SPA;SW1;PU3293,1295;PD3499,1295;
+LVCT   33SPA;SW1;PU2007,1646;PD2206,1646;
+LVCT   33SPA;SW1;PU2301,1646;PD2507,1646;
+LVCT   33SPA;SW1;PU2606,1646;PD2809,1646;
+****    0
+0001    501610
+LNST   10LS03211NIL
+LIND   38RCRTCL02016070147002305002610160701209
+LXPO   73regulated one-way recommended route centreline, not based on fixed marks
+LCRF    6ATRFCD
+LVCT   33SPA;SW1;PU2608,1470;PD2912,1470;
+LVCT   33SPA;SW1;PU2105,1470;PD2409,1470;
+LVCT   33SPA;SW1;PU3110,1470;PD3422,1470;
+LVCT   33SPA;SW1;PU3608,1470;PD3912,1470;
+LVCT   33SPA;SW1;PU1607,1470;PD1907,1470;
+LVCT   45SPA;SW1;PU3355,1209;PD3508,1293;PD3366,1380;
+LVCT   33SPA;SW1;PU2690,1299;PD2900,1299;
+LVCT   33SPA;SW1;PU2992,1299;PD3194,1299;
+LVCT   33SPA;SW1;PU3293,1295;PD3499,1295;
+****    0
+0001    501611
+LNST   10LS02239NIL
+LIND   38RCRTCL03016070147002305005230160701209
+LXPO   69regulated two-way recommended route centreline, based on fixed-marks
+LCRF    6ATRFCD
+LVCT   33SPA;SW1;PU1607,1470;PD3912,1470;
+LVCT   45SPA;SW1;PU2152,1564;PD2000,1648;PD2167,1732;
+LVCT   45SPA;SW1;PU3355,1209;PD3508,1293;PD3366,1380;
+LVCT   33SPA;SW1;PU2690,1299;PD2900,1299;
+LVCT   33SPA;SW1;PU2992,1299;PD3194,1299;
+LVCT   33SPA;SW1;PU3293,1295;PD3499,1295;
+LVCT   33SPA;SW1;PU2007,1646;PD2206,1646;
+LVCT   33SPA;SW1;PU2301,1646;PD2507,1646;
+LVCT   33SPA;SW1;PU2606,1646;PD2809,1646;
+****    0
+0001    501612
+LNST   10LS03212NIL
+LIND   38RCRTCL04016070147002305002610160701209
+LXPO   69regulated one-way recommended route centreline, based on fixed marks
+LCRF    6ATRFCD
+LVCT   33SPA;SW1;PU1607,1470;PD3912,1470;
+LVCT   45SPA;SW1;PU3355,1209;PD3508,1293;PD3366,1380;
+LVCT   33SPA;SW1;PU2690,1299;PD2900,1299;
+LVCT   33SPA;SW1;PU2992,1299;PD3194,1299;
+LVCT   33SPA;SW1;PU3293,1295;PD3499,1295;
+****    0
+0001    501613
+LNST   10LS03335NIL
+LIND   38RECDEF02009320141502220004430110701179
+LXPO   63non-regulated recommended track, direction not defined in data
+LCRF    6ACHGRD
+LVCT   33SPA;SW1;PU1107,1415;PD1423,1415;
+LVCT   33SPA;SW1;PU1596,1415;PD1921,1415;
+LVCT   33SPA;SW1;PU3002,1415;PD3327,1415;
+LVCT  309SPA;SW1;PU2358,1275;PD2371,1247;PD2384,1222;PD2401,1205;PD2418,1188;PD2444,1179;PD2467,1181;PD2497,1190;PD2525,1196;PD2548,1230;PD2566,1249;PD2578,1271;PD2578,1296;PD2566,1320;PD2553,1341;PD2533,1365;PD2510,1390;PD2489,1416;PD2476,1435;PD2465,1452;PD2463,1474;PD2463,1497;PD2463,1514;PD2465,1536;PD2465,1538;
+LVCT   33SPA;SW2;PU2444,1622;PD2497,1622;
+LVCT   45SPA;SW1;PU2254,1570;PD2049,1415;PD2254,1260;
+LVCT   45SPA;SW1;PU2660,1566;PD2865,1411;PD2660,1256;
+****    0
+0001    501614
+LNST   10LS03336NIL
+LIND   38RECTRC09009320141502220003100110701260
+LXPO   66non-regulated recommended two-way track, not based on fixed marks
+LCRF    6ACHGRD
+LVCT   33SPA;SW1;PU1107,1415;PD1423,1415;
+LVCT   33SPA;SW1;PU1596,1415;PD1921,1415;
+LVCT   33SPA;SW1;PU2299,1415;PD2624,1415;
+LVCT   33SPA;SW1;PU3002,1415;PD3327,1415;
+LVCT   45SPA;SW1;PU2299,1570;PD2094,1415;PD2299,1260;
+LVCT   45SPA;SW1;PU2624,1570;PD2829,1415;PD2624,1260;
+****    0
+0001    501615
+LNST   10LS03337NIL
+LIND   38RECTRC10011130141402013003100111301259
+LXPO   62non-regulated recommended two-way track, based on fixed-marks
+LCRF    6ACHGRD
+LVCT   45SPA;SW1;PU2217,1569;PD2012,1414;PD2217,1259;
+LVCT   33SPA;SW1;PU1113,1414;PD3126,1414;
+LVCT   45SPA;SW1;PU2542,1569;PD2747,1414;PD2542,1259;
+****    0
+0001    501616
+LNST   10LS03338NIL
+LIND   38RECTRC11009320141501526003100110701260
+LXPO   66non-regulated recommended one-way track, not based on fixed marks
+LCRF    6ACHGRD
+LVCT   33SPA;SW1;PU1107,1415;PD1432,1415;
+LVCT   33SPA;SW1;PU1605,1415;PD1930,1415;
+LVCT   33SPA;SW1;PU2308,1415;PD2633,1415;
+LVCT   45SPA;SW1;PU1930,1570;PD2135,1415;PD1930,1260;
+****    0
+0001    501617
+LNST   10LS03339NIL
+LIND   38RECTRC12011070141501526003100110701260
+LXPO   62non-regulated recommended one-way track, based on fixed marks
+LCRF    6ACHGRD
+LVCT   33SPA;SW1;PU1107,1415;PD2633,1415;
+LVCT   45SPA;SW1;PU1930,1570;PD2135,1415;PD1930,1260;
+****    0
+0001    501618
+LNST   10LS03161NIL
+LIND   38RESARE51019040146800304001500210501470
+LXPO   30boundary of a restricted area
+LCRF    6ACHMGD
+LVCT   33SPA;SW2;PU2105,1470;PD2409,1470;
+LVCT   33SPA;SW2;PU2261,1470;PD2261,1620;
+****    0
+0001    501619
+LNST   10LS03420NIL
+LIND   38SCLBDY51002630059800610002440026400597
+LXPO   65chart scale boundary, the double line indicates the larger scale
+LCRF    6ACHGRF
+LVCT   29SPA;SW3;PU264,597;PD874,597;
+LVCT   29SPA;SW1;PU264,772;PD865,772;
+LVCT   29SPA;SW1;PU264,841;PD865,841;
+****    0
+0001    501620
+LNST   10LS03421NIL
+LIND   38TIDINF51001040083003276003360029800656
+LXPO   57boundary of an area for which there is tidal information
+LCRF    6ACHGRD
+LVCT   29SPA;SW1;PU298,822;PD898,822;
+LVCT   31SPA;SW1;PU2970,816;PD3574,816;
+LVCT   42SPA;SW1;PU3136,820;PD3297,981;PD3467,813;
+LVCT   31SPA;SW1;PU2166,823;PD2766,823;
+LVCT   42SPA;SW1;PU2302,822;PD2468,987;PD2632,822;
+LVCT   39SPA;SW1;PU432,831;PD588,986;PD753,824;
+LVCT   31SPA;SW1;PU1083,824;PD1967,824;
+LVCT  394SPA;SW1;PU1155,823;PD1155,799;PD1162,768;PD1177,735;PD1199,707;PD1228,685;PD1262,669;PD1295,658;PD1326,656;PD1359,663;PD1389,673;PD1417,690;PD1440,709;PD1462,741;PD1479,779;PD1479,808;PD1479,823;PD1479,849;PD1484,878;PD1491,901;PD1506,930;PD1536,955;PD1548,965;PD1565,975;PD1585,984;PD1616,990;PD1651,992;PD1690,984;PD1719,971;PD1752,950;PD1776,926;PD1795,892;PD1806,860;PD1811,843;PD1811,823;
+****    0
+0001    501621
+LNST   10LS02033NIL
+LIND   38UNITFTH1002760064401109004320027600644
+LXPO   50change of depth unit line, bounds 'fathom depths'
+LCRF    6ACHGRD
+LVCT   30SPA;SW1;PU276,644;PD1385,644;
+LVCT   41SPA;SW1;PU915,770;PD915,1076;PD714,1076;
+LVCT   29SPA;SW1;PU915,921;PD705,921;
+****    0
+0001    501622
+LNST   10LS02034NIL
+LIND   38UNITMTR1002760064401109004470027600644
+LXPO   49change of depth unit line, bounds 'metre depths'
+LCRF    6ACHGRD
+LVCT   30SPA;SW1;PU276,644;PD1385,644;
+LVCT   61SPA;SW1;PU701,782;PD701,1089;PD804,786;PD908,1091;PD908,786;
+****    0
+0001    501623
+PATT   10PT02000NIL
+PATD   55AIRARE02VSTGCON0200010000022590225600618005280043500452
+PXPO   39pattern of symbols for an airport area
+PCRF    6ALANDF
+PVCT  150SPA;SW1;PU623,980;PD859,980;PD790,901;PD790,801;PD1053,801;PD810,638;PD810,516;PD751,452;PD680,516;PD680,638;PD435,795;PD684,797;PD684,907;PD623,980;
+****    0
+0001    501624
+PATT   10PT01206NIL
+PATD   55DIAMOND1VLINCON0000000000022500225002250043130112500093
+PXPO   43area of depth less than the safety contour
+PCRF    6\DEPCN
+PVCT   53SP\;SW1;PU1125,93;PD3375,4406;PU1125,4406;PD3375,93;
+****    0
+0001    501625
+PATT   10PT03373NIL
+PATD   55DQUALA11VSTGCON0140010000024230141701697011840157000856
+PXPO   74pattern of symbols for a chart of 5m accuracy with full seafloor coverage
+PCRF    6ACHGRD
+PVCT   33SPA;SW1;PU2779,1066;PD3080,1066;
+PVCT   32SPA;SW1;PU3021,947;PD2840,1187;
+PVCT   32SPA;SW1;PU2841,947;PD3021,1186;
+PVCT   33SPA;SW1;PU2257,1066;PD2558,1066;
+PVCT   32SPA;SW1;PU2499,947;PD2318,1187;
+PVCT   32SPA;SW1;PU2319,947;PD2499,1186;
+PVCT   33SPA;SW1;PU1728,1071;PD2029,1071;
+PVCT   32SPA;SW1;PU1970,952;PD1789,1192;
+PVCT   32SPA;SW1;PU1790,952;PD1970,1191;
+PVCT   33SPA;SW1;PU2009,1420;PD2310,1420;
+PVCT   33SPA;SW1;PU2251,1301;PD2070,1541;
+PVCT   33SPA;SW1;PU2071,1301;PD2251,1540;
+PVCT   33SPA;SW1;PU2537,1415;PD2838,1415;
+PVCT   33SPA;SW1;PU2779,1296;PD2598,1536;
+PVCT   33SPA;SW1;PU2599,1296;PD2779,1535;
+PVCT   33SPA;SW1;PU2287,1756;PD2588,1756;
+PVCT   33SPA;SW1;PU2529,1637;PD2348,1877;
+PVCT   33SPA;SW1;PU2349,1637;PD2529,1876;
+PVCT  322SPA;SW1;PU1600,1052;PD1570,971;PD1581,917;PD1600,879;PD1647,859;PD1678,856;PD3105,856;PD3186,863;PD3236,886;PD3256,914;PD3267,944;PD3267,968;PD3267,1006;PD3248,1037;PD2561,1951;PD2534,1982;PD2507,2013;PD2480,2029;PD2461,2032;PD2442,2040;PD2407,2036;PD2388,2032;PD2349,2021;PD2330,2005;PD2310,1982;PD2287,1955;PD1600,1052;
+****    0
+0001    501626
+PATT   10PT03374NIL
+PATD   55DQUALA21VSTGCON0140010000024010124701697011840157000856
+PXPO   77pattern of symbols for a chart with 20m accuracy with full seafloor coverage
+PCRF    6ACHGRD
+PVCT   33SPA;SW1;PU2779,1066;PD3080,1066;
+PVCT   32SPA;SW1;PU3021,947;PD2840,1187;
+PVCT   32SPA;SW1;PU2841,947;PD3021,1186;
+PVCT   33SPA;SW1;PU2257,1066;PD2558,1066;
+PVCT   32SPA;SW1;PU2499,947;PD2318,1187;
+PVCT   32SPA;SW1;PU2319,947;PD2499,1186;
+PVCT   33SPA;SW1;PU1728,1071;PD2029,1071;
+PVCT   32SPA;SW1;PU1970,952;PD1789,1192;
+PVCT   32SPA;SW1;PU1790,952;PD1970,1191;
+PVCT   33SPA;SW1;PU2009,1420;PD2310,1420;
+PVCT   33SPA;SW1;PU2251,1301;PD2070,1541;
+PVCT   33SPA;SW1;PU2071,1301;PD2251,1540;
+PVCT   33SPA;SW1;PU2537,1415;PD2838,1415;
+PVCT   33SPA;SW1;PU2779,1296;PD2598,1536;
+PVCT   33SPA;SW1;PU2599,1296;PD2779,1535;
+PVCT  322SPA;SW1;PU1600,1052;PD1570,971;PD1581,917;PD1600,879;PD1647,859;PD1678,856;PD3105,856;PD3186,863;PD3236,886;PD3256,914;PD3267,944;PD3267,968;PD3267,1006;PD3248,1037;PD2561,1951;PD2534,1982;PD2507,2013;PD2480,2029;PD2461,2032;PD2442,2040;PD2407,2036;PD2388,2032;PD2349,2021;PD2330,2005;PD2310,1982;PD2287,1955;PD1600,1052;
+****    0
+0001    501627
+PATT   10PT03375NIL
+PATD   55DQUALB01VSTGCON0140010000024010124701697011840157000856
+PXPO  109pattern of symbols for a chart with 50m accuracy from standard survey based on lines of continuous soundings
+PCRF    6ACHGRD
+PVCT   33SPA;SW1;PU2779,1066;PD3080,1066;
+PVCT   32SPA;SW1;PU3021,947;PD2840,1187;
+PVCT   32SPA;SW1;PU2841,947;PD3021,1186;
+PVCT   33SPA;SW1;PU2257,1066;PD2558,1066;
+PVCT   32SPA;SW1;PU2499,947;PD2318,1187;
+PVCT   32SPA;SW1;PU2319,947;PD2499,1186;
+PVCT   33SPA;SW1;PU1728,1071;PD2029,1071;
+PVCT   32SPA;SW1;PU1970,952;PD1789,1192;
+PVCT   32SPA;SW1;PU1790,952;PD1970,1191;
+PVCT   33SPA;SW1;PU2253,1459;PD2554,1459;
+PVCT   33SPA;SW1;PU2495,1340;PD2314,1580;
+PVCT   33SPA;SW1;PU2315,1340;PD2495,1579;
+PVCT  322SPA;SW1;PU1600,1052;PD1570,971;PD1581,917;PD1600,879;PD1647,859;PD1678,856;PD3105,856;PD3186,863;PD3236,886;PD3256,914;PD3267,944;PD3267,968;PD3267,1006;PD3248,1037;PD2561,1951;PD2534,1982;PD2507,2013;PD2480,2029;PD2461,2032;PD2442,2040;PD2407,2036;PD2388,2032;PD2349,2021;PD2330,2005;PD2310,1982;PD2287,1955;PD1600,1052;
+****    0
+0001    501628
+PATT   10PT03376NIL
+PATD   55DQUALC01VSTGCON0160010000024070106201604004300159100837
+PXPO   58pattern of symbols for a low accuracy or incomplete chart
+PCRF    6ACHGRD
+PVCT   32SPA;SW1;PU3021,947;PD2840,1187;
+PVCT   32SPA;SW1;PU2841,947;PD3021,1186;
+PVCT   32SPA;SW1;PU2499,947;PD2318,1187;
+PVCT   32SPA;SW1;PU2319,947;PD2499,1186;
+PVCT   32SPA;SW1;PU1970,952;PD1789,1192;
+PVCT   32SPA;SW1;PU1790,952;PD1970,1191;
+PVCT  171SPA;SW1;PU1783,837;PD1738,851;PD1703,865;PD1668,890;PD1640,925;PD1615,967;PD1601,1005;PD1591,1054;PD1598,1106;PD1608,1148;PD1626,1194;PD1671,1232;PD1727,1257;PD1776,1267;
+PVCT  218SPA;SW1;PU1769,841;PD3024,841;PD3066,855;PD3118,883;PD3150,914;PD3178,953;PD3185,981;PD3192,1019;PD3195,1054;PD3192,1099;PD3185,1141;PD3167,1183;PD3150,1208;PD3111,1243;PD3083,1260;PD3059,1267;PD3024,1264;PD1773,1264;
+PVCT   33SPA;SW1;PU1731,1069;PD2031,1069;
+PVCT   33SPA;SW1;PU2780,1066;PD3080,1066;
+PVCT   33SPA;SW1;PU2249,1064;PD2549,1064;
+****    0
+0001    501629
+PATT   10PT03377NIL
+PATD   55DQUALD01VSTGCON0160010000022680107801604004300146600843
+PXPO   43pattern of symbols for an unreliable chart
+PCRF    6ACHGRD
+PVCT   32SPA;SW1;PU2739,942;PD2558,1182;
+PVCT   32SPA;SW1;PU2559,942;PD2739,1181;
+PVCT   32SPA;SW1;PU1970,952;PD1789,1192;
+PVCT   32SPA;SW1;PU1790,952;PD1970,1191;
+PVCT  171SPA;SW1;PU1658,843;PD1613,857;PD1578,871;PD1543,896;PD1515,931;PD1490,973;PD1476,1011;PD1466,1060;PD1473,1112;PD1483,1154;PD1501,1200;PD1546,1238;PD1602,1263;PD1651,1273;
+PVCT  218SPA;SW1;PU1644,847;PD2899,847;PD2941,861;PD2993,889;PD3025,920;PD3053,959;PD3060,987;PD3067,1025;PD3070,1060;PD3067,1105;PD3060,1147;PD3042,1189;PD3025,1214;PD2986,1249;PD2958,1266;PD2934,1273;PD2899,1270;PD1648,1270;
+PVCT   33SPA;SW1;PU1734,1069;PD2034,1067;
+PVCT   33SPA;SW1;PU2493,1064;PD2815,1064;
+****    0
+0001    501630
+PATT   10PT03378NIL
+PATD   55DQUALU01VSTGCON0160010000029270105901604004300212400841
+PXPO   57pattern of symbols for a chart with quality not assessed
+PCRF    6ACHGRD
+PVCT  171SPA;SW1;PU2316,841;PD2271,855;PD2236,869;PD2201,894;PD2173,929;PD2148,971;PD2134,1009;PD2124,1058;PD2131,1110;PD2141,1152;PD2159,1198;PD2204,1236;PD2260,1261;PD2309,1271;
+PVCT  218SPA;SW1;PU2302,845;PD3557,845;PD3599,859;PD3651,887;PD3683,918;PD3711,957;PD3718,985;PD3725,1023;PD3728,1058;PD3725,1103;PD3718,1145;PD3700,1187;PD3683,1212;PD3644,1247;PD3616,1264;PD3592,1271;PD3557,1268;PD2306,1268;
+PVCT  139SPA;SW1;PU2841,946;PD2842,1137;PD2852,1166;PD2873,1186;PD2904,1201;PD2938,1203;PD2961,1193;PD2990,1175;PD3008,1158;PD3018,1134;PD3018,946;
+****    0
+0001    501631
+PATT   10PT01207NIL
+PATD   55DRGARE01VLINCON0015000000015000150000200002000150001300
+PXPO   13dredged area
+PCRF    6DCHGRD
+PVCT   39SPD;SW1;PU1500,1300;PD;PU1700,1500;PD;
+****    0
+0001    501632
+PATT   10PT03416NIL
+PATD   55FOULAR01VLINCON0015010000008370072800570006840102000850
+PXPO   35foul area, not safe for navigation
+PCRF    6ACHGRD
+PVCT   33SPA;SW1;PU1590,1294;PD1409,1534;
+PVCT   33SPA;SW1;PU1410,1294;PD1590,1533;
+PVCT   32SPA;SW1;PU1022,850;PD1200,1090;
+PVCT   32SPA;SW1;PU1196,853;PD1020,1093;
+****    0
+0001    501633
+PATT   10PT02001NIL
+PATD   55FSHFAC03VSTGCON0200010000046430216800604001510313502173
+PXPO   51pattern of symbols for an area with fishing stakes
+PCRF    6ACHGRD
+PVCT   57SPA;SW1;PU3135,2173;PD3135,2323;PD3739,2323;PD3739,2180;
+PVCT   33SPA;SW1;PU3290,2176;PD3290,2324;
+PVCT   33SPA;SW1;PU3438,2179;PD3438,2321;
+PVCT   33SPA;SW1;PU3590,2179;PD3590,2321;
+****    0
+0001    501634
+PATT   10PT01211NIL
+PATD   55FSHFAC04VSTGCON0200010000017530018900511004380049200416
+PXPO   71pattern of symbols for an area with fish traps, fish weirs, tunny nets
+PCRF    6ACHGRD
+PVCT   61SPA;SW1;PU492,854;PD1003,854;PD1003,632;PD494,632;PD494,853;
+PVCT   29SPA;SW1;PU558,416;PD776,634;
+****    0
+0001    501635
+PATT   10PT01212NIL
+PATD   55FSHHAV02VSTGCON0200010000022000147000790003200071001335
+PXPO   36pattern of symbols for a fish haven
+PCRF    6ECHGRD
+PVCT  122SPE;SW1;PU835,1490;PD725,1355;PU835,1500;PD710,1625;PU835,1500;PD890,1440;PD960,1390;PD1050,1350;PD1150,1335;PD1240,1340;
+PVCT  119PD1340,1360;PD1425,1400;PD1485,1445;PD1500,1475;PU840,1495;PD925,1565;PD1030,1625;PD1140,1650;PD1220,1655;PD1305,1640;
+PVCT   85PD1385,1610;PD1450,1550;PD1500,1480;PU1365,1380;PD1340,1445;PD1340,1535;PD1385,1600;
+****    0
+0001    501636
+PATT   10PT01213NIL
+PATD   55ICEARE04VLINCON0000000000022500225001434013310238100815
+PXPO   51continuous pattern for an ice area (glacier, etc.)
+PCRF    6ECHGRD
+PVCT  116SPE;SW1;PU2559,2146;PD2775,1978;PU2381,1153;PD2596,1396;PU2981,1537;PD3253,1603;PU2953,1059;PD3028,815;PU3131,2043;
+PVCT   61PD3412,1959;PU3665,1593;PD3731,1865;PU3553,1125;PD3815,1106;
+****    0
+0001    501637
+PATT   10PT02003NIL
+PATD   55MARCUL02VSTGCON0200010000024090036900705004030041600568
+PXPO   37pattern of symbols for a marine farm
+PCRF    6ACHGRD
+PVCT  202SPA;SW1;PU505,847;PD641,725;PD714,684;PD771,668;PD822,662;PD882,673;PD944,693;PD984,717;PD1018,744;PD1039,774;PD1004,803;PD968,831;PD914,855;PD871,869;PD819,871;PD763,869;PD709,857;PD670,835;PD505,698;
+PVCT   51SPA;SW1;PU416,669;PD416,570;PD1117,570;PD1117,666;
+PVCT   51SPA;SW1;PU416,871;PD416,971;PD1120,971;PD1121,871;
+PVCT   29SPA;SW1;PU564,871;PD564,971;
+PVCT   29SPA;SW1;PU965,871;PD965,971;
+PVCT   29SPA;SW1;PU765,870;PD765,968;
+PVCT   29SPA;SW1;PU564,571;PD564,672;
+PVCT   29SPA;SW1;PU965,569;PD965,670;
+PVCT   29SPA;SW1;PU764,568;PD764,668;
+****    0
+0001    501638
+PATT   10PT03143NIL
+PATD   55MARSHES1VSTGCON0150015000007500101600400003930055000499
+PXPO   31pattern of symbols for a marsh
+PCRF    6ACHBRN
+PVCT   29SPA;SW2;PU751,765;PD751,499;
+PVCT   29SPA;SW2;PU626,892;PD876,892;
+PVCT   29SPA;SW2;PU550,810;PD950,810;
+PVCT   29SPA;SW2;PU664,799;PD592,634;
+PVCT   29SPA;SW2;PU830,799;PD901,637;
+****    0
+0001    501639
+PATT   10PT03113NIL
+PATD   55NODATA03VSTGCON0010010000029420104000602003960234200645
+PXPO   22area of no chart data
+PCRF    6ACHGRD
+PVCT   33SPA;SW2;PU2342,1041;PD2542,1040;
+****    0
+0001    501640
+PATT   10PT03372NIL
+PATD   55OVERSC01VLINCON0000010000042430122700000004000344300827
+PXPO   82overscale part of a display containing data from more than one navigation purpose
+PCRF    6ACHGRD
+PVCT   32SPA;SW1;PU3443,827;PD3443,1227;
+****    0
+0001    501641
+PATT   10PT03371NIL
+PATD   55PRTSUR01VSTGCON0100010000023470064000201000000244800741
+PXPO   27incompletely surveyed area
+PCRF    6ACHGRD
+PVCT   31SPA;SW2;PU2448,741;PD2650,741;
+****    0
+0001    501642
+PATT   10PT02007NIL
+PATD   55QUESMRK1VSTGCON0200010000016760150100220004430156801227
+PXPO  137pattern of symbols for an area which is not sufficiently described to be symbolized, or for which no symbol exists in the symbol library
+PCRF    6ACHMGD
+PVCT  309SPA;SW1;PU1568,1323;PD1581,1295;PD1594,1270;PD1611,1253;PD1628,1236;PD1654,1227;PD1677,1229;PD1707,1238;PD1735,1244;PD1758,1278;PD1776,1297;PD1788,1319;PD1788,1344;PD1776,1368;PD1763,1389;PD1743,1413;PD1720,1438;PD1699,1464;PD1686,1483;PD1675,1500;PD1673,1522;PD1673,1545;PD1673,1562;PD1675,1584;PD1675,1586;
+PVCT   33SPA;SW2;PU1654,1670;PD1707,1670;
+****    0
+0001    501643
+PATT   10PT03180NIL
+PATD   55RCKLDG01VLINCON0000000000023190215201541013320232500814
+PXPO   28rock or coral drying ledges
+PCRF    6ALANDF
+PVCT   33SPA;SW1;PU2559,2146;PD2775,1978;
+PVCT   33SPA;SW1;PU2490,1030;PD2593,1337;
+PVCT   33SPA;SW1;PU3102,1412;PD2942,1642;
+PVCT   32SPA;SW1;PU2953,1059;PD3028,815;
+PVCT   33SPA;SW1;PU3194,1797;PD3244,2086;
+PVCT   33SPA;SW1;PU3751,1761;PD3866,1506;
+PVCT   33SPA;SW1;PU3768,1160;PD3518,1079;
+PVCT   33SPA;SW1;PU2593,1331;PD2325,1193;
+PVCT   33SPA;SW1;PU2951,1640;PD2924,1374;
+PVCT   33SPA;SW1;PU2554,2146;PD2583,1889;
+PVCT   33SPA;SW1;PU3197,1797;PD3409,1967;
+PVCT   33SPA;SW1;PU3859,1507;PD3595,1618;
+PVCT   32SPA;SW1;PU3522,1083;PD3727,948;
+PVCT   32SPA;SW1;PU3028,814;PD3138,1049;
+****    0
+0001    501644
+PATT   10PT02238NIL
+PATD   55SNDWAV01VSTGCON0200010000000290049901179001790003700316
+PXPO   34pattern of symbols for sand waves
+PCRF    6ACHGRD
+PVCT  222SPA;SW1;PU37,495;PD121,495;PD163,473;PD228,328;PD263,328;PD320,473;PD347,495;PD523,495;PD554,468;PD600,331;PD626,331;PD677,468;PD710,495;PD856,495;PD906,495;PD937,476;PD998,316;PD1036,316;PD1093,473;PD1132,495;PD1216,495;
+****    0
+0001    501645
+PATT   10PT01224NIL
+PATD   55TSSJCT02VLINCON0000000000007500075000500005000075000250
+PXPO   73precautionary area or a traffic separation scheme crossing or roundabout
+PCRF    6UTRFCF
+PVCT   30SPU;SW1;PU750,750;PD1250,250;
+****    0
+0001    501646
+PATT   10PT02008NIL
+PATD   55VEGATN03VSTGCON0100010000007490074600362003990057400348
+PXPO   36pattern of symbols for wooded areas
+PCRF    6ALANDF
+PVCT   29SPA;SW1;PU750,747;PD750,350;
+PVCT   29SPA;SW1;PU574,447;PD936,447;
+PVCT   29SPA;SW1;PU641,545;PD858,545;
+PVCT   29SPA;SW1;PU684,348;PD814,348;
+PVCT   29SPA;SW1;PU695,747;PD815,747;
+PVCT   29SPA;SW1;PU638,396;PD858,396;
+PVCT   29SPA;SW1;PU621,494;PD883,494;
+****    0
+0001    501647
+PATT   10PT03001NIL
+PATD   55VEGATN04VSTGCON0100010000007500075000400003000055000450
+PXPO   33pattern of symbols for mangroves
+PCRF    6ALANDF
+PVCT   25SPA;SW1;PU750,600;CI150;
+PVCT   29SPA;SW1;PU550,750;PD950,750;
+PVCT   29SPA;SW1;PU750,748;PD750,587;
+****    0
+0001    501648
+SYMB   10SY02035NIL
+SYMD   39ACHARE02V012670105200402005030106100789
+SXPO   90anchorage area as a point at small scale, or anchor points of mooring trot at large scale
+SCRF    6ACHMGD
+SVCT   32SPA;SW1;PU1264,789;PD1264,1291;
+SVCT   31SPA;SW1;PU1108,938;PD1412,938;
+SVCT   57SPA;SW1;PU1061,1188;PD1167,1292;PD1365,1292;PD1463,1191;
+****    0
+0001    501649
+SYMB   10SY01228NIL
+SYMD   39ACHARE51V022500225001229013040162101471
+SXPO   15anchorage area
+SCRF    6JCHMGF
+SVCT  117SPJ;SW1;PU2193,1471;PD2193,1800;PD1875,1800;PD1875,1912;PD2193,1912;PD2193,2596;PD1987,2550;PD1800,2409;PD1621,2409;
+SVCT  121PD1875,2596;PD2250,2775;PD2596,2596;PD2850,2409;PD2700,2409;PD2475,2550;PD2268,2596;PD2268,1912;PD2596,1912;PD2596,1800;
+SVCT   37PD2268,1800;PD2268,1471;PD2193,1471;
+****    0
+0001    501650
+SYMB   10SY03003NIL
+SYMD   39ACHBRT07V012640106200506005060101000783
+SXPO   44designated anchor berth for a single vessel
+SCRF    6ACHMGD
+SVCT   32SPA;SW1;PU1262,783;PD1262,1004;
+SVCT   33SPA;SW1;PU1262,1289;PD1262,1207;
+SVCT   57SPA;SW1;PU1516,1135;PD1364,1287;PD1167,1287;PD1010,1132;
+SVCT   27SPA;SW1;PU1261,1101;CI101;
+SVCT   31SPA;SW1;PU1107,937;PD1424,937;
+****    0
+0001    501651
+SYMB   10SY03399NIL
+SYMD   39ACHRES51V009810095801229013040162101471
+SXPO   49area where anchoring is prohibited or restricted
+SCRF    6ACHMGF
+SVCT  273SPA;SW1;PU2193,1471;PD2193,1800;PD1875,1800;PD1875,1912;PD2193,1912;PD2193,2596;PD1987,2550;PD1800,2409;PD1621,2409;PD1875,2596;PD2250,2775;PD2596,2596;PD2850,2409;PD2700,2409;PD2475,2550;PD2268,2596;PD2268,1912;PD2596,1912;PD2596,1800;PD2268,1800;PD2268,1471;PD2193,1471;
+SVCT   33SPA;SW3;PU2703,1671;PD1701,2679;
+****    0
+0001    501652
+SYMB   10SY03401NIL
+SYMD   39ACHRES61V009810095801435013040162101471
+SXPO   70area where anchoring is prohibited or restricted, with other cautions
+SCRF   12ACHMGFCCHMGD
+SVCT  273SPA;SW1;PU2193,1471;PD2193,1800;PD1875,1800;PD1875,1912;PD2193,1912;PD2193,2596;PD1987,2550;PD1800,2409;PD1621,2409;PD1875,2596;PD2250,2775;PD2596,2596;PD2850,2409;PD2700,2409;PD2475,2550;PD2268,2596;PD2268,1912;PD2596,1912;PD2596,1800;PD2268,1800;PD2268,1471;PD2193,1471;
+SVCT   33SPA;SW3;PU2703,1671;PD1701,2679;
+SVCT   33SPC;SW3;PU3001,2775;PD3056,2775;
+SVCT   33SPC;SW1;PU3019,2325;PD3019,2671;
+****    0
+0001    501653
+SYMB   10SY03403NIL
+SYMD   39ACHRES71V009810095801447013040140301471
+SXPO   73area where anchoring is prohibited or restricted, with other information
+SCRF   12ACHMGFCCHMGD
+SVCT  273SPA;SW1;PU2193,1471;PD2193,1800;PD1875,1800;PD1875,1912;PD2193,1912;PD2193,2596;PD1987,2550;PD1800,2409;PD1621,2409;PD1875,2596;PD2250,2775;PD2596,2596;PD2850,2409;PD2700,2409;PD2475,2550;PD2268,2596;PD2268,1912;PD2596,1912;PD2596,1800;PD2268,1800;PD2268,1471;PD2193,1471;
+SVCT   33SPA;SW3;PU2703,1671;PD1701,2679;
+SVCT   45SPC;SW1;PU1495,2761;PD1495,2527;PD1403,2526;
+SVCT   33SPC;SW1;PU1404,2762;PD1591,2762;
+SVCT   33SPC;SW1;PU1449,2408;PD1452,2448;
+****    0
+0001    501654
+SYMB   10SY02075NIL
+SYMD   39AIRARE02V007490075000796007960035100352
+SXPO   30symbol for airport as a point
+SCRF    6ALANDF
+SVCT   25SPA;SW1;PU749,750;CI398;
+SVCT  150SPA;SW1;PU623,980;PD859,980;PD790,901;PD790,801;PD1053,801;PD810,638;PD810,516;PD751,452;PD680,516;PD680,638;PD435,795;PD684,797;PD684,907;PD623,980;
+****    0
+0001    501655
+SYMB   10SY03500NIL
+SYMD   39AISDEF01V004720105000562007510034500391
+SXPO   53AIS target whose heading and course are both unknown
+SCRF   12AARPATCCHMGD
+SVCT   40SPA;SW1;ST0;PU472,1052;PM0;CI31;PM2;FP;
+SVCT   52SPA;SW2;PU350,1142;PD598,1142;PD469,391;PD345,1140;
+SVCT  259SPC;SW1;PU687,638;PD700,610;PD713,585;PD730,568;PD747,551;PD773,542;PD796,544;PD826,553;PD854,559;PD877,593;PD895,612;PD907,634;PD907,659;PD895,683;PD882,704;PD862,728;PD839,753;PD818,779;PD805,798;PD794,815;PD792,837;PD792,860;PD792,877;PD794,899;PD794,901;
+SVCT   29SPC;SW1;PU775,929;PD811,929;
+****    0
+0001    501656
+SYMB   10SY03216NIL
+SYMD   39AISONE01V073710161500405001710717401445
+SXPO   31one minute mark for AIS vector
+SCRF    6AARPAT
+SVCT   57SPA;SW1;PU7174,1616;PD7579,1616;PD7372,1445;PD7174,1616;
+****    0
+0001    501657
+SYMB   10SY03215NIL
+SYMD   39AISSIX01V073710165700405002140717301445
+SXPO   31six minute mark for AIS vector
+SCRF    6AARPAT
+SVCT   57SPA;SW1;PU7173,1617;PD7578,1616;PD7371,1445;PD7173,1617;
+SVCT   33SPA;SW3;PU7173,1659;PD7578,1659;
+****    0
+0001    501658
+SYMB   10SY03210NIL
+SYMD   39AISSLP01V004720105000253007510034500391
+SXPO   20sleeping AIS target
+SCRF    6AARPAT
+SVCT   40SPA;SW1;ST0;PU472,1052;PM0;CI31;PM2;FP;
+SVCT   52SPA;SW2;PU350,1142;PD598,1142;PD469,391;PD345,1140;
+****    0
+0001    501659
+SYMB   10SY03209NIL
+SYMD   39AISVES01V004950105000499012480024500051
+SXPO   48active AIS target showing vector and/or heading
+SCRF    6AARPAT
+SVCT   51SPA;SW2;PU245,1299;PD744,1299;PD495,51;PD245,1299;
+SVCT   40SPA;SW1;ST0;PU495,1050;PM0;CI31;PM2;FP;
+****    0
+0001    501660
+SYMB   10SY03221NIL
+SYMD   39ARPATG01V015000150000500005000125001250
+SXPO   12ARPA target
+SCRF    6eARPAT
+SVCT   59SPe;PU1500,1500;SW2;CI250;PU1500,1500;ST0;PM0;CI62;PM2;FP;
+****    0
+0001    501661
+SYMB   10SY03219NIL
+SYMD   39ARPONE01V050330216700394000000482802167
+SXPO   31one minute mark on ARPA vector
+SCRF    6AARPAT
+SVCT   33SPA;SW2;PU4828,2167;PD5222,2167;
+****    0
+0001    501662
+SYMB   10SY03220NIL
+SYMD   39ARPSIX01V053840214500397000000518902146
+SXPO   31six minute mark on ARPA vector
+SCRF    6AARPAT
+SVCT   33SPA;SW5;PU5189,2146;PD5586,2146;
+****    0
+0001    501663
+SYMB   10SY01232NIL
+SYMD   39BCNCAR01V007500075000402006120055000445
+SXPO   35cardinal beacon, north, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  113SPH;ST0;PU750,780;PM0;PD552,1052,950,1057,750,780;PM2;FP;PU750,445;PM0;PD952,720,555,720,750,445;PM2;FP;SPm;SW1;
+SVCT   84PU750,445;PD550,720;PD950,720;PD750,445;PU550,1055;PD950,1055;PD750,780;PD550,1055;
+****    0
+0001    501664
+SYMB   10SY01233NIL
+SYMD   39BCNCAR02V007500075000400006600055000420
+SXPO   34cardinal beacon, east, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  112SPH;ST0;PU750,420;PM0;PD947,695,552,692,750,420;PM2;FP;PU585,805;PM0;PD947,805,750,1080,552,805;PM2;FP;SPm;SW1;
+SVCT   82PU750,420;PD550,695;PD950,695;PD750,420;PU550,805;PD950,805;PD750,1080;PD550,805;
+****    0
+0001    501665
+SYMB   10SY01234NIL
+SYMD   39BCNCAR03V007500075000400006100055000445
+SXPO   35cardinal beacon, south, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  112SPH;ST0;PU585,445;PM0;PD947,447,750,720,555,445;PM2;FP;PU585,780;PM0;PD947,780,750,1055,552,780;PM2;FP;SPm;SW1;
+SVCT   82PU750,720;PD550,445;PD950,445;PD750,720;PU550,780;PD950,780;PD750,1055;PD550,780;
+****    0
+0001    501666
+SYMB   10SY01235NIL
+SYMD   39BCNCAR04V007500075000400006100055000445
+SXPO   34cardinal beacon, west, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  113SPH;ST0;PU750,780;PM0;PD552,1055,947,1055,750,780;PM2;FP;PU585,445;PM0;PD947,445,750,720,550,445;PM2;FP;SPm;SW1;
+SVCT   84PU550,1055;PD950,1055;PD750,780;PD550,1055;PU750,720;PD550,445;PD950,445;PD750,720;
+****    0
+0001    501667
+SYMB   10SY02039NIL
+SYMD   39BCNDEF13V012380145200527004460113501234
+SXPO   40default symbol for a beacon, simplified
+SCRF   18ACHMGDCCHGRDDOUTLW
+SVCT  309SPA;SW1;PU1442,1329;PD1455,1301;PD1468,1276;PD1486,1260;PD1503,1243;PD1529,1234;PD1552,1237;PD1582,1246;PD1610,1253;PD1632,1287;PD1650,1306;PD1662,1329;PD1661,1354;PD1649,1377;PD1635,1398;PD1615,1422;PD1591,1446;PD1570,1472;PD1557,1491;PD1546,1508;PD1543,1529;PD1542,1552;PD1542,1568;PD1543,1590;PD1543,1591;
+SVCT   33SPA;SW2;PU1518,1680;PD1570,1680;
+SVCT   84SPC;SW1;ST0;PU1135,1250;PM0;PD1135,1649;PD1335,1649;PD1335,1251;PD1135,1250;PM2;FP;
+SVCT   69SPD;SW1;PU1135,1250;PD1135,1649;PD1335,1649;PD1335,1251;PD1135,1250;
+SVCT   41SPD;SW1;ST0;PU1238,1452;PM0;CI15;PM2;FP;
+****    0
+0001    501668
+SYMB   10SY01238NIL
+SYMD   39BCNGEN01V007500075000300005150060000300
+SXPO   31beacon in general, paper-chart
+SCRF    6CCHBLK
+SVCT  121SPC;ST0;PU675,743;PM0;PD675,300,818,300,818,750,809,725,806,706,800,700,778,690,759,684,753,681,734,684,725,684,712,690;
+SVCT  109PD703,703,693,718,681,725,675,740,675,750;PM2;FP;PU750,750;SW2;CI65;PU600,750;PD684,750;PU815,750;PD900,750;
+****    0
+0001    501669
+SYMB   10SY02040NIL
+SYMD   39BCNGEN03V012310163400593005000108101188
+SXPO   39default symbol for beacon, paper-chart
+SCRF   12ACHMGDCCHBLK
+SVCT  309SPA;SW1;PU1454,1302;PD1467,1274;PD1480,1249;PD1497,1232;PD1514,1215;PD1540,1206;PD1563,1208;PD1593,1217;PD1621,1223;PD1644,1257;PD1662,1276;PD1674,1298;PD1674,1323;PD1662,1347;PD1649,1368;PD1629,1392;PD1606,1417;PD1585,1443;PD1572,1462;PD1561,1479;PD1559,1501;PD1559,1524;PD1559,1541;PD1561,1563;PD1561,1565;
+SVCT   33SPA;SW2;PU1540,1652;PD1593,1652;
+SVCT   33SPC;SW2;PU1081,1577;PD1399,1576;
+SVCT   26SPC;SW2;PU1231,1634;CI54;
+SVCT   33SPC;SW4;PU1230,1575;PD1227,1188;
+****    0
+0001    501670
+SYMB   10SY02041NIL
+SYMD   39BCNISD21V014930062300222005440138100354
+SXPO   35isolated danger beacon, simplified
+SCRF   12AOUTLWCCHRED
+SVCT   41SPC;SW1;ST0;PU1493,464;PM0;CI110;PM2;FP;
+SVCT   41SPC;SW1;ST0;PU1491,788;PM0;CI110;PM2;FP;
+SVCT   26SPA;SW1;PU1493,464;CI110;
+SVCT   26SPA;SW1;PU1491,788;CI110;
+****    0
+0001    501671
+SYMB   10SY01253NIL
+SYMD   39BCNLAT15V006000085000200004000050000650
+SXPO   38major lateral beacon, red, simplified
+SCRF   12FCHREDmOUTLW
+SVCT  117SPF;ST0;PU500,1050;PM0;PD700,1050,700,650,500,650,500,1050;PM2;FP;SPm;SW1;PU500,1050;PD700,1050;PD700,650;PD500,650;
+SVCT   12PD500,1050;
+SVCT   39SPm;SW1;ST0;PU600,850;PM0;CI15;PM2;FP;
+****    0
+0001    501672
+SYMB   10SY01254NIL
+SYMD   39BCNLAT16V007500075000200004000065000550
+SXPO   40major lateral beacon, green, simplified
+SCRF   12GCHGRNmOUTLW
+SVCT  112SPG;ST0;PU650,950;PM0;PD850,950,850,550,650,550,650,950;PM2;FP;SPm;SW1;PU650,950;PD850,950;PD850,550;PD650,550;
+SVCT   11PD650,950;
+SVCT   39SPm;SW1;ST0;PU750,750;PM0;CI15;PM2;FP;
+****    0
+0001    501673
+SYMB   10SY02042NIL
+SYMD   39BCNLAT21V004130075600133005000034700505
+SXPO   38minor lateral beacon, red, simplified
+SCRF   12aOUTLWbCHRED
+SVCT   77SPb;SW1;ST0;PU347,1005;PM0;PD347,505;PD480,505;PD480,1005;PD347,1005;PM2;FP;
+SVCT   62SPa;SW1;PU347,1005;PD347,505;PD480,505;PD480,1005;PD347,1005;
+SVCT   39SPa;SW1;ST0;PU413,756;PM0;CI15;PM2;FP;
+****    0
+0001    501674
+SYMB   10SY02043NIL
+SYMD   39BCNLAT22V004130075600133005000034700505
+SXPO   40minor lateral beacon, green, simplified
+SCRF   12aOUTLWbCHGRN
+SVCT   77SPb;SW1;ST0;PU347,1005;PM0;PD347,505;PD480,505;PD480,1005;PD347,1005;PM2;FP;
+SVCT   62SPa;SW1;PU347,1005;PD347,505;PD480,505;PD480,1005;PD347,1005;
+SVCT   39SPa;SW1;ST0;PU413,756;PM0;CI15;PM2;FP;
+****    0
+0001    501675
+SYMB   10SY01255NIL
+SYMD   39BCNLTC01V007500075000500005180050000300
+SXPO   28lattice beacon, paper-chart
+SCRF    6CCHBLK
+SVCT  113SPC;PU750,750;SW2;CI68;PU500,750;PD678,750;PU812,750;PD1000,750;SW2;PU550,750;PD650,300;PD850,300;PD950,750;SW2;
+SVCT   81PU562,675;PD903,543;PU596,543;PD931,671;PU640,343;PD884,465;PU850,350;PD609,468;
+****    0
+0001    501676
+SYMB   10SY01260NIL
+SYMD   39BCNSAW13V007500075000200004000065000550
+SXPO   36major safe water beacon, simplified
+SCRF   18CCHBLKmOUTLWBDEPVS
+SVCT  112SPC;ST0;PU650,950;PM0;PD850,950,850,550,650,550,650,950;PM2;FP;SPm;SW1;PU650,950;PD850,950;PD850,550;PD650,550;
+SVCT   11PD650,950;
+SVCT   39SPB;SW1;ST0;PU750,750;PM0;CI15;PM2;FP;
+****    0
+0001    501677
+SYMB   10SY02044NIL
+SYMD   39BCNSAW21V004130075600133005000034700505
+SXPO   36minor safe water beacon, simplified
+SCRF   18aOUTLWbCHBLKBDEPVS
+SVCT   77SPb;SW1;ST0;PU347,1005;PM0;PD347,505;PD480,505;PD480,1005;PD347,1005;PM2;FP;
+SVCT   62SPa;SW1;PU347,1005;PD347,505;PD480,505;PD480,1005;PD347,1005;
+SVCT   39SPB;SW1;ST0;PU413,756;PM0;CI15;PM2;FP;
+****    0
+0001    501678
+SYMB   10SY01265NIL
+SYMD   39BCNSPP13V007500075000200004000065000550
+SXPO   41major special purpose beacon, simplified
+SCRF   12HCHYLWmOUTLW
+SVCT  112SPH;ST0;PU650,950;PM0;PD850,950,850,550,650,550,650,950;PM2;FP;SPm;SW1;PU650,950;PD850,950;PD850,550;PD650,550;
+SVCT   11PD650,950;
+SVCT   39SPm;SW1;ST0;PU750,750;PM0;CI15;PM2;FP;
+****    0
+0001    501679
+SYMB   10SY02045NIL
+SYMD   39BCNSPP21V004130075600133005000034700505
+SXPO   41minor special purpose beacon, simplified
+SCRF   12aOUTLWbCHYLW
+SVCT   77SPb;SW1;ST0;PU347,1005;PM0;PD347,505;PD480,505;PD480,1005;PD347,1005;PM2;FP;
+SVCT   62SPa;SW1;PU347,1005;PD347,505;PD480,505;PD480,1005;PD347,1005;
+SVCT   39SPa;SW1;ST0;PU413,756;PM0;CI15;PM2;FP;
+****    0
+0001    501680
+SYMB   10SY02046NIL
+SYMD   39BCNSTK02V007500075000250004510062500299
+SXPO   41minor, stake or pole beacon, paper-chart
+SCRF    6ACHBLK
+SVCT   29SPA;SW2;PU625,750;PD875,750;
+SVCT   29SPA;SW2;PU754,299;PD755,749;
+****    0
+0001    501681
+SYMB   10SY01267NIL
+SYMD   39BCNTOW01V007500075000500005200050000300
+SXPO   26beacon tower, paper-chart
+SCRF    6CCHBLK
+SVCT  109SPC;PU750,750;SW2;CI70;PU500,750;PD680,750;PU815,750;PD1000,750;SW2;PU550,750;PD650,300;PD850,300;PD950,750;
+****    0
+0001    501682
+SYMB   10SY03650NIL
+SYMD   41BLKADJ01V01070020250200002000000370098239
+SXPO   82symbol to be used for checking and adjusting the brightness and contrast controls
+SCRF   12ABKAJ1BBKAJ2
+SVCT   75SPA;SW1;ST0;PU37,982;PM0;PD2037,982;PD2037,2982;PD37,2982;PD37,982;PM2;FP;
+SVCT   81SPB;SW1;ST0;PU448,1377;PM0;PD1648,1377;PD1648,2577;PD448,2577;PD448,1377;PM2;FP;
+****    0
+0001    501683
+SYMB   10SY01268NIL
+SYMD   39BOYBAR01V015000150000615004400114501110
+SXPO   25barrel buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  122SPC;PU1500,1500;SW2;CI50;PU1200,1500;PD1200,1345;PD1210,1280;PD1245,1210;PU1290,1165;PD1335,1140;PD1390,1120;PD1435,1110;
+SVCT  121PD1480,1110;PD1530,1120;PD1590,1155;PD1635,1185;PD1660,1215;PD1685,1265;PD1700,1310;PD1705,1345;PD1705,1500;PU1275,1175;
+SVCT  121PD1310,1230;PD1345,1280;PD1365,1335;PD1380,1410;PD1390,1455;PD1390,1500;PU1545,1500;PD1760,1500;PU1245,1210;PD1290,1165;
+SVCT   25PU1445,1500;PD1145,1500;
+****    0
+0001    501684
+SYMB   10SY01269NIL
+SYMD   39BOYCAN01V007500075000616003940046200415
+SXPO   22can buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  110SPC;PU750,750;SW2;CI59;PU462,750;PD690,750;PU803,750;PD1031,750;SW2;PU509,750;PD603,415;PD1078,415;PD984,750;
+****    0
+0001    501685
+SYMB   10SY01270NIL
+SYMD   39BOYCAR01V007500075000416006160048400437
+SXPO   33cardinal buoy, north, simplified
+SCRF   12HCHYLWmOUTLW
+SVCT  113SPH;ST0;PU778,790;PM0;PD778,1050,484,1053,778,790;PM2;FP;PU593,718;PM0;PD900,718,900,437,593,718;PM2;FP;SPm;SW1;
+SVCT   83PU593,718;PD900,718;PD900,440;PD593,718;PU778,787;PD778,1053;PD484,1053;PD778,787;
+****    0
+0001    501686
+SYMB   10SY01271NIL
+SYMD   39BOYCAR02V007650075000365006600058500420
+SXPO   32cardinal buoy, east, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  112SPH;ST0;PU640,695;PM0;PD950,692,947,420,640,695;PM2;FP;PU585,805;PM0;PD890,805,585,1080,585,805;PM2;FP;SPm;SW1;
+SVCT   82PU585,805;PD892,805;PD585,1080;PD585,805;PU640,695;PD950,695;PD950,420;PD640,695;
+****    0
+0001    501687
+SYMB   10SY01272NIL
+SYMD   39BOYCAR03V007150075000415006100059500445
+SXPO   33cardinal buoy, south, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  113SPH;ST0;PU735,445;PM0;PD1010,445,700,715,700,445;PM2;FP;PU625,780;PM0;PD900,780,595,1052,595,780;PM2;FP;SPm;SW1;
+SVCT   93PU595,780;PD900,780;PD595,1055;PD595,780;PU700,445;PD1010,445;PD700,715;PU700,445;PD700,715;
+****    0
+0001    501688
+SYMB   10SY01273NIL
+SYMD   39BOYCAR04V007500075000607006100044500445
+SXPO   32cardinal buoy, west, simplified
+SCRF   12mOUTLWHCHYLW
+SVCT  114SPH;ST0;PU750,720;PM0;PD750,445,1050,445,750,720;PM2;FP;PU750,780;PM0;PD750,1055,452,1055,750,780;PM2;FP;SPm;SW1;
+SVCT   84PU750,780;PD750,1055;PD445,1055;PD750,780;PU750,445;PD750,720;PD1052,445;PD750,445;
+****    0
+0001    501689
+SYMB   10SY01274NIL
+SYMD   39BOYCON01V007500075000575004940046200312
+SXPO   26conical buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW2;CI56;PU525,750;PD537,659;PD556,584;PD584,528;PD625,468;PD659,425;PD709,381;PD775,343;PD825,312;
+SVCT  112PD843,343;PD884,415;PD925,509;PD940,575;PU462,750;PD696,750;PU940,575;PD953,659;PD959,750;PU800,750;PD1037,750;
+****    0
+0001    501690
+SYMB   10SY03356NIL
+SYMD   39BOYDEF03V012360142900721004430103301203
+SXPO   36default symbol for buoy, simplified
+SCRF   18ACHMGDCCHGRDDOUTLW
+SVCT  309SPA;SW1;PU1534,1299;PD1547,1271;PD1560,1246;PD1577,1229;PD1594,1212;PD1620,1203;PD1643,1205;PD1673,1214;PD1701,1220;PD1724,1254;PD1742,1273;PD1754,1295;PD1754,1320;PD1742,1344;PD1729,1365;PD1709,1389;PD1686,1414;PD1665,1440;PD1652,1459;PD1641,1476;PD1639,1498;PD1639,1521;PD1639,1538;PD1641,1560;PD1641,1562;
+SVCT   33SPA;SW2;PU1620,1646;PD1673,1646;
+SVCT   42SPC;SW1;ST0;PU1236,1429;PM0;CI203;PM2;FP;
+SVCT   27SPD;SW1;PU1236,1429;CI203;
+SVCT   41SPD;SW1;ST0;PU1236,1429;PM0;CI15;PM2;FP;
+****    0
+0001    501691
+SYMB   10SY02048NIL
+SYMD   39BOYGEN03V012360168400765004740097001263
+SXPO   37default symbol for buoy, paper-chart
+SCRF   12ACHMGDCCHBLK
+SVCT  309SPA;SW1;PU1515,1359;PD1528,1331;PD1541,1306;PD1558,1289;PD1575,1272;PD1601,1263;PD1624,1265;PD1654,1274;PD1682,1280;PD1705,1314;PD1723,1333;PD1735,1355;PD1735,1380;PD1723,1404;PD1710,1425;PD1690,1449;PD1667,1474;PD1646,1500;PD1633,1519;PD1622,1536;PD1620,1558;PD1620,1581;PD1620,1598;PD1622,1620;PD1622,1622;
+SVCT   33SPA;SW2;PU1601,1706;PD1654,1706;
+SVCT   27SPC;SW2;PU1231,1453;CI173;
+SVCT   26SPC;SW2;PU1233,1682;CI55;
+SVCT   33SPC;SW2;PU1288,1682;PD1495,1682;
+SVCT   32SPC;SW2;PU970,1682;PD1176,1682;
+****    0
+0001    501692
+SYMB   10SY01279NIL
+SYMD   39BOYINB01V007500075000857004990032100316
+SXPO   31installation buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  122SPC;PU750,750;SW2;CI65;PU750,387;CI71;PU321,750;PD681,750;PU809,750;PD1178,750;PU368,750;PD465,465;PD1034,465;PD1131,750;
+****    0
+0001    501693
+SYMB   10SY02049NIL
+SYMD   39BOYISD12V015540062400384005440136600353
+SXPO   33isolated danger buoy, simplified
+SCRF   12AOUTLWCCHRED
+SVCT   41SPC;SW1;ST0;PU1640,463;PM0;CI110;PM2;FP;
+SVCT   41SPC;SW1;ST0;PU1476,787;PM0;CI110;PM2;FP;
+SVCT   26SPA;SW1;PU1640,463;CI110;
+SVCT   26SPA;SW1;PU1476,787;CI110;
+****    0
+0001    501694
+SYMB   10SY02050NIL
+SYMD   39BOYLAT13V010330096000433004390072400663
+SXPO   40conical lateral buoy, green, simplified
+SCRF   12ACHGRNBOUTLW
+SVCT   70SPA;SW1;ST0;PU1157,1102;PM0;PD1157,663;PD724,1102;PD1157,1102;PM2;FP;
+SVCT   55SPB;SW1;PU1157,1102;PD1157,663;PD724,1102;PD1157,1102;
+SVCT   40SPB;SW1;ST0;PU1033,960;PM0;CI15;PM2;FP;
+****    0
+0001    501695
+SYMB   10SY02051NIL
+SYMD   39BOYLAT14V010330096000433004390072400663
+SXPO   38conical lateral buoy, red, simplified
+SCRF   12ACHREDbOUTLW
+SVCT   70SPA;SW1;ST0;PU1157,1102;PM0;PD1157,663;PD724,1102;PD1157,1102;PM2;FP;
+SVCT   55SPb;SW1;PU1157,1102;PD1157,663;PD724,1102;PD1157,1102;
+SVCT   40SPb;SW1;ST0;PU1033,960;PM0;CI15;PM2;FP;
+****    0
+0001    501696
+SYMB   10SY02052NIL
+SYMD   39BOYLAT23V007740069000528003990049900499
+SXPO   42can shape lateral buoy, green, simplified
+SCRF   12ACHGRNBOUTLW
+SVCT   75SPA;SW1;ST0;PU499,898;PM0;PD651,500;PD1027,500;PD901,898;PD499,898;PM2;FP;
+SVCT   60SPB;SW1;PU499,898;PD651,500;PD1027,499;PD901,898;PD499,898;
+SVCT   39SPB;SW1;ST0;PU774,690;PM0;CI15;PM2;FP;
+****    0
+0001    501697
+SYMB   10SY02053NIL
+SYMD   39BOYLAT24V007740069000528004000049900499
+SXPO   40can shape lateral buoy, red, simplified
+SCRF   12ACHREDbOUTLW
+SVCT   75SPA;SW1;ST0;PU499,899;PM0;PD651,500;PD1027,499;PD901,898;PD499,899;PM2;FP;
+SVCT   60SPb;SW1;PU499,899;PD651,500;PD1027,499;PD901,898;PD499,899;
+SVCT   39SPb;SW1;ST0;PU774,690;PM0;CI15;PM2;FP;
+****    0
+0001    501698
+SYMB   10SY01290NIL
+SYMD   39BOYMOR01V007500075000610005800040500230
+SXPO   40mooring buoy, barrel shape, paper-chart
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW2;CI60;PU705,295;CI65;PU800,750;PD1015,750;PU685,750;PD405,750;SW2;PU960,750;PD960,620;PU450,750;
+SVCT  121PD450,620;PD455,570;PD475,510;PD505,460;PD530,435;PD567,402;PD610,382;PD652,370;PD705,362;PD752,367;PD805,385;PD857,412;
+SVCT  121PD895,447;PD930,497;PD950,547;PD960,595;PD960,620;PU530,435;PD567,490;PU612,580;PD632,662;PD637,712;PD640,750;PU567,490;
+SVCT   11PD612,580;
+****    0
+0001    501699
+SYMB   10SY01291NIL
+SYMD   39BOYMOR03V007500075000616005560046500259
+SXPO   37mooring buoy, can shape, paper-chart
+SCRF    6CCHBLK
+SVCT  115SPC;PU750,750;SW2;CI65;PU843,334;CI75;PU465,750;PD681,750;PU809,750;PD1034,750;SW2;PU512,750;PD606,418;PD1081,418;
+SVCT   11PD990,750;
+****    0
+0001    501700
+SYMB   10SY02054NIL
+SYMD   39BOYMOR11V007480057000759003500036800395
+SXPO   47installation buoy and mooring buoy, simplified
+SCRF    6ACHBLK
+SVCT   39SPA;SW2;ST0;PU755,466;PM0;CI71;PM2;FP;
+SVCT   76SPA;SW1;ST0;PU368,745;PM0;PD433,548;PD1060,548;PD1127,745;PD368,745;PM2;FP;
+****    0
+0001    501701
+SYMB   10SY01292NIL
+SYMD   39BOYPIL01V007500075000597004910044300318
+SXPO   25pillar buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  119SPC;PU750,750;SW2;CI59;PU443,750;PD693,750;PU803,750;PD1040,750;SW2;PU509,750;PD728,515;PD793,318;PD890,318;PD890,556;
+SVCT   11PD984,750;
+****    0
+0001    501702
+SYMB   10SY01294NIL
+SYMD   39BOYSAW12V007500075000406004060054700547
+SXPO   28safe water buoy, simplified
+SCRF   12CCHREDmOUTLW
+SVCT   60SPC;PU750,750;ST0;PM0;CI200;PM2;FP;SPm;PU750,750;SW1;CI203;
+SVCT   39SPm;SW1;ST0;PU750,750;PM0;CI15;PM2;FP;
+****    0
+0001    501703
+SYMB   10SY01295NIL
+SYMD   39BOYSPH01V007340075000565004910045600312
+SXPO   28spherical buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  115SPC;PU734,750;SW2;CI53;PU790,750;PD1021,750;PU515,750;PD487,681;PD478,625;PD471,565;PD484,500;PD503,453;PD537,409;
+SVCT  112PD575,371;PD621,340;PD665,325;PD718,312;PD775,312;PD831,325;PD893,353;PD934,384;PD962,425;PD996,478;PD1015,531;
+SVCT   63PD1018,590;PD1012,662;PD990,712;PD971,750;PU456,750;PD681,750;
+****    0
+0001    501704
+SYMB   10SY01297NIL
+SYMD   39BOYSPP11V007500075000400004000055000550
+SXPO   33special purpose buoy, simplified
+SCRF   12HCHYLWmOUTLW
+SVCT   60SPH;PU750,750;ST0;PM0;CI200;PM2;FP;SPm;PU750,750;SW1;CI200;
+SVCT   39SPm;SW1;ST0;PU750,750;PM0;CI15;PM2;FP;
+****    0
+0001    501705
+SYMB   10SY03121NIL
+SYMD   39BOYSPP15V010130096200433004390072400663
+SXPO   84special purpose TSS buoy marking the starboard side of the traffic lane, simplified
+SCRF   12ACHYLWBOUTLW
+SVCT   70SPA;SW1;ST0;PU1157,1102;PM0;PD1157,663;PD724,1102;PD1157,1102;PM2;FP;
+SVCT   55SPB;SW1;PU1157,1102;PD1157,663;PD724,1102;PD1157,1102;
+SVCT   40SPB;SW1;ST0;PU1013,960;PM0;CI15;PM2;FP;
+****    0
+0001    501706
+SYMB   10SY03122NIL
+SYMD   39BOYSPP25V007740069000528004000049900499
+SXPO   79special purpose TSS buoy marking the port side of the traffic lane, simplified
+SCRF   12ACHYLWBOUTLW
+SVCT   75SPA;SW1;ST0;PU499,899;PM0;PD651,500;PD1027,499;PD901,898;PD499,899;PM2;FP;
+SVCT   60SPB;SW1;PU499,899;PD651,500;PD1027,499;PD901,898;PD499,899;
+SVCT   39SPB;SW1;ST0;PU774,690;PM0;CI15;PM2;FP;
+****    0
+0001    501707
+SYMB   10SY01299NIL
+SYMD   39BOYSPR01V007500075000287004910060300318
+SXPO   23spar buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  120SPC;ST0;PU690,750;PM0;PD793,318,890,318,800,709,781,703,771,696,753,693,731,700,687,750,715,706,706,712,690,750;PM2;FP;
+SVCT   60PU750,750;SW2;CI59;PU603,750;PD690,750;PU803,750;PD890,750;
+****    0
+0001    501708
+SYMB   10SY01300NIL
+SYMD   39BOYSUP01V007500075000857005400032100275
+SXPO   24super-buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  121SPC;PU750,750;SW2;CI65;PU321,750;PD681,750;PU809,750;PD1178,750;PU368,750;PD465,465;PD1034,465;PD1131,750;SW2;PU750,465;
+SVCT   11PD750,275;
+****    0
+0001    501709
+SYMB   10SY02055NIL
+SYMD   39BOYSUP02V007490051300767004750036500275
+SXPO   36super-buoy ODAS & LANBY, simplified
+SCRF    6ACHBLK
+SVCT   51SPA;SW2;PU368,750;PD465,465;PD1034,465;PD1131,750;
+SVCT   29SPA;SW2;PU750,465;PD750,275;
+SVCT   76SPA;SW1;ST0;PU365,747;PM0;PD464,462;PD1036,465;PD1132,747;PD365,747;PM2;FP;
+****    0
+0001    501710
+SYMB   10SY01301NIL
+SYMD   39BOYSUP03V007500075000857006310031800178
+SXPO   31LANBY, super-buoy, paper-chart
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW2;CI59;PU750,318;PD750,178;PU653,225;PD840,318;PU653,318;PD840,225;PU318,750;PD690,750;PU803,750;
+SVCT   78PD1175,750;PU365,750;PD462,462;PD1031,462;PD1128,750;SW2;PU750,462;PD750,265;
+****    0
+0001    501711
+SYMB   10SY03127NIL
+SYMD   39BRIDGE01V005510081600400004000035100617
+SXPO   26symbol for opening bridge
+SCRF    6ACHMGD
+SVCT   25SPA;SW1;PU551,817;CI200;
+SVCT   25SPA;SW1;PU552,813;CI115;
+****    0
+0001    501712
+SYMB   10SY01358NIL
+SYMD   39BRTHNO01V007500075000700007000040000400
+SXPO   20berth number symbol
+SCRF    6JCHMGD
+SVCT   25SPJ;PU750,750;SW1;CI350;
+****    0
+0001    501713
+SYMB   10SY02240NIL
+SYMD   39BUAARE02V011420143600518005180088601182
+SXPO   14built-up area
+SCRF    6ALANDF
+SVCT   42SPA;SW1;ST0;PU1145,1441;PM0;CI259;PM2;FP;
+****    0
+0001    501714
+SYMB   10SY02057NIL
+SYMD   39BUIREL01V007500075000400004000055000550
+SXPO   46non-conspicuous religious building, Christian
+SCRF    6ALANDF
+SVCT   64SPA;SW1;ST0;PU650,550;PM0;PD850,550;PD750,650;PD650,550;PM2;FP;
+SVCT   64SPA;SW1;ST0;PU950,650;PM0;PD950,850;PD850,750;PD950,650;PM2;FP;
+SVCT   64SPA;SW1;ST0;PU650,950;PM0;PD850,950;PD750,850;PD650,950;PM2;FP;
+SVCT   64SPA;SW1;ST0;PU550,650;PM0;PD550,850;PD650,750;PD550,650;PM2;FP;
+SVCT   29SPA;SW1;PU650,750;PD850,750;
+SVCT   29SPA;SW1;PU750,650;PD750,850;
+SVCT   49SPA;SW1;PU650,550;PD850,550;PD750,650;PD650,550;
+SVCT   49SPA;SW1;PU950,650;PD950,850;PD850,750;PD950,650;
+SVCT   49SPA;SW1;PU850,950;PD650,950;PD750,850;PD850,950;
+SVCT   49SPA;SW1;PU550,650;PD550,850;PD650,750;PD550,650;
+****    0
+0001    501715
+SYMB   10SY02056NIL
+SYMD   39BUIREL04V007500075000572002840046200603
+SXPO   50non-conspicuous religious building, non-Christian
+SCRF    6ALANDF
+SVCT   30SPA;SW1;PU462,603;PD1034,887;
+SVCT   30SPA;SW1;PU462,887;PD1034,603;
+SVCT   59SPA;SW1;PU534,637;PD959,637;PD959,853;PD534,853;PD534,637;
+****    0
+0001    501716
+SYMB   10SY03118NIL
+SYMD   39BUIREL05V007460088600349004660056100518
+SXPO   18mosque or minaret
+SCRF    6ALANDF
+SVCT  149SPA;SW1;PU561,518;PD588,552;PD618,579;PD652,596;PD686,610;PD723,615;PD752,613;PD783,610;PD818,598;PD844,582;PD867,567;PD887,546;PD901,529;PD910,518;
+SVCT   24SPA;SW1;PU743,885;CI99;
+SVCT   29SPA;SW1;PU743,781;PD743,613;
+SVCT   39SPA;SW1;ST0;PU745,884;PM0;CI20;PM2;FP;
+****    0
+0001    501717
+SYMB   10SY02057NIL
+SYMD   39BUIREL13V007500075000400004000055000550
+SXPO   42conspicuous religious building, Christian
+SCRF    6ACHBLK
+SVCT   64SPA;SW1;ST0;PU650,550;PM0;PD850,550;PD750,650;PD650,550;PM2;FP;
+SVCT   64SPA;SW1;ST0;PU950,650;PM0;PD950,850;PD850,750;PD950,650;PM2;FP;
+SVCT   64SPA;SW1;ST0;PU650,950;PM0;PD850,950;PD750,850;PD650,950;PM2;FP;
+SVCT   64SPA;SW1;ST0;PU550,650;PM0;PD550,850;PD650,750;PD550,650;PM2;FP;
+SVCT   29SPA;SW1;PU650,750;PD850,750;
+SVCT   29SPA;SW1;PU750,650;PD750,850;
+SVCT   49SPA;SW1;PU650,550;PD850,550;PD750,650;PD650,550;
+SVCT   49SPA;SW1;PU950,650;PD950,850;PD850,750;PD950,650;
+SVCT   49SPA;SW1;PU850,950;PD650,950;PD750,850;PD850,950;
+SVCT   49SPA;SW1;PU550,650;PD550,850;PD650,750;PD550,650;
+****    0
+0001    501718
+SYMB   10SY02058NIL
+SYMD   39BUIREL14V007500075000572002840046200603
+SXPO   46conspicuous religious building, non-Christian
+SCRF    6ACHBLK
+SVCT   30SPA;SW1;PU462,603;PD1034,887;
+SVCT   30SPA;SW1;PU462,887;PD1034,603;
+SVCT   59SPA;SW1;PU534,637;PD959,637;PD959,853;PD534,853;PD534,637;
+****    0
+0001    501719
+SYMB   10SY03119NIL
+SYMD   39BUIREL15V007440088300349004660056100518
+SXPO   30conspicuous mosque or minaret
+SCRF    6ACHBLK
+SVCT  149SPA;SW1;PU561,518;PD588,552;PD618,579;PD652,596;PD686,610;PD723,615;PD752,613;PD783,610;PD818,598;PD844,582;PD867,567;PD887,546;PD901,529;PD910,518;
+SVCT   24SPA;SW1;PU743,885;CI99;
+SVCT   29SPA;SW1;PU743,781;PD743,613;
+SVCT   39SPA;SW1;ST0;PU745,884;PM0;CI20;PM2;FP;
+****    0
+0001    501720
+SYMB   10SY01307NIL
+SYMD   39BUISGL01V007500075000250002500061800618
+SXPO   16single building
+SCRF   12WLANDFKCHBRN
+SVCT  112SPK;ST0;PU618,618;PM0;PD868,618,868,868,618,868,618,618;PM2;FP;SPW;SW2;PU618,618;PD868,618;PD868,868;PD618,868;
+SVCT   11PD618,618;
+****    0
+0001    501721
+SYMB   10SY01308NIL
+SYMD   39BUISGL11V007500075000250002500061800618
+SXPO   28conspicuous single building
+SCRF   12WLANDFCCHBLK
+SVCT  112SPW;ST0;PU618,618;PM0;PD868,618,868,868,618,868,618,618;PM2;FP;SPC;SW2;PU618,618;PD868,618;PD868,868;PD618,868;
+SVCT   11PD618,618;
+****    0
+0001    501722
+SYMB   10SY01309NIL
+SYMD   39CAIRNS01V007500075000468004770051000310
+SXPO    6cairn
+SCRF    6WLANDF
+SVCT  121SPW;ST0;PU768,312;PM0;PD809,325,834,337,859,368,868,393,875,412,875,443,862,468,853,487,837,509,828,518,837,493,850,468;
+SVCT  114PD853,450,853,418,843,387,834,368,818,350,793,328,768,312;PM2;FP;PU675,512;PM0;PD700,534,725,553,734,568,743,593;
+SVCT  122PD750,618,750,637,737,668,728,693,712,712,684,734,700,709,712,684,725,634,725,609,712,578,703,559,684,528,675,512;PM2;FP;
+SVCT  121PU878,525;PM0;PD903,528,928,537,953,562,962,578,975,603,978,628,975,653,968,675,959,693,943,709,925,734,943,700,953,668;
+SVCT  119PD959,634,950,593,934,568,912,543,893,534,878,525;PM2;FP;PU750,750;SW2;CI37;PU628,628;CI118;PU756,425;CI115;PU868,634;
+SVCT   47CI109;PU528,750;PD709,750;PU787,750;PD962,750;
+****    0
+0001    501723
+SYMB   10SY01310NIL
+SYMD   39CAIRNS11V007500075000468004770051000310
+SXPO   18conspicuous cairn
+SCRF    6CCHBLK
+SVCT  121SPC;ST0;PU768,312;PM0;PD809,325,834,337,859,368,868,393,875,412,875,443,862,468,853,487,837,509,828,518,837,493,850,468;
+SVCT  114PD853,450,853,418,843,387,834,368,818,350,793,328,768,312;PM2;FP;PU675,512;PM0;PD700,534,725,553,734,568,743,593;
+SVCT  122PD750,618,750,637,737,668,728,693,712,712,684,734,700,709,712,684,725,634,725,609,712,578,703,559,684,528,675,512;PM2;FP;
+SVCT  121PU878,525;PM0;PD903,528,928,537,953,562,962,578,975,603,978,628,975,653,968,675,959,693,943,709,925,734,943,700,953,668;
+SVCT  119PD959,634,950,593,934,568,912,543,893,534,878,525;PM2;FP;PU750,750;SW2;CI37;PU628,628;CI118;PU756,425;CI115;PU868,634;
+SVCT   47CI109;PU528,750;PD709,750;PU787,750;PD962,750;
+****    0
+0001    501724
+SYMB   10SY03384NIL
+SYMD   39CBLARE51V031010185500374011120293001289
+SXPO   11cable area
+SCRF    6ACHMGF
+SVCT   57SPA;SW3;PU3246,1289;PD2930,1791;PD3304,1923;PD3005,2401;
+****    0
+0001    501725
+SYMB   10SY02059NIL
+SYMD   39CGUSTA02V001980074800702004690016900312
+SXPO   19coastguard station
+SCRF   18ALANDFBCHWHTCCHMGF
+SVCT   39SPA;SW1;ST0;PU200,750;PM0;CI31;PM2;FP;
+SVCT   74SPB;SW2;ST0;PU271,312;PM0;PD871,312;PD871,687;PD271,687;PD271,312;PM2;FP;
+SVCT   59SPC;SW2;PU271,312;PD871,312;PD871,687;PD271,687;PD271,312;
+SVCT   49SPC;SW2;PU496,437;PD446,412;PD399,412;PD346,440;
+SVCT   49SPC;SW2;PU346,562;PD396,612;PD446,612;PD496,571;
+SVCT  109SPC;SW2;PU746,437;PD696,412;PD649,412;PD596,440;PD596,562;PD652,612;PD696,612;PD746,587;PD746,515;PD696,515;
+SVCT   29SPC;SW2;PU346,440;PD346,562;
+****    0
+0001    501726
+SYMB   10SY03357NIL
+SYMD   39CHCRDEL1V007800054400805008050037100143
+SXPO   50this object has been manually deleted or modified
+SCRF    6ACHCOR
+SVCT   30SPA;SW1;PU371,948;PD1176,143;
+****    0
+0001    501727
+SYMB   10SY03357NIL
+SYMD   39CHCRID01V055190185400098006020547001851
+SXPO   38this object has been manually updated
+SCRF    6ACHCOR
+SVCT   26SPA;SW1;PU5519,2404;CI49;
+SVCT   33SPA;SW1;PU5519,1851;PD5519,2353;
+****    0
+0001    501728
+SYMB   10SY01313NIL
+SYMD   39CHIMNY01V007430075000478006720056800134
+SXPO    8chimney
+SCRF    6WLANDF
+SVCT  114SPW;PU743,750;SW2;CI56;PU806,750;PD918,750;PU656,750;PD703,290;PD775,290;PD831,750;PU568,750;PD681,750;PU703,290;
+SVCT  113PD687,237;PD690,193;PD715,168;PD753,153;PD812,153;PD840,165;PD871,146;PD925,134;PD975,137;PD1021,140;PD1043,162;
+SVCT   62PD1046,206;PD993,237;PD931,240;PD928,268;PD868,293;PD775,293;
+****    0
+0001    501729
+SYMB   10SY01314NIL
+SYMD   39CHIMNY11V007430075000478006720056800134
+SXPO   20conspicuous chimney
+SCRF    6CCHBLK
+SVCT  114SPC;PU743,750;SW2;CI56;PU806,750;PD918,750;PU656,750;PD703,290;PD775,290;PD831,750;PU568,750;PD681,750;PU703,290;
+SVCT  113PD687,237;PD690,193;PD715,168;PD753,153;PD812,153;PD840,165;PD871,146;PD925,134;PD975,137;PD1021,140;PD1043,162;
+SVCT   62PD1046,206;PD993,237;PD931,240;PD928,268;PD868,293;PD775,293;
+****    0
+0001    501730
+SYMB   10SY02076NIL
+SYMD   39CHINFO06V011420143600518005180088601182
+SXPO   16HO caution note
+SCRF    6ACHMGD
+SVCT   27SPA;SW1;PU1145,1441;CI259;
+SVCT   33SPA;SW1;PU1141,1267;PD1141,1492;
+SVCT   33SPA;SW1;PU1109,1585;PD1164,1585;
+****    0
+0001    501731
+SYMB   10SY02077NIL
+SYMD   39CHINFO07V026350185900495004950240001605
+SXPO   20HO information note
+SCRF    6ACHMGD
+SVCT   69SPA;SW1;PU2400,2100;PD2895,2100;PD2895,1605;PD2400,1605;PD2400,2100;
+SVCT   33SPA;SW1;PU2631,1672;PD2631,1714;
+SVCT   45SPA;SW1;PU2635,2009;PD2635,1775;PD2543,1774;
+SVCT   33SPA;SW1;PU2544,2010;PD2711,2010;
+****    0
+0001    501732
+SYMB   10SY02063NIL
+SYMD   39CHINFO08V026350185900495004950240001605
+SXPO   27mariner's information note
+SCRF    6ANINFO
+SVCT   69SPA;SW1;PU2400,2100;PD2895,2100;PD2895,1605;PD2400,1605;PD2400,2100;
+SVCT   33SPA;SW1;PU2631,1672;PD2631,1714;
+SVCT   45SPA;SW1;PU2635,2009;PD2635,1775;PD2543,1774;
+SVCT   33SPA;SW1;PU2544,2010;PD2711,2010;
+****    0
+0001    501733
+SYMB   10SY02064NIL
+SYMD   39CHINFO09V011420143600518005180088601182
+SXPO   22mariners caution note
+SCRF    6ANINFO
+SVCT   27SPA;SW1;PU1145,1441;CI259;
+SVCT   33SPA;SW1;PU1141,1267;PD1141,1492;
+SVCT   33SPA;SW1;PU1109,1585;PD1164,1585;
+****    0
+0001    501734
+SYMB   10SY02062NIL
+SYMD   39CHINFO10V026350185900495004950240001605
+SXPO   32manufacturer's information note
+SCRF    6AADINF
+SVCT   69SPA;SW1;PU2400,2100;PD2895,2100;PD2895,1605;PD2400,1605;PD2400,2100;
+SVCT   33SPA;SW1;PU2631,1672;PD2631,1714;
+SVCT   45SPA;SW1;PU2635,2009;PD2635,1775;PD2543,1774;
+SVCT   33SPA;SW1;PU2544,2010;PD2711,2010;
+****    0
+0001    501735
+SYMB   10SY02061NIL
+SYMD   39CHINFO11V011420143600518005180088601182
+SXPO   28manufacturer's caution note
+SCRF    6AADINF
+SVCT   27SPA;SW1;PU1145,1441;CI259;
+SVCT   33SPA;SW1;PU1141,1267;PD1141,1492;
+SVCT   33SPA;SW1;PU1109,1585;PD1164,1585;
+****    0
+0001    501736
+SYMB   10SY03204NIL
+SYMD   39CHKSYM01V006800068200501005000041900433
+SXPO   65test symbol for checking symbol sizes, should measure 5mm by 5mm
+SCRF   12ACHBLKBOUTLW
+SVCT   74SPA;SW1;ST0;PU419,433;PM0;PD419,933;PD920,933;PD920,433;PD419,433;PM2;FP;
+SVCT   59SPB;SW1;PU419,433;PD419,933;PD920,933;PD920,433;PD419,433;
+****    0
+0001    501737
+SYMB   10SY01319NIL
+SYMD   39CLRLIN01V014990150000325006070133700893
+SXPO   39arrow head for mariner's clearing line
+SCRF    6fNINFO
+SVCT   63SPf;ST0;PU1337,1500;PM0;PD1500,893,1662,1500,1337,1500;PM2;FP;
+****    0
+0001    501738
+SYMB   10SY01322NIL
+SYMD   39CRANES01V007500075000600005000050000300
+SXPO    7cranes
+SCRF    6WLANDF
+SVCT  114SPW;PU750,750;SW2;CI50;PU550,750;PD700,750;PU800,750;PD950,750;PU700,750;PD700,350;PU800,750;PD800,350;PU500,300;
+SVCT   52PD1100,300;PU500,300;PD500,450;PD600,450;PD600,350;
+****    0
+0001    501739
+SYMB   10SY03385NIL
+SYMD   39CTNARE51V020610124600994009940027601846
+SXPO   46caution area, a specific caution note applies
+SCRF    6ATRFCF
+SVCT   31SPA;SW3;PU775,2471;PD775,2006;
+SVCT   31SPA;SW2;PU708,2651;PD851,2651;
+SVCT   26SPA;SW2;PU773,2343;CI497;
+****    0
+0001    501740
+SYMB   10SY03362NIL
+SYMD   39CTYARE51V020610124600994009940027601846
+SXPO   56cautionary area (e.g. ferry area) navigate with caution
+SCRF    6ATRFCF
+SVCT   31SPA;SW3;PU775,2471;PD775,2006;
+SVCT   31SPA;SW2;PU708,2651;PD851,2651;
+SVCT   26SPA;SW2;PU773,2343;CI497;
+****    0
+0001    501741
+SYMB   10SY03387NIL
+SYMD   39CTYARE71V020610124601235009940003501846
+SXPO   41cautionary area with further information
+SCRF   12ATRFCFDCHMGD
+SVCT   31SPA;SW3;PU775,2471;PD775,2006;
+SVCT   31SPA;SW2;PU708,2651;PD851,2651;
+SVCT   26SPA;SW2;PU773,2343;CI497;
+SVCT   41SPD;SW1;PU127,2827;PD127,2593;PD35,2592;
+SVCT   30SPD;SW1;PU36,2828;PD223,2828;
+SVCT   29SPD;SW1;PU81,2474;PD84,2514;
+****    0
+0001    501742
+SYMB   10SY03431NIL
+SYMD   39CURDEF01V007850081001009009080029700362
+SXPO   53current or tidal stream whose direction is not known
+SCRF    6ACHGRD
+SVCT   42SPA;SW1;PU685,1257;PD785,1058;PD884,1257;
+SVCT   41SPA;SW1;PU685,1073;PD785,874;PD884,1073;
+SVCT   30SPA;SW1;PU785,362;PD785,1270;
+SVCT   39SPA;SW1;PU684,611;PD786,364;PD886,611;
+SVCT  259SPA;SW1;PU297,710;PD310,682;PD323,657;PD340,640;PD357,623;PD383,614;PD406,616;PD436,625;PD464,631;PD487,665;PD505,684;PD517,706;PD517,731;PD505,755;PD492,776;PD472,800;PD449,825;PD428,851;PD415,870;PD404,887;PD402,909;PD402,932;PD402,949;PD404,971;PD404,973;
+SVCT   31SPA;SW2;PU383,1057;PD436,1057;
+SVCT  284SPA;SW1;PU1086,704;PD1099,676;PD1112,651;PD1129,634;PD1146,617;PD1172,608;PD1195,610;PD1225,619;PD1253,625;PD1276,659;PD1294,678;PD1306,700;PD1306,725;PD1294,749;PD1281,770;PD1261,794;PD1238,819;PD1217,845;PD1204,864;PD1193,881;PD1191,903;PD1191,926;PD1191,943;PD1193,965;PD1193,967;
+SVCT   33SPA;SW2;PU1172,1051;PD1225,1051;
+****    0
+0001    501743
+SYMB   10SY03128NIL
+SYMD   39CURENT01V007850081000202009080068400362
+SXPO   18non-tidal current
+SCRF    6ACHGRD
+SVCT   42SPA;SW1;PU685,1257;PD785,1058;PD884,1257;
+SVCT   41SPA;SW1;PU685,1073;PD785,874;PD884,1073;
+SVCT   30SPA;SW1;PU785,362;PD785,1270;
+SVCT   39SPA;SW1;PU684,611;PD786,364;PD886,611;
+****    0
+0001    501744
+SYMB   10SY01328NIL
+SYMD   39CURSRA01V023840224701000010000187501755
+SXPO   16ordinary cursor
+SCRF    6BCURSR
+SVCT   57SPB;SW3;PU2379,1755;PD2379,2755;PU1875,2250;PD2875,2250;
+****    0
+0001    501745
+SYMB   10SY03365NIL
+SYMD   39CURSRB01V021720225401001010010167301752
+SXPO   24cursor with open centre
+SCRF    6ACURSR
+SVCT   33SPA;SW3;PU1673,2252;PD2073,2252;
+SVCT   33SPA;SW3;PU2274,2252;PD2674,2252;
+SVCT   33SPA;SW3;PU2170,1752;PD2170,2152;
+SVCT   33SPA;SW3;PU2171,2353;PD2171,2753;
+****    0
+0001    501746
+SYMB   10SY01329NIL
+SYMD   39DANGER01V006200081400800006320022500496
+SXPO   39underwater hazard with a defined depth
+SCRF   12ADEPVSBCHBLK
+SVCT  242SPA;SW1;ST0;PU234,731;PM0;PD300,637;PD387,556;PD459,525;PD553,496;PD706,496;PD806,525;PD909,584;PD975,662;PD1025,762;PD1025,881;PD959,975;PD881,1050;PD784,1100;PD681,1128;PD540,1125;PD440,1087;PD350,1028;PD275,950;PD225,850;PD234,731;PM2;FP;
+SVCT   22SPB;SW1;PU387,556;PD;
+SVCT   22SPB;SW1;PU300,637;PD;
+SVCT   22SPB;SW1;PU234,731;PD;
+SVCT   22SPB;SW1;PU225,850;PD;
+SVCT   22SPB;SW1;PU275,950;PD;
+SVCT   23SPB;SW1;PU350,1028;PD;
+SVCT   23SPB;SW1;PU440,1087;PD;
+SVCT   23SPB;SW1;PU540,1125;PD;
+SVCT   23SPB;SW1;PU681,1128;PD;
+SVCT   23SPB;SW1;PU784,1100;PD;
+SVCT   23SPB;SW1;PU881,1050;PD;
+SVCT   22SPB;SW1;PU959,975;PD;
+SVCT   23SPB;SW1;PU1025,881;PD;
+SVCT   23SPB;SW1;PU1025,762;PD;
+SVCT   22SPB;SW1;PU975,662;PD;
+SVCT   22SPB;SW1;PU906,584;PD;
+SVCT   22SPB;SW1;PU809,528;PD;
+SVCT   22SPB;SW1;PU706,500;PD;
+SVCT   22SPB;SW1;PU553,500;PD;
+SVCT   22SPB;SW1;PU459,525;PD;
+****    0
+0001    501747
+SYMB   10SY03183NIL
+SYMD   39DANGER02V006300082800800006280022500500
+SXPO   52underwater hazard with depth greater than 20 metres
+SCRF    6ACHBLK
+SVCT   22SPA;SW1;PU387,556;PD;
+SVCT   22SPA;SW1;PU300,637;PD;
+SVCT   22SPA;SW1;PU234,731;PD;
+SVCT   22SPA;SW1;PU225,850;PD;
+SVCT   22SPA;SW1;PU275,950;PD;
+SVCT   23SPA;SW1;PU350,1028;PD;
+SVCT   23SPA;SW1;PU440,1087;PD;
+SVCT   23SPA;SW1;PU540,1125;PD;
+SVCT   23SPA;SW1;PU681,1128;PD;
+SVCT   23SPA;SW1;PU784,1100;PD;
+SVCT   23SPA;SW1;PU881,1050;PD;
+SVCT   22SPA;SW1;PU959,975;PD;
+SVCT   23SPA;SW1;PU1025,881;PD;
+SVCT   23SPA;SW1;PU1025,762;PD;
+SVCT   22SPA;SW1;PU975,662;PD;
+SVCT   22SPA;SW1;PU906,584;PD;
+SVCT   22SPA;SW1;PU809,528;PD;
+SVCT   22SPA;SW1;PU706,500;PD;
+SVCT   22SPA;SW1;PU553,500;PD;
+SVCT   22SPA;SW1;PU459,525;PD;
+****    0
+0001    501748
+SYMB   10SY03610NIL
+SYMD   39DANGER03V006200081400800006320022500496
+SXPO   44underwater hazard which covers and uncovers
+SCRF   12ADEPITBCHBLK
+SVCT  242SPA;SW1;ST0;PU234,731;PM0;PD300,637;PD387,556;PD459,525;PD553,496;PD706,496;PD806,525;PD909,584;PD975,662;PD1025,762;PD1025,881;PD959,975;PD881,1050;PD784,1100;PD681,1128;PD540,1125;PD440,1087;PD350,1028;PD275,950;PD225,850;PD234,731;PM2;FP;
+SVCT   22SPB;SW1;PU387,556;PD;
+SVCT   22SPB;SW1;PU300,637;PD;
+SVCT   22SPB;SW1;PU234,731;PD;
+SVCT   22SPB;SW1;PU225,850;PD;
+SVCT   22SPB;SW1;PU275,950;PD;
+SVCT   23SPB;SW1;PU350,1028;PD;
+SVCT   23SPB;SW1;PU440,1087;PD;
+SVCT   23SPB;SW1;PU540,1125;PD;
+SVCT   23SPB;SW1;PU681,1128;PD;
+SVCT   23SPB;SW1;PU784,1100;PD;
+SVCT   23SPB;SW1;PU881,1050;PD;
+SVCT   22SPB;SW1;PU959,975;PD;
+SVCT   23SPB;SW1;PU1025,881;PD;
+SVCT   23SPB;SW1;PU1025,762;PD;
+SVCT   22SPB;SW1;PU975,662;PD;
+SVCT   22SPB;SW1;PU906,584;PD;
+SVCT   22SPB;SW1;PU809,528;PD;
+SVCT   22SPB;SW1;PU706,500;PD;
+SVCT   22SPB;SW1;PU553,500;PD;
+SVCT   22SPB;SW1;PU459,525;PD;
+****    0
+0001    501749
+SYMB   10SY03224NIL
+SYMD   39DAYSQR01V007530060300401005830054900204
+SXPO   42square or rectangular daymark, simplified
+SCRF    6ACHMGD
+SVCT   59SPA;SW1;PU549,204;PD549,605;PD950,605;PD950,204;PD549,204;
+SVCT   29SPA;SW1;PU751,634;PD751,787;
+SVCT   39SPA;SW1;ST0;PU751,602;PM0;CI50;PM2;FP;
+SVCT   24SPA;SW1;PU751,602;CI50;
+****    0
+0001    501750
+SYMB   10SY03225NIL
+SYMD   39DAYSQR21V007470082400401006830054900204
+SXPO   43square or rectangular daymark, paper chart
+SCRF    6ACHMGD
+SVCT   59SPA;SW1;PU549,204;PD549,605;PD950,605;PD950,204;PD549,204;
+SVCT   29SPA;SW1;PU750,605;PD750,758;
+SVCT   24SPA;SW1;PU751,822;CI65;
+SVCT   29SPA;SW1;PU603,822;PD687,822;
+SVCT   29SPA;SW1;PU816,822;PD901,822;
+****    0
+0001    501751
+SYMB   10SY03226NIL
+SYMD   39DAYTRI01V007660050400500006010051700103
+SXPO   41triangular daymark, point up, simplified
+SCRF    6ACHMGD
+SVCT   50SPA;SW1;PU517,505;PD1017,505;PD768,103;PD517,505;
+SVCT   29SPA;SW1;PU766,506;PD766,704;
+SVCT   39SPA;SW1;ST0;PU766,505;PM0;CI50;PM2;FP;
+SVCT   24SPA;SW1;PU766,505;CI50;
+****    0
+0001    501752
+SYMB   10SY03227NIL
+SYMD   39DAYTRI05V007500061900503006070050000224
+SXPO   43triangular daymark, point down, simplified
+SCRF    6ACHMGD
+SVCT   50SPA;SW1;PU500,225;PD1003,224;PD751,626;PD500,228;
+SVCT   39SPA;SW1;ST0;PU750,617;PM0;CI50;PM2;FP;
+SVCT   24SPA;SW1;PU750,617;CI50;
+SVCT   29SPA;SW1;PU751,642;PD751,831;
+****    0
+0001    501753
+SYMB   10SY03228NIL
+SYMD   39DAYTRI21V007460075100500007270049800088
+SXPO   42triangular daymark, point up, paper chart
+SCRF    6ACHMGD
+SVCT   48SPA;SW1;PU498,490;PD998,490;PD749,88;PD498,490;
+SVCT   29SPA;SW1;PU747,491;PD747,689;
+SVCT   24SPA;SW1;PU747,750;CI65;
+SVCT   29SPA;SW1;PU599,748;PD683,748;
+SVCT   29SPA;SW1;PU812,748;PD897,748;
+****    0
+0001    501754
+SYMB   10SY03229NIL
+SYMD   39DAYTRI25V007510081000503006490050000224
+SXPO   44triangular daymark, point down, paper chart
+SCRF    6ACHMGD
+SVCT   50SPA;SW1;PU500,225;PD1003,224;PD751,626;PD500,228;
+SVCT   29SPA;SW1;PU751,625;PD751,747;
+SVCT   24SPA;SW1;PU750,808;CI65;
+SVCT   29SPA;SW1;PU602,808;PD686,808;
+SVCT   29SPA;SW1;PU815,808;PD900,808;
+****    0
+0001    501755
+SYMB   10SY03500NIL
+SYMD   39DIRBOY01V-23240077801312008270012800355
+SXPO   21direction of buoyage
+SCRF    6ACHMGD
+SVCT   94SPA;SW1;PU478,983;PD179,983;PD783,381;PD1381,983;PD1079,983;PD1079,1182;PD478,1182;PD478,983;
+SVCT   25SPA;SW2;PU280,507;CI152;
+SVCT   26SPA;SW2;PU1291,507;CI149;
+****    0
+0001    501756
+SYMB   10SY03133NIL
+SYMD   39DIRBOYA1V-23240077801312008270012800355
+SXPO   86direction and color of buoyage for approaching harbour in IALA region A (red to port)
+SCRF   18ACHMGDBCHREDCCHGRN
+SVCT   94SPA;SW1;PU478,983;PD179,983;PD783,381;PD1381,983;PD1079,983;PD1079,1182;PD478,1182;PD478,983;
+SVCT   25SPB;SW2;PU280,507;CI152;
+SVCT   26SPC;SW2;PU1291,507;CI149;
+****    0
+0001    501757
+SYMB   10SY03132NIL
+SYMD   39DIRBOYB1V-23240077801312008270012800355
+SXPO   88direction and color of buoyage for approaching harbour in IALA region B (green to port)
+SCRF   18ACHMGDBCHGRNCCHRED
+SVCT   94SPA;SW1;PU478,983;PD179,983;PD783,381;PD1381,983;PD1079,983;PD1079,1182;PD478,1182;PD478,983;
+SVCT   25SPB;SW2;PU280,507;CI152;
+SVCT   26SPC;SW2;PU1291,507;CI149;
+****    0
+0001    501758
+SYMB   10SY02242NIL
+SYMD   39DISMAR03V008250075600865004000033500494
+SXPO   14distance mark
+SCRF    6ACHMGD
+SVCT   29SPA;SW1;PU650,494;PD650,894;
+SVCT   29SPA;SW1;PU785,644;PD650,794;
+SVCT   29SPA;SW1;PU695,744;PD800,894;
+SVCT   29SPA;SW1;PU900,644;PD900,894;
+SVCT   82SPA;SW1;PU900,684;PD935,649;PD965,639;PD985,639;PD1027,659;PD1050,689;PD1050,894;
+SVCT   86SPA;SW1;PU1050,694;PD1070,654;PD1110,636;PD1135,636;PD1177,654;PD1200,694;PD1200,894;
+SVCT   24SPA;SW1;PU419,765;CI84;
+****    0
+0001    501759
+SYMB   10SY03386NIL
+SYMD   39DISMAR04V008250075600550004000065000494
+SXPO   28distance point with no mark
+SCRF    6ACHMGD
+SVCT   29SPA;SW1;PU650,494;PD650,894;
+SVCT   29SPA;SW1;PU785,644;PD650,794;
+SVCT   29SPA;SW1;PU695,744;PD800,894;
+SVCT   29SPA;SW1;PU900,644;PD900,894;
+SVCT   82SPA;SW1;PU900,684;PD935,649;PD965,639;PD985,639;PD1027,659;PD1050,689;PD1050,894;
+SVCT   86SPA;SW1;PU1050,694;PD1070,654;PD1110,636;PD1135,636;PD1177,654;PD1200,694;PD1200,894;
+****    0
+0001    501760
+SYMB   10SY01331NIL
+SYMD   39DNGHILITV015000150000587005870120601206
+SXPO   47transparent danger highlight for mariner's use
+SCRF    6SDNGHL
+SVCT  114SPS;ST3;PU1206,1206;PM0;PD1793,1206,1793,1793,1206,1793,1206,1206;PM2;FP;SW3;PU1206,1793;PD1793,1793;PD1793,1206;
+SVCT   25PD1206,1206;PD1206,1793;
+****    0
+0001    501761
+SYMB   10SY03114NIL
+SYMD   39DOMES001V007470053000503004210049800150
+SXPO    5dome
+SCRF    6ALANDF
+SVCT   24SPA;SW1;PU745,528;CI43;
+SVCT   29SPA;SW1;PU703,524;PD539,524;
+SVCT  270SPA;SW1;PU538,525;PD528,509;PD514,475;PD502,440;PD498,410;PD498,370;PD508,328;PD526,280;PD542,250;PD570,220;PD600,194;PD628,180;PD676,162;PD716,154;PD760,150;PD809,158;PD855,172;PD901,202;PD943,242;PD965,274;PD987,316;PD999,368;PD1001,430;PD991,471;PD977,505;PD963,521;
+SVCT   39SPA;SW1;PU787,524;PD961,524;PD964,516;
+****    0
+0001    501762
+SYMB   10SY03115NIL
+SYMD   39DOMES011V007460052100503004210049800150
+SXPO   17conspicuous dome
+SCRF    6ACHBLK
+SVCT   24SPA;SW1;PU745,528;CI43;
+SVCT   29SPA;SW1;PU703,524;PD539,524;
+SVCT  270SPA;SW1;PU538,525;PD528,509;PD514,475;PD502,440;PD498,410;PD498,370;PD508,328;PD526,280;PD542,250;PD570,220;PD600,194;PD628,180;PD676,162;PD716,154;PD760,150;PD809,158;PD855,172;PD901,202;PD943,242;PD965,274;PD987,316;PD999,368;PD1001,430;PD991,471;PD977,505;PD963,521;
+SVCT   39SPA;SW1;PU787,524;PD961,524;PD964,516;
+****    0
+0001    501763
+SYMB   10SY01332NIL
+SYMD   39DSHAER01V007500075000350005310058400259
+SXPO   12dish aerial
+SCRF    6WLANDF
+SVCT  114SPW;PU750,750;SW2;CI40;PU584,750;PD700,750;PU790,750;PD900,750;PU659,265;PD934,434;PU800,350;PD825,309;PU725,490;
+SVCT  121PD665,750;PU781,506;PD825,750;PU659,259;PD640,300;PD634,365;PD646,418;PD684,468;PD725,490;PD781,506;PD818,506;PD865,500;
+SVCT   21PD909,475;PD934,434;
+****    0
+0001    501764
+SYMB   10SY01333NIL
+SYMD   39DSHAER11V007500075000350005310058400259
+SXPO   24conspicuous dish aerial
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW2;CI40;PU584,750;PD700,750;PU790,750;PD900,750;PU659,265;PD934,434;PU800,350;PD825,309;PU725,490;
+SVCT  121PD665,750;PU781,506;PD825,750;PU659,259;PD640,300;PD634,365;PD646,418;PD684,468;PD725,490;PD781,506;PD818,506;PD865,500;
+SVCT   21PD909,475;PD934,434;
+****    0
+0001    501765
+SYMB   10SY03392NIL
+SYMD   39DWRTPT51V023130261501570009340158700881
+SXPO   25part of deep water route
+SCRF    6ATRFCF
+SVCT  113SPA;SW2;PU1587,901;PD1587,1802;PD1880,1804;PD1987,1751;PD2089,1603;PD2089,1105;PD1987,952;PD1883,901;PD1587,901;
+SVCT   66SPA;SW2;PU2353,899;PD2551,1806;PD2755,904;PD2954,1815;PD3157,881;
+****    0
+0001    501766
+SYMB   10SY03501NIL
+SYMD   39DWRUTE51V022500225000619014990194401500
+SXPO   70reciprocal traffic directions in a two-way part of a deep-water route
+SCRF    6ATRFCD
+SVCT  152SPA;ST3;PU2096,1950;PM0;PD1944,1950;PD2245,1500;PD2553,1950;PD2401,1950;PD2401,2546;PD2563,2546;PD2252,2999;PD1947,2546;PD2096,2546;PD2096,1950;PM2;FP;
+SVCT  141SPA;SW1;PU2096,1950;PD1944,1950;PD2245,1500;PD2553,1950;PD2401,1950;PD2401,2546;PD2563,2546;PD2252,2999;PD1947,2546;PD2096,2546;PD2096,1950;
+****    0
+0001    501767
+SYMB   10SY03361NIL
+SYMD   39EBBSTR01V007850088300202008840068400364
+SXPO   33ebb stream, rate at spring tides
+SCRF    6ACHGRD
+SVCT   39SPA;SW1;PU684,611;PD786,364;PD886,611;
+SVCT   30SPA;SW1;PU785,366;PD785,1248;
+****    0
+0001    501768
+SYMB   10SY03391NIL
+SYMD   39EBLVRM11V007500075000200002000065000650
+SXPO   41point of origin for an offset EBL or VRM
+SCRF    6CNINFO
+SVCT   36SPC;PU750,750;ST0;PM0;CI100;PM2;FP;
+****    0
+0001    501769
+SYMB   10SY03405NIL
+SYMD   39ENTRES51V022650272100994009940027601846
+SXPO   64area where entry is prohibited or restricted or "to be avoided"
+SCRF    6ATRFCF
+SVCT   32SPA;SW3;PU1020,2344;PD558,2344;
+SVCT   26SPA;SW2;PU773,2343;CI497;
+****    0
+0001    501770
+SYMB   10SY03406NIL
+SYMD   39ENTRES61V022650272101171010190027601846
+SXPO   85area where entry is prohibited or restricted or "to be avoided", with other cautions
+SCRF   12ATRFCFCCHMGD
+SVCT   32SPA;SW3;PU1020,2344;PD558,2344;
+SVCT   26SPA;SW2;PU773,2343;CI497;
+SVCT   33SPC;SW1;PU1392,2865;PD1447,2865;
+SVCT   33SPC;SW1;PU1405,2479;PD1405,2814;
+****    0
+0001    501771
+SYMB   10SY03407NIL
+SYMD   39ENTRES71V022650272101235009940003501846
+SXPO   88area where entry is prohibited or restricted or "to be avoided", with other information
+SCRF   12ATRFCFCCHMGD
+SVCT   32SPA;SW3;PU1020,2344;PD558,2344;
+SVCT   26SPA;SW2;PU773,2343;CI497;
+SVCT   41SPC;SW1;PU127,2827;PD127,2593;PD35,2592;
+SVCT   30SPC;SW1;PU36,2828;PD223,2828;
+SVCT   29SPC;SW1;PU81,2474;PD84,2514;
+****    0
+0001    501772
+SYMB   10SY03393NIL
+SYMD   39ERBLTIK1V026740132001226001270207201317
+SXPO   23range mark for an ERBL
+SCRF    6ANINFO
+SVCT   33SPA;SW2;PU2601,1317;PD2750,1317;
+SVCT   33SPA;SW2;PU2322,1353;PD2468,1324;
+SVCT   33SPA;SW2;PU2893,1327;PD3039,1355;
+SVCT   33SPA;SW2;PU2072,1436;PD2211,1379;
+SVCT   33SPA;SW2;PU3157,1396;PD3298,1444;
+****    0
+0001    501773
+SYMB   10SY01334NIL
+SYMD   39EVENTS02V007500075000400003000055000600
+SXPO   21mariner's event mark
+SCRF    6fNINFO
+SVCT   79SPf;SW2;PU550,600;PD950,600;PD950,900;PD550,900;PD550,600;PU550,900;PD950,600;
+****    0
+0001    501774
+SYMB   10SY03184NIL
+SYMD   39FAIRWY51V022500225000600013500195001500
+SXPO   52fairway with one-way traffic in direction indicated
+SCRF    6ACHGRD
+SVCT  105SPA;SW1;PU2100,2850;PD2100,1950;PD1950,1950;PD2250,1500;PD2550,1950;PD2400,1950;PD2400,2850;PD2100,2850;
+****    0
+0001    501775
+SYMB   10SY03185NIL
+SYMD   39FAIRWY52V022500225000601013510194801499
+SXPO   29fairway with two-way traffic
+SCRF    6ACHGRD
+SVCT   69SPA;SW1;PU2403,2397;PD2549,2397;PD2251,2850;PD1953,2397;PD2102,2397;
+SVCT   93SPA;SW1;PU2097,2396;PD2097,1949;PD1948,1949;PD2250,1499;PD2544,1949;PD2399,1949;PD2399,2396;
+****    0
+0001    501776
+SYMB   10SY01335NIL
+SYMD   39FLASTK01V015000150000300006070135000943
+SXPO   12flare stack
+SCRF    6WLANDF
+SVCT  120SPW;ST0;PU1450,1500;PM0;PD1450,1175,1550,1175,1550,1500,1537,1462,1500,1450,1468,1462,1450,1500;PM2;FP;PU1500,1500;SW2;
+SVCT  123CI50;PU1550,1500;PD1650,1500;PU1450,1500;PD1350,1500;PU1556,943;PD1525,962;PD1493,981;PD1468,1012;PD1450,1050;PD1450,1087;
+SVCT  119PD1456,1106;PD1481,1118;PD1512,1118;PD1531,1106;PD1543,1081;PD1537,1050;PD1525,1037;PD1525,1012;PD1525,987;PD1543,968;
+SVCT   12PD1556,943;
+****    0
+0001    501777
+SYMB   10SY01336NIL
+SYMD   39FLASTK11V015000150000300006070135000943
+SXPO   24conspicuous flare stack
+SCRF    6CCHBLK
+SVCT  120SPC;ST0;PU1450,1500;PM0;PD1450,1175,1550,1175,1550,1500,1537,1462,1500,1450,1468,1462,1450,1500;PM2;FP;PU1500,1500;SW2;
+SVCT  123CI50;PU1550,1500;PD1650,1500;PU1450,1500;PD1350,1500;PU1556,943;PD1525,962;PD1493,981;PD1468,1012;PD1450,1050;PD1450,1087;
+SVCT  119PD1456,1106;PD1481,1118;PD1512,1118;PD1531,1106;PD1543,1081;PD1537,1050;PD1525,1037;PD1525,1012;PD1525,987;PD1543,968;
+SVCT   12PD1556,943;
+****    0
+0001    501778
+SYMB   10SY03360NIL
+SYMD   39FLDSTR01V007850081000202009150068400362
+SXPO   35flood stream, rate at spring tides
+SCRF    6ACHGRD
+SVCT   30SPA;SW1;PU785,362;PD785,1277;
+SVCT   39SPA;SW1;PU684,611;PD786,364;PD886,611;
+SVCT   30SPA;SW1;PU787,894;PD886,1099;
+SVCT   31SPA;SW1;PU787,1079;PD886,1277;
+SVCT   31SPA;SW1;PU785,1103;PD785,1161;
+****    0
+0001    501779
+SYMB   10SY01337NIL
+SYMD   39FLGSTF01V007500075000300005000060000300
+SXPO   20flagstaff, flagpole
+SCRF    6WLANDF
+SVCT  114SPW;PU750,750;SW2;CI50;PU750,700;PD750,300;PD900,300;PD900,475;PD750,475;PU600,750;PD700,750;PU800,750;PD900,750;
+****    0
+0001    501780
+SYMB   10SY01338NIL
+SYMD   39FOGSIG01V007500075000363004000026500771
+SXPO   11fog signal
+SCRF    6JCHMGF
+SVCT  113SPJ;SW2;PU265,787;PD284,890;PD328,990;PD378,1062;PD446,1125;PD512,1171;PU387,781;PD406,859;PD446,934;PD487,1000;
+SVCT   73PD537,1043;PD571,1059;PU509,771;PD525,850;PD568,912;PD609,946;PD628,962;
+****    0
+0001    501781
+SYMB   10SY01339NIL
+SYMD   39FORSTC01V007500075000400004000055000550
+SXPO   20fortified structure
+SCRF    6WLANDF
+SVCT   59SPW;SW2;PU550,550;PD950,550;PD950,950;PD550,950;PD550,550;
+****    0
+0001    501782
+SYMB   10SY01340NIL
+SYMD   39FORSTC11V007500075000400004000055000550
+SXPO   31conspicuous fortified structure
+SCRF    6CCHBLK
+SVCT   59SPC;SW2;PU550,550;PD950,550;PD950,950;PD550,950;PD550,550;
+****    0
+0001    501783
+SYMB   10SY03129NIL
+SYMD   39FOULGND1V006660076700390003000046500613
+SXPO   62foul area of seabed safe for navigation but not for anchoring
+SCRF    6ACHGRD
+SVCT   29SPA;SW1;PU641,613;PD539,913;
+SVCT   29SPA;SW1;PU793,613;PD688,912;
+SVCT   29SPA;SW1;PU507,713;PD855,713;
+SVCT   29SPA;SW1;PU465,814;PD821,814;
+****    0
+0001    501784
+SYMB   10SY03390NIL
+SYMD   39FRYARE51V016310086302560002980051300728
+SXPO   11ferry area
+SCRF    6ACHMGF
+SVCT   29SPA;SW1;PU823,863;PD513,863;
+SVCT   31SPA;SW1;PU2279,863;PD2571,863;
+SVCT   88SPA;SW1;PU1883,728;PD1086,728;PD1086,1026;PD1882,1022;PD2083,923;PD2084,823;PD1883,728;
+SVCT   31SPA;SW1;PU2778,868;PD3073,868;
+****    0
+0001    501785
+SYMB   10SY03390NIL
+SYMD   39FRYARE52V016310086302560002980051300728
+SXPO   17cable ferry area
+SCRF    6ACHBLK
+SVCT   29SPA;SW1;PU823,863;PD513,863;
+SVCT   31SPA;SW1;PU2279,863;PD2571,863;
+SVCT   88SPA;SW1;PU1883,728;PD1086,728;PD1086,1026;PD1882,1022;PD2083,923;PD2084,823;PD1883,728;
+SVCT   31SPA;SW1;PU2778,868;PD3073,868;
+****    0
+0001    501786
+SYMB   10SY01342NIL
+SYMD   39FSHFAC02V007720063100511004380049200416
+SXPO   32fish trap, fish weir, tunny net
+SCRF    6ACHGRD
+SVCT   61SPA;SW1;PU492,854;PD1003,854;PD1003,632;PD494,632;PD494,853;
+SVCT   29SPA;SW1;PU558,416;PD776,634;
+****    0
+0001    501787
+SYMB   10SY02067NIL
+SYMD   39FSHFAC03V034370225400604001510313502173
+SXPO   12fish stakes
+SCRF    6ACHGRD
+SVCT   57SPA;SW1;PU3135,2173;PD3135,2323;PD3739,2323;PD3739,2180;
+SVCT   33SPA;SW1;PU3290,2176;PD3290,2324;
+SVCT   33SPA;SW1;PU3438,2179;PD3438,2321;
+SVCT   33SPA;SW1;PU3590,2179;PD3590,2321;
+****    0
+0001    501788
+SYMB   10SY03193NIL
+SYMD   39FSHGRD01V012020154701104004490052701303
+SXPO   15fishing ground
+SCRF    6ACHGRD
+SVCT   31SPA;SW2;PU699,1532;PD546,1344;
+SVCT   31SPA;SW2;PU701,1535;PD527,1708;
+SVCT  126SPA;SW2;PU703,1532;PD780,1449;PD877,1379;PD1002,1323;PD1141,1303;PD1267,1309;PD1406,1337;PD1525,1393;PD1608,1455;PD1629,1498;
+SVCT  114SPA;SW2;PU697,1530;PD816,1628;PD962,1711;PD1115,1745;PD1226,1752;PD1345,1732;PD1456,1691;PD1546,1607;PD1616,1509;
+SVCT   57SPA;SW2;PU1333,1322;PD1287,1442;PD1287,1609;PD1370,1729;
+SVCT   33SPA;SW2;PU1631,1497;PD1614,1511;
+****    0
+0001    501789
+SYMB   10SY01343NIL
+SYMD   39FSHHAV01V008750075000984006160025000440
+SXPO   11fish haven
+SCRF    6DCHGRD
+SVCT  122SPD;SW2;PU384,868;PD525,750;PD584,684;PD653,640;PD725,612;PD793,593;PD868,593;PD931,600;PD1006,625;PD1075,656;PD1137,703;
+SVCT  115PD1175,750;PD1128,793;PD1084,831;PD1028,859;PD956,887;PD884,900;PD803,900;PD725,887;PD650,859;PD584,815;PD540,778;
+SVCT  114PU500,731;PD400,603;PU1018,628;PD1000,662;PD987,709;PD984,740;PD984,765;PD993,809;PD1012,865;PU540,778;PD500,731;
+SVCT  123PU750,440;PD;PU750,1056;PD;PU1234,750;PD;PU250,750;PD;PU465,500;PD;PU1015,500;PD;PU1025,1000;PD;PU309,900;PD;PU309,590;PD;
+SVCT  121PU1175,590;PD;PU1175,900;PD;PU390,534;PD;PU265,650;PD;PU275,850;PD;PU1215,850;PD;PU1209,650;PD;PU1106,550;PD;PU1115,950;
+SVCT  122PD;PU365,950;PD;PU565,465;PD;PU665,450;PD;PU815,450;PD;PU915,459;PD;PU915,1031;PD;PU815,1050;PD;PU665,1050;PD;PU565,1031;
+SVCT   18PD;PU465,1000;PD;
+****    0
+0001    501790
+SYMB   10SY03400NIL
+SYMD   39FSHRES51V008210142601021006020186900411
+SXPO   59area where fishing or trawling is prohibited or restricted
+SCRF    6ACHMGF
+SVCT  251SPA;SW2;PU1869,874;PD2051,722;PD2127,637;PD2216,580;PD2309,544;PD2397,519;PD2494,519;PD2575,528;PD2672,561;PD2761,601;PD2841,661;PD2890,722;PD2830,777;PD2773,827;PD2701,863;PD2608,899;PD2515,916;PD2410,916;PD2309,899;PD2212,863;PD2127,806;PD2070,758;
+SVCT   31SPA;SW2;PU2037,717;PD1908,551;
+SVCT   31SPA;SW3;PU2075,759;PD2035,712;
+SVCT   32SPA;SW3;PU2801,411;PD2199,1013;
+****    0
+0001    501791
+SYMB   10SY03402NIL
+SYMD   39FSHRES61V008210142601153006070186900411
+SXPO   80area where fishing or trawling is prohibited or restricted, with other cautions
+SCRF   12ACHMGFECHMGD
+SVCT  251SPA;SW2;PU1869,874;PD2051,722;PD2127,637;PD2216,580;PD2309,544;PD2397,519;PD2494,519;PD2575,528;PD2672,561;PD2761,601;PD2841,661;PD2890,722;PD2830,777;PD2773,827;PD2701,863;PD2608,899;PD2515,916;PD2410,916;PD2309,899;PD2212,863;PD2127,806;PD2070,758;
+SVCT   31SPA;SW2;PU2037,717;PD1908,551;
+SVCT   31SPA;SW3;PU2075,759;PD2035,712;
+SVCT   32SPA;SW3;PU2801,411;PD2199,1013;
+SVCT   31SPE;SW1;PU2992,700;PD2992,925;
+SVCT   33SPE;SW1;PU2967,1018;PD3022,1018;
+****    0
+0001    501792
+SYMB   10SY03404NIL
+SYMD   39FSHRES71V008210142601287006020160300411
+SXPO   83area where fishing or trawling is prohibited or restricted, with other information
+SCRF   12ACHMGFECHMGD
+SVCT  251SPA;SW2;PU1869,874;PD2051,722;PD2127,637;PD2216,580;PD2309,544;PD2397,519;PD2494,519;PD2575,528;PD2672,561;PD2761,601;PD2841,661;PD2890,722;PD2830,777;PD2773,827;PD2701,863;PD2608,899;PD2515,916;PD2410,916;PD2309,899;PD2212,863;PD2127,806;PD2070,758;
+SVCT   31SPA;SW2;PU2037,717;PD1908,551;
+SVCT   31SPA;SW3;PU2075,759;PD2035,712;
+SVCT   32SPA;SW3;PU2801,411;PD2199,1013;
+SVCT   43SPE;SW1;PU1695,1007;PD1695,773;PD1603,772;
+SVCT   33SPE;SW1;PU1604,1008;PD1791,1008;
+SVCT   31SPE;SW1;PU1649,654;PD1652,694;
+****    0
+0001    501793
+SYMB   10SY01344NIL
+SYMD   39GATCON03V015000150000700007000115001150
+SXPO   20navigable lock gate
+SCRF    6JTRFCD
+SVCT  123SPJ;PU1500,1500;SW1;CI350;PU1181,1350;PD1812,1350;PU1181,1650;PD1818,1650;PU1250,1500;PD1500,1250;PU1250,1500;PD1500,1750;
+SVCT   37PU1650,1250;PD1400,1500;PD1650,1750;
+****    0
+0001    501794
+SYMB   10SY01345NIL
+SYMD   39GATCON04V015000150000700007000115001150
+SXPO   24non-navigable lock gate
+SCRF    6JTRFCD
+SVCT   99SPJ;PU1500,1500;SW1;CI350;PU1181,1350;PD1812,1350;PU1181,1650;PD1818,1650;PU1500,1250;PD1500,1750;
+****    0
+0001    501795
+SYMB   10SY03366NIL
+SYMD   39HILTOP01V005860107001007008360007800631
+SXPO   21hill or mountain top
+SCRF    6ALANDF
+SVCT   66SPA;SW1;ST0;PU849,993;PM0;PD873,1078;PD1085,969;PD849,993;PM2;FP;
+SVCT   69SPA;SW1;ST0;PU849,1122;PM0;PD795,1191;PD1016,1306;PD849,1122;PM2;FP;
+SVCT   68SPA;SW1;ST0;PU389,1202;PM0;PD334,1132;PD172,1322;PD389,1202;PM2;FP;
+SVCT   68SPA;SW1;ST0;PU558,1254;PM0;PD475,1224;PD425,1467;PD558,1254;PM2;FP;
+SVCT   68SPA;SW1;ST0;PU722,1203;PM0;PD640,1244;PD789,1445;PD722,1203;PM2;FP;
+SVCT   74SPA;SW1;ST0;PU726,890;PM0;PD796,946;PD912,726;PD729,891;PD726,890;PM2;FP;
+SVCT   65SPA;SW1;ST0;PU304,1071;PM0;PD328,983;PD78,959;PD304,1071;PM2;FP;
+SVCT   74SPA;SW1;ST0;PU548,887;PM0;PD639,871;PD550,631;PD546,888;PD548,887;PM2;FP;
+SVCT   74SPA;SW1;ST0;PU389,958;PM0;PD462,908;PD281,728;PD388,957;PD389,958;PM2;FP;
+****    0
+0001    501796
+SYMB   10SY03367NIL
+SYMD   39HILTOP11V005860107001007008360007800631
+SXPO   33conspicuous hill or mountain top
+SCRF    6ACHBLK
+SVCT   66SPA;SW1;ST0;PU849,993;PM0;PD873,1078;PD1085,969;PD849,993;PM2;FP;
+SVCT   69SPA;SW1;ST0;PU849,1122;PM0;PD795,1191;PD1016,1306;PD849,1122;PM2;FP;
+SVCT   68SPA;SW1;ST0;PU389,1202;PM0;PD334,1132;PD172,1322;PD389,1202;PM2;FP;
+SVCT   68SPA;SW1;ST0;PU558,1254;PM0;PD475,1224;PD425,1467;PD558,1254;PM2;FP;
+SVCT   68SPA;SW1;ST0;PU722,1203;PM0;PD640,1244;PD789,1445;PD722,1203;PM2;FP;
+SVCT   74SPA;SW1;ST0;PU726,890;PM0;PD796,946;PD912,726;PD729,891;PD726,890;PM2;FP;
+SVCT   65SPA;SW1;ST0;PU304,1071;PM0;PD328,983;PD78,959;PD304,1071;PM2;FP;
+SVCT   74SPA;SW1;ST0;PU548,887;PM0;PD639,871;PD550,631;PD546,888;PD548,887;PM2;FP;
+SVCT   74SPA;SW1;ST0;PU389,958;PM0;PD462,908;PD281,728;PD388,957;PD389,958;PM2;FP;
+****    0
+0001    501797
+SYMB   10SY02068NIL
+SYMD   39HRBFAC09V007500075000584005840045300459
+SXPO   16fishing harbour
+SCRF    6ACHMGD
+SVCT  353SPA;SW1;PU553,750;PD578,725;PD612,693;PD643,675;PD675,659;PD712,643;PD737,637;PD775,634;PD803,634;PD837,634;PD878,643;PD909,653;PD943,668;PD978,687;PD1003,709;PD1025,728;PD1037,750;PD1009,775;PD975,809;PD934,828;PD903,843;PD862,853;PD828,859;PD803,859;PD768,859;PD737,853;PD712,850;PD693,843;PD659,828;PD628,809;PD603,787;PD578,775;PD568,762;PD553,750;
+SVCT   99SPA;SW1;PU553,750;PD462,837;PD459,809;PD453,775;PD453,743;PD453,703;PD462,662;PD475,643;PD553,750;
+SVCT  130SPA;SW1;PU540,550;PD584,512;PD612,493;PD656,475;PD715,462;PD775,459;PD831,468;PD875,484;PD915,509;PD959,550;PD993,590;PD1003,603;
+SVCT  147SPA;SW1;PU534,946;PD581,990;PD631,1015;PD668,1031;PD706,1040;PD756,1043;PD787,1043;PD828,1034;PD871,1018;PD912,993;PD943,968;PD971,943;PD1006,900;
+****    0
+0001    501798
+SYMB   10SY01348NIL
+SYMD   39HULKES01V007500075000488002660049600609
+SXPO    5hulk
+SCRF   12XCSTLNECHBRN
+SVCT  121SPE;ST0;PU500,840;PM0;PD550,856,606,865,656,875,700,875,750,859,784,856,825,840,859,825,884,809,925,790,950,775,965,756;
+SVCT  115PD984,750,965,725,940,700,909,684,881,665,850,650,815,634,790,625,753,618,715,609,681,609,656,609,625,615,600,615;
+SVCT  116PD565,625,540,631,525,640,500,650,500,840,500,840;PM2;FP;SPX;SW2;PU500,840;PD500,650;PU496,653;PD559,621;PD643,609;
+SVCT  121PD718,609;PD800,625;PD878,665;PD953,709;PD984,746;PD906,803;PD831,840;PD771,862;PD690,875;PD659,875;PD609,868;PD500,843;
+****    0
+0001    501799
+SYMB   10SY03396NIL
+SYMD   39INFARE51V-13940242700804008050000202172
+SXPO   52area with minor restrictions or information notices
+SCRF    6ACHMGF
+SVCT   31SPA;SW2;PU231,2854;PD672,2854;
+SVCT   31SPA;SW2;PU348,2228;PD355,2323;
+SVCT   40SPA;SW2;PU2,2977;PD806,2977;PD803,2173;
+SVCT   38SPA;SW2;PU4,2972;PD4,2172;PD799,2172;
+SVCT   42SPA;SW2;PU446,2853;PD446,2440;PD267,2440;
+****    0
+0001    501800
+SYMB   10SY03369NIL
+SYMD   39INFORM01V039910300401601015980380801599
+SXPO   65this object has additional information available by cursor query
+SCRF    6ACHMGD
+SVCT   69SPA;SW2;PU4914,2094;PD5409,2094;PD5409,1599;PD4914,1599;PD4914,2094;
+SVCT   33SPA;SW2;PU5145,1666;PD5145,1708;
+SVCT   45SPA;SW2;PU5159,2003;PD5159,1769;PD5067,1768;
+SVCT   33SPA;SW2;PU5058,2004;PD5225,2004;
+SVCT   27SPA;SW2;PU3916,3089;CI108;
+SVCT   33SPA;SW2;PU4908,2092;PD3988,3011;
+****    0
+0001    501801
+SYMB   10SY01392NIL
+SYMD   39ISODGR01V006000070000700007000025000350
+SXPO   54isolated danger of depth less than the safety contour
+SCRF    6RISDNG
+SVCT  114SPR;ST0;PU600,350;PM0;PD850,450,950,700,850,950,600,1050,350,950,250,700,350,450,600,350,600,625,737,487,812,562;
+SVCT  112PD675,700,812,837,737,912,600,775,462,912,387,837,525,700,387,562,462,487,600,625,600,350;PM2;FP;SW1;PU250,700;
+SVCT   82PD350,450;PD600,350;PD850,450;PD950,700;PD850,950;PD600,1050;PD350,950;PD250,700;
+****    0
+0001    501802
+SYMB   10SY03395NIL
+SYMD   39ITZARE51V021890131001149009010169300910
+SXPO   24area of inshore traffic
+SCRF    6ACHMGF
+SVCT   32SPA;SW2;PU1834,1811;PD1834,910;
+SVCT   32SPA;SW2;PU2540,913;PD2540,1811;
+SVCT   31SPA;SW2;PU1693,911;PD1986,911;
+SVCT   33SPA;SW2;PU1703,1809;PD1986,1809;
+SVCT   31SPA;SW2;PU2233,911;PD2842,911;
+****    0
+0001    501803
+SYMB   10SY01395NIL
+SYMD   39LIGHTDEFV015000150000237007000137500800
+SXPO   12light flare
+SCRF    6ICHMGD
+SVCT  117SPI;ST2;PU1500,1500;PM0;PD1375,937,1375,912,1381,875,1400,843,1418,825,1450,800,1493,800,1531,800,1556,806,1593,831;
+SVCT  122PD1606,868,1612,900,1612,931,1500,1500;PM2;FP;SPI;SW1;PU1500,1500;PD1375,925;PD1375,887;PD1387,850;PD1406,831;PD1437,806;
+SVCT  101PD1475,800;PD1500,800;PD1531,800;PD1562,812;PD1587,837;PD1606,875;PD1612,900;PD1606,956;PD1500,1500;
+****    0
+0001    501804
+SYMB   10SY01396NIL
+SYMD   39LIGHTS01V007500075000240007000062800050
+SXPO   17light flare, red
+SCRF    6OLITRD
+SVCT  114SPO;ST2;PU750,750;PM0;PD631,190,631,165,634,131,650,96,671,75,703,56,746,50,787,53,812,62,846,87,862,121,868,153;
+SVCT  115PD865,184,750,750;PM2;FP;SW1;PU750,750;PD628,178;PD631,140;PD643,106;PD662,84;PD693,59;PD728,50;PD756,50;PD787,53;
+SVCT   59PD818,68;PD843,93;PD859,125;PD868,156;PD859,209;PD750,750;
+****    0
+0001    501805
+SYMB   10SY01397NIL
+SYMD   39LIGHTS02V022500222000285008100210001492
+SXPO   19light flare, green
+SCRF   12PLITGN^DEPMD
+SVCT  117SPP;ST2;PU2250,2220;PM0;PD2130,1657,2130,1635,2130,1597,2145,1560,2167,1545,2197,1522,2242,1515,2280,1522,2310,1530;
+SVCT  120PD2340,1552,2355,1590,2362,1620,2362,1650,2250,2220;PM2;FP;SP^;SW1;PU2385,1605;PD2377,1642;PD2250,2302;SPP;PU2250,2220;
+SVCT  121PD2122,1642;PD2130,1605;PD2137,1575;PD2160,1552;PD2190,1522;PD2227,1515;PD2250,1515;PD2280,1522;PD2317,1537;PD2340,1560;
+SVCT  113PD2355,1590;PD2362,1620;PD2355,1672;PD2250,2220;SP^;PU2250,2302;PD2100,1627;PD2122,1560;PD2182,1507;PD2250,1492;
+SVCT   37PD2310,1515;PD2347,1537;PD2377,1597;
+****    0
+0001    501806
+SYMB   10SY01398NIL
+SYMD   39LIGHTS03V007500075000246007000062500050
+SXPO   29light flare, white or yellow
+SCRF    6QLITYW
+SVCT  114SPQ;ST2;PU750,750;PM0;PD628,187,628,162,631,128,650,93,668,75,700,53,743,50,784,50,809,59,843,84,871,131,871,178;
+SVCT  115PD871,178,750,750;PM2;FP;SW1;PU750,750;PD625,175;PD628,137;PD640,103;PD659,81;PD690,56;PD725,50;PD753,50;PD784,50;
+SVCT   59PD815,65;PD840,90;PD862,115;PD865,153;PD856,206;PD750,750;
+****    0
+0001    501807
+SYMB   10SY02079NIL
+SYMD   39LIGHTS81V006420074500374002510045000624
+SXPO   12strip light
+SCRF    6ACHMGD
+SVCT   69SPA;SW1;PU450,873;PD524,624;PD598,874;PD674,624;PD749,875;PD824,624;
+****    0
+0001    501808
+SYMB   10SY01399NIL
+SYMD   39LIGHTS82V007680212300584003020003001742
+SXPO   11floodlight
+SCRF    6ACHMGD
+SVCT   26SPA;SW1;PU146,1928;CI116;
+SVCT   31SPA;SW1;PU301,2031;PD514,2039;
+SVCT   31SPA;SW1;PU270,1817;PD555,1742;
+SVCT   31SPA;SW1;PU319,1923;PD614,1893;
+****    0
+0001    501809
+SYMB   10SY01400NIL
+SYMD   39LITFLT01V007500075000900003220030000500
+SXPO   25light float, paper-chart
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW2;CI72;PU300,750;PD650,750;SW2;PU450,750;PD350,550;PU1050,750;PD1150,550;SW2;PU600,600;PD650,500;
+SVCT   83PD850,500;PD900,600;PU350,550;PD550,600;PD950,600;PD1150,550;PU850,750;PD1200,750;
+****    0
+0001    501810
+SYMB   10SY02245NIL
+SYMD   39LITFLT02V007410060100613002240043400489
+SXPO   24light float, simplified
+SCRF    6ACHBLK
+SVCT   65SPA;SW1;ST0;PU435,539;PM0;PD529,713;PD963,713;PD1047,538;PM2;FP;
+SVCT   75SPA;SW1;ST0;PU434,538;PM0;PD529,713;PD963,713;PD1044,538;PD434,538;PM2;FP;
+SVCT   49SPA;SW1;PU637,537;PD637,489;PD847,489;PD847,538;
+****    0
+0001    501811
+SYMB   10SY01402NIL
+SYMD   39LITVES01V007500075000900006700030000150
+SXPO   26light vessel, paper-chart
+SCRF    6CCHBLK
+SVCT  120SPC;PU750,750;SW2;CI70;PU300,750;PD650,750;SW2;PU450,750;PD350,500;PU1050,750;PD1150,500;PU750,575;PD750,300;PU550,575;
+SVCT  115PD550,395;PU950,575;PD950,400;SW2;PU750,300;PD750,150;PU650,200;PD850,300;PU650,300;PD850,200;PU410,650;PD575,650;
+SVCT   94PU925,650;PD1090,650;PU350,500;PD547,575;PD750,575;PD950,575;PD1150,500;PU850,750;PD1200,750;
+****    0
+0001    501812
+SYMB   10SY02080NIL
+SYMD   39LITVES02V007470056300745003640037500381
+SXPO   25light vessel, simplified
+SCRF    6ACHBLK
+SVCT   75SPA;SW1;ST0;PU497,745;PM0;PD375,541;PD1120,541;PD987,745;PD497,745;PM2;FP;
+SVCT   29SPA;SW1;PU747,540;PD747,381;
+****    0
+0001    501813
+SYMB   10SY01403NIL
+SYMD   39LNDARE01V022500225000206002060214702147
+SXPO   31land as a point at small scale
+SCRF   12VLANDAXCSTLN
+SVCT   63SPV;PU2250,2250;ST0;PM0;CI93;PM2;FP;SPX;PU2250,2250;SW2;CI103;
+****    0
+0001    501814
+SYMB   10SY03197NIL
+SYMD   39LOCMAG01V014140067000181009330130000201
+SXPO   51cursor pick site for a magnetic anomaly at a point
+SCRF    6ACHMGD
+SVCT   32SPA;SW1;PU1481,869;PD1481,1134;
+SVCT  130SPA;SW1;PU1481,897;PD1481,201;PD1300,727;PD1335,738;PD1363,753;PD1386,772;PD1404,792;PD1423,811;PD1445,841;PD1462,867;PD1479,893;
+****    0
+0001    501815
+SYMB   10SY03198NIL
+SYMD   39LOCMAG51V01430-214600374018590116000000
+SXPO   69cursor pick site for a magnetic anomaly along a line or over an area
+SCRF    6ACHMGF
+SVCT  138SPA;SW2;PU1526,1403;PD1526,0;PD1160,1060;PD1231,1082;PD1288,1112;PD1334,1150;PD1370,1191;PD1408,1229;PD1452,1289;PD1487,1341;PD1522,1395;
+SVCT   33SPA;SW2;PU1534,1395;PD1534,1859;
+****    0
+0001    501816
+SYMB   10SY03398NIL
+SYMD   39LOWACC01V027920262101129012350156201291
+SXPO   38point feature or area of low accuracy
+SCRF    6ACHBLK
+SVCT   33SPA;SW1;PU1654,1670;PD1707,1670;
+SVCT  273SPA;SW1;PU1673,1579;PD1672,1520;PD1673,1501;PD1690,1481;PD1703,1463;PD1729,1437;PD1740,1412;PD1746,1383;PD1739,1352;PD1729,1332;PD1708,1313;PD1685,1297;PD1665,1294;PD1642,1291;PD1619,1297;PD1598,1307;PD1578,1327;PD1568,1347;PD1562,1366;PD1562,1384;PD1567,1401;PD1572,1406;
+SVCT   33SPA;SW1;PU1747,1582;PD2691,2526;
+****    0
+0001    501817
+SYMB   10SY03199NIL
+SYMD   39MAGVAR01V014080064800181009150130000201
+SXPO   51cursor pick site for magnetic variation at a point
+SCRF    6ACHMGD
+SVCT  156SPA;SW1;ST0;PU1481,897;PM0;PD1481,201;PD1300,727;PD1335,738;PD1363,753;PD1386,772;PD1404,792;PD1423,811;PD1445,841;PD1462,867;PD1479,893;PD1481,897;PM2;FP;
+SVCT   32SPA;SW1;PU1475,202;PD1475,1116;
+****    0
+0001    501818
+SYMB   10SY03196NIL
+SYMD   39MAGVAR51V014130367400366018540116000000
+SXPO   69cursor pick site for magnetic variation along a line or over an area
+SCRF    6ACHMGF
+SVCT  165SPA;SW2;ST0;PU1526,1403;PM0;PD1526,0;PD1160,1060;PD1231,1082;PD1288,1112;PD1334,1150;PD1370,1191;PD1408,1229;PD1452,1289;PD1487,1341;PD1522,1395;PD1526,1403;PM2;FP;
+SVCT   33SPA;SW2;PU1521,1390;PD1521,1854;
+****    0
+0001    501819
+SYMB   10SY02081NIL
+SYMD   39MARCUL02V007690077000705004030041600568
+SXPO   10fish farm
+SCRF    6ACHGRD
+SVCT  202SPA;SW1;PU505,847;PD641,725;PD714,684;PD771,668;PD822,662;PD882,673;PD944,693;PD984,717;PD1018,744;PD1039,774;PD1004,803;PD968,831;PD914,855;PD871,869;PD819,871;PD763,869;PD709,857;PD670,835;PD505,698;
+SVCT   51SPA;SW1;PU416,669;PD416,570;PD1117,570;PD1117,666;
+SVCT   51SPA;SW1;PU416,871;PD416,971;PD1120,971;PD1121,871;
+SVCT   29SPA;SW1;PU564,871;PD564,971;
+SVCT   29SPA;SW1;PU965,871;PD965,971;
+SVCT   29SPA;SW1;PU765,870;PD765,968;
+SVCT   29SPA;SW1;PU564,571;PD564,672;
+SVCT   29SPA;SW1;PU965,569;PD965,670;
+SVCT   29SPA;SW1;PU764,568;PD764,668;
+****    0
+0001    501820
+SYMB   10SY02082NIL
+SYMD   39MONUMT02V007470074900400004060055000394
+SXPO    9monument
+SCRF    6ALANDF
+SVCT   24SPA;SW2;PU750,750;CI50;
+SVCT   29SPA;SW2;PU550,750;PD687,750;
+SVCT   29SPA;SW2;PU800,750;PD950,750;
+SVCT   29SPA;SW1;PU809,401;PD653,564;
+SVCT   29SPA;SW1;PU825,506;PD650,700;
+SVCT   29SPA;SW1;PU837,631;PD775,700;
+SVCT   49SPA;SW1;PU628,749;PD673,394;PD811,397;PD850,747;
+****    0
+0001    501821
+SYMB   10SY02083NIL
+SYMD   39MONUMT12V007470074900400004060055000394
+SXPO   21conspicuous monument
+SCRF    6ACHBLK
+SVCT   24SPA;SW2;PU750,750;CI50;
+SVCT   29SPA;SW2;PU550,750;PD687,750;
+SVCT   29SPA;SW2;PU800,750;PD950,750;
+SVCT   29SPA;SW1;PU809,401;PD653,564;
+SVCT   29SPA;SW1;PU825,506;PD650,700;
+SVCT   29SPA;SW1;PU837,631;PD775,700;
+SVCT   49SPA;SW1;PU628,749;PD673,394;PD811,397;PD850,747;
+****    0
+0001    501822
+SYMB   10SY02084NIL
+SYMD   39MORFAC03V007470074600253002530060300617
+SXPO   16mooring dolphin
+SCRF   12ALANDABCHBLK
+SVCT   74SPA;SW2;ST0;PU603,617;PM0;PD856,617;PD856,870;PD605,870;PD603,617;PM2;FP;
+SVCT   59SPB;SW2;PU603,617;PD856,617;PD856,870;PD605,870;PD603,617;
+****    0
+0001    501823
+SYMB   10SY02085NIL
+SYMD   39MORFAC04V007500075000350005030057000247
+SXPO   26deviation mooring dolphin
+SCRF    6ACHBLK
+SVCT   29SPA;SW1;PU570,750;PD920,750;
+SVCT   49SPA;SW1;PU610,750;PD655,390;PD835,390;PD875,750;
+SVCT   29SPA;SW1;PU747,749;PD747,247;
+****    0
+0001    501824
+SYMB   10SY01412NIL
+SYMD   39MSTCON04V007500075000300006500060000150
+SXPO    5mast
+SCRF    6WLANDF
+SVCT   94SPW;PU750,750;SW2;CI50;PU600,750;PD700,750;PU800,750;PD900,750;PU700,750;PD750,150;PD800,750;
+****    0
+0001    501825
+SYMB   10SY01414NIL
+SYMD   39MSTCON14V007500075000300006500060000150
+SXPO   17conspicuous mast
+SCRF    6CCHBLK
+SVCT   94SPC;PU750,750;SW2;CI50;PU600,750;PD700,750;PU800,750;PD900,750;PU700,750;PD750,150;PD800,750;
+****    0
+0001    501826
+SYMB   10SY02086NIL
+SYMD   39NORTHAR1V015000082500355015000130700000
+SXPO   12north arrow
+SCRF    6ASCLBR
+SVCT   30SPA;SW2;PU1500,1500;PD1500,0;
+SVCT   53SPA;SW2;PU1325,987;PD1325,656;PD1662,987;PD1662,662;
+SVCT   66SPA;SW1;ST0;PU1307,395;PM0;PD1662,395;PD1495,3;PD1307,395;PM2;FP;
+****    0
+0001    501827
+SYMB   10SY02087NIL
+SYMD   39NOTBRD11V007500075000300004000060000350
+SXPO   25conspicuous notice board
+SCRF    6CCHBLK
+SVCT   99SPC;SW1;PU650,750;PD850,750;PU750,750;PD750,550;PU600,550;PD900,550;PD900,350;PD600,350;PD600,550;
+****    0
+0001    501828
+SYMB   10SY02248NIL
+SYMD   39OBSTRN01V015000150000410004100129501295
+SXPO   30obstruction, depth not stated
+SCRF   12ADEPVSBCHBLK
+SVCT   42SPA;SW1;ST0;PU1500,1500;PM0;CI205;PM2;FP;
+SVCT   24SPB;SW1;PU1500,1300;PD;
+SVCT   24SPB;SW1;PU1500,1700;PD;
+SVCT   24SPB;SW1;PU1700,1500;PD;
+SVCT   24SPB;SW1;PU1300,1500;PD;
+SVCT   24SPB;SW1;PU1580,1310;PD;
+SVCT   24SPB;SW1;PU1640,1360;PD;
+SVCT   24SPB;SW1;PU1685,1425;PD;
+SVCT   24SPB;SW1;PU1675,1585;PD;
+SVCT   24SPB;SW1;PU1640,1640;PD;
+SVCT   24SPB;SW1;PU1575,1680;PD;
+SVCT   24SPB;SW1;PU1420,1680;PD;
+SVCT   24SPB;SW1;PU1360,1635;PD;
+SVCT   24SPB;SW1;PU1315,1570;PD;
+SVCT   24SPB;SW1;PU1320,1420;PD;
+SVCT   24SPB;SW1;PU1355,1355;PD;
+SVCT   24SPB;SW1;PU1420,1315;PD;
+****    0
+0001    501829
+SYMB   10SY03130NIL
+SYMD   39OBSTRN02V015000150000400004000130001300
+SXPO   35obstruction in the intertidal area
+SCRF    6ACHBLK
+SVCT   24SPA;SW1;PU1500,1300;PD;
+SVCT   24SPA;SW1;PU1500,1700;PD;
+SVCT   24SPA;SW1;PU1700,1500;PD;
+SVCT   24SPA;SW1;PU1300,1500;PD;
+SVCT   24SPA;SW1;PU1580,1310;PD;
+SVCT   24SPA;SW1;PU1640,1360;PD;
+SVCT   24SPA;SW1;PU1685,1425;PD;
+SVCT   24SPA;SW1;PU1675,1585;PD;
+SVCT   24SPA;SW1;PU1640,1640;PD;
+SVCT   24SPA;SW1;PU1575,1680;PD;
+SVCT   24SPA;SW1;PU1420,1680;PD;
+SVCT   24SPA;SW1;PU1360,1635;PD;
+SVCT   24SPA;SW1;PU1315,1570;PD;
+SVCT   24SPA;SW1;PU1320,1420;PD;
+SVCT   24SPA;SW1;PU1355,1355;PD;
+SVCT   24SPA;SW1;PU1420,1315;PD;
+****    0
+0001    501830
+SYMB   10SY03611NIL
+SYMD   39OBSTRN03V015000150000410004100129501295
+SXPO   38obstruction which covers and uncovers
+SCRF   12ADEPITBCHBLK
+SVCT   42SPA;SW1;ST0;PU1500,1500;PM0;CI205;PM2;FP;
+SVCT   24SPB;SW1;PU1500,1300;PD;
+SVCT   24SPB;SW1;PU1500,1700;PD;
+SVCT   24SPB;SW1;PU1700,1500;PD;
+SVCT   24SPB;SW1;PU1300,1500;PD;
+SVCT   24SPB;SW1;PU1580,1310;PD;
+SVCT   24SPB;SW1;PU1640,1360;PD;
+SVCT   24SPB;SW1;PU1685,1425;PD;
+SVCT   24SPB;SW1;PU1675,1585;PD;
+SVCT   24SPB;SW1;PU1640,1640;PD;
+SVCT   24SPB;SW1;PU1575,1680;PD;
+SVCT   24SPB;SW1;PU1420,1680;PD;
+SVCT   24SPB;SW1;PU1360,1635;PD;
+SVCT   24SPB;SW1;PU1315,1570;PD;
+SVCT   24SPB;SW1;PU1320,1420;PD;
+SVCT   24SPB;SW1;PU1355,1355;PD;
+SVCT   24SPB;SW1;PU1420,1315;PD;
+****    0
+0001    501831
+SYMB   10SY03131NIL
+SYMD   39OBSTRN11V006980077300200002000059800672
+SXPO   59obstruction in the water which is always above water level
+SCRF   12BCSTLNALANDA
+SVCT   74SPA;SW1;ST0;PU598,672;PM0;PD598,872;PD798,872;PD797,672;PD598,672;PM2;FP;
+SVCT   49SPB;SW2;PD598,872;PD798,872;PD797,672;PD598,672;
+****    0
+0001    501832
+SYMB   10SY01418NIL
+SYMD   39OFSPLF01V007500075000400004000055000550
+SXPO   18offshore platform
+SCRF    6CCHBLK
+SVCT   89SPC;PU750,750;ST0;PM0;CI50;PM2;FP;SW2;PU550,550;PD950,550;PD950,950;PD550,950;PD550,550;
+****    0
+0001    501833
+SYMB   10SY03600NIL
+SYMD   39OSPONE02V050330216700394000000482802167
+SXPO   35one minute mark for ownship vector
+SCRF    6ASHIPS
+SVCT   33SPA;SW2;PU4828,2167;PD5222,2167;
+****    0
+0001    501834
+SYMB   10SY03601NIL
+SYMD   39OSPSIX02V053840214500397000000518902146
+SXPO   35six minute mark for ownship vector
+SCRF    6ASHIPS
+SVCT   33SPA;SW5;PU5189,2146;PD5586,2146;
+****    0
+0001    501835
+SYMB   10SY03191NIL
+SYMD   39OWNSHP01V014900149700992009920098501009
+SXPO   31own ship symbol, constant size
+SCRF    6ASHIPS
+SVCT   27SPA;SW2;PU1481,1505;CI496;
+SVCT   27SPA;SW2;PU1481,1501;CI261;
+****    0
+0001    501836
+SYMB   10SY02251NIL
+SYMD   39OWNSHP05V006740115400401016880047400316
+SXPO   53own ship drawn to scale with conning position marked
+SCRF    6ASHIPS
+SVCT   81SPA;SW2;PU773,316;PD873,570;PD875,2004;PD475,2004;PD474,573;PD570,321;PD773,316;
+****    0
+0001    501837
+SYMB   10SY01422NIL
+SYMD   39PASTRK01V001980090400402000000000000905
+SXPO   24time mark on past track
+SCRF    6APSTRK
+SVCT   27SPA;SW2;PU0,905;PD402,905;
+****    0
+0001    501838
+SYMB   10SY01422NIL
+SYMD   39PASTRK02V001980090400402000000000000905
+SXPO   34time mark on secondary past track
+SCRF    6ASYTRK
+SVCT   27SPA;SW2;PU0,905;PD402,905;
+****    0
+0001    501839
+SYMB   10SY01423NIL
+SYMD   39PILBOP02V006000083700524005320034100568
+SXPO   21pilot boarding place
+SCRF    6JCHMGD
+SVCT   85SPJ;ST0;PU603,568;PM0;PD500,837,603,1100,712,837,603,568;PM2;FP;PU603,837;SW1;CI262;
+****    0
+0001    501840
+SYMB   10SY01424NIL
+SYMD   39PILPNT02V007500075000200002000065000650
+SXPO   16pile or bollard
+SCRF    6CCHBLK
+SVCT   36SPC;PU750,750;ST0;PM0;CI100;PM2;FP;
+****    0
+0001    501841
+SYMB   10SY01425NIL
+SYMD   39PLNPOS01V024370234302382009190249302343
+SXPO   66surrounding ellipse for arrival date and time at planned position
+SCRF    6kPLRTE
+SVCT  117SPk;SW1;PU2493,2793;PD2521,2671;PD2681,2521;PD2943,2418;PD3225,2371;PD3656,2343;PD4096,2371;PD4396,2418;PD4650,2521;
+SVCT  121PD4818,2671;PD4875,2793;PD4856,2962;PD4650,3093;PD4396,3187;PD4096,3262;PD3656,3262;PD3225,3262;PD2943,3196;PD2643,3093;
+SVCT   25PD2503,2971;PD2493,2793;
+****    0
+0001    501842
+SYMB   10SY01426NIL
+SYMD   39PLNPOS02V001980090400402000000000000905
+SXPO   31crossline for planned position
+SCRF    6APLRTE
+SVCT   27SPA;SW2;PU0,905;PD402,905;
+****    0
+0001    501843
+SYMB   10SY02090NIL
+SYMD   39PLNSPD03V009000075000840005710107400864
+SXPO   42box for speed to make good, planned route
+SCRF    6APLRTE
+SVCT   67SPA;SW1;PU1074,1431;PD1076,864;PD1914,864;PD1914,1435;PD1074,1431;
+****    0
+0001    501844
+SYMB   10SY02091NIL
+SYMD   39PLNSPD04V009000075000840005710107400864
+SXPO   44box for speed to make good, alternate route
+SCRF    6AAPLRT
+SVCT   67SPA;SW1;PU1074,1431;PD1076,864;PD1914,864;PD1914,1435;PD1074,1431;
+****    0
+0001    501845
+SYMB   10SY01437NIL
+SYMD   39POSGEN01V007500075000400004000055000550
+SXPO   28position of a point feature
+SCRF    6WLANDF
+SVCT   55SPW;PU750,750;SW2;CI200;PU750,750;ST0;PM0;CI50;PM2;FP;
+****    0
+0001    501846
+SYMB   10SY03004NIL
+SYMD   39POSGEN03V007500075000400004000055000550
+SXPO   40position of a conspicuous point feature
+SCRF    6WCHBLK
+SVCT   55SPW;PU750,750;SW2;CI200;PU750,750;ST0;PM0;CI50;PM2;FP;
+****    0
+0001    501847
+SYMB   10SY02092NIL
+SYMD   39POSGEN04V007500075000200002000065000650
+SXPO   42position of an elevation or control point
+SCRF    6WCHBLK
+SVCT   25SPW;PU750,750;SW1;CI100;
+****    0
+0001    501848
+SYMB   10SY01439NIL
+SYMD   39POSITN02V022500225000590005900195001950
+SXPO   21ownship position fix
+SCRF    6fNINFO
+SVCT   75SPf;PU2250,2250;SW1;CI290;PU2250,1950;PD2250,2540;PU1950,2250;PD2540,2250;
+****    0
+0001    501849
+SYMB   10SY03189NIL
+SYMD   39PRCARE12V007340207700366005030055701784
+SXPO   43point symbol for traffic precautionary area
+SCRF    6ATRFCD
+SVCT   53SPA;SW1;PU557,2287;PD923,2287;PD732,1784;PD560,2287;
+SVCT   31SPA;SW1;PU710,2216;PD765,2216;
+SVCT   31SPA;SW1;PU734,2160;PD734,1962;
+****    0
+0001    501850
+SYMB   10SY03397NIL
+SYMD   39PRCARE51V007310037401042013010021901258
+SXPO   27traffic precautionary area
+SCRF    6ATRFCF
+SVCT   54SPA;SW2;PU219,2559;PD741,1258;PD1261,2559;PD219,2559;
+SVCT   31SPA;SW4;PU738,2219;PD738,1754;
+SVCT   31SPA;SW2;PU671,2399;PD814,2399;
+****    0
+0001    501851
+SYMB   10SY01441NIL
+SYMD   39PRDINS02V007500075000505004200049500475
+SXPO   13mine, quarry
+SCRF   12WLANDFKCHBRN
+SVCT  114SPK;ST0;PU665,555;PM0;PD520,700,495,675,495,565,585,475,665,555;PM2;FP;PU830,560;PM0;PD910,475,1000,565,1000,670;
+SVCT  118PD970,700,830,560;PM2;FP;SPW;SW2;PU615,615;PD895,895;PU885,615;PD605,895;SW1;PU585,475;PD665,555;PD520,700;PD495,675;
+SVCT   83PD495,565;PD585,475;PU910,475;PD1000,565;PD1000,670;PD970,700;PD830,560;PD910,475;
+****    0
+0001    501852
+SYMB   10SY01442NIL
+SYMD   39PRICKE03V007500075000293004350062500315
+SXPO   30withy, port-hand, paper-chart
+SCRF    6CCHBLK
+SVCT  119SPC;SW1;PU650,750;PD859,750;PU750,750;PD750,550;PU625,528;PD687,600;PD750,618;PD825,612;PD890,581;PD918,550;PU678,393;
+SVCT   81PD725,431;PD756,437;PD809,434;PD859,390;PU790,315;PD778,350;PD756,437;PD750,550;
+****    0
+0001    501853
+SYMB   10SY01443NIL
+SYMD   39PRICKE04V007500075000253004440060900306
+SXPO   35withy, starboard-hand, paper-chart
+SCRF    6CCHBLK
+SVCT  119SPC;SW1;PU650,750;PD859,750;PU750,750;PD750,550;PU609,628;PD675,559;PD750,540;PD809,550;PD862,606;PU668,437;PD728,387;
+SVCT   71PD768,384;PD818,406;PD856,459;PU800,306;PD768,381;PD753,450;PD750,550;
+****    0
+0001    501854
+SYMB   10SY03370NIL
+SYMD   39QUAPOS01V001320062900351003070000000473
+SXPO   21position approximate
+SCRF    6ACHBLK
+SVCT   25SPA;SW1;PU1,479;PD1,780;
+SVCT   27SPA;SW1;PU0,478;PD100,478;
+SVCT   27SPA;SW1;PU1,630;PD100,630;
+SVCT   29SPA;SW1;PU133,502;PD133,601;
+SVCT   29SPA;SW1;PU100,477;PD134,502;
+SVCT   29SPA;SW1;PU100,628;PD134,601;
+SVCT   39SPA;SW1;PU181,774;PD263,473;PD351,776;
+SVCT   29SPA;SW1;PU215,646;PD313,646;
+****    0
+0001    501855
+SYMB   10SY03165NIL
+SYMD   39QUARRY01V007500075000672006720039800342
+SXPO    7quarry
+SCRF    6ALANDF
+SVCT   84SPA;SW1;ST0;PU665,555;PM0;PD520,700;PD495,675;PD495,565;PD585,475;PD665,555;PM2;FP;
+SVCT   86SPA;SW1;ST0;PU830,560;PM0;PD910,475;PD1000,565;PD1000,670;PD970,700;PD830,560;PM2;FP;
+SVCT   29SPA;SW2;PU615,615;PD895,895;
+SVCT   29SPA;SW2;PU885,615;PD605,895;
+SVCT   69SPA;SW1;PU585,475;PD665,555;PD520,700;PD495,675;PD495,565;PD585,475;
+SVCT   71SPA;SW1;PU910,475;PD1000,565;PD1000,670;PD970,700;PD830,560;PD910,475;
+SVCT   25SPA;SW1;PU734,678;CI336;
+****    0
+0001    501856
+SYMB   10SY02094NIL
+SYMD   39QUESMRK1V016780144900220004430156801227
+SXPO  113object which is not sufficiently described to be symbolized, or for which no symbol exists in the symbol library
+SCRF    6ACHMGD
+SVCT  309SPA;SW1;PU1568,1323;PD1581,1295;PD1594,1270;PD1611,1253;PD1628,1236;PD1654,1227;PD1677,1229;PD1707,1238;PD1735,1244;PD1758,1278;PD1776,1297;PD1788,1319;PD1788,1344;PD1776,1368;PD1763,1389;PD1743,1413;PD1720,1438;PD1699,1464;PD1686,1483;PD1675,1500;PD1673,1522;PD1673,1545;PD1673,1562;PD1675,1584;PD1675,1586;
+SVCT   33SPA;SW2;PU1654,1670;PD1720,1670;
+****    0
+0001    501857
+SYMB   10SY03132NIL
+SYMD   39RACNSP01V011080104900606006100080400744
+SXPO   51symbol indicating this object is radar conspicuous
+SCRF    6ACHMGD
+SVCT   27SPA;SW1;PU1110,1051;CI149;
+SVCT   31SPA;SW1;PU1111,902;PD1111,744;
+SVCT   33SPA;SW1;PU1110,1201;PD1110,1354;
+SVCT   33SPA;SW1;PU1260,1048;PD1410,1048;
+SVCT   31SPA;SW1;PU960,1049;PD804,1049;
+SVCT   33SPA;SW1;PU1214,1155;PD1324,1265;
+SVCT   31SPA;SW1;PU1322,840;PD1213,950;
+SVCT   32SPA;SW1;PU1002,1156;PD892,1266;
+SVCT   30SPA;SW1;PU896,839;PD1005,949;
+****    0
+0001    501858
+SYMB   10SY03133NIL
+SYMD   39RADRFL03V011080104900606006100080400744
+SXPO   16radar reflector
+SCRF    6ACHMGD
+SVCT   27SPA;SW1;PU1110,1051;CI149;
+SVCT   31SPA;SW1;PU1111,902;PD1111,744;
+SVCT   33SPA;SW1;PU1110,1201;PD1110,1354;
+SVCT   33SPA;SW1;PU1260,1048;PD1410,1048;
+SVCT   31SPA;SW1;PU960,1049;PD804,1049;
+SVCT   33SPA;SW1;PU1214,1155;PD1324,1265;
+SVCT   31SPA;SW1;PU1322,840;PD1213,950;
+SVCT   32SPA;SW1;PU1002,1156;PD892,1266;
+SVCT   30SPA;SW1;PU896,839;PD1005,949;
+****    0
+0001    501859
+SYMB   10SY03116NIL
+SYMD   39RASCAN01V007480075000398005440054600256
+SXPO   14radar scanner
+SCRF    6ALANDF
+SVCT   24SPA;SW1;PU750,750;CI50;
+SVCT   29SPA;SW1;PU635,747;PD635,395;
+SVCT   39SPA;SW1;PU851,745;PD851,395;PD678,395;
+SVCT   29SPA;SW1;PU703,748;PD568,748;
+SVCT   29SPA;SW1;PU800,746;PD936,746;
+SVCT   29SPA;SW1;PU681,395;PD635,395;
+SVCT   29SPA;SW1;PU742,391;PD742,257;
+SVCT   29SPA;SW2;PU546,256;PD944,256;
+****    0
+0001    501860
+SYMB   10SY03124NIL
+SYMD   39RASCAN11V007530074800398005440054600256
+SXPO   26conspicuous radar scanner
+SCRF    6ACHBLK
+SVCT   24SPA;SW1;PU750,750;CI50;
+SVCT   29SPA;SW1;PU635,747;PD635,395;
+SVCT   39SPA;SW1;PU851,745;PD851,395;PD678,395;
+SVCT   29SPA;SW1;PU703,748;PD568,748;
+SVCT   29SPA;SW1;PU800,746;PD936,746;
+SVCT   29SPA;SW1;PU681,395;PD635,395;
+SVCT   29SPA;SW1;PU742,391;PD742,257;
+SVCT   29SPA;SW2;PU546,256;PD944,256;
+****    0
+0001    501861
+SYMB   10SY03430NIL
+SYMD   39RCLDEF01V007510074801209010370014400231
+SXPO   52radio calling-in point whose direction is not known
+SCRF    6ACHMGD
+SVCT  259SPA;SW1;PU144,651;PD157,623;PD170,598;PD187,581;PD204,564;PD230,555;PD253,557;PD283,566;PD311,572;PD334,606;PD352,625;PD364,647;PD364,672;PD352,696;PD339,717;PD319,741;PD296,766;PD275,792;PD262,811;PD251,828;PD249,850;PD249,873;PD249,890;PD251,912;PD251,914;
+SVCT   29SPA;SW2;PU230,998;PD283,998;
+SVCT  284SPA;SW1;PU1133,651;PD1146,623;PD1159,598;PD1176,581;PD1193,564;PD1219,555;PD1242,557;PD1272,566;PD1300,572;PD1323,606;PD1341,625;PD1353,647;PD1353,672;PD1341,696;PD1328,717;PD1308,741;PD1285,766;PD1264,792;PD1251,811;PD1240,828;PD1238,850;PD1238,873;PD1238,890;PD1240,912;PD1240,914;
+SVCT   31SPA;SW2;PU1219,998;PD1272,998;
+SVCT   25SPA;SW1;PU750,750;CI218;
+SVCT   39SPA;SW1;PU580,589;PD752,231;PD923,589;
+SVCT   40SPA;SW1;PU920,914;PD742,1268;PD580,911;
+****    0
+0001    501862
+SYMB   10SY02098NIL
+SYMD   39RCTLPT52V022570207700597013900196001383
+SXPO  120recommended traffic direction between parts of a traffic separation scheme, or for ships not needing a deep water route
+SCRF    6ATRFCD
+SVCT   45SPA;SW3;PU2134,1545;PD2257,1383;PD2380,1545;
+SVCT   45SPA;SW3;PU2043,1661;PD1960,1777;PD2108,1777;
+SVCT   45SPA;SW3;PU2394,1773;PD2557,1773;PD2463,1650;
+SVCT   57SPA;SW3;PU2104,2585;PD2104,2773;PD2408,2773;PD2408,2588;
+SVCT   33SPA;SW3;PU2104,2435;PD2104,2278;
+SVCT   33SPA;SW3;PU2104,1937;PD2104,2087;
+SVCT   33SPA;SW3;PU2423,1929;PD2423,2075;
+SVCT   33SPA;SW3;PU2419,2270;PD2419,2422;
+****    0
+0001    501863
+SYMB   10SY02099NIL
+SYMD   39RDOCAL02V007500075000436007370053200231
+SXPO   57radio calling-in point for traffic in one direction only
+SCRF    6ATRFCD
+SVCT   25SPA;SW1;PU750,750;CI218;
+SVCT   39SPA;SW1;PU580,589;PD752,231;PD923,589;
+****    0
+0001    501864
+SYMB   10SY03099NIL
+SYMD   39RDOCAL03V007500075000436010370053200231
+SXPO   54radio calling-in point for traffic in both directions
+SCRF    6ATRFCD
+SVCT   25SPA;SW1;PU750,750;CI218;
+SVCT   39SPA;SW1;PU580,589;PD752,231;PD923,589;
+SVCT   40SPA;SW1;PU920,914;PD742,1268;PD580,911;
+****    0
+0001    501865
+SYMB   10SY02100NIL
+SYMD   39RDOSTA02V007500075000590005900045300455
+SXPO   14radio station
+SCRF    6ACHMGD
+SVCT   25SPA;SW1;PU748,750;CI295;
+****    0
+0001    501866
+SYMB   10SY03340NIL
+SYMD   39RECDEF51V024700141301731004430159601179
+SXPO   60recommended track as an area, direction not defined in data
+SCRF    6ACHGRD
+SVCT   33SPA;SW1;PU1596,1415;PD1921,1415;
+SVCT   33SPA;SW1;PU3002,1415;PD3327,1415;
+SVCT  309SPA;SW1;PU2358,1275;PD2371,1247;PD2384,1222;PD2401,1205;PD2418,1188;PD2444,1179;PD2467,1181;PD2497,1190;PD2525,1196;PD2548,1230;PD2566,1249;PD2578,1271;PD2578,1296;PD2566,1320;PD2553,1341;PD2533,1365;PD2510,1390;PD2489,1416;PD2476,1435;PD2465,1452;PD2463,1474;PD2463,1497;PD2463,1514;PD2465,1536;PD2465,1538;
+SVCT   33SPA;SW2;PU2444,1622;PD2497,1622;
+SVCT   45SPA;SW1;PU2254,1570;PD2049,1415;PD2254,1260;
+SVCT   45SPA;SW1;PU2660,1566;PD2865,1411;PD2660,1256;
+****    0
+0001    501867
+SYMB   10SY03341NIL
+SYMD   39RECTRC55V024610141300310017280230800551
+SXPO   63recommended two-way track as an area, not based on fixed marks
+SCRF    6ACHGRD
+SVCT   33SPA;SW1;PU2461,2279;PD2461,1954;
+SVCT   33SPA;SW1;PU2463,1577;PD2463,1253;
+SVCT   31SPA;SW1;PU2462,876;PD2461,551;
+SVCT   45SPA;SW1;PU2615,1576;PD2462,1781;PD2308,1578;
+SVCT   45SPA;SW1;PU2618,1253;PD2462,1048;PD2308,1253;
+****    0
+0001    501868
+SYMB   10SY03342NIL
+SYMD   39RECTRC56V023770141000310014860222500665
+SXPO   59recommended two-way track as an area, based on fixed marks
+SCRF    6ACHGRD
+SVCT   45SPA;SW1;PU2534,1570;PD2379,1774;PD2225,1570;
+SVCT   45SPA;SW1;PU2535,1246;PD2380,1041;PD2225,1246;
+SVCT   32SPA;SW1;PU2378,665;PD2378,2151;
+****    0
+0001    501869
+SYMB   10SY03343NIL
+SYMD   39RECTRC57V021370141500308010250198100920
+SXPO   63recommended one-way track as an area, not based on fixed marks
+SCRF    6ACHGRD
+SVCT   33SPA;SW1;PU2135,1945;PD2136,1621;
+SVCT   32SPA;SW1;PU2135,1245;PD2135,920;
+SVCT   45SPA;SW1;PU2289,1621;PD2135,1418;PD1981,1621;
+****    0
+0001    501870
+SYMB   10SY03344NIL
+SYMD   39RECTRC58V020110141600309012060185400795
+SXPO   59recommended one-way track as an area, based on fixed marks
+SCRF    6ACHGRD
+SVCT   45SPA;SW1;PU2163,1499;PD2008,1294;PD1854,1499;
+SVCT   32SPA;SW1;PU2007,795;PD2007,2001;
+****    0
+0001    501871
+SYMB   10SY01451NIL
+SYMD   39REFPNT02V015500165001000010000105001150
+SXPO   49reference point, 'ghost cursor' (user interface)
+SCRF    6fNINFO
+SVCT  117SPf;SW1;PU1050,1550;PD1450,1550;PD1450,1150;PU1050,1750;PD1450,1750;PD1450,2150;PU1650,1150;PD1650,1550;PD2050,1550;
+SVCT   37PU2050,1750;PD1650,1750;PD1650,2150;
+****    0
+0001    501872
+SYMB   10SY03125NIL
+SYMD   39RETRFL01V007500075000203003470105200405
+SXPO   29retro reflector, paper chart
+SCRF    6ACHMGD
+SVCT   31SPA;SW1;PU1053,465;PD1255,465;
+SVCT   31SPA;SW1;PU1052,688;PD1254,688;
+SVCT   31SPA;SW1;PU1053,574;PD1255,574;
+SVCT   31SPA;SW1;PU1052,405;PD1052,752;
+****    0
+0001    501873
+SYMB   10SY03126NIL
+SYMD   39RETRFL02V007000070000204003470097900534
+SXPO   28retro reflector, simplified
+SCRF    6ACHMGD
+SVCT   30SPA;SW1;PU979,594;PD1181,594;
+SVCT   30SPA;SW1;PU981,821;PD1183,821;
+SVCT   30SPA;SW1;PU981,709;PD1183,709;
+SVCT   29SPA;SW1;PU980,534;PD980,881;
+****    0
+0001    501874
+SYMB   10SY03166NIL
+SYMD   39RFNERY01V015100126500680006800116900912
+SXPO    9refinery
+SCRF    6ALANDF
+SVCT  120SPA;SW1;ST0;PU1450,1500;PM0;PD1450,1175;PD1550,1175;PD1550,1500;PD1537,1462;PD1500,1450;PD1468,1462;PD1450,1500;PM2;FP;
+SVCT  207SPA;SW2;PU1556,943;PD1525,962;PD1493,981;PD1468,1012;PD1450,1050;PD1450,1087;PD1456,1106;PD1481,1118;PD1512,1118;PD1531,1106;PD1543,1081;PD1537,1050;PD1525,1037;PD1525,1012;PD1525,987;PD1543,968;PD1556,943;
+SVCT   27SPA;SW1;PU1509,1252;CI340;
+SVCT   33SPA;SW2;PU1349,1496;PD1644,1496;
+****    0
+0001    501875
+SYMB   10SY03167NIL
+SYMD   39RFNERY11V015010127000680006800116900912
+SXPO   21conspicuous refinery
+SCRF    6ACHBLK
+SVCT  120SPA;SW1;ST0;PU1450,1500;PM0;PD1450,1175;PD1550,1175;PD1550,1500;PD1537,1462;PD1500,1450;PD1468,1462;PD1450,1500;PM2;FP;
+SVCT  207SPA;SW2;PU1556,943;PD1525,962;PD1493,981;PD1468,1012;PD1450,1050;PD1450,1087;PD1456,1106;PD1481,1118;PD1512,1118;PD1531,1106;PD1543,1081;PD1537,1050;PD1525,1037;PD1525,1012;PD1525,987;PD1543,968;PD1556,943;
+SVCT   27SPA;SW1;PU1509,1252;CI340;
+SVCT   33SPA;SW2;PU1349,1496;PD1644,1496;
+****    0
+0001    501876
+SYMB   10SY03368NIL
+SYMD   39ROLROL01V003670061900720003040000000477
+SXPO   14RoRo terminal
+SCRF    6ACHBLK
+SVCT   25SPA;SW1;PU1,479;PD1,780;
+SVCT   29SPA;SW1;PU398,481;PD398,781;
+SVCT   27SPA;SW1;PU0,478;PD100,478;
+SVCT   27SPA;SW1;PU1,630;PD100,630;
+SVCT   29SPA;SW1;PU398,481;PD497,481;
+SVCT   29SPA;SW1;PU398,632;PD497,632;
+SVCT   29SPA;SW1;PU100,628;PD137,778;
+SVCT   29SPA;SW1;PU497,630;PD533,781;
+SVCT   29SPA;SW1;PU532,603;PD532,503;
+SVCT   29SPA;SW1;PU135,601;PD135,501;
+SVCT   29SPA;SW1;PU100,477;PD134,502;
+SVCT   29SPA;SW1;PU100,628;PD134,601;
+SVCT   29SPA;SW1;PU497,630;PD532,604;
+SVCT   29SPA;SW1;PU496,480;PD532,505;
+SVCT   24SPA;SW1;PU261,702;CI74;
+SVCT   24SPA;SW1;PU647,702;CI73;
+****    0
+0001    501877
+SYMB   10SY01452NIL
+SYMD   39RSCSTA02V003010075000500004000005000550
+SXPO   15rescue station
+SCRF    6ACHBLK
+SVCT   74SPA;SW1;ST0;PU300,550;PM0;PD200,750;PD300,950;PD400,750;PD300,550;PM2;FP;
+SVCT   87SPA;SW1;PU50,750;PD50,750;PD200,750;PD300,750;PD350,750;PD500,750;PD550,750;PD550,750;
+****    0
+0001    501878
+SYMB   10SY03408NIL
+SYMD   39RSRDEF51V022650272101319009940028601837
+SXPO   43area in which undefined restrictions exist
+SCRF   12ATRFCFDCHMGD
+SVCT   31SPA;SW3;PU785,2462;PD785,1997;
+SVCT   31SPA;SW2;PU718,2642;PD861,2642;
+SVCT   26SPA;SW2;PU783,2334;CI497;
+SVCT  309SPD;SW1;PU1385,2484;PD1398,2456;PD1411,2431;PD1428,2414;PD1445,2397;PD1471,2388;PD1494,2390;PD1524,2399;PD1552,2405;PD1575,2439;PD1593,2458;PD1605,2480;PD1605,2505;PD1593,2529;PD1580,2550;PD1560,2574;PD1537,2599;PD1516,2625;PD1503,2644;PD1492,2661;PD1490,2683;PD1490,2706;PD1490,2723;PD1492,2745;PD1492,2747;
+SVCT   33SPD;SW2;PU1471,2831;PD1524,2831;
+****    0
+0001    501879
+SYMB   10SY03409NIL
+SYMD   39RTLDEF51V022570207701177013900163901383
+SXPO  154recommended route between parts of a traffic separation scheme, or for ships not needing a deep water route, with the direction not specified in the data
+SCRF   12ATRFCDICHMGD
+SVCT   45SPA;SW3;PU2134,1545;PD2257,1383;PD2380,1545;
+SVCT   45SPA;SW3;PU2043,1661;PD1960,1777;PD2108,1777;
+SVCT   45SPA;SW3;PU2394,1773;PD2557,1773;PD2463,1650;
+SVCT   57SPA;SW3;PU2104,2585;PD2104,2773;PD2408,2773;PD2408,2588;
+SVCT   33SPA;SW3;PU2104,2435;PD2104,2278;
+SVCT   33SPA;SW3;PU2104,1937;PD2104,2087;
+SVCT   33SPA;SW3;PU2423,1929;PD2423,2075;
+SVCT   33SPA;SW3;PU2419,2270;PD2419,2422;
+SVCT  309SPI;SW1;PU1639,2104;PD1652,2076;PD1665,2051;PD1682,2034;PD1699,2017;PD1725,2008;PD1748,2010;PD1778,2019;PD1806,2025;PD1829,2059;PD1847,2078;PD1859,2100;PD1859,2125;PD1847,2149;PD1834,2170;PD1814,2194;PD1791,2219;PD1770,2245;PD1757,2264;PD1746,2281;PD1744,2303;PD1744,2326;PD1744,2343;PD1746,2365;PD1746,2367;
+SVCT   33SPI;SW2;PU1725,2451;PD1778,2451;
+SVCT  309SPI;SW1;PU2596,2102;PD2609,2074;PD2622,2049;PD2639,2032;PD2656,2015;PD2682,2006;PD2705,2008;PD2735,2017;PD2763,2023;PD2786,2057;PD2804,2076;PD2816,2098;PD2816,2123;PD2804,2147;PD2791,2168;PD2771,2192;PD2748,2217;PD2727,2243;PD2714,2262;PD2703,2279;PD2701,2301;PD2701,2324;PD2701,2341;PD2703,2363;PD2703,2365;
+SVCT   33SPI;SW2;PU2682,2449;PD2735,2449;
+****    0
+0001    501880
+SYMB   10SY02259NIL
+SYMD   39RTPBCN02V007200080600643006080039900502
+SXPO   25radar transponder beacon
+SCRF    6ACHMGD
+SVCT   42SPA;SW2;PU1019,705;PD1042,808;PD1021,916;
+SVCT   39SPA;SW2;PU420,707;PD399,808;PD420,913;
+SVCT   41SPA;SW2;PU657,1110;PD556,1060;PD496,990;
+SVCT   41SPA;SW2;PU802,1104;PD893,1058;PD959,988;
+SVCT   39SPA;SW2;PU657,502;PD556,554;PD492,618;
+SVCT   39SPA;SW2;PU804,506;PD899,558;PD959,622;
+****    0
+0001    501881
+SYMB   10SY02105NIL
+SYMD   39SCALEB10V011500305500001020000115001055
+SXPO   58one mile scalebar for display scales larger than 1/80,000
+SCRF   12ASCLBRDCHGRD
+SVCT   33SPD;SW4;PU1150,1255;PD1150,1055;
+SVCT   33SPA;SW4;PU1150,1455;PD1150,1255;
+SVCT   33SPD;SW4;PU1150,1655;PD1150,1455;
+SVCT   33SPA;SW4;PU1150,1855;PD1150,1655;
+SVCT   33SPD;SW4;PU1150,2055;PD1150,1855;
+SVCT   33SPA;SW4;PU1150,2255;PD1150,2055;
+SVCT   33SPD;SW4;PU1150,2455;PD1150,2255;
+SVCT   33SPA;SW4;PU1150,2655;PD1150,2455;
+SVCT   33SPD;SW4;PU1150,2855;PD1150,2655;
+SVCT   33SPA;SW4;PU1150,3055;PD1150,2855;
+****    0
+0001    501882
+SYMB   10SY02106NIL
+SYMD   39SCALEB11V011500305500001020000115001055
+SXPO   6410 mile latitude scale for display scales smaller than 1/80,000
+SCRF   12ASNDG2DSNDG1
+SVCT   33SPA;SW4;PU1150,1055;PD1150,1455;
+SVCT   33SPD;SW4;PU1150,1455;PD1150,1855;
+SVCT   33SPA;SW4;PU1150,1855;PD1150,2255;
+SVCT   33SPD;SW4;PU1150,2255;PD1150,2655;
+SVCT   33SPA;SW4;PU1150,2655;PD1150,3055;
+****    0
+0001    501883
+SYMB   10SY01458NIL
+SYMD   39SILBUI01V007500075000300003000060000600
+SXPO    5silo
+SCRF   12WLANDFKCHBRN
+SVCT   60SPK;PU750,750;ST0;PM0;CI150;PM2;FP;SPW;PU750,750;SW2;CI150;
+****    0
+0001    501884
+SYMB   10SY01459NIL
+SYMD   39SILBUI11V007500075000300003000060000600
+SXPO   17conspicuous silo
+SCRF   12WLANDFCCHBLK
+SVCT   60SPW;PU750,750;ST0;PM0;CI150;PM2;FP;SPC;PU750,750;SW2;CI150;
+****    0
+0001    501885
+SYMB   10SY02107NIL
+SYMD   39SISTAT02V004840075800640004250046000734
+SXPO   15signal station
+SCRF   18ACHWHTBLANDFCCHMGF
+SVCT   78SPA;SW1;ST0;PU500,787;PM0;PD1100,787;PD1100,1159;PD500,1159;PD500,787;PM2;FP;
+SVCT   53SPC;SW2;PU1100,787;PD1100,1159;PD500,1159;PD500,787;
+SVCT  133SPC;SW2;PU725,875;PD700,850;PD650,850;PD625,875;PD625,925;PD650,950;PD690,950;PD725,975;PD725,1025;PD700,1059;PD634,1059;PD606,1028;
+SVCT  133SPC;SW2;PU975,875;PD950,850;PD900,850;PD875,875;PD875,925;PD903,950;PD940,950;PD975,975;PD975,1028;PD950,1053;PD884,1053;PD856,1025;
+SVCT   30SPC;SW2;PU500,787;PD1100,787;
+SVCT   39SPB;SW1;ST0;PU485,759;PM0;CI25;PM2;FP;
+****    0
+0001    501886
+SYMB   10SY02108NIL
+SYMD   39SMCFAC02V007500075000680006850041000405
+SXPO   22yacht harbour, marina
+SCRF    6ACHMGD
+SVCT  181SPA;SW1;PU830,420;PD750,545;PD750,930;PD1050,865;PD1020,840;PD990,815;PD960,785;PD930,745;PD905,705;PD880,665;PD860,620;PD845,570;PD835,525;PD830,480;PD830,450;PD830,425;PD830,420;
+SVCT  379SPA;SW1;PU750,505;PD720,515;PD690,540;PD665,555;PD640,580;PD620,605;PD600,635;PD580,660;PD565,695;PD550,730;PD540,760;PD535,800;PD530,840;PD530,895;PD535,925;PD535,965;PD545,950;PD570,925;PD590,905;PD620,890;PD655,880;PD675,875;PD705,875;PD690,855;PD675,825;PD660,780;PD655,750;PD655,725;PD655,700;PD655,670;PD665,635;PD675,600;PD695,570;PD710,545;PD730,520;PD745,505;PD750,505;
+SVCT   25SPA;SW1;PU750,750;CI340;
+SVCT   29SPA;SW1;PU750,405;PD750,990;
+SVCT   29SPA;SW1;PU508,989;PD989,989;
+****    0
+0001    501887
+SYMB   10SY01462NIL
+SYMD   39SNDWAV02V006170041701179001790003700316
+SXPO   11sand waves
+SCRF    6ACHGRD
+SVCT  222SPA;SW1;PU37,495;PD121,495;PD163,473;PD228,328;PD263,328;PD320,473;PD347,495;PD523,495;PD554,468;PD600,331;PD626,331;PD677,468;PD710,495;PD856,495;PD906,495;PD937,476;PD998,316;PD1036,316;PD1093,473;PD1132,495;PD1216,495;
+****    0
+0001    501888
+SYMB   10SY01463NIL
+SYMD   39SOUNDG00V007500075000125002500080000625
+SXPO   42deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;
+****    0
+0001    501889
+SYMB   10SY01464NIL
+SYMD   39SOUNDG01V007500075000075002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   39SPY;SW1;PU800,700;PD875,625;PD875,875;
+****    0
+0001    501890
+SYMB   10SY01465NIL
+SYMD   39SOUNDG02V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU800,650;PD825,625;PD900,625;PD925,650;PD925,700;PD800,825;PD800,850;PD800,875;PD925,875;
+****    0
+0001    501891
+SYMB   10SY01466NIL
+SYMD   39SOUNDG03V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,737;PD825,737;PU900,737;PD925,762;PD925,850;PD900,875;PD825,875;PD800,850;PU831,625;PD900,625;PD925,650;
+SVCT   41PD925,712;PD900,737;PU806,650;PD831,625;
+****    0
+0001    501892
+SYMB   10SY02109NIL
+SYMD   39SOUNDG04V007500075000155002980079900600
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6ASNDG1
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501893
+SYMB   10SY01468NIL
+SYMD   39SOUNDG05V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU925,625;PD800,625;PD800,737;PD900,737;PD925,762;PD925,850;PD900,875;PD825,875;PD800,850;
+****    0
+0001    501894
+SYMB   10SY01469NIL
+SYMD   39SOUNDG06V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,759;PD900,734;PD825,734;PD800,759;
+SVCT   21PU900,625;PD918,643;
+****    0
+0001    501895
+SYMB   10SY01470NIL
+SYMD   39SOUNDG07V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   49SPY;SW1;PU800,625;PD925,625;PD850,775;PD850,875;
+****    0
+0001    501896
+SYMB   10SY01471NIL
+SYMD   39SOUNDG08V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,725;PD825,750;PD900,750;PD925,725;PD925,650;PD900,625;PU825,750;PD800,775;
+SVCT   61PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;
+****    0
+0001    501897
+SYMB   10SY01472NIL
+SYMD   39SOUNDG09V007500075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;PD800,650;PD800,740;PD825,765;PD900,765;
+SVCT   11PD925,740;
+****    0
+0001    501898
+SYMB   10SY01473NIL
+SYMD   39SOUNDG10V007500075000125002500057500625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU600,625;PD575,650;PD575,850;PD600,875;PD675,875;PD700,850;PD700,650;PU675,625;PD600,625;PU700,650;PD675,625;
+****    0
+0001    501899
+SYMB   10SY01474NIL
+SYMD   39SOUNDG11V009750075000075002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   39SPY;SW1;PU800,700;PD875,625;PD875,875;
+****    0
+0001    501900
+SYMB   10SY01475NIL
+SYMD   39SOUNDG12V009750075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU800,650;PD825,625;PD900,625;PD925,650;PD925,700;PD800,825;PD800,850;PD800,875;PD925,875;
+****    0
+0001    501901
+SYMB   10SY01476NIL
+SYMD   39SOUNDG13V009750075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,734;PD825,734;PU900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;PU828,625;PD900,625;PD925,650;
+SVCT   41PD925,709;PD900,734;PU803,650;PD828,625;
+****    0
+0001    501902
+SYMB   10SY02110NIL
+SYMD   39SOUNDG14V009740075000155002980079900600
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6ASNDG1
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501903
+SYMB   10SY01478NIL
+SYMD   39SOUNDG15V009750075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU925,625;PD800,625;PD800,734;PD900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;
+****    0
+0001    501904
+SYMB   10SY01479NIL
+SYMD   39SOUNDG16V009750075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,756;PD900,731;PD825,731;PD800,756;
+SVCT   21PU900,625;PD915,640;
+****    0
+0001    501905
+SYMB   10SY01480NIL
+SYMD   39SOUNDG17V009750072500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   49SPY;SW1;PU800,625;PD925,625;PD850,775;PD850,875;
+****    0
+0001    501906
+SYMB   10SY01481NIL
+SYMD   39SOUNDG18V009750075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,725;PD825,750;PD900,750;PD925,725;PD925,650;PD900,625;PU825,750;PD800,775;
+SVCT   61PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;
+****    0
+0001    501907
+SYMB   10SY01482NIL
+SYMD   39SOUNDG19V009750075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;PD800,650;PD800,737;PD825,762;PD900,762;
+SVCT   11PD925,737;
+****    0
+0001    501908
+SYMB   10SY01483NIL
+SYMD   39SOUNDG20V007500075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU375,625;PD350,650;PD350,850;PD375,875;PD450,875;PD475,850;PD475,650;PD450,625;PD375,625;
+****    0
+0001    501909
+SYMB   10SY01484NIL
+SYMD   39SOUNDG21V009750075000075002500057500625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   39SPY;SW1;PU575,700;PD650,625;PD650,875;
+****    0
+0001    501910
+SYMB   10SY01485NIL
+SYMD   39SOUNDG22V012000075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU800,650;PD825,625;PD900,625;PD925,650;PD925,700;PD800,825;PD800,850;PD800,875;PD925,875;
+****    0
+0001    501911
+SYMB   10SY01486NIL
+SYMD   39SOUNDG23V012000075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,734;PD825,734;PU900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;PU828,625;PD900,625;PD925,650;
+SVCT   41PD925,709;PD900,734;PU803,650;PD828,625;
+****    0
+0001    501912
+SYMB   10SY02111NIL
+SYMD   39SOUNDG24V011990075000155002980079900600
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6ASNDG1
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501913
+SYMB   10SY01488NIL
+SYMD   39SOUNDG25V012000075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU925,625;PD800,625;PD800,734;PD900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;
+****    0
+0001    501914
+SYMB   10SY01489NIL
+SYMD   39SOUNDG26V012000075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,756;PD900,731;PD825,731;PD800,756;
+SVCT   21PU900,625;PD915,640;
+****    0
+0001    501915
+SYMB   10SY01490NIL
+SYMD   39SOUNDG27V012000072500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   49SPY;SW1;PU800,625;PD925,625;PD850,775;PD850,875;
+****    0
+0001    501916
+SYMB   10SY01491NIL
+SYMD   39SOUNDG28V012000075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,725;PD825,750;PD900,750;PD925,725;PD925,650;PD900,625;PU825,750;PD800,775;
+SVCT   61PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;
+****    0
+0001    501917
+SYMB   10SY01492NIL
+SYMD   39SOUNDG29V012000075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;PD800,650;PD800,737;PD825,762;PD900,762;
+SVCT   11PD925,737;
+****    0
+0001    501918
+SYMB   10SY01493NIL
+SYMD   39SOUNDG30V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU375,625;PD350,650;PD350,850;PD375,875;PD450,875;PD475,850;PD475,650;PD450,625;PD375,625;
+****    0
+0001    501919
+SYMB   10SY01494NIL
+SYMD   39SOUNDG31V009750075000075002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   39SPY;SW1;PU350,700;PD425,625;PD425,875;
+****    0
+0001    501920
+SYMB   10SY01495NIL
+SYMD   39SOUNDG32V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU350,650;PD375,625;PD450,625;PD475,650;PD475,700;PD350,825;PD350,850;PD350,875;PD475,875;
+****    0
+0001    501921
+SYMB   10SY01496NIL
+SYMD   39SOUNDG33V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU450,731;PD375,731;PU450,731;PD475,756;PD475,850;PD450,875;PD375,875;PD350,850;PU375,625;PD450,625;PD475,650;
+SVCT   41PD475,706;PD450,731;PU350,650;PD375,625;
+****    0
+0001    501922
+SYMB   10SY02112NIL
+SYMD   39SOUNDG34V014220075000155002980079900600
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6ASNDG1
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501923
+SYMB   10SY01498NIL
+SYMD   39SOUNDG35V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU475,625;PD350,625;PD350,731;PD450,731;PD475,756;PD475,850;PD450,875;PD375,875;PD350,850;
+****    0
+0001    501924
+SYMB   10SY01499NIL
+SYMD   39SOUNDG36V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU450,625;PD375,625;PD350,650;PD350,850;PD375,875;PD450,875;PD475,850;PD475,753;PD450,728;PD375,728;PD350,753;
+SVCT   21PU450,625;PD462,637;
+****    0
+0001    501925
+SYMB   10SY01500NIL
+SYMD   39SOUNDG37V009750075000125002500035000650
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   49SPY;SW1;PU350,650;PD475,650;PD400,800;PD400,900;
+****    0
+0001    501926
+SYMB   10SY01501NIL
+SYMD   39SOUNDG38V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU450,625;PD375,625;PD350,650;PD350,725;PD375,750;PD450,750;PD475,725;PD475,650;PD450,625;PU375,750;PD350,775;
+SVCT   61PD350,850;PD375,875;PD450,875;PD475,850;PD475,775;PD450,750;
+****    0
+0001    501927
+SYMB   10SY01502NIL
+SYMD   39SOUNDG39V009750075000125002500035000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU350,850;PD375,875;PD450,875;PD475,850;PD475,650;PD450,625;PD375,625;PD350,650;PD350,734;PD375,759;PD450,759;
+SVCT   11PD475,734;
+****    0
+0001    501928
+SYMB   10SY01503NIL
+SYMD   39SOUNDG40V007500075000125002500102500625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  108SPY;SW1;PU1050,625;PD1125,625;PD1150,650;PD1150,850;PD1125,875;PD1050,875;PD1025,850;PD1025,650;PD1050,625;
+****    0
+0001    501929
+SYMB   10SY01504NIL
+SYMD   39SOUNDG41V005250075000075002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   39SPY;SW1;PU800,700;PD875,625;PD875,875;
+****    0
+0001    501930
+SYMB   10SY01505NIL
+SYMD   39SOUNDG42V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU800,650;PD825,625;PD900,625;PD925,650;PD925,700;PD800,825;PD800,850;PD800,875;PD925,875;
+****    0
+0001    501931
+SYMB   10SY01506NIL
+SYMD   39SOUNDG43V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,734;PD825,734;PU900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;PU828,625;PD900,625;PD925,650;
+SVCT   41PD925,709;PD900,734;PU803,650;PD828,625;
+****    0
+0001    501932
+SYMB   10SY02113NIL
+SYMD   39SOUNDG44V005250075000155002980079900600
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6ASNDG1
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501933
+SYMB   10SY01508NIL
+SYMD   39SOUNDG45V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU925,625;PD800,625;PD800,734;PD900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;
+****    0
+0001    501934
+SYMB   10SY01509NIL
+SYMD   39SOUNDG46V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,756;PD900,731;PD825,731;PD800,756;
+SVCT   21PU900,625;PD915,640;
+****    0
+0001    501935
+SYMB   10SY01510NIL
+SYMD   39SOUNDG47V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   49SPY;SW1;PU800,625;PD925,625;PD850,775;PD850,875;
+****    0
+0001    501936
+SYMB   10SY01511NIL
+SYMD   39SOUNDG48V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,725;PD825,750;PD900,750;PD925,725;PD925,650;PD900,625;PU825,750;PD800,775;
+SVCT   61PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;
+****    0
+0001    501937
+SYMB   10SY01512NIL
+SYMD   39SOUNDG49V005250075000125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;PD800,650;PD800,737;PD825,762;PD900,762;
+SVCT   11PD925,737;
+****    0
+0001    501938
+SYMB   10SY01513NIL
+SYMD   39SOUNDG50V007500075000125002500080000750
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  101SPY;SW1;PU825,750;PD900,750;PD925,775;PD925,975;PD900,1000;PD825,1000;PD800,975;PD800,775;PD825,750;
+****    0
+0001    501939
+SYMB   10SY01514NIL
+SYMD   39SOUNDG51V007500062500075002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   39SPY;SW1;PU800,700;PD875,625;PD875,875;
+****    0
+0001    501940
+SYMB   10SY01515NIL
+SYMD   39SOUNDG52V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU800,650;PD825,625;PD900,625;PD925,650;PD925,700;PD800,825;PD800,850;PD800,875;PD925,875;
+****    0
+0001    501941
+SYMB   10SY01516NIL
+SYMD   39SOUNDG53V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,734;PD825,734;PU900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;PU828,625;PD900,625;PD925,650;
+SVCT   41PD925,709;PD900,734;PU803,650;PD828,625;
+****    0
+0001    501942
+SYMB   10SY02114NIL
+SYMD   39SOUNDG54V007490062500155002980079900600
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6ASNDG1
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501943
+SYMB   10SY01518NIL
+SYMD   39SOUNDG55V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   99SPY;SW1;PU925,625;PD800,625;PD800,734;PD900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;
+****    0
+0001    501944
+SYMB   10SY01519NIL
+SYMD   39SOUNDG56V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,756;PD900,731;PD825,731;PD800,756;
+SVCT   21PU900,625;PD915,640;
+****    0
+0001    501945
+SYMB   10SY01520NIL
+SYMD   39SOUNDG57V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT   49SPY;SW1;PU800,625;PD925,625;PD850,775;PD850,875;
+****    0
+0001    501946
+SYMB   10SY01521NIL
+SYMD   39SOUNDG58V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU900,625;PD825,625;PD800,650;PD800,725;PD825,750;PD900,750;PD925,725;PD925,650;PD900,625;PU825,750;PD800,775;
+SVCT   61PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;
+****    0
+0001    501947
+SYMB   10SY01522NIL
+SYMD   39SOUNDG59V007500062500125002500080000625
+SXPO   46for deep soundings, greater than safety depth
+SCRF    6YSNDG1
+SVCT  119SPY;SW1;PU800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;PD800,650;PD800,737;PD825,762;PD900,762;
+SVCT   11PD925,737;
+****    0
+0001    501948
+SYMB   10SY01524NIL
+SYMD   39SOUNDGB1V007500075000631001310034000928
+SXPO   77symbol for swept sounding, used for deep soundings greater than safety depth
+SCRF    6YSNDG1
+SVCT   51SPY;SW1;PU340,928;PD340,1059;PD971,1059;PD971,928;
+****    0
+0001    501949
+SYMB   10SY03422NIL
+SYMD   39SOUNDGC2V073500154600854008540692301119
+SXPO   25sounding of low accuracy
+SCRF    6ASNDG1
+SVCT   27SPA;SW1;PU7350,1546;CI427;
+****    0
+0001    501950
+SYMB   10SY01526NIL
+SYMD   39SOUNDS00V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;
+****    0
+0001    501951
+SYMB   10SY01527NIL
+SYMD   39SOUNDS01V007500075000075002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   39SPZ;SW1;PU800,700;PD875,625;PD875,875;
+****    0
+0001    501952
+SYMB   10SY01528NIL
+SYMD   39SOUNDS02V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU800,650;PD825,625;PD900,625;PD925,650;PD925,700;PD800,825;PD800,850;PD800,875;PD925,875;
+****    0
+0001    501953
+SYMB   10SY01529NIL
+SYMD   39SOUNDS03V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU900,734;PD825,734;PU900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;PU828,625;PD900,625;PD925,650;
+SVCT   41PD925,709;PD900,734;PU803,650;PD828,625;
+****    0
+0001    501954
+SYMB   10SY02115NIL
+SYMD   39SOUNDS04V007500075000155002980079900600
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ASNDG2
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501955
+SYMB   10SY01531NIL
+SYMD   39SOUNDS05V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU925,625;PD800,625;PD800,734;PD900,734;PD925,759;PD925,850;PD900,875;PD825,875;PD800,850;
+****    0
+0001    501956
+SYMB   10SY01532NIL
+SYMD   39SOUNDS06V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU900,625;PD825,625;PD800,650;PD800,850;PD825,875;PD900,875;PD925,850;PD925,756;PD900,731;PD825,731;PD800,756;
+SVCT   21PU900,625;PD915,640;
+****    0
+0001    501957
+SYMB   10SY01533NIL
+SYMD   39SOUNDS07V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   49SPZ;SW1;PU800,625;PD925,625;PD850,775;PD850,875;
+****    0
+0001    501958
+SYMB   10SY01534NIL
+SYMD   39SOUNDS08V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU900,625;PD825,625;PD800,650;PD800,725;PD825,750;PD900,750;PD925,725;PD925,650;PD900,625;PU825,750;PD800,775;
+SVCT   61PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;
+****    0
+0001    501959
+SYMB   10SY01535NIL
+SYMD   39SOUNDS09V007500075000125002500080000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU800,850;PD825,875;PD900,875;PD925,850;PD925,650;PD900,625;PD825,625;PD800,650;PD800,737;PD825,762;PD900,762;
+SVCT   11PD925,737;
+****    0
+0001    501960
+SYMB   10SY01536NIL
+SYMD   39SOUNDS10V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU600,625;PD575,650;PD575,850;PD600,875;PD675,875;PD700,850;PD700,650;PU675,625;PD600,625;PU700,650;PD675,625;
+****    0
+0001    501961
+SYMB   10SY01537NIL
+SYMD   39SOUNDS11V007500075000075002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   39SPZ;SW1;PU575,700;PD650,625;PD650,875;
+****    0
+0001    501962
+SYMB   10SY01538NIL
+SYMD   39SOUNDS12V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU575,650;PD600,625;PD675,625;PD700,650;PD700,700;PD575,825;PD575,850;PD575,875;PD700,875;
+****    0
+0001    501963
+SYMB   10SY01539NIL
+SYMD   39SOUNDS13V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU675,731;PD600,731;PU675,731;PD700,756;PD700,850;PD675,875;PD600,875;PD575,850;PU600,625;PD675,625;PD700,650;
+SVCT   41PD700,706;PD675,731;PU575,650;PD600,625;
+****    0
+0001    501964
+SYMB   10SY02116NIL
+SYMD   39SOUNDS14V009740075000155002980079900600
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ASNDG2
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501965
+SYMB   10SY01541NIL
+SYMD   39SOUNDS15V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU700,625;PD575,625;PD575,731;PD675,731;PD700,756;PD700,850;PD675,875;PD600,875;PD575,850;
+****    0
+0001    501966
+SYMB   10SY01542NIL
+SYMD   39SOUNDS16V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU675,625;PD600,625;PD575,650;PD575,850;PD600,875;PD675,875;PD700,850;PD700,753;PD675,728;PD600,728;PD575,753;
+SVCT   21PU675,625;PD687,637;
+****    0
+0001    501967
+SYMB   10SY01543NIL
+SYMD   39SOUNDS17V007500075000125002500057500650
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   49SPZ;SW1;PU575,650;PD700,650;PD625,800;PD625,900;
+****    0
+0001    501968
+SYMB   10SY01544NIL
+SYMD   39SOUNDS18V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU675,625;PD600,625;PD575,650;PD575,725;PD600,750;PD675,750;PD700,725;PD700,650;PD675,625;PU600,750;PD575,775;
+SVCT   61PD575,850;PD600,875;PD675,875;PD700,850;PD700,775;PD675,750;
+****    0
+0001    501969
+SYMB   10SY01545NIL
+SYMD   39SOUNDS19V007500075000125002500057500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU575,850;PD600,875;PD675,875;PD700,850;PD700,650;PD675,625;PD600,625;PD575,650;PD575,734;PD600,759;PD675,759;
+SVCT   11PD700,734;
+****    0
+0001    501970
+SYMB   10SY01546NIL
+SYMD   39SOUNDS20V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU375,625;PD350,650;PD350,850;PD375,875;PD450,875;PD475,850;PD475,650;PD450,625;PD375,625;
+****    0
+0001    501971
+SYMB   10SY01547NIL
+SYMD   39SOUNDS21V007500075000075002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   39SPZ;SW1;PU350,700;PD425,625;PD425,875;
+****    0
+0001    501972
+SYMB   10SY01548NIL
+SYMD   39SOUNDS22V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU350,650;PD375,625;PD450,625;PD475,650;PD475,700;PD350,825;PD350,850;PD350,875;PD475,875;
+****    0
+0001    501973
+SYMB   10SY01549NIL
+SYMD   39SOUNDS23V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU450,731;PD375,731;PU450,731;PD475,756;PD475,850;PD450,875;PD375,875;PD350,850;PU375,625;PD450,625;PD475,650;
+SVCT   41PD475,706;PD450,731;PU350,650;PD375,625;
+****    0
+0001    501974
+SYMB   10SY02117NIL
+SYMD   39SOUNDS24V011990075000155002980079900600
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ASNDG2
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501975
+SYMB   10SY01551NIL
+SYMD   39SOUNDS25V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU475,625;PD350,625;PD350,731;PD450,731;PD475,756;PD475,850;PD450,875;PD375,875;PD350,850;
+****    0
+0001    501976
+SYMB   10SY01552NIL
+SYMD   39SOUNDS26V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU450,625;PD375,625;PD350,650;PD350,850;PD375,875;PD450,875;PD475,850;PD475,753;PD450,728;PD375,728;PD350,753;
+SVCT   21PU450,625;PD462,637;
+****    0
+0001    501977
+SYMB   10SY01553NIL
+SYMD   39SOUNDS27V007500075000125002500035000650
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   49SPZ;SW1;PU350,650;PD475,650;PD400,800;PD400,900;
+****    0
+0001    501978
+SYMB   10SY01554NIL
+SYMD   39SOUNDS28V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU450,625;PD375,625;PD350,650;PD350,725;PD375,750;PD450,750;PD475,725;PD475,650;PD450,625;PU375,750;PD350,775;
+SVCT   61PD350,850;PD375,875;PD450,875;PD475,850;PD475,775;PD450,750;
+****    0
+0001    501979
+SYMB   10SY01555NIL
+SYMD   39SOUNDS29V007500075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU350,850;PD375,875;PD450,875;PD475,850;PD475,650;PD450,625;PD375,625;PD350,650;PD350,734;PD375,759;PD450,759;
+SVCT   11PD475,734;
+****    0
+0001    501980
+SYMB   10SY01556NIL
+SYMD   39SOUNDS30V009750075000125002500035000625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU375,625;PD350,650;PD350,850;PD375,875;PD450,875;PD475,850;PD475,650;PD450,625;PD375,625;
+****    0
+0001    501981
+SYMB   10SY01557NIL
+SYMD   39SOUNDS31V007500075000075002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   39SPZ;SW1;PU125,700;PD200,625;PD200,875;
+****    0
+0001    501982
+SYMB   10SY01558NIL
+SYMD   39SOUNDS32V007500075000125002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU125,650;PD150,625;PD225,625;PD250,650;PD250,700;PD125,825;PD125,850;PD125,875;PD250,875;
+****    0
+0001    501983
+SYMB   10SY01559NIL
+SYMD   39SOUNDS33V007500075000125002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU225,728;PD150,728;PU225,728;PD250,753;PD250,850;PD225,875;PD150,875;PD125,850;PU150,625;PD225,625;PD250,650;
+SVCT   41PD250,703;PD225,728;PU125,650;PD150,625;
+****    0
+0001    501984
+SYMB   10SY03006NIL
+SYMD   39SOUNDS34V014220075000155002980079900600
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ASNDG2
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501985
+SYMB   10SY01561NIL
+SYMD   39SOUNDS35V007500075000125002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   99SPZ;SW1;PU250,625;PD125,625;PD125,728;PD225,728;PD250,753;PD250,850;PD225,875;PD150,875;PD125,850;
+****    0
+0001    501986
+SYMB   10SY01562NIL
+SYMD   39SOUNDS36V007500075000125002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU225,625;PD150,625;PD125,650;PD125,850;PD150,875;PD225,875;PD250,850;PD250,750;PD225,725;PD150,725;PD125,750;
+SVCT   21PU225,625;PD234,634;
+****    0
+0001    501987
+SYMB   10SY01563NIL
+SYMD   39SOUNDS37V007500075000125002500012500650
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   49SPZ;SW1;PU125,650;PD250,650;PD175,800;PD175,900;
+****    0
+0001    501988
+SYMB   10SY01564NIL
+SYMD   39SOUNDS38V007500075000125002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU225,625;PD150,625;PD125,650;PD125,725;PD150,750;PD225,750;PD250,725;PD250,650;PD225,625;PU150,750;PD125,775;
+SVCT   61PD125,850;PD150,875;PD225,875;PD250,850;PD250,775;PD225,750;
+****    0
+0001    501989
+SYMB   10SY01565NIL
+SYMD   39SOUNDS39V007500075000125002500012500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU125,850;PD150,875;PD225,875;PD250,850;PD250,650;PD225,625;PD150,625;PD125,650;PD125,731;PD150,756;PD225,756;
+SVCT   11PD250,731;
+****    0
+0001    501990
+SYMB   10SY01566NIL
+SYMD   39SOUNDS40V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  108SPZ;SW1;PU1050,625;PD1125,625;PD1150,650;PD1150,850;PD1125,875;PD1050,875;PD1025,850;PD1025,650;PD1050,625;
+****    0
+0001    501991
+SYMB   10SY01567NIL
+SYMD   39SOUNDS41V007500075000075002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   42SPZ;SW1;PU1025,700;PD1100,625;PD1100,875;
+****    0
+0001    501992
+SYMB   10SY01568NIL
+SYMD   39SOUNDS42V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  108SPZ;SW1;PU1025,650;PD1050,625;PD1125,625;PD1150,650;PD1150,700;PD1025,825;PD1025,850;PD1025,875;PD1150,875;
+****    0
+0001    501993
+SYMB   10SY01569NIL
+SYMD   39SOUNDS43V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU1125,731;PD1050,731;PU1125,731;PD1150,756;PD1150,850;PD1125,875;PD1050,875;PD1025,850;PU1050,625;PD1125,625;
+SVCT   56PD1150,650;PD1150,706;PD1125,731;PU1025,650;PD1050,625;
+****    0
+0001    501994
+SYMB   10SY02119NIL
+SYMD   39SOUNDS44V005250075000155002980079900600
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ASNDG2
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    501995
+SYMB   10SY01571NIL
+SYMD   39SOUNDS45V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  108SPZ;SW1;PU1150,625;PD1025,625;PD1025,731;PD1125,731;PD1150,756;PD1150,850;PD1125,875;PD1050,875;PD1025,850;
+****    0
+0001    501996
+SYMB   10SY01572NIL
+SYMD   39SOUNDS46V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU1125,625;PD1050,625;PD1025,650;PD1025,850;PD1050,875;PD1125,875;PD1150,850;PD1150,753;PD1125,728;PD1050,728;
+SVCT   34PD1025,753;PU1125,625;PD1137,637;
+****    0
+0001    501997
+SYMB   10SY02260NIL
+SYMD   39SOUNDS47V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   53SPZ;SW1;PU1025,625;PD1150,625;PD1075,775;PD1075,875;
+****    0
+0001    501998
+SYMB   10SY01574NIL
+SYMD   39SOUNDS48V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU1125,625;PD1050,625;PD1025,650;PD1025,725;PD1050,750;PD1125,750;PD1150,725;PD1150,650;PD1125,625;PU1050,750;
+SVCT   78PD1025,775;PD1025,850;PD1050,875;PD1125,875;PD1150,850;PD1150,775;PD1125,750;
+****    0
+0001    501999
+SYMB   10SY01575NIL
+SYMD   39SOUNDS49V007500075000125002500102500625
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU1025,850;PD1050,875;PD1125,875;PD1150,850;PD1150,650;PD1125,625;PD1050,625;PD1025,650;PD1025,734;PD1050,759;
+SVCT   23PD1125,759;PD1150,734;
+****    0
+0001    502000
+SYMB   10SY01576NIL
+SYMD   39SOUNDS50V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  101SPZ;SW1;PU825,750;PD900,750;PD925,775;PD925,975;PD900,1000;PD825,1000;PD800,975;PD800,775;PD825,750;
+****    0
+0001    502001
+SYMB   10SY01577NIL
+SYMD   39SOUNDS51V007500075000075002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   40SPZ;SW1;PU800,825;PD875,750;PD875,1000;
+****    0
+0001    502002
+SYMB   10SY01578NIL
+SYMD   39SOUNDS52V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  101SPZ;SW1;PU800,775;PD825,750;PD900,750;PD925,775;PD925,825;PD800,950;PD800,975;PD800,1000;PD925,1000;
+****    0
+0001    502003
+SYMB   10SY01579NIL
+SYMD   39SOUNDS53V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  121SPZ;SW1;PU900,856;PD825,856;PU900,856;PD925,881;PD925,975;PD900,1000;PD825,1000;PD800,975;PU825,750;PD900,750;PD925,775;
+SVCT   41PD925,831;PD900,856;PU800,775;PD825,750;
+****    0
+0001    502004
+SYMB   10SY02120NIL
+SYMD   39SOUNDS54V007490062500155002980079900600
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ASNDG2
+SVCT   49SPA;SW1;PU925,898;PD925,600;PD799,774;PD954,774;
+****    0
+0001    502005
+SYMB   10SY01581NIL
+SYMD   39SOUNDS55V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  101SPZ;SW1;PU925,750;PD800,750;PD800,856;PD900,856;PD925,881;PD925,975;PD900,1000;PD825,1000;PD800,975;
+****    0
+0001    502006
+SYMB   10SY01582NIL
+SYMD   39SOUNDS56V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  121SPZ;SW1;PU900,750;PD825,750;PD800,775;PD800,975;PD825,1000;PD900,1000;PD925,975;PD925,878;PD900,853;PD825,853;PD800,878;
+SVCT   21PU900,750;PD912,762;
+****    0
+0001    502007
+SYMB   10SY01583NIL
+SYMD   39SOUNDS57V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT   50SPZ;SW1;PU800,750;PD925,750;PD850,900;PD850,1000;
+****    0
+0001    502008
+SYMB   10SY01584NIL
+SYMD   39SOUNDS58V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  119SPZ;SW1;PU900,750;PD825,750;PD800,775;PD800,850;PD825,875;PD900,875;PD925,850;PD925,775;PD900,750;PU825,875;PD800,900;
+SVCT   63PD800,975;PD825,1000;PD900,1000;PD925,975;PD925,900;PD900,875;
+****    0
+0001    502009
+SYMB   10SY01585NIL
+SYMD   39SOUNDS59V007500075000125002500080000750
+SXPO   58shallow soundings, less than or equal to the safety depth
+SCRF    6ZSNDG2
+SVCT  121SPZ;SW1;PU800,975;PD825,1000;PD900,1000;PD925,975;PD925,775;PD900,750;PD825,750;PD800,775;PD800,859;PD825,884;PD900,884;
+SVCT   11PD925,859;
+****    0
+0001    502010
+SYMB   10SY01586NIL
+SYMD   39SOUNDSA1V007500075000205000010052500970
+SXPO   89symbol for drying height, used for shallow soundings, less than or equal to safety depth
+SCRF    6ZSNDG2
+SVCT   29SPZ;SW1;PU525,970;PD730,970;
+****    0
+0001    502011
+SYMB   10SY01587NIL
+SYMD   39SOUNDSB1V007500075000631001310034000928
+SXPO   90symbol for swept sounding, used for shallow soundings, less than or equal to safety depth
+SCRF    6ZSNDG2
+SVCT   51SPZ;SW1;PU340,928;PD340,1059;PD971,1059;PD971,928;
+****    0
+0001    502012
+SYMB   10SY03423NIL
+SYMD   39SOUNDSC2V073500154600854008540692301119
+SXPO   25sounding of low accuracy
+SCRF    6ASNDG1
+SVCT   27SPA;SW1;PU7350,1546;CI427;
+****    0
+0001    502013
+SYMB   10SY01589NIL
+SYMD   39SPRING02V015000150000300003500135001150
+SXPO    7spring
+SCRF    6ECHGRD
+SVCT  114SPE;SW3;PU1500,1500;PD;PU1550,1500;PD;PU1600,1500;PD;PU1450,1500;PD;PU1400,1500;PD;PU1500,1450;PD;PU1500,1400;PD;
+SVCT  121PU1500,1350;PD;PU1500,1300;PD;PU1500,1250;PD;PU1500,1200;PD;PU1550,1150;PD;PU1600,1150;PD;PU1650,1200;PD;PU1650,1250;PD;
+SVCT   61PU1450,1150;PD;PU1400,1150;PD;PU1350,1200;PD;PU1350,1250;PD;
+****    0
+0001    502014
+SYMB   10SY03410NIL
+SYMD   39SWPARE51V006510117701101003040009100869
+SXPO   11swept area
+SCRF    6ACHGRF
+SVCT   51SPA;SW3;PU91,872;PD91,1173;PD1192,1173;PD1192,869;
+****    0
+0001    502015
+SYMB   10SY02262NIL
+SYMD   39TIDCUR01V016360228700408014850143501667
+SXPO   44predicted tidal stream or current direction
+SCRF    6ANINFO
+SVCT   45SPA;SW1;PU1442,2172;PD1638,1972;PD1840,2175;
+SVCT   45SPA;SW1;PU1438,2474;PD1638,2275;PD1840,2474;
+SVCT   33SPA;SW1;PU1638,1683;PD1638,1879;
+SVCT   33SPA;SW1;PU1638,1976;PD1638,2168;
+SVCT   33SPA;SW1;PU1638,2275;PD1638,2477;
+SVCT   45SPA;SW1;PU1435,1866;PD1638,1667;PD1843,1876;
+SVCT   33SPA;SW1;PU1637,2952;PD1638,3152;
+SVCT   33SPA;SW1;PU1637,2619;PD1638,2819;
+****    0
+0001    502016
+SYMB   10SY01591NIL
+SYMD   39TIDCUR02V022500225000403012000204301650
+SXPO   41actual tidal stream or current direction
+SCRF    6fNINFO
+SVCT  117SPf;SW1;PU2343,1650;PD2343,1650;PU2250,1650;PD2250,2850;PU2250,1650;PD2043,1846;PU2250,1650;PD2446,1846;PU2250,2146;
+SVCT   85PD2043,2343;PU2250,2146;PD2446,2343;PU2250,1884;PD2043,2090;PU2250,1893;PD2446,2100;
+****    0
+0001    502017
+SYMB   10SY01592NIL
+SYMD   39TIDCUR03V022500225000815004680118102803
+SXPO   25box for current strength
+SCRF    6fNINFO
+SVCT  105SPf;SW1;PU1181,2803;PD1996,2803;PU1181,3271;PD1996,3271;PU1996,2803;PD1996,3271;PU1181,2803;PD1181,3271;
+****    0
+0001    502018
+SYMB   10SY03188NIL
+SYMD   39TIDEHT01V007070075000884003360031100581
+SXPO   53point for which tide height information is available
+SCRF    6ACHGRD
+SVCT   30SPA;SW1;PU311,749;PD1195,749;
+SVCT  364SPA;SW1;PU383,748;PD383,724;PD390,693;PD405,660;PD427,632;PD456,610;PD490,594;PD523,583;PD554,581;PD587,588;PD617,598;PD645,615;PD668,634;PD690,666;PD707,704;PD707,733;PD707,748;PD707,774;PD712,803;PD719,826;PD734,855;PD764,880;PD776,890;PD793,900;PD813,909;PD844,915;PD879,917;PD918,909;PD947,896;PD980,875;PD1004,851;PD1023,817;PD1034,785;PD1039,768;PD1039,748;
+****    0
+0001    502019
+SYMB   10SY03136NIL
+SYMD   39TIDSTR01V007410074700566005600046100466
+SXPO   58point or area for which a tidal stream table is available
+SCRF    6ACHGRD
+SVCT   61SPA;SW1;PU461,749;PD744,466;PD1027,749;PD742,1026;PD461,749;
+****    0
+0001    502020
+SYMB   10SY03106NIL
+SYMD   39TMARDEF1V014320159800000003470142400661
+SXPO   54topmark for beacons, flag or other shape, paper-chart
+SCRF    6ACHBLK
+SVCT   32SPA;SW2;PU1424,1008;PD1424,661;
+****    0
+0001    502021
+SYMB   10SY03107NIL
+SYMD   39TMARDEF2V015070160300136003640157400737
+SXPO   52topmark for buoys, flag or other shape, paper-chart
+SCRF    6ACHBLK
+SVCT   32SPA;SW2;PU1574,1101;PD1710,737;
+****    0
+0001    502022
+SYMB   10SY01347NIL
+SYMD   39TMBYRD01V007500075000400004000055000550
+SXPO   12timber yard
+SCRF    6WLANDF
+SVCT   89SPW;SW2;PU550,650;PD950,650;PU550,850;PD950,850;PU650,550;PD650,950;PU850,550;PD850,950;
+****    0
+0001    502023
+SYMB   10SY02121NIL
+SYMD   39TNKCON02V014980149800284002840135601357
+SXPO    5tank
+SCRF    6ALANDF
+SVCT   27SPA;SW2;PU1498,1499;CI142;
+****    0
+0001    502024
+SYMB   10SY02122NIL
+SYMD   39TNKCON12V014980149800284002840135601357
+SXPO   17conspicuous tank
+SCRF    6ACHBLK
+SVCT   27SPA;SW2;PU1498,1499;CI142;
+****    0
+0001    502025
+SYMB   10SY03168NIL
+SYMD   39TNKFRM01V011270168500766007660073701285
+SXPO   10tank farm
+SCRF    6ALANDF
+SVCT   41SPA;SW1;ST0;PU1016,1574;PM0;CI97;PM2;FP;
+SVCT   41SPA;SW1;ST0;PU1018,1800;PM0;CI97;PM2;FP;
+SVCT   41SPA;SW1;ST0;PU1232,1794;PM0;CI90;PM2;FP;
+SVCT   41SPA;SW1;ST0;PU1237,1576;PM0;CI97;PM2;FP;
+SVCT   27SPA;SW1;PU1120,1668;CI383;
+****    0
+0001    502026
+SYMB   10SY03170NIL
+SYMD   39TNKFRM11V011320169000766007660074001285
+SXPO   22conspicuous tank farm
+SCRF    6ACHBLK
+SVCT   41SPA;SW1;ST0;PU1019,1574;PM0;CI97;PM2;FP;
+SVCT   41SPA;SW1;ST0;PU1021,1800;PM0;CI97;PM2;FP;
+SVCT   41SPA;SW1;ST0;PU1235,1794;PM0;CI90;PM2;FP;
+SVCT   41SPA;SW1;ST0;PU1240,1576;PM0;CI97;PM2;FP;
+SVCT   27SPA;SW1;PU1123,1668;CI383;
+****    0
+0001    502027
+SYMB   10SY01595NIL
+SYMD   39TOPMAR02V015100146000210001650152000785
+SXPO   46topmark for buoys, cone point up, paper-chart
+SCRF    6CCHBLK
+SVCT   60SPC;ST0;PU1665,785;PM0;PD1520,950,1730,950,1665,785;PM2;FP;
+****    0
+0001    502028
+SYMB   10SY01596NIL
+SYMD   39TOPMAR04V015150147000220001550156000785
+SXPO   48topmark for buoys, cone point down, paper-chart
+SCRF    6CCHBLK
+SVCT   60SPC;ST0;PU1560,785;PM0;PD1780,785,1620,940,1560,785;PM2;FP;
+****    0
+0001    502029
+SYMB   10SY01597NIL
+SYMD   39TOPMAR05V015050147000275004050151500545
+SXPO   53topmark for buoys, 2 cones point upward, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1665,770;PM0;PD1515,950,1750,950,1665,770;PM2;FP;PU1715,545;PM0;PD1565,730,1790,730,1715,545;PM2;FP;
+****    0
+0001    502030
+SYMB   10SY01598NIL
+SYMD   39TOPMAR06V015100147000280003700155500565
+SXPO   55topmark for buoys, 2 cones point downward, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1555,775;PM0;PD1790,775,1615,935,1555,775;PM2;FP;PU1600,565;PM0;PD1835,565,1665,725,1600,565;PM2;FP;
+****    0
+0001    502031
+SYMB   10SY01599NIL
+SYMD   39TOPMAR07V015050147000240003950155000555
+SXPO   53topmark for buoys, 2 cones base to base, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1550,785;PM0;PD1785,785,1615,950,1550,785;PM2;FP;PU1700,555;PM0;PD1570,730,1790,730,1700,555;PM2;FP;
+****    0
+0001    502032
+SYMB   10SY01600NIL
+SYMD   39TOPMAR08V015050147000330003850151000565
+SXPO   55topmark for buoys, 2 cones point to point, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1665,770;PM0;PD1510,950,1750,950,1665,770;PM2;FP;PU1595,565;PM0;PD1840,565,1670,730,1595,565;PM2;FP;
+****    0
+0001    502033
+SYMB   10SY01601NIL
+SYMD   39TOPMAR10V015150147000180001800155000775
+SXPO   39topmark for buoys, sphere, paper-chart
+SCRF    6CCHBLK
+SVCT   36SPC;PU1640,865;ST0;PM0;CI90;PM2;FP;
+****    0
+0001    502034
+SYMB   10SY01602NIL
+SYMD   39TOPMAR12V015100147000220004000155000555
+SXPO   42topmark for buoys, 2 spheres, paper-chart
+SCRF    6CCHBLK
+SVCT   63SPC;PU1640,865;ST0;PM0;CI90;PM2;FP;PU1680,645;PM0;CI90;PM2;FP;
+****    0
+0001    502035
+SYMB   10SY01603NIL
+SYMD   39TOPMAR13V015050147500215002200153500730
+SXPO   41topmark for buoys, cylinder, paper-chart
+SCRF    6CCHBLK
+SVCT   64SPC;SW2;PU1535,950;PD1685,950;PD1750,730;PD1590,730;PD1535,950;
+****    0
+0001    502036
+SYMB   10SY01604NIL
+SYMD   39TOPMAR14V015000148000240001400151000810
+SXPO   38topmark for buoys, board, paper-chart
+SCRF    6CCHBLK
+SVCT   64SPC;SW2;PU1510,950;PD1720,950;PD1750,810;PD1540,810;PD1510,950;
+****    0
+0001    502037
+SYMB   10SY01606NIL
+SYMD   39TOPMAR16V015000147000205001950153000765
+SXPO   46topmark for buoys, cube point up, paper-chart
+SCRF    6CCHBLK
+SVCT   75SPC;SW2;PU1530,840;PD1610,960;PD1735,885;PD1660,765;PU1530,840;PD1655,765;
+****    0
+0001    502038
+SYMB   10SY03108NIL
+SYMD   39TOPMAR17V015070160300421002630142200843
+SXPO   52topmark for buoys, flag or other shape, paper-chart
+SCRF    6ACHBLK
+SVCT   31SPA;SW2;PU1422,961;PD1843,961;
+SVCT   32SPA;SW2;PU1670,843;PD1574,1106;
+****    0
+0001    502039
+SYMB   10SY03383NIL
+SYMD   39TOPMAR18V015070160300421002630145500843
+SXPO   40topmark for buoys, T-Shape, paper-chart
+SCRF    6ACHBLK
+SVCT   31SPA;SW2;PU1455,844;PD1876,844;
+SVCT   32SPA;SW2;PU1670,843;PD1574,1106;
+****    0
+0001    502040
+SYMB   10SY01607NIL
+SYMD   39TOPMAR22V015000147500220001750139000745
+SXPO   48topmark for beacons, cone point up, paper-chart
+SCRF    6CCHBLK
+SVCT   60SPC;ST0;PU1500,745;PM0;PD1390,920,1610,920,1500,745;PM2;FP;
+****    0
+0001    502041
+SYMB   10SY01608NIL
+SYMD   39TOPMAR24V015000147000220001700139000750
+SXPO   50topmark for beacons, cone point down, paper-chart
+SCRF    6CCHBLK
+SVCT   60SPC;ST0;PU1390,750;PM0;PD1610,750,1500,920,1390,750;PM2;FP;
+****    0
+0001    502042
+SYMB   10SY01609NIL
+SYMD   39TOPMAR25V015000147000220003950139000525
+SXPO   55topmark for beacons, 2 cones point upward, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1500,525;PM0;PD1390,700,1610,700,1500,525;PM2;FP;PU1500,745;PM0;PD1390,920,1610,920,1500,745;PM2;FP;
+****    0
+0001    502043
+SYMB   10SY01610NIL
+SYMD   39TOPMAR26V015000146000220003900139000530
+SXPO   57topmark for beacons, 2 cones point downward, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1390,750;PM0;PD1610,750,1500,920,1390,750;PM2;FP;PU1390,530;PM0;PD1610,530,1500,700,1390,530;PM2;FP;
+****    0
+0001    502044
+SYMB   10SY01611NIL
+SYMD   39TOPMAR27V015000147500220003950139000525
+SXPO   55topmark for beacons, 2 cones base to base, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1500,525;PM0;PD1390,700,1610,700,1500,525;PM2;FP;PU1390,750;PM0;PD1610,750,1500,920,1390,750;PM2;FP;
+****    0
+0001    502045
+SYMB   10SY01612NIL
+SYMD   39TOPMAR28V015000147000220003900139000530
+SXPO   57topmark for beacons, 2 cones point to point, paper-chart
+SCRF    6CCHBLK
+SVCT  111SPC;ST0;PU1390,530;PM0;PD1610,530,1500,700,1390,530;PM2;FP;PU1500,745;PM0;PD1390,920,1610,920,1500,745;PM2;FP;
+****    0
+0001    502046
+SYMB   10SY01613NIL
+SYMD   39TOPMAR30V015000148000220002200139000730
+SXPO   41topmark for beacons, sphere, paper-chart
+SCRF    6CCHBLK
+SVCT   37SPC;PU1500,840;ST0;PM0;CI110;PM2;FP;
+****    0
+0001    502047
+SYMB   10SY01614NIL
+SYMD   39TOPMAR32V015000147000220004950139000455
+SXPO   44topmark for beacons, 2 spheres, paper-chart
+SCRF    6CCHBLK
+SVCT   65SPC;PU1500,565;ST0;PM0;CI110;PM2;FP;PU1500,840;PM0;CI110;PM2;FP;
+****    0
+0001    502048
+SYMB   10SY01615NIL
+SYMD   39TOPMAR33V015000147500150002450142500675
+SXPO   43topmark for beacons, cylinder, paper-chart
+SCRF    6CCHBLK
+SVCT   64SPC;SW2;PU1425,920;PD1575,920;PD1575,675;PD1425,675;PD1425,920;
+****    0
+0001    502049
+SYMB   10SY01616NIL
+SYMD   39TOPMAR34V015000148500240001350138000785
+SXPO   40topmark for beacons, board, paper-chart
+SCRF    6CCHBLK
+SVCT   64SPC;SW2;PU1380,920;PD1380,785;PD1620,785;PD1620,920;PD1380,920;
+****    0
+0001    502050
+SYMB   10SY01618NIL
+SYMD   39TOPMAR36V015000147500255002550137000700
+SXPO   48topmark for beacons, cube point up, paper-chart
+SCRF    6CCHBLK
+SVCT   64SPC;SW2;PU1500,700;PD1370,830;PD1500,955;PD1625,830;PD1500,700;
+****    0
+0001    502051
+SYMB   10SY02123NIL
+SYMD   39TOPMAR65V015150147500279002260149500757
+SXPO   40topmark for buoys, x-shape, paper-chart
+SCRF    6ACHBLK
+SVCT   31SPA;SW2;PU1533,757;PD1746,983;
+SVCT   31SPA;SW2;PU1774,793;PD1495,926;
+****    0
+0001    502052
+SYMB   10SY02124NIL
+SYMD   39TOPMAR85V015000148000220002200139000730
+SXPO   42topmark for beacons, x-shape, paper-chart
+SCRF    6CCHBLK
+SVCT   53SPC;SW2;PU1390,730;PD1610,950;PU1610,730;PD1390,950;
+****    0
+0001    502053
+SYMB   10SY03110NIL
+SYMD   39TOPMAR86V014360164800420003990121900680
+SXPO   48topmark for beacons, upright cross, paper-chart
+SCRF    6ACHBLK
+SVCT   32SPA;SW2;PU1429,680;PD1429,1079;
+SVCT   31SPA;SW2;PU1219,878;PD1639,878;
+****    0
+0001    502054
+SYMB   10SY02125NIL
+SYMD   39TOPMAR87V014960154100259003010137200721
+SXPO   51topmark for beacons, besom point down, paper-chart
+SCRF    6ACHBLK
+SVCT   32SPA;SW1;PU1499,722;PD1498,1021;
+SVCT   32SPA;SW1;PU1372,1022;PD1454,721;
+SVCT   32SPA;SW1;PU1631,1021;PD1540,721;
+****    0
+0001    502055
+SYMB   10SY02126NIL
+SYMD   39TOPMAR88V015030121600258003010137000401
+SXPO   49topmark for beacons, besom point up, paper-chart
+SCRF    6ACHBLK
+SVCT   31SPA;SW1;PU1502,701;PD1503,402;
+SVCT   31SPA;SW1;PU1628,401;PD1546,702;
+SVCT   31SPA;SW1;PU1370,402;PD1461,702;
+****    0
+0001    502056
+SYMB   10SY03111NIL
+SYMD   39TOPMAR89V014380167200420002800122100799
+SXPO   42topmark for beacons, T-shape, paper-chart
+SCRF    6ACHBLK
+SVCT   31SPA;SW2;PU1221,799;PD1641,799;
+SVCT   32SPA;SW2;PU1429,799;PD1429,1079;
+****    0
+0001    502057
+SYMB   10SY01621NIL
+SYMD   39TOWERS01V007500075000345006350057700172
+SXPO    6tower
+SCRF    6WLANDF
+SVCT  114SPW;PU750,750;SW2;CI57;PU645,750;PD692,172;PD807,172;PD855,750;PU807,750;PD922,750;PU692,750;PD577,750;PU687,282;
+SVCT   11PD807,282;
+****    0
+0001    502058
+SYMB   10SY01622NIL
+SYMD   39TOWERS02V007500075000300005500060000250
+SXPO   12water tower
+SCRF    6WLANDF
+SVCT  114SPW;PU750,750;SW2;CI50;PU650,750;PD693,350;PU850,350;PD850,250;PD650,250;PD650,350;PD850,350;PU600,750;PD700,750;
+SVCT   41PU800,750;PD900,750;PU850,750;PD803,346;
+****    0
+0001    502059
+SYMB   10SY01623NIL
+SYMD   39TOWERS03V007500075000346006350057500171
+SXPO   18conspicuous tower
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW1;CI56;PU643,750;PD690,171;PD806,171;PD853,750;PU806,750;PD921,750;PU690,750;PD575,750;PU684,281;
+SVCT   11PD806,281;
+****    0
+0001    502060
+SYMB   10SY02127NIL
+SYMD   39TOWERS05V007000110000300007500055000400
+SXPO   24radio, television tower
+SCRF    6ALANDF
+SVCT   25SPA;SW2;PU700,1100;CI50;
+SVCT   51SPA;SW2;PU600,1100;PD650,600;PD750,600;PD784,1100;
+SVCT   29SPA;SW2;PU634,690;PD750,690;
+SVCT   31SPA;SW2;PU550,1100;PD650,1100;
+SVCT   31SPA;SW2;PU750,1100;PD850,1100;
+SVCT   39SPA;SW2;PU800,400;PD850,450;PD850,600;
+SVCT   39SPA;SW2;PU600,400;PD550,450;PD550,600;
+****    0
+0001    502061
+SYMB   10SY01625NIL
+SYMD   39TOWERS12V007500075000300005500060000250
+SXPO   24conspicuous water tower
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW1;CI50;PU650,750;PD693,350;PU850,350;PD850,250;PD650,250;PD650,350;PD850,350;PU600,750;PD700,750;
+SVCT   41PU800,750;PD900,750;PU850,750;PD803,346;
+****    0
+0001    502062
+SYMB   10SY02128NIL
+SYMD   39TOWERS15V007000110000300007500055000400
+SXPO   36conspicuous radio, television tower
+SCRF    6ACHBLK
+SVCT   25SPA;SW2;PU700,1100;CI50;
+SVCT   51SPA;SW2;PU600,1100;PD650,600;PD750,600;PD784,1100;
+SVCT   29SPA;SW2;PU634,690;PD750,690;
+SVCT   31SPA;SW2;PU550,1100;PD650,1100;
+SVCT   31SPA;SW2;PU750,1100;PD850,1100;
+SVCT   39SPA;SW2;PU800,400;PD850,450;PD850,600;
+SVCT   39SPA;SW2;PU600,400;PD550,450;PD550,600;
+****    0
+0001    502063
+SYMB   10SY02129NIL
+SYMD   39TREPNT04V007500074900362003990057400348
+SXPO   26general symbol for a tree
+SCRF    6ALANDF
+SVCT   29SPA;SW1;PU750,747;PD750,350;
+SVCT   29SPA;SW1;PU574,447;PD936,447;
+SVCT   29SPA;SW1;PU641,545;PD858,545;
+SVCT   29SPA;SW1;PU684,348;PD814,348;
+SVCT   29SPA;SW1;PU695,747;PD815,747;
+SVCT   29SPA;SW1;PU638,396;PD858,396;
+SVCT   29SPA;SW1;PU621,494;PD883,494;
+****    0
+0001    502064
+SYMB   10SY02130NIL
+SYMD   39TREPNT05V007500075000400003000055000450
+SXPO    9mangrove
+SCRF    6ALANDF
+SVCT   25SPA;SW1;PU750,600;CI150;
+SVCT   29SPA;SW1;PU550,750;PD950,750;
+SVCT   29SPA;SW1;PU750,748;PD750,587;
+****    0
+0001    502065
+SYMB   10SY03411NIL
+SYMD   39TSLDEF51V022500225001209013500164701500
+SXPO   88one way lane of a traffic separation scheme, with the direction not defined in the data
+SCRF   12ATRFCDCCHMGD
+SVCT  120SPA;SW1;ST3;PU1950,1950;PM0;PD2250,1500;PD2550,1950;PD2400,1950;PD2400,2850;PD2100,2850;PD2100,1950;PD1950,1950;PM2;FP;
+SVCT  105SPA;SW1;PU2100,2850;PD2100,1950;PD1950,1950;PD2250,1500;PD2550,1950;PD2400,1950;PD2400,2850;PD2100,2850;
+SVCT  309SPC;SW1;PU1647,2232;PD1660,2204;PD1673,2179;PD1690,2162;PD1707,2145;PD1733,2136;PD1756,2138;PD1786,2147;PD1814,2153;PD1837,2187;PD1855,2206;PD1867,2228;PD1867,2253;PD1855,2277;PD1842,2298;PD1822,2322;PD1799,2347;PD1778,2373;PD1765,2392;PD1754,2409;PD1752,2431;PD1752,2454;PD1752,2471;PD1754,2493;PD1754,2495;
+SVCT   33SPC;SW2;PU1733,2579;PD1786,2579;
+SVCT  309SPC;SW1;PU2636,2232;PD2649,2204;PD2662,2179;PD2679,2162;PD2696,2145;PD2722,2136;PD2745,2138;PD2775,2147;PD2803,2153;PD2826,2187;PD2844,2206;PD2856,2228;PD2856,2253;PD2844,2277;PD2831,2298;PD2811,2322;PD2788,2347;PD2767,2373;PD2754,2392;PD2743,2409;PD2741,2431;PD2741,2454;PD2741,2471;PD2743,2493;PD2743,2495;
+SVCT   33SPC;SW2;PU2722,2579;PD2775,2579;
+****    0
+0001    502066
+SYMB   10SY02131NIL
+SYMD   39TSSCRS51V020490119200994009940024801794
+SXPO   22traffic crossing area
+SCRF    6ATRFCF
+SVCT   31SPA;SW3;PU747,2419;PD747,1954;
+SVCT   31SPA;SW2;PU680,2599;PD823,2599;
+SVCT   26SPA;SW2;PU745,2291;CI497;
+****    0
+0001    502067
+SYMB   10SY01630NIL
+SYMD   39TSSLPT51V022500225000600013500195001500
+SXPO   67traffic direction in a one way lane of a traffic separation scheme
+SCRF    6TTRFCD
+SVCT  120SPT;ST3;PU1950,1950;PM0;PD2250,1500,2550,1950,2400,1950,2400,2850,2100,2850,2100,1950,1950,1950;PM2;FP;SW1;PU2100,2850;
+SVCT   85PD2100,1950;PD1950,1950;PD2250,1500;PD2550,1950;PD2400,1950;PD2400,2850;PD2100,2850;
+****    0
+0001    502068
+SYMB   10SY03412NIL
+SYMD   39TSSRON51V004480074100895009150000100284
+SXPO   19traffic roundabout
+SCRF    6ATRFCF
+SVCT  269SPA;SW2;PU1,793;PD27,884;PD66,980;PD123,1057;PD212,1124;PD315,1180;PD430,1199;PD522,1194;PD620,1165;PD721,1112;PD800,1031;PD862,925;PD896,812;PD896,688;PD862,560;PD798,452;PD704,373;PD591,313;PD464,294;PD320,313;PD217,364;PD128,424;PD298,594;PD1,594;PD1,284;PD133,424;
+****    0
+0001    502069
+SYMB   10SY03413NIL
+SYMD   39TWRDEF51V022570207701361014030156801383
+SXPO   89two way route of a traffic separation scheme, with the direction not defined in the data
+SCRF   12ATRFCDICHMGD
+SVCT   45SPA;SW3;PU2134,1545;PD2257,1383;PD2380,1545;
+SVCT   45SPA;SW3;PU2043,1661;PD1960,1777;PD2108,1777;
+SVCT   45SPA;SW3;PU2394,1773;PD2557,1773;PD2463,1650;
+SVCT   33SPA;SW3;PU2105,1965;PD2105,2178;
+SVCT   33SPA;SW3;PU2398,1959;PD2398,2170;
+SVCT   45SPA;SW3;PU2137,2616;PD2260,2786;PD2383,2616;
+SVCT   45SPA;SW3;PU2105,2373;PD1946,2373;PD2040,2493;
+SVCT   45SPA;SW3;PU2401,2373;PD2550,2373;PD2466,2504;
+SVCT  309SPI;SW1;PU1568,1942;PD1581,1914;PD1594,1889;PD1611,1872;PD1628,1855;PD1654,1846;PD1677,1848;PD1707,1857;PD1735,1863;PD1758,1897;PD1776,1916;PD1788,1938;PD1788,1963;PD1776,1987;PD1763,2008;PD1743,2032;PD1720,2057;PD1699,2083;PD1686,2102;PD1675,2119;PD1673,2141;PD1673,2164;PD1673,2181;PD1675,2203;PD1675,2205;
+SVCT   33SPI;SW2;PU1654,2289;PD1707,2289;
+SVCT  309SPI;SW1;PU2709,1939;PD2722,1911;PD2735,1886;PD2752,1869;PD2769,1852;PD2795,1843;PD2818,1845;PD2848,1854;PD2876,1860;PD2899,1894;PD2917,1913;PD2929,1935;PD2929,1960;PD2917,1984;PD2904,2005;PD2884,2029;PD2861,2054;PD2840,2080;PD2827,2099;PD2816,2116;PD2814,2138;PD2814,2161;PD2814,2178;PD2816,2200;PD2816,2202;
+SVCT   33SPI;SW2;PU2795,2286;PD2848,2286;
+****    0
+0001    502070
+SYMB   10SY02133NIL
+SYMD   39TWRTPT52V022570207700611014030194601383
+SXPO   80reciprocal traffic directions in a two-way route of a traffic separation scheme
+SCRF    6ATRFCD
+SVCT   45SPA;SW3;PU2134,1545;PD2257,1383;PD2380,1545;
+SVCT   45SPA;SW3;PU2043,1661;PD1960,1777;PD2108,1777;
+SVCT   45SPA;SW3;PU2394,1773;PD2557,1773;PD2463,1650;
+SVCT   33SPA;SW3;PU2105,1965;PD2105,2178;
+SVCT   33SPA;SW3;PU2398,1959;PD2398,2170;
+SVCT   45SPA;SW3;PU2137,2616;PD2260,2786;PD2383,2616;
+SVCT   45SPA;SW3;PU2105,2373;PD1946,2373;PD2040,2493;
+SVCT   45SPA;SW3;PU2401,2373;PD2550,2373;PD2466,2504;
+****    0
+0001    502071
+SYMB   10SY03141NIL
+SYMD   39TWRTPT53V022570207700597013900196001383
+SXPO   80single traffic direction in a two-way route part of a traffic separation scheme
+SCRF    6ATRFCD
+SVCT   45SPA;SW3;PU2134,1545;PD2257,1383;PD2380,1545;
+SVCT   45SPA;SW3;PU2043,1661;PD1960,1777;PD2108,1777;
+SVCT   45SPA;SW3;PU2394,1773;PD2557,1773;PD2463,1650;
+SVCT   57SPA;SW3;PU2104,2585;PD2104,2773;PD2408,2773;PD2408,2588;
+SVCT   33SPA;SW3;PU2104,2435;PD2104,2278;
+SVCT   33SPA;SW3;PU2104,1937;PD2104,2087;
+SVCT   33SPA;SW3;PU2423,1929;PD2423,2075;
+SVCT   33SPA;SW3;PU2419,2270;PD2419,2422;
+****    0
+0001    502072
+SYMB   10SY02234NIL
+SYMD   39UNITFTH1V014100079500306004660130000595
+SXPO   41depth unit at ship's position is fathoms
+SCRF    6ASCLBR
+SVCT   31SPA;SW3;PU1303,797;PD1603,797;
+SVCT   43SPA;SW3;PU1606,595;PD1303,595;PD1300,1061;
+****    0
+0001    502073
+SYMB   10SY03002NIL
+SYMD   39UNITMTR1V014690081300404004690126200581
+SXPO   40depth unit at ship's position is metres
+SCRF    6ASCLBR
+SVCT   66SPA;SW3;PU1262,1050;PD1263,581;PD1466,960;PD1666,588;PD1666,1044;
+****    0
+0001    502074
+SYMB   10SY02249NIL
+SYMD   39UWTROC03V015000150000402004020129901299
+SXPO   45dangerous underwater rock of uncertain depth
+SCRF   12ADEPVSBCHBLK
+SVCT   42SPA;SW1;ST0;PU1500,1500;PM0;CI201;PM2;FP;
+SVCT   24SPB;SW1;PU1500,1300;PD;
+SVCT   24SPB;SW1;PU1500,1700;PD;
+SVCT   24SPB;SW1;PU1700,1500;PD;
+SVCT   24SPB;SW1;PU1300,1500;PD;
+SVCT   24SPB;SW1;PU1580,1310;PD;
+SVCT   24SPB;SW1;PU1640,1360;PD;
+SVCT   24SPB;SW1;PU1685,1425;PD;
+SVCT   24SPB;SW1;PU1675,1585;PD;
+SVCT   24SPB;SW1;PU1640,1640;PD;
+SVCT   24SPB;SW1;PU1575,1680;PD;
+SVCT   24SPB;SW1;PU1420,1680;PD;
+SVCT   24SPB;SW1;PU1360,1635;PD;
+SVCT   24SPB;SW1;PU1315,1570;PD;
+SVCT   24SPB;SW1;PU1320,1420;PD;
+SVCT   24SPB;SW1;PU1355,1355;PD;
+SVCT   24SPB;SW1;PU1420,1315;PD;
+SVCT   33SPB;SW1;PU1502,1371;PD1502,1648;
+SVCT   33SPB;SW1;PU1364,1505;PD1637,1506;
+****    0
+0001    502075
+SYMB   10SY03164NIL
+SYMD   39UWTROC04V015000150000400003250129701336
+SXPO   56rock which covers and uncovers or is awash at low water
+SCRF    6ACHBLK
+SVCT   33SPA;SW1;PU1697,1500;PD1297,1500;
+SVCT   33SPA;SW1;PU1614,1337;PD1379,1661;
+SVCT   33SPA;SW1;PU1614,1655;PD1374,1336;
+****    0
+0001    502076
+SYMB   10SY03211NIL
+SYMD   39VECGND01V007580038500590004150046600384
+SXPO   66arrowhead for ownship vector for course and speed over the ground
+SCRF    6ASHIPS
+SVCT   39SPA;SW2;PU593,796;PD760,587;PD931,799;
+SVCT   40SPA;SW2;PU466,736;PD758,384;PD1056,738;
+****    0
+0001    502077
+SYMB   10SY03213NIL
+SYMD   39VECGND21V007580038500590004150046600384
+SXPO   70arrowhead for ARPA or AIS vector for course and speed over the ground
+SCRF    6AARPAT
+SVCT   39SPA;SW2;PU593,796;PD760,587;PD931,799;
+SVCT   40SPA;SW2;PU466,736;PD758,384;PD1056,738;
+****    0
+0001    502078
+SYMB   10SY03222NIL
+SYMD   39VECWTR01V007580038500590003540046600384
+SXPO   68arrowhead for ownship vector for course and speed through the water
+SCRF    6ASHIPS
+SVCT   40SPA;SW2;PU466,736;PD758,384;PD1056,738;
+****    0
+0001    502079
+SYMB   10SY03214NIL
+SYMD   39VECWTR21V007580038500590003540046600384
+SXPO   72arrowhead for ARPA or AIS vector for course and speed through the water
+SCRF    6AARPAT
+SVCT   40SPA;SW2;PU466,736;PD758,384;PD1056,738;
+****    0
+0001    502080
+SYMB   10SY03186NIL
+SYMD   39WATTUR02V018170133301518003110096701184
+SXPO   31overfalls, eddies and breakers
+SCRF    6ACHGRD
+SVCT  308SPA;SW1;PU967,1485;PD1060,1458;PD1167,1380;PD1270,1235;PD1371,1184;PD1483,1194;PD1371,1294;PD1371,1392;PD1486,1495;PD1584,1456;PD1674,1397;PD1780,1235;PD1892,1186;PD1983,1191;PD1878,1294;PD1873,1402;PD1993,1495;PD2093,1451;PD2179,1397;PD2284,1242;PD2399,1189;PD2485,1196;PD2382,1326;PD2387,1402;PD2480,1495;
+****    0
+0001    502081
+SYMB   10SY01634NIL
+SYMD   39WAYPNT01V007500075000600006000045000450
+SXPO   26waypoint on planned route
+SCRF    6kPLRTE
+SVCT   25SPk;PU750,750;SW4;CI300;
+****    0
+0001    502082
+SYMB   10SY01635NIL
+SYMD   39WAYPNT03V015000150000600006000120001200
+SXPO   36waypoint on alternate planned route
+SCRF    6lAPLRT
+SVCT   27SPl;PU1500,1500;SW2;CI300;
+****    0
+0001    502083
+SYMB   10SY03179NIL
+SYMD   39WAYPNT11V007500075000600006000045000450
+SXPO   31next waypoint on planned route
+SCRF    6APLRTE
+SVCT   25SPA;SW2;PU750,750;CI300;
+SVCT   25SPA;SW2;PU748,749;CI198;
+****    0
+0001    502084
+SYMB   10SY01636NIL
+SYMD   39WEDKLP03V007500072501000002500025000600
+SXPO   11weed, kelp
+SCRF    6ECHGRD
+SVCT  121SPE;SW1;PU250,750;PD450,700;PD650,750;PD850,700;PD1050,750;PD1250,700;PU300,600;PD450,700;PU500,850;PD650,750;PU700,600;
+SVCT   32PD850,700;PU900,850;PD1050,750;
+****    0
+0001    502085
+SYMB   10SY01637NIL
+SYMD   39WIMCON01V007500075000368006630060000137
+SXPO   10windmotor
+SCRF    6WLANDF
+SVCT  114SPW;PU750,750;SW2;CI50;PU750,300;PD750,700;PU800,750;PD900,750;PU700,750;PD600,750;PU750,300;PD968,300;PU750,300;
+SVCT   31PD612,137;PU750,300;PD612,462;
+****    0
+0001    502086
+SYMB   10SY01638NIL
+SYMD   39WIMCON11V007500075000368006630060000137
+SXPO   22conspicuous windmotor
+SCRF    6CCHBLK
+SVCT  114SPC;PU750,750;SW2;CI50;PU750,300;PD750,700;PU800,750;PD900,750;PU700,750;PD600,750;PU750,300;PD968,300;PU750,300;
+SVCT   31PD612,137;PU750,300;PD612,462;
+****    0
+0001    502087
+SYMB   10SY03172NIL
+SYMD   39WNDFRM51V007490046800778007780036000078
+SXPO   20wind generator farm
+SCRF    6ALANDF
+SVCT   29SPA;SW2;PU750,300;PD968,300;
+SVCT   29SPA;SW2;PU750,300;PD612,137;
+SVCT   29SPA;SW2;PU750,300;PD612,462;
+SVCT   25SPA;SW2;PU749,467;CI389;
+SVCT   29SPA;SW2;PU596,746;PD899,746;
+SVCT   29SPA;SW2;PU749,298;PD749,745;
+****    0
+0001    502088
+SYMB   10SY03171NIL
+SYMD   39WNDFRM61V007470047800778007780036000078
+SXPO   32conspicuous wind generator farm
+SCRF    6ACHBLK
+SVCT   29SPA;SW2;PU750,300;PD968,300;
+SVCT   29SPA;SW2;PU750,300;PD612,137;
+SVCT   29SPA;SW2;PU750,300;PD612,462;
+SVCT   25SPA;SW2;PU749,467;CI389;
+SVCT   29SPA;SW2;PU596,746;PD899,746;
+SVCT   29SPA;SW2;PU749,298;PD749,745;
+****    0
+0001    502089
+SYMB   10SY01639NIL
+SYMD   39WNDMIL02V007500075000300004100060000450
+SXPO    9windmill
+SCRF    6WLANDF
+SVCT   65SPW;PU750,750;SW1;CI110;PU900,450;PD600,750;PU600,450;PD900,750;
+****    0
+0001    502090
+SYMB   10SY02037NIL
+SYMD   39WNDMIL12V007500075000300004100060000450
+SXPO   21conspicuous windmill
+SCRF    6CCHBLK
+SVCT   65SPC;PU750,750;SW1;CI110;PU900,450;PD600,750;PU600,450;PD900,750;
+****    0
+0001    502091
+SYMB   10SY01641NIL
+SYMD   39WRECKS01V007500075000650003500040000450
+SXPO   76wreck showing any portion of hull or superstructure at level of chart datum
+SCRF    6DCHGRD
+SVCT  114SPD;ST0;PU550,750;PM0;PD400,550,1050,750,800,750,800,740,795,730,790,720,780,710,770,700,760,700,745,700,735,700;
+SVCT  115PD720,705,705,720,700,735,700,740,700,750,550,750,550,750;PM2;FP;PU750,750;SW3;CI50;PU400,750;PD700,750;PU800,750;
+SVCT   32PD1050,750;PU740,650;PD800,450;
+****    0
+0001    502092
+SYMB   10SY01642NIL
+SYMD   39WRECKS04V007500075000500003000050000600
+SXPO   35non-dangerous wreck, depth unknown
+SCRF    6ECHBLK
+SVCT  118SPE;SW1;PU750,900;PD750,900;SPE;SW1;PU600,650;PD600,850;PU900,650;PD900,850;PU500,750;PD1000,750;PU750,600;PD750,900;
+****    0
+0001    502093
+SYMB   10SY02038NIL
+SYMD   39WRECKS05V007500075000590004100045300540
+SXPO   31dangerous wreck, depth unknown
+SCRF   12ADEPVSCCHBLK
+SVCT  196SPA;SW1;ST0;PU453,700;PM0;PD512,625;PD600,575;PD687,550;PD800,540;PD900,575;PD981,625;PD1040,700;PD1040,800;PD987,859;PD900,915;PD800,950;PD700,950;PD600,915;PD525,865;PD453,800;PD453,700;PM2;FP;
+SVCT   29SPC;SW1;PU600,650;PD600,850;
+SVCT   29SPC;SW1;PU900,650;PD900,850;
+SVCT   30SPC;SW1;PU500,750;PD1000,750;
+SVCT   29SPC;SW1;PU750,600;PD750,900;
+SVCT   22SPC;SW1;PU453,700;PD;
+SVCT   22SPC;SW1;PU512,625;PD;
+SVCT   22SPC;SW1;PU600,575;PD;
+SVCT   22SPC;SW1;PU687,550;PD;
+SVCT   22SPC;SW1;PU800,540;PD;
+SVCT   22SPC;SW1;PU900,575;PD;
+SVCT   22SPC;SW1;PU981,625;PD;
+SVCT   23SPC;SW1;PU1040,700;PD;
+SVCT   22SPC;SW1;PU453,800;PD;
+SVCT   22SPC;SW1;PU525,865;PD;
+SVCT   22SPC;SW1;PU600,915;PD;
+SVCT   22SPC;SW1;PU700,950;PD;
+SVCT   22SPC;SW1;PU800,950;PD;
+SVCT   22SPC;SW1;PU900,915;PD;
+SVCT   22SPC;SW1;PU987,859;PD;
+SVCT   23SPC;SW1;PU1043,793;PD;
+****    0

Added: packages/openev/branches/upstream/current/contrib/S52/S52raz.s
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52raz.s	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52raz.s	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,25 @@
+# S52raz.s: make an ELF object from PLib data
+#
+#
+#    This file is part of the OpENCview project, a viewer of ENC
+#    Copyright (C) 2003-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+
+# definition of symbol name and location
+.align 32
+.global S52raz
+.global S52razLen
+
+	     .data
+	     .type   S52raz, @object
+                                            
+# fill data segment
+S52raz:
+         .incbin "S52raz-3.2.rle"
+S52razLen: .long   . - S52raz
+         .type   S52razLen, @object
+
+
+# this work also
+#objcopy -v -I binary -O elf32-little \
+#--rename-section .data=.rodata in_file out_file

Added: packages/openev/branches/upstream/current/contrib/S52/S52type.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52type.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52type.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,45 @@
+// S52type.h: S52 type to bridge to external geometry name
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S52TYPE_H_
+#define _S52TYPE_H_
+
+// callback for each layer to add app. specific stuff
+typedef void (*_loadLayer_cb)(const char *layername, void *ogrlayer);
+typedef void (*_loadObj_cb)  (const char *objname,   void *ogrobj);
+
+
+// Addressed Object Type
+typedef enum S52_Obj_t {
+//typedef enum {
+    _META_T  =  0 ,       // meta geo stuff (ex: C_AGGR)
+    GRP_1_T  =  1 ,       // group 1 area, earth
+    POINT_T  = 'P',
+    LINES_T  = 'L',
+    AREAS_T  = 'A',
+    N_OBJ_T  = 5          // number of object type
+//};
+} S52_Obj_t;
+
+#endif //_S52TYPE_H_

Added: packages/openev/branches/upstream/current/contrib/S52/S52utils.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52utils.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52utils.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,241 @@
+// S52utils.c: utility
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+#include "S52utils.h"
+#include <stdio.h>        // FILE
+#include <string.h>       // strncmp()
+#include <limits.h>       // PATH_MAX
+#include <stdlib.h>       // exit()
+//#include <ctype.h>        // isblank()
+#include <glib.h>
+
+// configuration file
+#define CONF_NAME   "./s52test.conf"
+
+///////////////////////////////////////////////////////////////////
+//
+//   GLOBAL MARINER PARAMETER (will move out)
+//
+// NOTE: value for Chart No 1 found in README
+//
+// Soundings      ON
+// Text           ON
+// Depth Shades   4
+// Safety Contour 10 m
+// Safety Depth   7 m
+// Shallow        5 m
+// Deep           30 m
+
+/* A) value for Chart No 1 */
+
+double SHOW_TEXT       = TRUE;    // view group 23
+double TWO_SHADES      = FALSE;   // flag indicating selection of two depth shades (on/off) [default ON]
+double SAFETY_CONTOUR  = 10.0;    // selected safety contour (meters) [IMO PS 3.6]
+double SAFETY_DEPTH    = 7.0;     // selected safety depth (meters) [IMO PS 3.7]
+double SHALLOW_CONTOUR = 5.0;     // selected shallow water contour (meters) (optional)
+double DEEP_CONTOUR    = 30.0;    // selected deepwatercontour (meters) (optional)
+
+
+/* B) value for testing */
+/*
+//gboolean TWO_SHADES      = TRUE;     // flag indicating selection of two depth shades (on/off) [default ON]
+gboolean TWO_SHADES      = FALSE;    // flag indicating selection of two depth shades (on/off) [default ON]
+gboolean SHOW_TEXT       = TRUE;     // view group 23
+//double    SAFETY_DEPTH    = 30.0;    // selected safety depth (meters) [IMO PS 3.7]
+//double    SHALLOW_CONTOUR = 2.0;     // selected shallow water contour (meters) (optional)
+double    SAFETY_DEPTH    = 15.0;    // selected safety depth (meters) [IMO PS 3.7]
+double    SHALLOW_CONTOUR = 5.0;     // selected shallow water contour (meters) (optional)
+//double    SAFETY_CONTOUR  = 30.0;    // selected safety contour (meters) [IMO PS 3.6]
+//double    DEEP_CONTOUR    = 30.0;    // selected deepwatercontour (meters) (optional)
+//double    SAFETY_CONTOUR  = 5.0;     // selected safety contour (meters) [IMO PS 3.6]
+//double    DEEP_CONTOUR    = 10.0;    // selected deepwatercontour (meters) (optional)
+double    SAFETY_CONTOUR  = 10.0;    // selected safety contour (meters) [IMO PS 3.6]
+double    DEEP_CONTOUR    = 15.0;    // selected deepwatercontour (meters) (optional)
+*/
+
+/* param needed for certain conditional symbology */
+/*
+gboolean SHALLOW_PATTERN = FALSE;    // flag indicating selection of shallow water highlight (on/off)(optional) [default OFF]
+gboolean SHIPS_OUTLINE   = FALSE;    // flag indicating selection of ship scale symbol (on/off) [IMO PS 8.4]
+double   DISTANCE_TAGS   = 0.0;      // selected spacing of "distance to run" tags at a route (nm)
+double   TIME_TAGS       = 0.0;      // selected spacing of time tags at the pasttrack (min)
+gboolean FULL_SECTORS    = TRUE;     // show full length light sector lines
+gboolean SYMBOLIZED_BND  = TRUE;     // symbolized area boundaries
+*/
+
+// WARNING: must be in sync with S52_MAR_param_t
+static char *_MARparamNm[]  = {
+    "S52_MAR_NONE",             //= 0,    // default
+    "S52_MAR_SHOW_TEXT",        //= 1,    // view group 23
+    "S52_MAR_TWO_SHADES",       //= 2,    // flag indicating selection of two depth shades (on/off) [default ON]
+    "S52_MAR_SAFETY_CONTOUR",   //= 3,    // selected safety contour (meters) [IMO PS 3.6]
+    "S52_MAR_SAFETY_DEPTH",     //= 4,    // selected safety depth (meters) [IMO PS 3.7]
+    "S52_MAR_SHALLOW_CONTOUR",  //= 5,    // selected shallow water contour (meters) (optional)
+    "S52_MAR_DEEP_CONTOUR",     //= 6,    // selected deepwatercontour (meters) (optional)
+    "S52_MAR_SHALLOW_PATTERN",  //= 7,    // flag indicating selection of shallow water highlight (on/off)(optional) [default OFF]
+    "S52_MAR_SHIPS_OUTLINE",    //= 8,    // flag indicating selection of ship scale symbol (on/off) [IMO PS 8.4]
+    "S52_MAR_DISTANCE_TAGS",    //= 9,    // selected spacing of "distance to run" tags at a route (nm)
+    "S52_MAR_TIME_TAGS",        //= 10,   // selected spacing of time tags at the pasttrack (min)
+    "S52_MAR_FULL_SECTORS",     //= 11,   // show full length light sector lines
+    "S52_MAR_SYMBOLIZED_BND",   //= 12,   // symbolized area boundaries
+    "S52_MAR_SYMPLIFIED_PNT",   //= 13,   // simplified point
+    "S52_MAR_DISP_CATEGORY",    //= 14,   // display category
+    "S52_MAR_COLOR_PALETTE",    //= 15,   // color palette
+
+    "S52_MAR_NUM"               //= 16    // number of parameters
+};
+
+// WARNING: must be in sync with _MARparamNm
+static double _MARparamVal[] = {
+    0.0,      // NONE
+    TRUE,     // SHOW_TEXT
+    FALSE,    // TWO_SHADES (flase -> 4 shade)
+    //TRUE,    // TWO_SHADES (SEABED01 defautl)
+
+    15.0,     // SAFETY_CONTOUR
+    //0.0,     // SAFETY_CONTOUR  --to test DEPCNT02 selection (GL) in CA49995A.000
+    //0.5,     // SAFETY_CONTOUR  --to test DEPCNT02 selection (GL) in CA49995A.000
+    //30.0,      // SAFETY_CONTOUR (SEABED01 defautl)
+
+    7.0,      // SAFETY_DEPTH
+    //5.0,      // SAFETY_DEPTH
+
+    2.0,      // SHALLOW_CONTOUR (SEABED01 defautl)
+    //5.0,      // SHALLOW_CONTOUR
+
+    //30.0,     // DEEP_CONTOUR (SEABED01 defautl)
+    15.0,     // DEEP_CONTOUR
+
+    //FALSE,    // SHALLOW_PATTERN (SEABED01 defautl)
+    TRUE,    // SHALLOW_PATTERN
+
+    FALSE,    // SHIPS_OUTLINE
+    0.0,      // DISTANCE_TAGS
+    0.0,      // TIME_TAGS
+    TRUE,     // FULL_SECTORS
+
+    //TRUE,     // SYMBOLIZED_BND
+    FALSE,     // SYMBOLIZED_BND
+
+    //TRUE,     // SYMPLIFIED_PNT
+    FALSE,     // SYMPLIFIED_PNT
+
+//    'D',      // S52_MAR_DISP_CATEGORY --DISPLAYBASE
+//    'S',      // S52_MAR_DISP_CATEGORY --STANDARD
+      'O',      // S52_MAR_DISP_CATEGORY --OTHER
+
+      0,        // S52_MAR_COLOR_PALETTE --DAY_BRIGHT
+//    1,        // S52_MAR_COLOR_PALETTE --DAY_BLACKBACK
+//    2,        // S52_MAR_COLOR_PALETTE --DAY_WHITEBACK
+//    3,        // S52_MAR_COLOR_PALETTE --DUSK
+//    4,        // S52_MAR_COLOR_PALETTE --NIGHT
+
+    16.0      // NUM
+};
+
+
+int isblank(char c) {
+
+  if (c == ' ' || c == 0x09)
+    return 1;
+  return 0;
+  
+
+}
+
+//////////////////////////////////////////////////////////////////
+
+int S52_getConfig(const char *label, valueBuf *vbuf)
+// return TRUE and string value in vbuf for label, FLASE if fail
+{
+   FILE *fp;
+//   int ret = 0;
+   int  ret = 1;
+   char lbuf[PATH_MAX] = {'#'};
+   char tmp [PATH_MAX];
+   char frmt[PATH_MAX];
+
+   fp = fopen(CONF_NAME, "r");
+   if (NULL == fp) {
+       PRINTF("ERROR opening " CONF_NAME "\n");
+       return 0;
+   }
+
+   // prevent buffer overflow
+   sprintf(frmt, "%s%i%s", "%s %", MAXL-1, "[^\n]s");
+   //printf("frmt:%s\n", frmt);
+
+   while (ret > 0) {
+       if (lbuf[0] != '#') {
+           if (0 == strncmp(lbuf, label, strlen(label))) {
+               //sscanf(tmp, "%255s", *vbuf);
+               char *c = tmp;
+               while (isblank(*c)) c++;
+               sscanf(c, "%255[^\n]", *vbuf);
+               PRINTF("label:%s value:%s \n", lbuf, *vbuf);
+               fclose(fp);
+               return 1;
+           }
+       }
+
+
+       //ret = fscanf(fp, frmt, lbuf, vbuf);
+       ret = fscanf(fp, "%s%255[^\n]\n", lbuf, tmp);
+       //printf("label:%s \n", lbuf);
+       //printf("value:%s \n", tmp);
+       //printf("ret:%i\n", ret);
+   }
+   fclose(fp);
+
+   *vbuf[0] = '\0';
+   return 0;
+
+}
+
+double S52_getMarinerParam(S52_MAR_param_t param)
+// return Mariner parameter or '0.0' if fail
+// FIXME: check mariner param against groups selection
+{
+    valueBuf vbuf;
+
+    if (S52_MAR_NONE<param && param<S52_MAR_NUM) {
+
+        if (S52_getConfig(_MARparamNm[param], &vbuf))
+            return atof(vbuf);
+        else
+            return _MARparamVal[param];
+    }
+
+    return 0.0;
+}
+
+int    S52_setMarinerParam(S52_MAR_param_t param, double val)
+{
+    if (S52_MAR_NONE<param && param<S52_MAR_NUM)
+        _MARparamVal[param] = val;
+    else
+        return FALSE;
+
+    return TRUE;
+}

Added: packages/openev/branches/upstream/current/contrib/S52/S52utils.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S52utils.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S52utils.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,75 @@
+// S52utils.h: utility
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+#ifndef _S52UTILS_H_
+#define _S52UTILS_H_
+
+#include <stdio.h>  // printf()
+
+#ifdef SOLARIS
+// well should be cc
+#define PRINTF printf(__FILE__":%i: : ", __LINE__),printf
+#else
+#define PRINTF printf(__FILE__":%i: %s(): ", __LINE__, __FUNCTION__),printf
+#endif
+
+// valid label in .conf file
+#define CONF_CATALOG  "CATALOG"
+#define CONF_PLIB     "PLIB"
+#define CONF_CHART    "CHART"
+//#define CONF_RGB      "DUMMY_RGB_BRIGHT"
+
+// global parameter for mariners' selection
+typedef
+    enum S52_MAR_param_t {
+    S52_MAR_NONE            = 0,    // default
+    S52_MAR_SHOW_TEXT       = 1,    // view group 23
+    S52_MAR_TWO_SHADES      = 2,    // flag indicating selection of two depth shades (on/off) [default ON]
+    S52_MAR_SAFETY_CONTOUR  = 3,    // selected safety contour (meters) [IMO PS 3.6]
+    S52_MAR_SAFETY_DEPTH    = 4,    // selected safety depth (meters) [IMO PS 3.7]
+    S52_MAR_SHALLOW_CONTOUR = 5,    // selected shallow water contour (meters) (optional)
+    S52_MAR_DEEP_CONTOUR    = 6,    // selected deepwatercontour (meters) (optional)
+    S52_MAR_SHALLOW_PATTERN = 7,    // flag indicating selection of shallow water highlight (on/off)(optional) [default OFF]
+    S52_MAR_SHIPS_OUTLINE   = 8,    // flag indicating selection of ship scale symbol (on/off) [IMO PS 8.4]
+    S52_MAR_DISTANCE_TAGS   = 9,    // selected spacing of "distance to run" tags at a route (nm)
+    S52_MAR_TIME_TAGS       = 10,   // selected spacing of time tags at the pasttrack (min)
+    S52_MAR_FULL_SECTORS    = 11,   // show full length light sector lines
+    S52_MAR_SYMBOLIZED_BND  = 12,   // symbolized area boundaries
+
+    S52_MAR_SYMPLIFIED_PNT  = 13,   // simplified point
+
+    S52_MAR_DISP_CATEGORY   = 14,   // display category
+
+    S52_MAR_COLOR_PALETTE   = 15,   // color palette
+
+    S52_MAR_NUM             = 16    // number of parameters
+} S52_MAR_param_t;
+
+#define MAXL 256    // MAX lenght of buffer _including_ '\0'
+typedef char valueBuf[MAXL];
+
+extern int    S52_getConfig(const char *label, valueBuf *vbuf);
+extern double S52_getMarinerParam(S52_MAR_param_t param);
+extern int    S52_setMarinerParam(S52_MAR_param_t param, double val);
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/S57data.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S57data.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S57data.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,842 @@
+// S57gvgeo.c: interface to OpenEV S57 geo data
+//
+// Project:  OpENCview/OpenEV
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+#include "S57data.h"    // S57_geo
+#include "S52utils.h"   // PRINTF()
+
+#include <glib.h>       // GArray
+
+#include <stdlib.h>     // exit()
+#include <string.h>     // strlen()
+
+
+// MAXINT-6 is how OGR tag an UNKNOWN value
+// see gdal/ogr/ogrsf_frmts/s57/s57.h:126
+// it is then turn into a string in gv_properties
+#define EMPTY_NUMBER_MARKER "2147483641"  /* MAXINT-6 */
+
+//typedef gvgeocoord geocoord;
+
+static int id = 0;
+
+typedef struct _pt3{ double x,y,z; } pt3;
+
+typedef struct _rect {
+    //double x;
+    //double y;
+    //double width;
+    //double height;
+    double x1;
+    double y1;
+    double x2;
+    double y2;
+
+} _rect;
+
+// dta for glDrawArrays()
+struct _prim {
+    int mode;
+    int first;
+    int count;
+} _prim;
+
+typedef struct _S57_prim {
+    //int     nbr;     // total number for primitive for this object
+    GArray *list;     // list of _prim in 'vertex'
+    GArray *vertex;
+} _S57_prim;
+
+// S57 object geo data
+typedef struct _S57_geo {
+    int          id;          // record id (debug)
+    char         name[9];     // object name 6/8 + '\0'
+    S52_Obj_t    obj_t;       // used in CS
+
+    _rect        rect;        // lat/lon extend of object
+
+    geocoord     pointxyz[3]; // point
+
+    guint        linexyznbr;  // line
+    geocoord    *linexyz;
+        
+    guint        ringnbr;     // area
+    int         *ringxyznbr;
+    geocoord   **ringxyz;
+
+    //GArray      *XY;          // line/area list of projected geocoordinate
+
+    S57_prim    *prim;
+
+    GData       *attribs;
+
+    S57_geo     *next;        // list of geo passed to CS
+
+    void        *hGeom;       // opaque pointer to OGR geo
+                              // used for CS
+} _S57_geo;
+
+/////////////////////////////////////////////////////
+// projection (PROJ4)
+static projPJ pj    = NULL;
+static projPJ pjsrc = NULL;          // projection source
+static projPJ pjdst = NULL;          // projection destination
+static int _setNewPROJ = TRUE;       // will set new projection
+
+static void   _destroy_func(gpointer data)
+{
+    g_string_free((GString*)data, TRUE);
+}
+
+int        S57_doneObject(S57_geo *geoData)
+{
+    if (NULL == geoData){
+        PRINTF("ERROR: deleting NULL geoData \n");
+        return 0;
+    }
+
+    printf("Deleting geoData %d\n", geoData->id);
+
+    //if (NULL != geoData->XY)
+    //    g_array_free(geoData->XY, TRUE);
+
+    if (NULL != geoData->linexyz)
+        g_free((geocoord*)geoData->linexyz);
+
+    if (NULL != geoData->ringxyz){
+        int i;
+        for(i = 0; i < geoData->ringnbr; ++i)
+            g_free((geocoord*)geoData->ringxyz[i]);
+        g_free((geocoord*)geoData->ringxyz);
+    }
+
+    if (NULL != geoData->ringxyznbr)
+        g_free(geoData->ringxyznbr);
+
+
+    if (NULL != geoData->attribs)
+        g_datalist_clear(&geoData->attribs);
+
+    g_free((gpointer*)geoData);
+
+    return 1;
+}
+
+static int    _geo2prj3dv(guint npt, pt3 *data)
+// convert to XY 'in-place'
+{
+    int   i = 0;
+    pt3 *pt = data;
+    int ret = 0;
+
+    if (NULL==pjsrc || NULL==pjdst)
+        return 1;
+
+    // deg to rad --latlon
+    for (i=0; i<npt; ++i, ++data)
+        data->x *= DEG_TO_RAD,
+        data->y *= DEG_TO_RAD;
+
+    // rad to cartesian  --mercator
+    ret = pj_transform(pjsrc, pjdst, npt, 3, &pt->x, &pt->y, &pt->z);
+    if (0 != ret) {
+        PRINTF("ERROR in transform (%i): %s\n", ret, pj_strerrno(pj_errno));
+        exit(0);
+    }
+
+    return 1;
+}
+
+S57_geo   *S57_setPOINT(geocoord x, geocoord y, geocoord z)
+{
+    _S57_geo *geoData = g_new0(_S57_geo, 1);
+
+    geoData->id          = id++;
+    geoData->obj_t       = POINT_T;
+    geoData->pointxyz[0] = x;
+    geoData->pointxyz[1] = y;
+    geoData->pointxyz[2] = z;
+
+    //_geo2prj3dv(1,  geoData->pointxyz);
+
+    return geoData;
+}
+
+S57_geo   *S57_setLINES(guint xyznbr, geocoord *xyz)
+{
+    _S57_geo *geoData = g_new0(_S57_geo, 1);
+
+    geoData->id         = id++;
+    geoData->obj_t      = LINES_T;
+    geoData->linexyznbr = xyznbr;
+    geoData->linexyz    = xyz;
+
+    //_geo2prj3dv(geoData->linexyznbr,  geoData->linexyz);
+
+    return geoData;
+}
+
+S57_geo   *S57_setAREAS(guint ringnbr, int *ringxyznbr, geocoord **ringxyz)
+{
+    _S57_geo *geoData = g_new0(_S57_geo, 1);
+
+    geoData->id         = id++;
+    geoData->obj_t      = AREAS_T;
+    geoData->ringnbr    = ringnbr;
+    geoData->ringxyznbr = ringxyznbr;
+    geoData->ringxyz    = ringxyz;
+
+    //int i = 0;
+    //for (i=0; i<geoData->ringnbr; ++i)
+    //    _geo2prj3dv(geoData->ringxyznbr[i],  geoData->ringxyz[i]);
+
+    return geoData;
+}
+
+S57_geo   *S57_set_META()
+{
+    _S57_geo *geoData = g_new0(_S57_geo, 1);
+
+    geoData->id       = id++;
+    geoData->obj_t    = _META_T;
+
+    return geoData;
+}
+
+int        S57_setName(S57_geo *geoData, const char *name)
+{
+    //strncat(geoData->name, name, 6);
+    strcat(geoData->name, name);
+
+    return 1;
+}
+
+char      *S57_getName(S57_geo *geoData)
+{
+    return geoData->name;
+}
+
+int        S57_setOGRGeo(S57_geo *geoData, void *hGeom)
+{
+    geoData->hGeom = hGeom;
+
+    return 1;
+}
+
+void      *S57_getOGRGeo(S57_geo *geoData)
+{
+    return geoData->hGeom;
+}
+
+guint      S57_getRingNbr(S57_geo *geoData)
+{
+    if (AREAS_T != geoData->obj_t)
+        return 1;
+    else
+        return geoData->ringnbr;
+}
+
+int        S57_getGeoData(S57_geo *geoData, int ringNo, guint *npt, double **ppt)
+// helper providing uniform access to geoData
+{
+    if (NULL == geoData) {
+        PRINTF("WARNING: geoData == NULL !?\n");
+        *npt = 0;
+        return 0;
+    }
+
+    if (ringNo<0 ||
+        //(LINES_T==geoData->obj_t && 1 != ringNo)  ||
+        (AREAS_T==geoData->obj_t && geoData->ringnbr<ringNo)) {
+        PRINTF("ERROR: invalid ring number requested! \n");
+        *npt = 0;
+        exit(0);
+        //return 0;
+    }
+
+    switch (geoData->obj_t) {
+        case POINT_T:
+            *npt = 1;
+            *ppt = geoData->pointxyz;
+            break;
+
+        case LINES_T:
+            *npt = geoData->linexyznbr;
+            *ppt = geoData->linexyz; 
+            break;
+
+        case AREAS_T:
+            if (NULL != geoData->ringxyznbr) {
+                *npt = geoData->ringxyznbr[ringNo];
+                *ppt = geoData->ringxyz[ringNo];
+            } else
+                PRINTF("ERROR: atempt to access a tessed area .. \n"
+                       "(there is no geo anymore!!)\n");
+
+            // WARNING: check if begin/end vertex are the same
+            // (OpenGIS: closed and simple ring)
+            // If so shorten it to help trace tesss (in combineCB)
+            /*
+            {
+                int     n = 3 * (*npt-1);
+                double *p = *ppt;
+                if (p[0] == p[n+0] &&
+                    p[1] == p[n+1] &&
+                    p[2] == p[n+2]) {
+                    //--(*npt);
+                    // *npt -= 1;          // <<< shorten
+                } else {
+                    PRINTF("not close/simple ring\n");
+                    //PRINTF("    START: x=%f y=%f z=%f\n", p[0],p[1],p[2]);
+                    //PRINTF("    END:   x=%f y=%f z=%f\n", p[n+0],p[n+1],p[n+2]);
+                    //exit(0);
+                }
+
+            }
+            */
+
+            //if (geodata->ringnbr > 1) {
+            //    PRINTF("WARNING!!! AREA_T ringnbr:%i only exterior ring used\n", geodata->ringnbr);
+            //}
+            break;
+        default:
+            PRINTF("ERROR object type invalid!\n");
+            //return 1;
+            exit(0);
+    }
+
+    return 1;
+}
+
+//GArray    *S57_getXY(S57_geo *geoData)
+//{
+//    // FIXME: reset array if projection change
+//    if (NULL == geoData->XY)
+//        geoData->XY = g_array_new(FALSE, FALSE, sizeof(double)*3);
+//
+//    return geoData->XY;
+//}
+
+S57_prim  *S57_initPrim(S57_prim *prim)
+// set/reset primitive holder
+{
+    if (NULL == prim) {
+        S57_prim *p = g_new0(S57_prim, 1);
+
+        p->list   = g_array_new(FALSE, FALSE, sizeof(_prim));
+        p->vertex = g_array_new(FALSE, FALSE, sizeof(double)*3);
+
+        return p;
+    } else {
+        g_array_set_size(prim->list,   0);
+        g_array_set_size(prim->vertex, 0);
+
+        return prim;
+    }
+}
+
+S57_prim  *S57_donePrim(S57_prim *prim)
+{
+    if (NULL != prim->list)   g_array_free(prim->list,   TRUE);
+    if (NULL != prim->vertex) g_array_free(prim->vertex, TRUE);
+
+    // failsafe
+    prim->list   = NULL;
+    prim->vertex = NULL;
+
+    return NULL;
+}
+
+S57_prim  *S57_initPrimGeo(S57_geo *geoData)
+{
+    //if (NULL == geoData->XY)
+    //    geoData->XY = g_array_new(FALSE, FALSE, sizeof(double)*3);
+
+    geoData->prim = S57_initPrim(geoData->prim);
+
+    //geoData->prim.list   = g_array_new(FALSE, FALSE, sizeof(_prim));
+    //geoData->prim.vertex = g_array_new(FALSE, FALSE, sizeof(double)*3);
+
+    return geoData->prim;
+}
+
+S57_geo   *S57_donePrimGeo(S57_geo *geoData)
+{
+    g_free(geoData->prim);
+
+    geoData->prim = NULL;
+
+    return NULL;
+}
+
+int        S57_begPrim(S57_prim *prim, int data)
+{
+    struct _prim p;
+
+    p.mode  = data;
+    p.first = prim->vertex->len;
+
+    g_array_append_val(prim->list, p);
+
+    return 1;
+
+}
+
+int        S57_addPrimVertex(S57_prim *prim, double *ptr)
+{
+    //g_array_append_val(prim->vertex, *ptr);
+    g_array_append_vals(prim->vertex, ptr, 1);
+
+    return 1;
+}
+
+int        S57_endPrim(S57_prim *prim)
+{
+    struct _prim *p = &g_array_index(prim->list, struct _prim, prim->list->len-1);
+
+    p->count = prim->vertex->len - p->first;
+    return 1;
+}
+
+S57_prim  *S57_getPrimGeo(S57_geo *geoData)
+{
+    return geoData->prim;
+}
+
+S57_prim  *S57_getPrimData(S57_prim *prim, int *primNbr, double **vertex)
+
+{
+    *primNbr =          prim->list->len;
+    *vertex  = (double*)prim->vertex->data;
+
+    return prim;
+}
+
+GArray    *S57_getPrimVertex(S57_prim *prim)
+{
+    return prim->vertex;
+}
+
+S57_prim  *S57_setPrimSize(S57_prim  *prim, int sz)
+{
+    g_array_set_size(prim->list,   sz);
+    g_array_set_size(prim->vertex, sz);
+
+    return prim;
+}
+
+int        S57_getPrimIdx(S57_prim *prim, int i, int *mode, int *first, int *count)
+{
+    struct _prim *p = &g_array_index(prim->list, struct _prim, i);
+
+    if (NULL == p) {
+        PRINTF("ERROR: no primitive at index: %i\n", i);
+        return 0;
+    }
+
+    *mode  = p->mode;
+    *first = p->first;
+    *count = p->count;
+
+    return 1;
+}
+
+int        S57_setExt(S57_geo *geoData, double x1, double y1, double x2, double y2)
+{
+    geoData->rect.x1 = x1;
+    geoData->rect.y1 = y1;
+    geoData->rect.x2 = x2;
+    geoData->rect.y2 = y2;
+
+    return 1;
+}
+
+int        S57_getExt(S57_geo *geoData, double *x1, double *y1, double *x2, double *y2)
+{
+    *x1 = geoData->rect.x1;
+    *y1 = geoData->rect.y1;
+    *x2 = geoData->rect.x2;
+    *y2 = geoData->rect.y2;
+
+    return 1;
+}
+
+S52_Obj_t  S57_getObjtype(S57_geo *geoData)
+{
+    if (NULL == geoData)
+        return _META_T;
+
+    return geoData->obj_t;
+}
+
+int        S57_samePtPos(S57_geo *geoA, S57_geo *geoB)
+// return TRUE if 2 point are at the same position else FALSE
+{
+    // check if its the same object
+    if (geoA == geoB)
+        return FALSE;
+
+    if (geoA->pointxyz[0] == geoB->pointxyz[0] &&
+        geoA->pointxyz[1] == geoB->pointxyz[1] &&
+        geoA->pointxyz[2] == geoB->pointxyz[2])
+        return TRUE;
+    else
+        return FALSE;
+}
+
+// return the number of attributes. 
+
+static void   _countItems(GQuark key_id, gpointer data, gpointer user_data)
+{
+    char    *attName  = g_quark_to_string(key_id);
+    if (6 == strlen(attName)){
+        int *cnt = (int*)user_data;
+        *cnt = *cnt + 1;
+    }
+}
+
+int        S57_getNumAtt(S57_geo *geoData)
+{
+    int cnt = 0;
+    g_datalist_foreach(&geoData->attribs, _countItems, &cnt);
+    return cnt;
+}
+
+
+struct _qwerty {
+    int currentIdx;
+    char featureName[20];
+    char **name;
+    char **value;
+};
+
+static void getAttValues(GQuark key_id, gpointer data, gpointer user_data){
+
+    struct _qwerty *attData = (struct _qwerty*)user_data;
+   
+    GString *attValue = (GString*) data;
+    char    *attName  = g_quark_to_string(key_id);
+
+    if (6 == strlen(attName)){
+        strcpy(attData->value[attData->currentIdx], attValue->str);
+        strcpy(attData->name [attData->currentIdx], attName );
+        printf("inserting %s %s %d", attName, attValue->str, attData->currentIdx);
+        attData->currentIdx += 1;
+    } else {
+        ;//      printf("sjov Att: %s  = %s \n",attName, attValue->str);
+
+    }
+}
+
+// recommend you count number of attributes in advance, to allocate the
+// propper amount of **. each char *name should be allocated 7 and the char
+// *val 20 ????
+int        S57_getAttributes(S57_geo *geoData, char **name, char **val){
+  struct _qwerty tmp;
+
+  tmp.currentIdx = 0;
+  tmp.name       = name;
+  tmp.value      = val;
+  
+  g_datalist_foreach(&geoData->attribs, getAttValues,  &tmp);
+  //  strcpy(name[tmp.currentIdx], "x");
+  //  strcpy(val[tmp.currentIdx], "y"); 
+  return tmp.currentIdx;
+
+  
+
+}
+
+
+GString   *S57_getAttVal(S57_geo *geoData, char *att_name)
+// return attribute string value or NULL if:
+//      1- attribute name abscent
+//      2- its a mandatory attribute but its value is not define (EMPTY_NUMBER_MARKER)
+{
+    GString *att = (GString*) g_datalist_get_data(&geoData->attribs, att_name);
+
+    // mandatory attribute with ommited value
+    if (NULL!=att && (0==strncmp(att->str, EMPTY_NUMBER_MARKER, 10)))
+        return NULL;
+
+    // undefined value
+    if (NULL!=att && 0==att->len)
+        return NULL;
+
+    return att;
+}
+
+GData     *S57_setAtt(S57_geo *geoData, const char *name, const char *val)
+{
+    GQuark   qname = g_quark_from_string(name);
+    GString *value = g_string_new(val);
+
+    if (NULL == geoData)
+        return NULL;
+
+    if (NULL == geoData->attribs)
+        g_datalist_init(&geoData->attribs);
+
+    g_datalist_id_set_data_full(&geoData->attribs, qname, value, _destroy_func);
+
+    return NULL;
+}
+
+S57_geo   *S57_linkObj(S57_geo *geoData, S57_geo *geoNew)
+// add geo to list of object, return geo
+{
+    // geo should not be linked to anyone
+    g_assert(NULL == geoNew->next);
+
+    // or linked to them self --degenerated case
+    g_assert(geoData != geoNew);
+
+    geoNew->next = geoData->next;
+    geoData->next= geoNew;
+
+    return geoData;
+}
+
+S57_geo   *S57_unlinkObj(S57_geo *geoData)
+// remove the link between objects in list
+{
+    while (NULL != geoData) {
+        S57_geo *tmp = geoData->next;
+
+        geoData->next = NULL;
+        geoData = tmp;
+    }
+
+    return geoData;  // NULL
+}
+
+static void   _printAtt(GQuark key_id, gpointer data, gpointer user_data)
+{
+    char    *attName  = g_quark_to_string(key_id);
+    GString *attValue = (GString*) data;
+
+    if (6 == strlen(attName))
+        printf("\t%s : %s\n", attName, (char*)attValue->str);
+}
+
+S57_geo   *S57_nextObj(S57_geo *geoData)
+// get next geo object in list --pop top node if TRUE
+{
+    if (NULL == geoData)
+        return NULL;
+
+    return geoData->next;
+}
+
+int        S57_dumpData(S57_geo *geoData)
+// debug
+{
+    guint npt = 0;
+    geocoord *ppt;
+
+    switch (geoData->obj_t) {
+        case POINT_T:
+            npt = 1;
+            ppt = geoData->pointxyz;
+            //printf("POINT_T");
+            break;
+
+        case LINES_T:
+            npt = geoData->linexyznbr;
+            ppt = geoData->linexyz;
+            //printf("LINES_T");
+            break;
+
+        case AREAS_T:
+            if (NULL != geoData->ringxyznbr) {
+                npt = geoData->ringxyznbr[0];
+                ppt = geoData->ringxyz[0];
+            } else
+                PRINTF("ERROR: atempt to access a tessed area .. \n"
+                       "(there is no geo anymore!!)\n");
+
+            //if (geoData->ringnbr > 1) {
+            //    PRINTF("WARNING!!! AREA_T ringnbr:%i only exterior ring drawn", geoData->ringnbr);
+            //}
+            //printf("AREAS_T");
+            break;
+        default:
+            //PRINTF("WARNING: invalid object type; %i\n", geoData->obj_t);
+            ;
+    }
+
+
+    /*
+    {
+        int i   = 0;
+        switch (geoData->obj_t) {
+            case POINT_T:  printf("POINT_T"); break;
+            case LINES_T:  printf("LINES_T"); break;
+            case AREAS_T:  printf("AREAS_T"); break;
+            default:
+                PRINTF("WARNING: invalid object type; %i\n", geoData->obj_t);
+        }
+        printf("(%i): ", npt);
+
+        for (i=0; i<npt; ++i) {
+            printf("(%f,%f),", ppt[0], ppt[1]);
+            ppt += 3;
+        }
+        printf("\n");
+    }
+    */
+
+    printf("\tS57_ID : %i\n", geoData->id);
+    g_datalist_foreach(&geoData->attribs, _printAtt, NULL);
+
+    return 1;
+}
+
+int        S57_initPROJ()
+{
+    if (FALSE == _setNewPROJ)
+        return 1;
+
+    {   // setup source projection
+        char *argssrc[] = { "proj=latlong", "ellps=WGS84"};
+        if (!(pjsrc = pj_init(2, argssrc))){
+            PRINTF("error init src PROJ4\n");
+            exit(1);
+        }
+    }
+
+    {   // setup destination projection
+        char *argsdst[] = { "proj=merc", "ellps=WGS84","unit=meter" };
+        if (!(pjdst = pj_init(3, argsdst))){
+//      char *argsdst[] = { "proj=tmerc", "lon_0=11.8825", "lat_0=55.7290" , "a=6378137", "b=6356752","k=0.99997" };
+//      if (!(pjdst = pj_init(6, argsdst))){
+
+            PRINTF("error init dst PROJ4\n");
+            exit(1);
+        }
+    }
+
+    {   // GL matrix setup
+        char *args[] = { "proj=merc", "ellps=WGS84" };
+        if (!(pj = pj_init(2, args))){
+//      char *args[] = { "proj=tmerc", "lon_0=11.8825", "lat_0=55.7290" , "a=6378137", "b=6356752","k=0.99997" };
+//      if (!(pj = pj_init(6, args))){
+            PRINTF("error init src PROJ4\n");
+            exit(1);
+        }
+    }
+
+    // FIXME: will need resetting for different projection
+    _setNewPROJ = FALSE;
+
+    return 1;
+}
+
+int        S57_donePROJ()
+{
+    if (NULL != pj)    pj_free(pj);
+    if (NULL != pjsrc) pj_free(pjsrc);
+    if (NULL != pjdst) pj_free(pjdst);
+
+    pj    = NULL;
+    pjsrc = NULL;
+    pjdst = NULL;
+
+    _setNewPROJ = TRUE;
+
+    return 1;
+}
+
+projXY     S57_prj2geo(projUV pixel_xy)
+// convert PROJ to geographic (LL)
+{
+    if (NULL != pj) {
+
+        pixel_xy = pj_inv(pixel_xy, pj);
+        if (0 != pj_errno) {
+            PRINTF("x=%f y=%f %s\n", pixel_xy.u, pixel_xy.v, pj_strerrno(pj_errno));
+        }
+        pixel_xy.u /= DEG_TO_RAD;
+        pixel_xy.v /= DEG_TO_RAD;
+    }
+
+    return pixel_xy;
+}
+
+projXY     S57_geo2prj(projUV pixel_xy)
+// convert geographic (LL) to PROJ
+{
+    if (NULL != pj) {
+        pixel_xy.u *= DEG_TO_RAD;
+        pixel_xy.v *= DEG_TO_RAD;
+
+        pixel_xy = pj_fwd(pixel_xy, pj);
+        if (0 != pj_errno) {
+            PRINTF("x=%f y=%f %s\n", pixel_xy.u, pixel_xy.v, pj_strerrno(pj_errno));
+        }
+    }
+
+    return pixel_xy;
+}
+
+
+void       S57_getGeoWindowBoundary(double lat, double lng, double scale, int width, int height, double *latMin, double *latMax, double *lngMin, double *lngMax){
+  
+  S57_initPROJ();
+  
+  {
+    projUV pc1, pc2;   // pixel center
+
+    pc1.v = lat;
+    pc1.u = lng;
+
+    pc1 = S57_geo2prj(pc1); // mercator center in meters
+    
+    // lower right
+    pc2.u = (width/2. +0.5)*scale + pc1.u;
+    pc2.v = (height/2. +0.5)*scale + pc1.v;
+    pc2 = S57_prj2geo(pc2);
+    *lngMax = pc2.u;
+    *latMax = pc2.v;
+    // upper left
+    pc2.u = -((width/2.)*scale +0.5) + pc1.u;
+    pc2.v = -((height/2.)*scale +0.5) + pc1.v;
+    pc2 = S57_prj2geo(pc2);
+    *lngMin = pc2.u;
+    *latMin = pc2.v;
+  }
+
+  printf("lat/lng: %lf/%lf scale: %lf, w/h: %d/%d lat: %lf/%lf lng: %lf/%lf\n", lat, lng, scale, width, height, *latMin, *latMax, *lngMin, *lngMax);
+  
+  //S57_donePROJ();
+
+}
+
+
+#if 0
+int main(int argc, char** argv)
+{
+
+   return 1;
+}
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/S57data.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S57data.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S57data.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,110 @@
+// S57data.h: interface to S57 geo data
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2002-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S57DATA_H_
+#define _S57DATA_H_
+
+#include "S52type.h"    // S52_Obj_t
+
+#include <glib.h>       // guint, GArray, GData, GString
+#include <proj_api.h>   // projXY
+
+typedef double geocoord;
+typedef struct _S57_geo  S57_geo;
+typedef struct _S57_prim S57_prim;
+
+
+// FIXME: not called yet
+extern int       S57_doneObject(S57_geo *geoData);
+
+extern S57_geo  *S57_setPOINT(geocoord x, geocoord y, geocoord z);
+extern S57_geo  *S57_setLINES(guint xyznbr, geocoord *xyz);
+extern S57_geo  *S57_setAREAS(guint ringnbr, int *ringxyznbr, geocoord **ringxyz);
+extern S57_geo  *S57_set_META();
+extern int       S57_setName(S57_geo *geoData, const char *name);
+extern char     *S57_getName(S57_geo *geoData);
+extern int       S57_setOGRGeo(S57_geo *geoData, void *hGeom);
+extern void     *S57_getOGRGeo(S57_geo *geoData);
+
+
+// get the number of rings
+extern guint     S57_getRingNbr(S57_geo *geoData);
+// get data
+extern int       S57_getGeoData(S57_geo *geoData, int ringNo, guint *npt, double **ppt);
+// get cartesian coords
+//extern GArray   *S57_getXY(S57_geo *geoData);
+
+// handling of S52/S57 object rendering primitive
+extern S57_prim *S57_initPrim(S57_prim *prim);
+extern S57_prim *S57_donePrim(S57_prim *prim);
+extern S57_prim *S57_initPrimGeo(S57_geo *geoData);
+extern S57_geo  *S57_donePrimGeo(S57_geo *geoData);
+extern int       S57_begPrim(S57_prim *prim, int data);
+extern int       S57_addPrimVertex(S57_prim *prim, double *ptr);
+extern int       S57_endPrim(S57_prim *prim);
+extern S57_prim *S57_getPrimGeo(S57_geo *geoData);
+extern S57_prim *S57_getPrimData(S57_prim *prim, int *primNbr, double **vertex);
+extern GArray   *S57_getPrimVertex(S57_prim *prim);
+extern int       S57_getPrimIdx(S57_prim *prim, int i, int *mode, int *first, int *count);
+extern S57_prim *S57_setPrimSize(S57_prim  *prim, int sz);
+
+// get/set extend
+extern int       S57_setExt(S57_geo *geoData, double x1, double y1, double x2, double y2);
+extern int       S57_getExt(S57_geo *geoData, double *x1, double *y1, double *x2, double *y2);
+// get type (primitive) of object
+extern S52_Obj_t S57_getObjtype(S57_geo *geoData);
+// return TRUE if same point
+extern int       S57_samePtPos(S57_geo *geoA, S57_geo *geoB);
+// return S57 attribute value of the attribute name
+extern GString  *S57_getAttVal(S57_geo *geoData, char *name);
+// set attribute name and value
+extern GData    *S57_setAtt(S57_geo *geoData, const char *name, const char *val);
+// link list of object --return current object in list
+extern S57_geo  *S57_linkObj(S57_geo *crnt, S57_geo *geoData);
+// remove the link between objects in list
+extern S57_geo  *S57_unlinkObj(S57_geo *geoData);
+// get next geo in list
+extern S57_geo  *S57_nextObj(S57_geo *geoData);
+
+// count the number of 'real (6length)' attributes
+extern int       S57_getNumAtt(S57_geo *geoData);
+  // return the 'real' attributes of the geodata. name and val must be preallocated, and be sufficient large. (use S57_getNumAtt for counting)
+extern int       S57_getAttributes(S57_geo *geoData, char **name, char **val);
+
+
+extern int       S57_dumpData(S57_geo *geoData);
+
+extern int       S57_initPROJ();
+extern int       S57_donePROJ();
+
+
+extern projXY    S57_geo2prj(projUV pixel_xy);
+extern projXY    S57_prj2geo(projUV pixel_xy);
+
+// returns the window boundary with the current projection. After  the geo2prj and initproj have been public, this function may be moved to application layer. 
+extern void S57_getGeoWindowBoundary(double lat, double lng, double scale, int width, int height, double *latMin, double *latMax, double *lngMin, double *lngMax);
+
+// PROJ
+
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/S57gv.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S57gv.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S57gv.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,147 @@
+// S57gvgeo.c: interface to OpenEV S57 geo data
+//
+// Project:  OpENCview/OpenEV
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "S57gv.h"
+#include "S57data.h"         // S57_geo
+#include "S52utils.h"        // PRINTF()
+#include "gvshapes.h"        // GvShape
+#include "gvshapeslayer.h"   // GvShapes
+
+//#include "ogr_api.h"         // GEOS
+
+//tatic GPtrArray *depareL  = g_ptr_array_new();
+
+typedef struct GvShape _srcData;
+
+static int    _loadAtt(S57_geo *geoData, GvShape *shape)
+{
+    GvProperties *props = gv_shape_get_properties(shape);
+    int           nProp = gv_properties_count(props);
+    int           index = 0;
+
+    //printf("SHAPE NO:%i\n", i);
+
+    //g_datalist_init(&geoData->attribs);
+
+    for (index = 0; index<nProp; ++index) {
+        const char *propName  = gv_properties_get_name_by_index (props, index);
+        const char *propValue = gv_properties_get_value_by_index(props, index);
+
+        if (NULL ==  propValue) {
+            PRINTF("gv_properties_value = null \n");
+            exit(0);
+        }
+
+        S57_setAtt(geoData, propName, propValue);
+    }
+
+    return 1;
+}
+
+//S57_geo   *S57_gvLoadObject(const char *name, srcData *shape)
+S57_geo   *S57_gvLoadObject(const char *name, void *shape)
+// get object geo data from openev GvShape
+// NOTE: caller responsible to free mem for the moment
+{
+    //_S57_geo *geoData = g_new0(_S57_geo, 1);
+    S57_geo *geoData   = NULL;
+    int      shapetype = 0;
+    GvRect   rect;
+
+    S57_initPROJ();
+
+
+    // return empty shape --for pick & Mariners' object
+    if (NULL==shape) {
+        geoData = S57_set_META();
+
+        if (NULL != name)
+            S57_setName(geoData, name);
+
+        return geoData;
+    }
+
+    // mariners' object
+        shapetype = gv_shape_type((GvShape*)shape);
+
+    // check that geometric data type are in sync with OpenGL
+    g_assert(sizeof(geocoord) == sizeof(double));
+
+    // get geo data
+    switch (shapetype) {
+
+        case GVSHAPE_POINT: {
+            GvPointShape *point  = (GvPointShape *) shape;
+
+            geoData = S57_setPOINT(point->x, point->y, point->z);
+
+            break;
+        }
+
+        case GVSHAPE_LINE: {
+            GvLineShape  *line   = (GvLineShape *) shape;
+
+            geoData = S57_setLINES(line->num_nodes, line->xyz_nodes);
+
+            break;
+        }
+
+        case GVSHAPE_AREA: {
+            GvAreaShape *area    = (GvAreaShape *) shape;
+
+            geoData = S57_setAREAS(area->num_rings, area->num_ring_nodes, area->xyz_ring_nodes);
+
+            break;
+            // NOTE: check if winding is OK (at load time)
+        }
+
+        case GVSHAPE_COLLECTION: {
+            // ogr SPLIT_MULTIPOINT prob !!
+            //GvCollectionShape *collection  = (GvCollectionShape *) shape;
+            //int nCollection = gv_shape_collection_get_count(shape);
+
+            //PRINTF("nCollection = %i\n", nCollection);
+            geoData = S57_set_META();
+
+            break;
+        }
+
+        default: {
+            // FIXME: find a decent default (see openev/gvshapes.h)!!!
+            PRINTF("ERROR: invalid object type GVSHAPE_??? = %i\n", shapetype);
+        }
+    }
+
+    gv_shape_get_extents((GvShape*)shape, &rect);
+    S57_setExt(geoData, rect.x, rect.y, rect.x+rect.width, rect.y+rect.height);
+    S57_setName(geoData, name);
+
+    _loadAtt(geoData, (GvShape*)shape);
+
+    //_dumpData(geoData);
+    if (0 == strcmp(name, "DEPARE")) {
+
+    }
+
+    return geoData;
+}

Added: packages/openev/branches/upstream/current/contrib/S52/S57gv.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S57gv.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S57gv.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,34 @@
+// S57gv.h: interface to get S57 data from openev (GV)
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2002-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S57GV_H_
+#define _S57GV_H_
+
+#include "S57data.h"   // S57_geo
+
+//typedef struct _srcData srcData;
+
+extern S57_geo  *S57_gvObjectClone(const char *name, void *shape);
+
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/S57ogr.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S57ogr.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S57ogr.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,342 @@
+// S57ogr.c: interface to OGR S57 object data
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "S57ogr.h"     // S57_geo
+
+#include "S52utils.h"   // PRINTF()
+
+#include "ogr_api.h"    // GEOS, OGR
+
+#include <glib.h>       // GPtrArray
+
+typedef OGRFeatureH _srcData;
+//typedef struct _srcData OGRFeatureH;
+
+static GPtrArray *_depareL = NULL;
+//static GPtrArray *depcntL  = NULL;
+
+// FIXME: *HACK* *HACK* *HACK*
+static int _keepGeom = FALSE; // keep OGR feature im memory to resolve CS
+
+static void       _getExtent(OGRGeometryH geometry, S57_geo *geoData)
+{
+    OGREnvelope envelope;
+
+    if (NULL == geometry)
+        return;
+
+    OGR_G_GetEnvelope( geometry, &envelope );
+
+    S57_setExt(geoData, envelope.MinX, envelope.MinY, envelope.MaxX, envelope.MaxY);
+
+    return;
+}
+
+
+static int        _getGeoPtCount(OGRGeometryH hGeom, int iGeo, OGRGeometryH *hGeomRef )
+{
+    int vert_count = 0;
+
+    *hGeomRef  = OGR_G_GetGeometryRef( hGeom, iGeo );
+    if( NULL != *hGeomRef )
+    {
+        vert_count = OGR_G_GetPointCount( *hGeomRef );
+    }
+    else
+    {
+        /* FIXME: something is wrong in OGR if we get here
+         * ie the geometry handle doesn't refer to a geometry!
+         */
+      PRINTF("WARNING: got null geometry\n" );
+    }
+
+    return vert_count;
+}
+
+static int        _loadAtt(OGRFeatureH hFeature, S57_geo *geoData)
+{
+    int field_index = 0;
+    int field_count = OGR_F_GetFieldCount(hFeature);
+
+    for (field_index=0; field_index<field_count; ++field_index) {
+        if (OGR_F_IsFieldSet(hFeature, field_index)) {
+            const char *propName  = OGR_Fld_GetNameRef(OGR_F_GetFieldDefnRef(hFeature,field_index));
+            const char *propValue = OGR_F_GetFieldAsString(hFeature, field_index);
+
+            S57_setAtt(geoData, propName, propValue);
+        }
+    }
+
+    return 1;
+}
+
+static GPtrArray *_addGeom(GPtrArray *list, S57_geo *geoData)
+{
+    if (NULL == list)
+        list = g_ptr_array_new();
+
+    g_ptr_array_add(list, geoData);
+
+    return list;
+}
+
+static int        _keepOGRgeom(const char *layername)
+{
+    // FIXME: could we build this list when loading the PLib !?!
+    if ((0==strncmp(layername, "OBSTRN", 6)) ||
+        (0==strncmp(layername, "DEPARE", 6)) ||
+        (0==strncmp(layername, "DRGARE", 6)) ||
+        (0==strncmp(layername, "UWTROC", 6))
+        )
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static int        _loadObj(const char *objname, OGRFeatureH hFeature)
+{
+    S57_geo      *geoData = S57_ogrLoadObject(objname, hFeature);
+    OGRGeometryH  hGeom   = OGR_F_GetGeometryRef(hFeature);
+
+    _depareL = _addGeom(_depareL, geoData);
+    S57_setOGRGeo(geoData, hGeom);
+
+    return 1;
+}
+
+static int        _loadAux(OGRDataSourceH hDS)
+// load auxilary data used to resolve CS
+{
+    {   // line and area
+        OGRLayerH depare = OGR_DS_GetLayerByName(hDS, "DEPARE");
+        if (NULL != depare) {
+            S57_ogrLoadLayer("DEPARE", depare, _loadObj);
+            OGR_L_ResetReading(depare);
+        }
+    }
+
+    {   // area
+        OGRLayerH drgare = OGR_DS_GetLayerByName(hDS, "DRGARE");
+        if (NULL != drgare) {
+            S57_ogrLoadLayer("DRGARE", drgare, _loadObj);
+            OGR_L_ResetReading(drgare);
+        }
+    }
+
+    return 1;
+}
+
+int            S57_ogrLoadCell(const char *filename, _loadLayer_cb cb)
+{
+    int            iLayer = 0;
+    OGRDataSourceH hDS;
+    OGRSFDriverH   hDriver;
+    OGRLayerH      m_covrLayer;
+    const char    *drvName = NULL;
+
+    OGRRegisterAll();
+    hDS = OGROpen(filename, FALSE, &hDriver);
+
+    drvName = OGR_Dr_GetName(hDriver);
+
+    _loadAux(hDS);
+
+    for (iLayer=0; iLayer<OGR_DS_GetLayerCount(hDS); ++iLayer) {
+        OGRLayerH       ogrlayer  = OGR_DS_GetLayer(hDS, iLayer);
+        OGRFeatureDefnH defn      = OGR_L_GetLayerDefn(ogrlayer);
+        const char     *layername = OGR_FD_GetName(defn);
+
+        cb(layername, ogrlayer);
+    }
+
+    OGR_DS_Destroy (hDS);
+
+    return 1;
+}
+
+int            S57_ogrLoadLayer(const char *layername, void *ogrlayer,
+                                _loadObj_cb cb)
+{
+    OGRFeatureH hFeature = NULL;
+
+    _keepGeom = _keepOGRgeom(layername);
+
+    while ( NULL != (hFeature = OGR_L_GetNextFeature((OGRLayerH)ogrlayer))) {
+        cb(layername, hFeature);
+
+        if (FALSE == _keepGeom)
+            OGR_F_Destroy(hFeature);
+    }
+
+    return 1;
+}
+
+
+S57_geo       *S57_ogrLoadObject(const char *objname, void *feature)
+{
+    S57_geo           *geoData = NULL;
+    OGRGeometryH       hGeom   = OGR_F_GetGeometryRef((srcData*)feature);
+    OGRwkbGeometryType eType   = wkbNone;
+
+    // check that geometric data type are in sync with OpenGL
+    //g_assert(sizeof(geocoord) == sizeof(double));
+
+    S57_initPROJ();
+
+    if (NULL != hGeom)
+        eType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
+    else
+        geoData = S57_set_META();
+
+    switch (eType) {
+        // POINT
+        case wkbPoint25D:
+        case wkbPoint:
+            geoData = S57_setPOINT(OGR_G_GetX(hGeom, 0),
+                                   OGR_G_GetY(hGeom, 0),
+                                   OGR_G_GetZ(hGeom, 0));
+            break;
+
+        // LINE
+        case wkbLineString25D:
+        case wkbLineString: {
+            int    idx, node, count = OGR_G_GetPointCount(hGeom);
+            geocoord    *linexyz;
+
+            linexyz    = g_new(geocoord, 3*count);
+
+            for (idx=0, node=count-1; node>=0; --node) {
+                linexyz[node*3+0] = OGR_G_GetX(hGeom, node);
+                linexyz[node*3+1] = OGR_G_GetY(hGeom, node);
+                linexyz[node*3+2] = OGR_G_GetZ(hGeom, node);
+            }
+
+            geoData = S57_setLINES(count, linexyz);
+
+            break;
+        }
+
+        // AREA
+        case wkbPolygon25D:
+        case wkbPolygon: {
+            OGRGeometryH hRing;
+            int          iRing = 0;
+            int          nRingCount = OGR_G_GetGeometryCount(hGeom);
+            int         *ringxyznbr;
+            geocoord   **ringxyz;
+
+            ringxyznbr = g_new(int, nRingCount);
+            ringxyz    = g_new(geocoord *, nRingCount);
+
+            for (iRing=0; iRing<nRingCount; ++iRing) {
+                int node;
+                int vert_count = _getGeoPtCount(hGeom, iRing, &hRing);
+
+                ringxyznbr[iRing]  = vert_count;
+                ringxyz[iRing]     = g_new(geocoord, (vert_count+1)*3*sizeof(geocoord));
+
+                for (node=vert_count-1; node>=0; --node) {
+                    ringxyz[iRing][node*3+0] = OGR_G_GetX(hRing, node);
+                    ringxyz[iRing][node*3+1] = OGR_G_GetY(hRing, node);
+                    ringxyz[iRing][node*3+2] = OGR_G_GetZ(hRing, node);
+                }
+            }
+
+            geoData = S57_setAREAS(nRingCount, ringxyznbr, ringxyz);
+
+            break;
+        }
+
+        case wkbGeometryCollection:
+        case wkbMultiLineString:
+        case wkbMultiPoint:
+            // ogr SPLIT_MULTIPOINT prob !!
+            //GvCollectionShape *collection  = (GvCollectionShape *) shape;
+            //int nCollection = gv_shape_collection_get_count(shape);
+
+            //PRINTF("nCollection = %i\n", nCollection);
+
+            //geoData = S57_set_META();
+            //geoData->obj_t        = _META_T;
+
+            //break;
+
+        case wkbNone: break; // META_T
+
+        default:
+            // FIXME: find a decent default (see openev/gvshapes.h)!!!
+            PRINTF("invalid object type GVSHAPE_??? = %i %0x\n", eType, eType);
+
+    }
+
+    _getExtent(hGeom, geoData);
+    _loadAtt(feature, geoData);
+    S57_setName(geoData, objname);
+
+    if (TRUE == _keepGeom)
+        S57_setOGRGeo(geoData, hGeom);
+
+    //_dumpData(geoData);
+
+    return geoData;
+}
+
+int            S57_ogrTouche(S57_geo *geoData, S52_Obj_t obj_t)
+{
+    int          i    = 0;
+    OGRGeometryH geom = S57_getOGRGeo(geoData);
+
+    // FIXME: pull geometry at run-time, since we can't tell before hand
+    // if a LUP will call this from CS (via OBSTRN04 for exemple).
+    // Other wise it mean to keep all OGR geometry in memory !!!
+    // Or keep a list of who call this as we build the new lookup table !?
+    // (how about unloading lookup then!) .. need to think this out !
+    if (NULL == geom) {
+        PRINTF("FATAL ERROR: null geometry .. exiting\n");
+        exit(0);
+    }
+
+    for (i=0; i<_depareL->len; ++i) {
+        S57_geo      *geo  = g_ptr_array_index(_depareL, i);
+        OGRGeometryH  area = S57_getOGRGeo(geo);
+
+        // special case (_DEPVAL01) only group 1 is needed
+        // weed out DEPARE of type line
+        if (GRP_1_T==obj_t && AREAS_T!=S57_getObjtype(geo))
+            continue;
+
+        if (TRUE == OGR_G_Touches(area, geom)) {
+            S57_linkObj(geoData, geo);
+        }
+    }
+
+    return 1;
+}
+
+#if 0
+int main(int argc, char** argv)
+{
+
+   return 1;
+}
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/S57ogr.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/S57ogr.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/S57ogr.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,43 @@
+// S57ogr.h: interface to load S57 from OGR
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2002-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+*/
+
+
+#ifndef _S57OGR_H_
+#define _S57OGR_H_
+
+#include "S57data.h"   // S57_geo
+#include "S52type.h"   // _loadLayer_cb(), _loadObj_cb(), S52_obj_t
+
+typedef struct _srcData srcData;
+
+// callback for each layer to add app. specific stuff
+//typedef void (*_loadLayer_cb)(void *userData);
+
+
+
+extern int      S57_ogrLoadCell(const  char  *filename, _loadLayer_cb cb);
+extern int      S57_ogrLoadLayer(const char  *layername, void *ogrlayer,
+                                 _loadObj_cb cb);
+extern S57_geo *S57_ogrLoadObject(const char *objname,   void *shape);
+extern int      S57_ogrTouche(S57_geo *geoData, S52_Obj_t obj_t);
+#endif

Added: packages/openev/branches/upstream/current/contrib/S52/TODO
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/TODO	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/TODO	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,61 @@
+TODO: jot pad for libS52.so missing stuff, bug, snag, ...
+
+Last update: AUG2004
+
+
+Curent focus (top priority *):
+
+    GEO:
+**  -BUG: remove lower priority lines from line/area object
+    
+    GL:
+*   -use GEOS instead of stencil for CS
+    -add symbol scale factor in config
+    -move centroid inside concave poly for centered symbol
+    -handle overlaping symbol at the center of the window
+    -SCAMIN scale filter (CS: SCANMIN set to INFINITE)
+    -draw text at 'display priority' 8
+    -patern scale spacing (PASP field) not implemented.
+    -compute scale to display sector radius in Nautical Miles
+    -optimise tesselation to triangle fan/strip
+    -handle GLU_TESS_MAX_COORD
+    -glPushAttrib() to save GL state of the caller
+ 
+ 
+    PL:
+*   -fix breaker color problem in CA49995A.000
+    -signal object that land in layer 0 (ex TOPMAR/CA49995A.000/geo.id:123)
+    -check/validate QUAPOS of spatial (not attribute) object
+    -dump vector (save display list!)
+    -WRECK03
+    -handle update: reparse CS (ex: new value for TX cmd)
+    
+    Coding:
+*   -jitter/mismatch "Line Style" or "Line Color" with "Area Color"
+    -handle signal (async) from openev (interrupt drawing, ...)
+    -handle multiple cells
+    -test for proper configure of openev when loading libS52.so
+    -thread-safe
+    -check user root
+
+
+In a day:
+    -GL: save display list (somehow!)
+    -PL: Mariner's object
+    -BUG: S-52 specs 3.2 for TOPMAR01 (init 'floating' inside loop)
+    -BUG: chart no 1 PLib put $LINE in layer 0
+    -gv: commit tess combinecallback
+    -gv: commit bizard float->int type cast (warning triggered by tcc)
+    -gv: jitter (probably linker to the defect above)
+    -OGR: validate CRC (the source code is at IHO)
+    -OGR: IHOOCDD.XXX
+    -GL: magnifying glass a la ghostview (debug)
+    -GL: curves
+    -GL: display dipping distance of light
+    -GL: bearing snap to light
+    -connect to postGIS/SQLite!
+    -GPS!
+    -SVG!
+    -WWW!
+    -implement some vector instruction command that are not used now
+    -installation on windows

Added: packages/openev/branches/upstream/current/contrib/S52/doc/C1.lup_collision.txt
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/doc/C1.lup_collision.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/doc/C1.lup_collision.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1412 @@
+Object Class has different display priority
+NEW LUP RecID:44 ObjNm:DAMCON prio:6 rad:S
+TOP LUP RecID:43 ObjNm:DAMCON prio:3 rad:S
+Object Class has different display priority
+NEW LUP RecID:169 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:168 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:170 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:168 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:171 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:168 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:172 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:168 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:173 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:168 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:179 ObjNm:SLOGRD prio:3 rad:S
+TOP LUP RecID:178 ObjNm:SLOGRD prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:189 ObjNm:TS_FEB prio:4 rad:S
+TOP LUP RecID:188 ObjNm:TS_FEB prio:2 rad:S
+Object Class has different display priority
+NEW LUP RecID:190 ObjNm:TS_FEB prio:4 rad:S
+TOP LUP RecID:188 ObjNm:TS_FEB prio:2 rad:S
+Object Class has different display priority
+NEW LUP RecID:191 ObjNm:TS_FEB prio:4 rad:S
+TOP LUP RecID:188 ObjNm:TS_FEB prio:2 rad:S
+Object Class has different display priority
+NEW LUP RecID:212 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:213 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:214 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:215 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:216 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:217 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:218 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:219 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:220 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:221 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:222 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:223 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:224 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:225 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:226 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:211 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:237 ObjNm:$AREAS prio:3 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:239 ObjNm:$AREAS prio:3 rad:S
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:240 ObjNm:$AREAS prio:2 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:241 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:242 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:243 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:244 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:245 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:246 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:247 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:248 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:249 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:250 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:251 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:252 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:253 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:254 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:255 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:256 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:257 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:258 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:259 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:260 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:234 ObjNm:$AREAS prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:299 ObjNm:DAMCON prio:6 rad:S
+TOP LUP RecID:298 ObjNm:DAMCON prio:3 rad:S
+Object Class has different display priority
+NEW LUP RecID:424 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:423 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:425 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:423 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:426 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:423 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:427 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:423 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:428 ObjNm:SBDARE prio:3 rad:S
+TOP LUP RecID:423 ObjNm:SBDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:434 ObjNm:SLOGRD prio:3 rad:S
+TOP LUP RecID:433 ObjNm:SLOGRD prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:444 ObjNm:TS_FEB prio:4 rad:S
+TOP LUP RecID:443 ObjNm:TS_FEB prio:2 rad:S
+Object Class has different display priority
+NEW LUP RecID:445 ObjNm:TS_FEB prio:4 rad:S
+TOP LUP RecID:443 ObjNm:TS_FEB prio:2 rad:S
+Object Class has different display priority
+NEW LUP RecID:446 ObjNm:TS_FEB prio:4 rad:S
+TOP LUP RecID:443 ObjNm:TS_FEB prio:2 rad:S
+Object Class has different display priority
+NEW LUP RecID:467 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:468 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:469 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:470 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:471 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:472 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:473 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:474 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:475 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:476 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:477 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:478 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:479 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:480 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:481 ObjNm:VEGATN prio:3 rad:S
+TOP LUP RecID:466 ObjNm:VEGATN prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:492 ObjNm:$AREAS prio:3 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:494 ObjNm:$AREAS prio:3 rad:S
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:495 ObjNm:$AREAS prio:2 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:496 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:497 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:498 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:499 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:500 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:501 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:502 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:503 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:504 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:505 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:506 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:507 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:508 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:509 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:510 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:511 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:512 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:513 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:514 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:515 ObjNm:$AREAS prio:5 rad:O
+TOP LUP RecID:489 ObjNm:$AREAS prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:539 ObjNm:CBLSUB prio:6 rad:O
+TOP LUP RecID:538 ObjNm:CBLSUB prio:3 rad:O
+Object Class has different display priority
+NEW LUP RecID:680 ObjNm:$LINES prio:3 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:682 ObjNm:$LINES prio:6 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:683 ObjNm:$LINES prio:6 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:684 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:685 ObjNm:$LINES prio:3 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:686 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:687 ObjNm:$LINES prio:2 rad:S
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:688 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:689 ObjNm:$LINES prio:0 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:690 ObjNm:$LINES prio:0 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:691 ObjNm:$LINES prio:4 rad:S
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:692 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:693 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:694 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:695 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:696 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:697 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:698 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:699 ObjNm:$LINES prio:3 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:700 ObjNm:$LINES prio:3 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:701 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:702 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:703 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:704 ObjNm:$LINES prio:9 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:705 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:706 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:707 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:708 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:709 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:710 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:711 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:712 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:713 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:714 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:715 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:716 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:717 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:718 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:719 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:720 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:721 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:722 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:723 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:724 ObjNm:$LINES prio:5 rad:O
+TOP LUP RecID:679 ObjNm:$LINES prio:5 rad:S
+Object Class has different display priority
+NEW LUP RecID:820 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:821 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:822 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:823 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:824 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:825 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:826 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:827 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:828 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:829 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:830 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:832 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:819 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:850 ObjNm:CURENT prio:5 rad:O
+TOP LUP RecID:849 ObjNm:CURENT prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:851 ObjNm:CURENT prio:5 rad:O
+TOP LUP RecID:849 ObjNm:CURENT prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:853 ObjNm:DAMCON prio:4 rad:O
+TOP LUP RecID:852 ObjNm:DAMCON prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:888 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:889 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:890 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:891 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:892 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:893 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:894 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:895 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:896 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:898 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:899 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:900 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:901 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:902 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:903 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:904 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:905 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:906 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:907 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:908 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:909 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:910 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:911 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:912 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:913 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:914 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:924 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:887 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:937 ObjNm:MORFAC prio:8 rad:O
+TOP LUP RecID:932 ObjNm:MORFAC prio:6 rad:O
+Object Class has different display priority
+NEW LUP RecID:948 ObjNm:PRDARE prio:4 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:949 ObjNm:PRDARE prio:4 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:950 ObjNm:PRDARE prio:4 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:951 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:952 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:953 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:954 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:955 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:947 ObjNm:PRDARE prio:0 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:1055 ObjNm:$CSYMB prio:5 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1056 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1057 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1058 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1059 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1060 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1061 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1062 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1063 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1064 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1065 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1068 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1069 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1070 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1071 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1072 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1077 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1078 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1079 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1080 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1081 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1082 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1083 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1084 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1085 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1086 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1087 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1088 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1089 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1090 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1091 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1092 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1093 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1094 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1095 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1096 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1097 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1098 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1099 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1100 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1101 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1102 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different RADAR priority
+NEW LUP RecID:1103 ObjNm:$CSYMB prio:5 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different RADAR priority
+NEW LUP RecID:1104 ObjNm:$CSYMB prio:5 rad:S
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1105 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1106 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1107 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1108 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1109 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1110 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1111 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1112 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1113 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1114 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1115 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1116 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1117 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1118 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1119 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1120 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1122 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1123 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1124 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1125 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1127 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1128 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1129 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1130 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1131 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1132 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1133 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1134 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1135 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1136 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1137 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1138 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1139 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1140 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1141 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1054 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1236 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1237 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1238 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1239 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1240 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1241 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1242 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1243 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1244 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1245 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1246 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1248 ObjNm:BUISGL prio:6 rad:O
+TOP LUP RecID:1235 ObjNm:BUISGL prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1266 ObjNm:CURENT prio:5 rad:O
+TOP LUP RecID:1265 ObjNm:CURENT prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1267 ObjNm:CURENT prio:5 rad:O
+TOP LUP RecID:1265 ObjNm:CURENT prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1269 ObjNm:DAMCON prio:4 rad:O
+TOP LUP RecID:1268 ObjNm:DAMCON prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1304 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1305 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1306 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1307 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1308 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1309 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1310 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1311 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1312 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1314 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1315 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1316 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1317 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1318 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1319 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1320 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1321 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1322 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1323 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1324 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1325 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1326 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1327 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1328 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1329 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1330 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1349 ObjNm:LNDMRK prio:6 rad:O
+TOP LUP RecID:1303 ObjNm:LNDMRK prio:4 rad:O
+Object Class has different display priority
+NEW LUP RecID:1358 ObjNm:MORFAC prio:8 rad:O
+TOP LUP RecID:1357 ObjNm:MORFAC prio:6 rad:O
+Object Class has different display priority
+NEW LUP RecID:1359 ObjNm:MORFAC prio:8 rad:O
+TOP LUP RecID:1357 ObjNm:MORFAC prio:6 rad:O
+Object Class has different display priority
+NEW LUP RecID:1364 ObjNm:MORFAC prio:8 rad:O
+TOP LUP RecID:1357 ObjNm:MORFAC prio:6 rad:O
+Object Class has different display priority
+NEW LUP RecID:1375 ObjNm:PRDARE prio:4 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1376 ObjNm:PRDARE prio:4 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1377 ObjNm:PRDARE prio:4 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1378 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1379 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1380 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1381 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different display priority
+NEW LUP RecID:1382 ObjNm:PRDARE prio:3 rad:O
+TOP LUP RecID:1374 ObjNm:PRDARE prio:0 rad:S
+Object Class has different RADAR priority
+NEW LUP RecID:1482 ObjNm:$CSYMB prio:5 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1483 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1484 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1485 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1486 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1487 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1488 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1489 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1490 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1491 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1492 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1495 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1496 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1497 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1498 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1499 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1504 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1505 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1506 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1507 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1508 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1509 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1510 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1511 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1512 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1513 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1514 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1515 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1516 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1517 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1518 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1519 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1520 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1521 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1522 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1523 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1524 ObjNm:$CSYMB prio:6 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1525 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1526 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1527 ObjNm:$CSYMB prio:4 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1528 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1529 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different RADAR priority
+NEW LUP RecID:1530 ObjNm:$CSYMB prio:5 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different RADAR priority
+NEW LUP RecID:1531 ObjNm:$CSYMB prio:5 rad:S
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1532 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1533 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1534 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1535 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1536 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1537 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1538 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1539 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1540 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1541 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1542 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1543 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1544 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1545 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1546 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1547 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1549 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1550 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1551 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1552 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1554 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1555 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1556 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1557 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1558 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1559 ObjNm:$CSYMB prio:7 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1560 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1561 ObjNm:$CSYMB prio:9 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1562 ObjNm:$CSYMB prio:8 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1563 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1564 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1565 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1566 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1567 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+Object Class has different display priority
+NEW LUP RecID:1568 ObjNm:$CSYMB prio:4 rad:O
+TOP LUP RecID:1481 ObjNm:$CSYMB prio:5 rad:O
+;S52_dummy.BRIGHT.txt: dummy RBG value for S52 color table DAY_BRIGHT
+;
+;
+NODTA 171 192 177
+CURSR 230 121 56
+CHBLK 0 0 0
+CHGRD 136 152 139
+CHGRF 171 192 177
+CHRED 238  90 108
+CHGRN 124 240 91
+CHYLW 243 229 77
+CHMGD 198 77 187
+CHMGF 212 177 221
+CHBRN 182 157 64
+CHWHT 216 244 225
+SCLBR 230 121 56
+CHCOR 230 121 56
+LITRD 238 90 108
+LITGN 124 240 91
+LITYW 243 229 77
+ISDNG 198 77 187
+DNGHL 238 90 108
+TRFCD 198 77 187
+TRFCF 212 177 221
+LANDA 204 197 123
+LANDF 147 116 39
+CSTLN 95 106 96
+SNDG1 136 152 139
+SNDG2 0 0 0
+DEPSC 95 106 96
+DEPCN 136 152 139
+DEPDW 216 244 225
+DEPMD 192 225 214
+DEPMS 162 210 229
+DEPVS 129 195 226
+DEPIT 143 191 147
+RADHI 82 153 60
+RADLO 124 240 91
+ARPAT 82 153 60
+NINFO 230 121 56
+RESBL 76 135 227
+ADINF 184 172 60
+RESGR 136 152 139
+SHIPS 0 0 0
+PSTRK 0 0 0
+SYTRK 136 152 139
+PLRTE 218 70 45
+APLRT 230 121 56
+UINFD 0 0 3
+UINFF 240 240 240
+UIBCK 150 150 150
+UIAFD 220 220 220
+UINFR 251 95 99
+UINFG 75 210 86
+UINFO 255 240 70
+UINFB 54 130 244
+UINFM 200 72 201
+UIBDR 125 125 125
+UIAFF 185 185 185
+OUTLW 0 0 0
+OUTLL 204 197 123
+RES01 171 192 177
+RES02 171 192 177
+RES03 171 192 177
+BKAJ1 0 0 0
+BKAJ2 0 0 0 
+Line LUP             :67
+Area Plain LUP       :119
+Area Symbolized LUP  :119
+Point Simplified LUP :116
+Point Paper Chart LUP:116
+Line Symbology       :52
+Pattern Symbology    :25
+Symbol Symbology     :446
+Conditional Symbology:26
+gv_S57_layer_class_init
+NAME: ACHARE
+LAYER NAME: ACHARE (2)
+NAME: BCNSPP
+LAYER NAME: BCNSPP (1)
+perfect match: break here with debugger line 363
+NAME: BUISGL
+LAYER NAME: BUISGL (2)
+NAME: BOYCAR
+LAYER NAME: BOYCAR (1)
+perfect match: break here with debugger line 363
+NAME: BOYSPP
+LAYER NAME: BOYSPP (2)
+NAME: COALNE
+LAYER NAME: COALNE (4)
+NAME: CRANES
+LAYER NAME: CRANES (1)
+NAME: DAMCON
+LAYER NAME: DAMCON (1)
+NAME: DEPARE
+LAYER NAME: DEPARE (12)
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+NAME: DEPCNT
+LAYER NAME: DEPCNT (6)
+NAME: DRGARE
+LAYER NAME: DRGARE (1)
+NAME: DMPGRD
+LAYER NAME: DMPGRD (1)
+perfect match: break here with debugger line 363
+NAME: HULKES
+LAYER NAME: HULKES (1)
+NAME: LNDARE
+LAYER NAME: LNDARE (3)
+NAME: LNDMRK
+LAYER NAME: LNDMRK (7)
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+NAME: LIGHTS
+LAYER NAME: LIGHTS (3)
+NAME: NAVLNE
+LAYER NAME: NAVLNE (2)
+NAME: OBSTRN
+LAYER NAME: OBSTRN (2)
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+NAME: PIPOHD
+LAYER NAME: PIPOHD (1)
+perfect match: break here with debugger line 363
+NAME: PIPSOL
+LAYER NAME: PIPSOL (1)
+NAME: RECTRC
+LAYER NAME: RECTRC (2)
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+NAME: RESARE
+LAYER NAME: RESARE (1)
+NAME: SBDARE
+LAYER NAME: SBDARE (13)
+NAME: SLCONS
+LAYER NAME: SLCONS (18)
+NAME: SILTNK
+LAYER NAME: SILTNK (1)
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+NAME: SLOTOP
+LAYER NAME: SLOTOP (4)
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+perfect match: break here with debugger line 363
+NAME: SLOGRD
+LAYER NAME: SLOGRD (1)
+NAME: SOUNDG
+LAYER NAME: SOUNDG (0)
+NAME: SWPARE
+LAYER NAME: SWPARE (1)
+NAME: TOPMAR
+LAYER NAME: TOPMAR (1)
+NAME: UWTROC
+LAYER NAME: UWTROC (3)
+NAME: M_ACCY
+LAYER NAME: M_ACCY (1)
+NAME: M_COVR
+LAYER NAME: M_COVR (1)
+NAME: M_NSYS
+LAYER NAME: M_NSYS (1)
+NAME: M_QUAL
+LAYER NAME: M_QUAL (1)
+perfect match: break here with debugger line 363
+NAME: C_AGGR
+LAYER NAME: C_AGGR (0)
+NAME: C_ASSO
+LAYER NAME: C_ASSO (0)

Added: packages/openev/branches/upstream/current/contrib/S52/doc/att.txt
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/doc/att.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/doc/att.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8812 @@
+    S-57 Appendix A Chapter 2 - Attributes Edition 3.0
+    CONTENTS
+
+Acronym Code
+
+        2.1 Introduction            2.1
+
+
+
+   2.2 Feature Object Attributes
+   2.3
+   Agency responsible for production        AGENCY 1   2.4
+   Beacon shape                             BCNSHP 2   2.5
+   Building shape                           BUISHP 3   2.6
+   Buoy shape                               BOYSHP 4   2.7
+   Buried depth                             BURDEP 5   2.8
+   Call sign                                CALSGN 6   2.9
+   Category of airport/airfield             CATAIR 7   2.10
+   Category of anchorage                    CATACH 8   2.11
+   Category of bridge                       CATBRG 9   2.12
+   Category of building, single             CATBUI     2.14
+   Category of built-up area                CATBUA 10  2.15
+   Category of cable                        CATCBL 11  2.16
+   Category of canal                        CATCAN 12  2.17
+   Category of cardinal mark                CATCAM 13  2.18
+   Category of checkpoint                   CATCHP 14  2.19
+   Category of coastline                    CATCOA 15  2.20
+   Category of control point                CATCTR 16  2.22
+   Category of conveyor                     CATCON 17  2.23
+   Category of coverage                     CATCOV 18  2.24
+   Category of crane                        CATCRN 19  2.25
+   Category of dam                          CATDAM 20  2.26
+   Category of distance mark                CATDIS 21  2.27
+   Category of dock                         CATDOC 22  2.28
+   Category of dumping ground               CATDPG 23  2.29
+   Category of dyke                         CATDYK     2.30
+   Category of fence/wall                   CATFNC 24  2.31
+   Category of ferry                        CATFRY 25  2.32
+   Category of fishing facility             CATFIF 26  2.33
+   Category of fog signal                   CATFOG 27  2.34
+   Category of fortified structure          CATFOR 28  2.36
+   Category of gate                         CATGAT 29  2.37
+   Category of harbour facility             CATHAF 30  2.38
+   Category of hulk                         CATHLK 31  2.39
+   Category of ice                          CATICE 32  2.40
+   Category of installation buoy            CATINB 33  2.41
+   Category of land region                  CATLND 34  2.42
+   Category of landmark                     CATLMK 35  2.44
+   Category of lateral mark                 CATLAM 36  2.46
+   Category of light                        CATLIT 37  2.47
+   Category of marine farm/culture          CATMFA 38  2.49
+   Category of mast                         CATMST     2.50
+   Category of military practice area       CATMPA 39  2.51
+   Category of monument                     CATMNT     2.52
+   Category of mooring/warping facility     CATMOR 40  2.53
+   Category of navigation line              CATNAV 41  2.54
+   Category of obstruction                  CATOBS 42  2.55
+   Category of offshore platform            CATOFP 43  2.57
+   Category of oil barrier                  CATOLB 44  2.59
+   Category of pile                         CATPLE 45  2.60
+   Category of pilot boarding place         CATPIL 46  2.61
+   Category of pipeline/pipe                CATPIP 47  2.62
+   Category of production area              CATPRA 48  2.63
+   Category of production installation      CATPRI     2.64
+   Category of pylon                        CATPYL 49  2.65
+   Category of quality of data              CATQUA 50  2.66
+   Category of radar station                CATRAS 51  2.67
+   Category of radar transponder beacon     CATRTB 52  2.68
+   Category of radio station                CATROS 53  2.69
+   Category of recommended track            CATTRK 54  2.71
+   Category of religious building           CATREB     2.72
+   Category of rescue station               CATRSC 55  2.73
+   Category of restricted area              CATREA 56  2.74
+   Category of road                         CATROD 57  2.76
+   Category of runway                       CATRUN 58  2.77
+   Category of sea area                     CATSEA 59  2.78
+   Category of shoreline construction       CATSLC 60  2.83
+   Category of signal station, traffic      CATSIT 61  2.85
+   Category of signal station, warning      CATSIW 62  2.86
+   Category of silo/tank                    CATSIL 63  2.88
+   Category of slope                        CATSLO 64  2.89
+   Category of small craft facility         CATSCF 65  2.90
+   Category of special purpose mark         CATSPM 66  2.92
+   Category of Tidal Stream                 CAT_TS 188 2.96
+   Category of tower                        CATTOW     2.97
+   Category of Traffic Separation Scheme    CATTSS 67  2.98
+   Category of tree                         CATTRE     2.99
+   Category of vegetation                   CATVEG 68  2.100
+   Category of water turbulence             CATWAT 69  2.102
+   Category of weed/kelp                    CATWED 70  2.103
+   Category of wreck                        CATWRK 71  2.104
+   Category of zone of confidence in data   CATZOC 72  2.105
+   Character spacing                        $SPACE 73  2.108
+   Character specification                  $CHARS 74  2.109
+   Colour                                   COLOUR 75  2.110
+   Colour of navigational mark              COLMAR     2.111
+   Colour pattern                           COLPAT 76  2.112
+   Communication channel                    COMCHA 77  2.113
+   Compass size                             $CSIZE 78  2.114
+   Compilation date                         CPDATE 79  2.115
+   Compilation scale                        CSCALE 80  2.116
+   Condition                                CONDTN 81  2.117
+   Conspicuous, radar                       CONRAD 82  2.118
+   Conspicuous, visually                    CONVIS 83  2.119
+   Current velocity                         CURVEL 84  2.120
+   Date end                                 DATEND 85  2.121
+   Date start                               DATSTA 86  2.122
+   Depth range value 1                      DRVAL1 87  2.123
+   Depth range value 2                      DRVAL2 88  2.124
+   Depth units                              DUNITS 89  2.125
+   Elevation                                ELEVAT 90  2.126
+   Estimated range of transmission          ESTRNG 91  2.127
+   Exhibition condition of light            EXCLIT 92  2.128
+   Exposition of sounding                   EXPSOU 93  2.129
+   Function                                 FUNCTN 94  2.130
+   Height                                   HEIGHT 95  2.133
+   Height/length units                      HUNITS 96  2.134
+   Horizontal accuracy                      HORACC 97  2.135
+   Horizontal clearance                     HORCLR 98  2.136
+   Horizontal length                        HORLEN 99  2.137
+   Horizontal width                         HORWID 100 2.138
+   Ice factor                               ICEFAC 101 2.139
+   Information                              INFORM 102 2.140
+   Jurisdiction                             JRSDTN 103 2.141
+   Justification - horizontal               $JUSTH 104 2.142
+   Justification - vertical                 $JUSTV 105 2.143
+   Lifting capacity                         LIFCAP 106 2.144
+   Light characteristic                     LITCHR 107 2.145
+   Light visibility                         LITVIS 108 2.147
+   Marks navigational - System of           MARSYS 109 2.148
+   Multiplicity of lights                   MLTYLT 110 2.149
+   Nationality                              NATION 111 2.150
+   Nature of construction                   NATCON 112 2.151
+   Nature of surface                        NATSUR 113 2.152
+   Nature of surface - qualifying terms     NATQUA 114 2.154
+   Notice to Mariners date                  NMDATE 115 2.156
+   Object name                              OBJNAM 116 2.157
+   Orientation                              ORIENT 117 2.158
+   Periodic date end                        PEREND 118 2.159
+   Periodic date start                      PERSTA 119 2.160
+   Pictorial representation                 PICREP 120 2.161
+   Pilot district                           PILDST 121 2.162
+   Positional accuracy units                PUNITS 189 2.163
+   Producing country                        PRCTRY 122 2.164
+   Product                                  PRODCT 123 2.165
+   Publication reference                    PUBREF 124 2.167
+   Quality of sounding measurement          QUASOU 125 2.168
+   Quality of vertical measurement          QUAVEM     2.170
+   Radar wave length                        RADWAL 126 2.171
+   Radius                                   RADIUS 127 2.172
+   Recording date                           RECDAT 128 2.173
+   Recording indication                     RECIND 129 2.174
+   Reference year for magnetic variation    RYRMGV 130 2.175
+   Restriction                              RESTRN 131 2.176
+   Scale maximum                            SCAMAX 132 2.178
+   Scale minimum                            SCAMIN 133 2.179
+   Scale value one                          SCVAL1 134 2.180
+   Scale value two                          SCVAL2 135 2.181
+   Sector limit one                         SECTR1 136 2.182
+   Sector limit two                         SECTR2 137 2.183
+   Shift parameters                         SHIPAM 138 2.184
+   Signal frequency                         SIGFRQ 139 2.186
+   Signal generation                        SIGGEN 140 2.187
+   Signal group                             SIGGRP 141 2.188
+   Signal period                            SIGPER 142 2.189
+   Signal sequence                          SIGSEQ 143 2.190
+   Sounding accuracy                        SOUACC 144 2.191
+   Sounding distance - maximum              SDISMX 145 2.192
+   Sounding distance - minimum              SDISMN 146 2.193
+   Source date                              SORDAT 147 2.194
+   Source indication                        SORIND 148 2.195
+   Status                                   STATUS 149 2.196
+   Supervision of light                     SUPLIT     2.198
+   Survey authority                         SURATH 150 2.199
+   Survey date - end                        SUREND 151 2.200
+   Survey date - start                      SURSTA 152 2.201
+   Survey type                              SURTYP 153 2.202
+   Symbol scaling factor                    $SCALE 154 2.203
+   Symbolization code                       $SCODE 155 2.204
+   Technique of sounding measurement        TECSOU 156 2.205
+   Text string                              $TXSTR 157 2.207
+   Textual description                      TXTDSC 158 2.208
+   Tidal stream - panel values              TS_TSP 159 2.209
+   Tidal stream, current -time series values TS_TSV 160 2.210
+   Tide - accuracy of water level           T_ACWL 161 2.211
+   Tide - high and low water values         T_HWLW 162 2.212
+   Tide - method of tidal prediction        T_MTOD 163 2.213
+   Tide - time and height differences       T_THDF 164 2.214
+   Tide - time series values                T_TSVL 166 2.215
+   Tide - value of harmonic constituents    T_VAHC 167 2.216
+   Tide, current - time interval of values  T_TINT 165 2.217
+   Time end                                 TIMEND 168 2.218
+   Time start                               TIMSTA 169 2.219
+   Tint                                     $TINTS 170 2.220
+   Topmark/daymark shape                    TOPSHP 171 2.221
+   Traffic flow                             TRAFIC 172 2.224
+   Value of annual change in magnetic variation VALACM 173 2.225
+   Value of depth contour                   VALDCO 174 2.226
+   Value of local magnetic anomaly          VALLMA 175 2.227
+   Value of magnetic variation              VALMAG 176 2.228
+   Value of maximum range                   VALMXR 177 2.229
+   Value of nominal range                   VALNMR 178 2.230
+   Value of sounding                        VALSOU 179 2.231
+   Vertical accuracy                        VERACC 180 2.232
+   Vertical clearance                       VERCLR 181 2.233
+   Vertical clearance, closed               VERCCL 182 2.234
+   Vertical clearance, open                 VERCOP 183 2.235
+   Vertical clearance, safe                 VERCSA 184 2.236
+   Vertical datum                           VERDAT 185 2.237
+   Vertical length                          VERLEN 186 2.241
+   Water level effect                       WATLEV 187 2.242
+
+   2.3 National Language Attributes 2.243
+   Information in national language         NINFOM 300 2.244
+   Object name in national language         NOBJNM 301 2.245
+   Pilot district in national language      NPLDST 302 2.246
+   Text string in national language         $NTXST 303 2.247
+   Textual description in national language NTXTDS 304 2.248
+
+   2.4 Spatial and Meta Object Attributes 2.249
+   Horizontal datum                         HORDAT 400 2.250
+   Positional Accuracy                      POSACC 401 2.253
+   Quality of position                      QUAPOS 402 2.254
+   
+   
+2.1 Introduction
+
+Each attribute is specified in a standardized way, under the following headings:
+
+  Attribute: Attribute name.
+
+  Acronym:    six character code for the Attribute.
+
+  Code: integer code to be used in the coding of data.
+
+   Attribute type: one character code for the Attribute type (see below)
+
+  Each Attribute is assigned to one of six types:
+
+  * enumerated (E'): The expected input is a number selected from a list of pre- defined attribute values. Exactly one value must be chosen. The abbreviation for this type is E'.
+
+  * list (L'): The expected input is a list of one or more numbers selected from a list of pre-defined attribute values. Where more than one value is used, they must normally be separated by commas but in special cases slashes ("/") may be used. The abbreviation for this type is L'.
+
+    Note: In some cases, dependency exists between different attributes of a given object e.g. a bridge (BRIDGE) may have the values concreted' and iron/steel' for the attribute NATCON (Nature of Construction) and the values red' and green' for the attribute COLOUR. Even if it is known that the concreted part of the bridge is red and the iron/steel part is green, the Object Catalogue provides no means of indicating this relationship. However, such relationships may be formalized for a given application in which case the relationship must be described in the appropriate Product Specification (see S-57 Appendix B).
+
+  * float (F'): The expected input is a floating point numeric value with defined range, resolution, units and format. The abbreviation for this type is F'.
+
+  * integer (I'): The expected input is an integer numeric value with defined range, units and format. The abbreviation for this type is I'.
+
+  * coded string (A'): The expected input is a string of ASCII characters in a predefined format. The information is encoded according to defined coding systems e.g.: the nationality will be encoded by a two character field specified by ISO 3166 Codes for the Representation of Names of Countries', e.g. Canada => CA' (refer to S-57 Appendix A Annex A). The abbreviation for this type is A'.
+
+  * free text (S'): The expected input is a free-format alphanumeric string. It may be a file name which points to a text or graphic file. The abbreviation for this type is S'.
+
+  Expected input:
+
+  Depending on the attribute type, the expected input is defined in the following ways:
+
+  For  E' and L' type attributes a list of ID-numbers with associated, defined, meanings is given. Where an attribute value which appeared in a previous edition of the Standard is no longer used, it is retained in the list but is struck-through.
+
+  For A', F', I' and S'-type attributes the expected input is indicated  in accordance with the type (see above).
+
+  In certain circumstances, it may be necessary to indicate to the recipient of a data set that the value of a certain attribute for an instance of an object class is unknown. This fact is encoded by a zero length attribute value sub-field, e.g. COLOUR (where  is the subfield delimiter). This applies to all attribute types (see S-57 Part 3 clause 2.1).
+
+  Definitions: a definition of the Attribute, or in the case of E' or L' type Attributes, a definition of each value of an Attribute.
+
+  References:
+
+  * INT 1: Reference to the system of numbering for the paper chart feature as used in the International Chart Series INT 1 - Symbols, Abbreviations, Terms used on Charts'. INT 1 was one of the major guidelines for the definition of attributes.
+
+  * M-4: Reference to the paragraph number in the Chart Specifications of the IHO', M-4. This was another guideline for the definition and description of the attributes.
+
+  Minimum Value: The minimum value for the expected input is indicated for floating point and integer attributes.
+
+  Maximum Value: The maximum value for the expected input is indicated for floating point and integer  attributes.
+
+  Remarks: Under Remarks', further comments and notes may be given.
+
+Depending on the type of attribute, the  following information is provided:
+
+  Indication: For coded string type attributes (S) it indicates the construction of the string.
+
+   For integer (I) and floating point (F) type attributes it indicates the units and resolution of the input.
+
+  Format: The Format' statement indicates the recommended standard input template. Attributes that are identified as requiring a mandatory format, are indicated by the term (mandatory). For other attributes, the format can be either implied by the domain of valid attribute values or will be variable in length depending on the attribute and its data type.
+
+  Example: an example of coded input.
+
+There are five National Language Attributes which are defined in Section 2.3. These are all string type attributes intended to hold text in a national language.
+
+There are three Attributes that are defined as attributes of spatial objects. For further information see Section 2.4. 2.2 Feature Object Attributes
+
+FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: AGENCY Code: 1
+Agency responsible for production     AGENCY 1
+Attribute type: A
+
+
+Definition:
+
+ This attribute identifies the agency which produced the data.
+
+References:
+
+ INT 1: not specified;
+
+ M-4: not specified;
+
+Indication:
+
+ The agency is encoded by a two character code derived from ISO3166 (refer to S-57 Appendix A Annex A).
+
+Format:
+
+ c2 (mandatory)
+
+Remarks:
+
+ No remarks.
+ 
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: BCNSHP    Code: 2
+Beacon shape   BCNSHP 2
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : stake, pole, perch, post IQ 90; 456.1;
+ 2 : withy  IQ 92;  456.1;
+ 3 : beacon tower  IQ 110;  456.4;
+ 4 : lattice beacon  IQ 111;  456.4;
+ 5 : pile beacon
+ 6 : cairn  IQ 100;  456.2;
+ 7 : buoyant beacon   459.1;
+
+Definitions:
+
+stake, pole, perch, post:
+  an elongated wood or metal pole, embedded in the bottom to serve as a navigational aid or a support for a navigational aid. (adapted from IHO Dictionary S-32, 5th Edition, 4960)
+
+withy :  a tree without roots stuck or spoiled into the bottom of the sea to serve as a navigational aid.
+
+beacon tower: a solid structure of the order of 10 metres in height used as a navigational aid.
+
+lattice beacon: a structure consisting of strips of metal or wood crossed or interlaced to form a structure to serve as an aid to navigation or as a support for an aid to navigation.
+
+pile beacon: a long heavy timber(s) or section(s) of steel, wood, concrete, etc., forced into the seabed to serve as an aid to navigation or as a support for an aid to navigation.(Adapted from IHO Dictionary, S-32, 5th Edition, 3840 and Navigation Dictionary, US National Oceanic and Atmospheric Administration - NOAA, 1969)
+
+cairn: a mound of stones, usually conical or pyramidal, raised specifically for maritime navigation. (adapted from IHO Dictionary, S-32, 5th Edition, 601).
+
+buoyant beacon: a tall spar-like beacon fitted with a permanently submerged buoyancy chamber, the lower end of the body is secured to seabed sinker either by a flexible joint or by a cable under tension. (IHO Specifications, M-4, 459.1)
+
+Remarks:
+
+ The beacon shape describes the characteristic geometric form of the beacon. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: BUISHP    Code: 3
+Building shape    BUISHP 3
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : no specific shape
+ 2 : tower
+ 3 : spire
+ 4 : cupola (dome)
+ 5 : high-rise building
+ 6 : pyramid
+ 7 : cylindrical
+ 8 : spherical
+ 9 : cubic
+
+Definitions:
+
+high-rise building: a building having many storeys. (The New Shorter Oxford English Dictionary, 1993)
+
+pyramid: a polyhedron of which one face is a polygon of any number of sides, and the other faces are triangles with a common vertex. (The New Shorter Oxford English Dictionary, 1993)
+
+cylindrical: shaped like a cylinder, which is a solid geometrical figure generated by straight lines fixed in direction and describing with one of its points a closed curve, especially a circle. (The New Shorter Oxford English Dictionary, 1993)
+
+spherical: shaped like a sphere, which is a body the surface of which is at all points equidistant from the centre. (The New Shorter Oxford English Dictionary, 1993)
+
+cubic: a shape the sides of which are six equal squares; a regular hexahedron. (The New Shorter Oxford English Dictionary, 1993)
+
+Remarks:
+
+ The attribute `building shape` encodes some specific shapes of buildings.
+
+Values 2, 3 and 4 (tower, spire, and cupola/dome) have been transferred to the attribute category of landmark (CATLMK). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: BOYSHP    Code: 4
+Buoy shape    BOYSHP 4
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : conical (nun, ogival) IQ 20; 462.2
+ 2 : can (cylindrical) IQ 21; 462.3
+ 3 : spherical IQ 22; 462.4
+ 4 : pillar IQ 23; 462.5
+ 5 : spar (spindle) IQ 24; 462.6
+ 6 : barrel (tun) IQ 25; 462.7
+ 7 : super-buoy IQ 26; 462.9
+ 8 : ice buoy
+
+Definitions:
+
+conical/nun/ogival: the upper part of the body above the water-line, or the greater part of the superstructure, has approximately the shape or the appearance of a pointed cone with the point upwards.
+
+can/cylindrical: the upper part of the body above the water-line, or the greater part of the superstructure, has the shape of a cylinder, or a truncated cone that approximates to a cylinder, with a flat end uppermost.
+
+spherical:  the upper part of the body above the water-line, or the greater part of the superstructure, has the shape of a part of a sphere.
+
+pillar: the upper part of the body above the water-line, or the greater part of the superstructure is a narrow vertical structure,  pillar or lattice tower.
+
+spar/spindle: the upper part of the body above the water-line, or the greater part of the superstructure, has the form of a pole, or of a  very long  cylinder, floating upright.
+
+barrel:  the upper part of the body above the water-line, or the greater part of the superstructure, has the form of a barrel or cylinder floating horizontally.
+
+super-buoy:  a very large buoy, generally more than 5m in diameter.
+
+ice buoy:   a specially constructed shuttle shaped buoy which is used in ice conditions.
+
+Remarks:
+
+ The principal shapes are those recommended in the International Association of Lighthouse Authorities - IALA System. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: BURDEP    Code: 5
+Buried depth  BURDEP 5
+Attribute type: F
+
+
+Definition:
+
+ The depth below the sea bed to which an object is buried.
+
+
+References:
+
+ INT 1:  IL 42;
+
+ M-4:  444.5:
+
+
+Minimum Value: 0
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 2.5  for a depth of 2.5 metres.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CALSGN    Code: 6
+Call sign     CALSGN
+Attribute type: S
+
+
+Definition:
+
+ The designated call-sign of a radio station.
+
+References:
+
+ INT 1: not specified;
+
+ M-4: not specified;
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATAIR  Code: 7
+Category of airport/airfield   CATAIR 7
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : military aeroplane airport
+ 2 : civil aeroplane airport
+ 3 : military heliport
+ 4 : civil heliport
+ 5 : glider airfield
+ 6 : small planes airfield
+ 7 : helicopter platform
+ 8 : emergency airfield
+
+Definitions:
+
+military aeroplane airport:
+  a large military airfield usually equipped with a control tower, hangars and accommodation for the receiving and discharging of passengers or cargo. (adapted from The Macquarie Dictionary, 1988)
+
+civil aeroplane airport: a large airfield usually equipped with a control tower, hangars and accommodation for the receiving and discharging of passengers or cargo. (The Macquarie Dictionary, 1988)
+
+military heliport: a landing place for helicopters controlled by the military.
+
+civil heliport: a landing place for helicopters, often the roof of a building. (The Macquarie Dictionary, 1988)
+
+glider airfield: an area of land set aside for the take-off and landing of gliders.
+
+small planes airfield: an area of land set aside for the take-off and landing of small aeroplanes.
+
+emergency airfield: an area of land set aside for the take-off and landing of aeroplanes or helicopters in times of emergency.
+
+Remarks:
+
+ No remarks.
+
+Value number 7 (helicopter platform) has been transferred to the attribute category of runway (CATRUN). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATACH  Code: 8
+Category of anchorage     CATACH 8
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : unrestricted anchorage
+ 2 : deep water anchorage IN 12.4; 431.3;
+ 3 : tanker anchorage IN 12.5; 431.3;
+ 4 : explosives anchorage IN 12.7; 431.3;
+ 5 : quarantine anchorage IN 12.8; 431.3;
+ 6 : sea-plane anchorage IN 14; 449.6;
+ 7 : small craft anchorage
+ 8 : small craft mooring area IQ 44; 431.7;
+ 9 : anchorage for periods up to 24 hours IN 12.6; 431.3;
+
+Definitions:
+
+unrestricted anchorage: an area in which vessels anchor or may anchor. (IHO Dictionary, S-32, 5th Edition, 130)
+
+deep water anchorage: an area in which vessels of deep draught anchor or may anchor.
+
+tanker anchorage: an area in which tankers anchor or may anchor.
+
+explosives anchorage: an area set apart for anchored ships discharging or receiving explosives. (IHO Dictionary, S-32, 5th Edition, 1732)
+
+quarantine anchorage: an area where a vessel anchors when satisfying quarantine regulations. (IHO Dictionary, S-32, 5th Edition, 4117)
+
+sea-plane anchorage: an area in which sea-planes anchor or may anchor.
+
+small craft anchorage: an area in which yachts and small boats anchor or may anchor.
+
+small craft mooring area:
+  an area in which yachts and small boats moor.
+
+anchorage for periods up to 24 hours:
+  an area in which vessels anchor or may anchor for periods of up to 24 hours.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATBRG  Code: 9
+Category of bridge    CATBRG 9
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : fixed bridge ID 22; 381.1;
+ 2 : opening bridge ID 23.1; 381.3;
+ 3 : swing bridge ID 23.2; 381.3;
+ 4 : lifting bridge ID 23.3; 381.3;
+ 5 : bascule bridge ID 23.4; 381.3;
+ 6 : pontoon bridge ID 23.5; 381.3;
+ 7 : draw bridge ID 23.6; 381.3;
+ 8 : transporter bridge ID 24; 381.2;
+ 9 : footbridge
+ 10 : viaduct
+ 11 : aqueduct
+ 12 : suspension bridge
+
+Definitions:
+
+fixed bridge: a bridge having permanent horizontal and vertical alignment. (McGraw-Hill Dictionary of Scientific and Technical Terms, 3rd Edition, 1984)
+
+opening bridge: a bridge that is closed when set for carrying road traffic and open when set to permit marine traffic to pass through the waterway it crosses.  Modern opening (movable) bridges are either bascule, vertical lift or swing. (adapted from McGraw-Hill Encyclopaedia of Science and Technology, 7th Edition, 1992)
+
+swing bridge: a movable bridge (or span thereof) which rotates in a horizontal plane about a vertical pivot to allow the passage of vessels. (adapted from McGraw-Hill Encyclopaedia of Science and Technology, 7th Edition, 1992)
+
+lifting bridge: a movable bridge (or span thereof) which is capable of being lifted vertically to allow vessels to pass beneath. (adapted from IHO Dictionary, S-32, 5th Edition, 547)
+
+bascule bridge: a counterpoise bridge rotated in a vertical plane about an axis at one or both ends.  Also called a balance. (IHO Dictionary, S-32, 5th Edition, 545)
+
+pontoon bridge: a fixed floating bridge supported by pontoons. (McGraw-Hill Dictionary of Scientific and Technical Terms, 3rd Edition, 1984)
+
+draw bridge: a general name for bridges of which part or the entire span of the bridge may be raised or drawn aside to allow ships to pass through. (IHO Dictionary, S-32, 5th Edition, 546)
+
+transporter bridge: a bridge that has towers on each side of the waterway connected by a girder system on which a carriage runs. (IHO Chart Specifications, M-4, 381.2)
+
+foot bridge: a bridge structure used only for pedestrian traffic. (McGraw-Hill Dictionary of Scientific and Technical Terms, 3rd Edition, 1984)
+
+viaduct: a long bridge consisting of a series of beams, spans or girders (of steel, timber or concrete) supported on towers or piers and used to carry a road, railroad, etc. (adapted from McGraw-Hill Encyclopaedia of Science and Technology, 7th Edition, 1992)
+
+aqueduct: a bridge supporting an artificially elevated channel, for the conveyance of water. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+suspension bridge: a fixed bridge consisting of either a roadway or a truss suspended from two or more cables which pass over towers and are anchored by backstays to a firm foundation. (McGraw-Hill Encyclopaedia of Science and Technology, 7th Edition, 1992)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATBUI
+Category of building, single  CATBUI
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : building without function/service of major interest ID 5-6; 370.3,5;
+ 2 : harbour-master`s office IF 60; 325.1;
+ 3 : custom office IF 61; 325.2;
+ 4 : health office IF 62.1; 325.3;
+ 5 : hospital IF 62.2; 325.3;
+ 6 : post office IF 63; 372.1;
+ 7 : hotel
+ 8 : railway station ID 13; 362.2;
+ 9 : police station
+ 10 : water-police station
+ 11 : pilot office IT 3; 491.4;
+ 12 : pilot lookout IT 2; 491.3;
+ 13 : power station
+ 14 : bank office
+ 15 : headquarters for district control
+ 16 : transit shed/warehouse IF 51; 328.1;
+ 17 : factory
+ 18 : administrative
+ 19 : educational facility
+ 20 : inhabited building/house
+ 21 : uninhabited building/house
+ 22 : church IE 10; 373.2;
+ 23 : chapel
+ 24 : temple IE 13,16; 373.2;
+ 25 : pagoda IE 14; 373.3;
+ 26 : shinto-shrine IE 15; 373.3;
+ 27 : buddhist temple IE 16; 373.3;
+ 28 : mosque IE 17; 373.4;
+ 29 : marabout IE 18; 373.5;
+ 30 : coastguard building IT 10; 492.1-2;
+ 31 : stadium
+
+Remarks:
+
+ The attribute `category of single building` encodes the various types of single building.
+
+This attribute is obsolete. It is only included here for reasons of backward compatibility. Categories of building may now be encoded using the attribute function (FUNCTN).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATBUA  Code: 10
+Category of built-up area     CATBUA 10
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : urban area ID 1; 370.3-4;
+ 2 : settlement ID 2,3; 370.5,7;
+ 3 : village ID 4; 370.6;
+ 4 : town
+ 5 : city
+ 6 : holiday village
+
+Definitions:
+
+urban area: an area predominantly occupied by man-made structures used for residential, commercial, and industrial purposes. (Nautical Chart Manual, US Department of Commerce, 1992)
+
+settlement: a small collection of dwellings in a remote area.
+
+village: a collection of houses in a rural district, usually smaller than a town.
+
+town: any considerable collection of dwellings and other buildings larger than a village, but not incorporated as a city.
+
+city:  a major town inhabited by a large permanent community with all essential services.
+
+holiday village: a collection of smaller houses (cottages, mobile homes etc.) which is mainly populated on a seasonal basis.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCBL  Code: 11
+Category of cable     CATCBL 11
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : power line ID 26; IL 31.1; 382.1;
+ 2 : telephone/telegraph
+ 3 : transmission line
+ 4 : telephone ID 27; 382.2;
+ 5 : telegraph ID 27; 382.2;
+ 6 : mooring cable/chain
+
+Definitions:
+
+power line: a cable used for the supply of electricity.
+
+transmission line: multiple un-insulated cables usually supported by steel lattice towers. Such features are generally more prominent than normal power lines.
+
+telephone: a cable used for the transmission of telephone signals.
+
+telegraph: a cable used for the transmission of telegraph signals.
+
+mooring cable/chain: a cable or chain used to secure a mooring buoy or other floating structure.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCAN  Code: 12
+Category of canal     CATCAN 12
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : transportation
+ 2 : drainage
+ 3 : irrigation
+
+Definitions:
+
+transportation: a canal used for navigation as part of a transport system.
+
+drainage: a canal used to drain excess water from surrounding land.
+
+irrigation: a canal used to supply water for the purpose of irrigation.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCAM  Code: 13
+Category of cardinal mark     CATCAM 13
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1
+
+ 1 : north cardinal mark IQ 130.3;
+ 2 : east cardinal mark IQ 130.3;
+ 3 : south cardinal mark IQ 130.3;
+ 4 : west cardinal mark IQ 130.3;
+
+Definitions:
+
+ The four quadrants (north, east, south and west) are bounded by the true bearings NW-NE, NE-SE, SE-SW and SW-NW taken from the point of interest.
+
+ A cardinal mark is named after the quadrant in which it is placed.
+
+ The name of the cardinal mark indicates that it should be passed to the named side of the mark.
+
+Remarks:
+
+ Cardinal marks do not have a distinctive shape but are normally pillar or spar.  They are always painted in yellow and black horizontal bands and their distinctive double cone top-marks are always black. (Note that such top-marks are encoded as separate TOPMAR objects).  Cardinal marks may also have a special system of flashing white lights and if such lights are fitted they are encoded as separate LIGHTS objects. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCHP  Code: 14
+Category of checkpoint    CATCHP 14
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : custom
+
+Definitions:
+
+custom: an office, especially in ports, at which customs dues are collected or administrated. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCOA  Code: 15
+Category of coastline     CATCOA 15
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : steep coast IC 3; 312.1;
+ 2 : flat coast IC 5; 312.2;
+ 3 : sandy shore IC 6; 312.2;
+ 4 : stony shore IC 7; 312.2;
+ 5 : shingly shore IC 7; 312.2;
+ 6 : glacier (seaward end) IC 25; 353.8;
+ 7 : mangrove IC 32; 312.4;
+ 8 : marshy shore IC 33; 312.2;
+ 9 : coral reef
+ 10 : ice coast
+
+Definitions:
+
+steep coast: a coast backed by rock or earth cliffs, gives a good radar return and is useful for visual identification from a considerable distance off, where cliffs alternate with low lying coast along the shoreline. (IHO Chart Specifications, M-4)
+
+flat coast: a level coast with no obvious topographic features.
+
+sandy shore: a shoreline area made up of sand, i.e. loose material consisting of small but easily distinguishable, separate grains, between 0.0625 and 2.000 millimetres in diameter. (adapted from IHO Dictionary, S-32, 5th Edition, 4497)
+
+stony shore: a shoreline area made up of rock and rock fragments ranging in size from pebbles and gravel to boulders or large rock masses. (adapted from IHO Dictionary, S-32, 5th Edition, 5059)
+
+shingly shore: a shoreline area made up of rounded, often flat waterworn rock fragments larger than approximately 16 millimetres. (adapted from IHO Dictionary, S-32, 5th Edition, 4683)
+
+glacier, seaward end: projecting seaward extension of glacier, usually afloat. Also called glacier tongue. (IHO Hydrographic Dictionary, S-32, 5th Edition, 2043)
+
+mangrove: one of several genera of tropical trees or shrubs which produce many prop roots and grow along low lying coasts into shallow water. (IHO Hydrographic Dictionary, S-32, 5th Edition, 3064)
+
+marshy shore: a shoreline area made up of spongy land saturated with water. It may have a shallow covering of water, usually with a considerable amount of vegetation appearing above the surface. (adapted from IHO Dictionary, S-32, 5th Edition, 5240)
+
+coral reef: a reef, often of large extent, composed chiefly of coral and its derivatives. (IHO Dictionary, S-32, 5th Edition, 1063)
+
+ice coast: a vertical cliff forming the seaward edge of an ice shelf, ranging in height from 2m to 50m or more above sea level.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCTR  Code: 16
+Category of control point     CATCTR 16
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : triangulation point IB 20; 304.1
+ 2 : observation spot IB 21; 304.2
+ 3 : fixed point IB 22; 305.1
+ 4 : bench-mark IB 23; 304.3
+ 5 : boundary mark IB 24; 306
+ 6 : horizontal control, main station
+ 7 : horizontal control, secondary station
+
+Definitions:
+
+triangulation point: a recoverable point on the earth, whose geographic coordinates have been determined by angular methods with geodetic instruments. A triangulation point is a selected point, which has been marked with a station mark, or it is a conspicuous natural or artificial object. Also called trigonometric station or triangulation station. (IHO Dictionary, S-32, 5th Edition, 5646)
+
+observation spot: a point used by surveyors for determining precise position by astronomical means. (IHO Chart Specifications, M-4)
+
+fixed point:  a point whose position has been  accurately determined and plotted. (IHO Chart Specifications, M-4)
+
+bench-mark:  a permanent, stable object containing a marked point of known elevation with respect to a datum used as a reference level for tidal observations or as a control point for levelling. (IHO Dictionary, S-32, 5th Edition, 462)
+
+boundary mark:  a marker identifying the location of a surveyed  boundary line (Digital Geographic Information Standard - DIGEST, Oct.87)
+
+horizontal control, main station:
+  a station in a network of permanently marked control points having their geographic positions established to form third order accuracy or better. (Canadian Hydrographic Service, Survey Standing Order, 3.1-85)
+
+horizontal control, secondary station:
+  a station in a network of control points of a localized nature utilized for shoreline plots, sounding marks, stadia work, etc., whose geographic position may be established to a slightly lower order than main control points. (Canadian Hydrographic Service, Survey Standing Order, 3.1-85)
+
+Remarks:
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCON  Code: 17
+Category of conveyor   CATCON 17
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : aerial cableway (telepheric) ID 25; 382.3;
+ 2 : belt conveyor
+
+Definitions:
+
+aerial cableway (telepheric):
+  a conveyor along which material or people are transported by means of overhead cables supporting buckets, cable cars, etc.
+
+belt conveyor: a conveyor along which material or people are transported by means of a moving belt.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCOV  Code: 18
+Category of coverage     CATCOV
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : coverage available
+ 2 : no coverage available
+
+Definitions:
+
+coverage available: continuous coverage of spatial objects is available within this area.
+
+no coverage available: an area containing no spatial objects.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATCRN  Code: 19
+Category of crane     CATCRN 19
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : crane without specific construction
+ 2 : container crane/gantry IF 53.2; 328.3;
+ 3 : sheerlegs IF 53.3; 328.3;
+ 4 : travelling crane IF 53.1; 328.3;
+ 5 : A-frame
+
+Definitions:
+
+container crane/gantry: a high speed, shore-based crane used in the lift-on/lift-off operation of specially constructed containers. (adapted from Nautical Chart Manual, US Department of Commerce, Coast and Geodetic Survey, 7th Edition)
+
+sheerlegs: a tripodal structure used in dockyards and harbours for stepping masts or lifting loads in to and out of vessels.
+
+travelling crane: a crane mounted on rails (track) that can move (usually parallel to the wharf face) in order to load and unload cargo vessels. (Canadian Hydrographic Service)
+
+A-frame: a type of crane shaped like the letter A'.  They are often positioned on river banks or the coastline and are used for lifting logs from logging trucks and depositing them in the water. (Canadian Hydrographic Service)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATDAM  Code: 20
+Category of dam   CATDAM 20
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : weir
+ 2 : dam IF 44; 364.2;
+ 3 : flood barrage IF 43; 326.7;
+
+Definitions:
+
+weir: a dam erected across a river to raise the level of the water.  A fence of stakes set in a river or along the shore as a trap for fish.
+  The word is now restricted to smaller works, the larger are called dams. (IHO Dictionary, S-32, 5th Edition, 5967)
+
+dam: a barrier to check or confine anything in motion; particularly one constructed to hold back water and raise its level to form a reservoir, or to prevent flooding. (IHO Dictionary, S-32, 5th Edition, 1196)
+
+flood barrage: an opening dam across a channel which, when required, is closed to control flood waters. (IHO Chart Specifications, M-4 326.7)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATDIS  Code: 21
+Category of distance mark     CATDIS 21
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : distance mark not physically installed IF 40;
+ 2 : visible mark, pole IF 40;
+ 3 : visible mark, board IF 40;
+ 4 : visible mark, unknown shape IF 40;
+
+Definitions:
+
+distance mark not physically installed:
+  a point at which a distance from an origin along a feature is given for information, but at which no specific marker exists.
+
+visible mark, pole: a point at which a distance from an origin along a feature is given for information and which is marked by a pole.
+
+visible mark, board: a point at which a distance from an origin along a feature is given for information and which is marked by a board.
+
+visible mark, unknown shape:
+  a point at which a distance from an origin along a feature is given for information and which is physically marked, but the shape of the mark is not known or not given.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATDOC  Code: 22
+Category of dock  CATDOC 22
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : tidal IF 28; 326.4;
+ 2 : non-tidal (wet dock) IF 27; 326.3;
+
+Definitions:
+
+tidal: a dock which is open to the sea and in which the water level is affected by tides.
+
+non-tidal (wet dock): a dock in which water can be maintained at any level by closing a gate when the water is at the desired level. (IHO Dictionary, S-32, 5th Edition, 1429)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATDPG  Code: 23
+Category of dumping ground    CATDPG 23
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : general dumping ground
+ 2 : chemical waste dumping ground IN 24; 442.1-3;
+ 3 : nuclear waste dumping ground
+ 4 : explosives dumping ground IN 23.1-2; 442.1-3;
+ 5 : spoil ground IN 62.1-2; 446.1-2;
+ 6 : vessel dumping ground
+
+Definitions:
+
+chemical waste dumping ground:
+  an area at sea where chemical waste is dumped.
+
+nuclear waste dumping ground:
+  an area at sea where nuclear waste is dumped.
+
+explosives dumping ground:
+  an area at sea where explosives are dumped.
+
+spoil ground: an area at sea where dredged material is deposited. Also called dumping ground. (IHO Dictionary, S-32, 5th Edition, 4930)
+
+vessel dumping ground:
+  an area at sea where disused vessels are scuttled.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+DELETED - DO NOT USE
+
+
+Acronym: CATDYK
+Category of dyke  CATDYK
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : summer dyke IF 1; 313.1;
+ 2 : winter dyke IF 1; 313.1;
+
+Remarks:
+
+ The attribute `category of dyke` encodes the various types of dyke.
+
+This attribute is obsolete.  It is only included here for reasons of backward compatibility.
+
+
+DELETED - DO NOT USE
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATFNC Code: 24
+Category of fenceline     CATFNC 24
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : fence
+ 2 : muir
+ 3 : hedge
+ 4 : wall
+
+Definitions:
+
+fence: a man made barrier used as an enclosure or boundary or for protection. (Digital Geographic Information Working Group -DGIWG, Oct. 1987)
+
+hedge: a continuous growth of shrubbery planted as a fence, a boundary or a wind break. (Digital Geographic Information Standard - DIGEST)
+
+wall: a fence constructed from masonry or stone.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATFRY  Code: 25
+Category of ferry     CATFRY 25
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : free-moving' ferry IM 50; 438.1;
+ 2 : cable ferry IM 51; 438.2;
+ 3 : ice ferry
+
+Definitions:
+
+free-moving' ferry: a ferry which may have routes that vary with weather, tide and traffic. (adapted from M-4)
+
+cable ferry: a ferry that follows a fixed route guided by a cable. (adapted from IHO Specifications, M-4)
+
+ice ferry: a winter-time ferry which crosses a lead. (Finnish Maritime Administration)
+
+Remarks:
+
+ The attribute `category of ferry` does not encode the various types of ferry vessel, but the manoeuvrability of the ferry. The value `cable ferry` indicates a ferry that follows a fixed route guided by a cable. A cable ferry may hinder the flow of other traffic. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATFIF  Code: 26
+Category of fishing facility  CATFIF 26
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : fishing stake IK 44.1; 447.1;
+ 2 : fish trap IK 44.2, 45; 447.2-3;
+ 3 : fish weir IK 44.2; 447.2;
+ 4 : tunny net IK 44.2, 45; 447.2;
+
+Definitions:
+
+fishing stake: a pole or stake placed in shallow water to outline a fishing ground or to catch fish (IHO Dictionary, S-32, 5th Edition, 1818).
+
+fish trap: a structure (usually portable) for catching fish (IHO Dictionary, S-32, 5th Edition, 1819).
+
+fish weir: a fence of stakes or stones set in a river or along the shore to trap fish (IHO Dictionary, S-32, 5th Edition, 5967).
+
+tunny net: a net built at sea for catching tunny (IHO Dictionary, S-32, 5th Edition, 5700).
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATFOG  Code: 27
+Category of fog signal    CATFOG 27
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : explosive IR 10; 452.1;
+ 2 : diaphone IR 11; 452.2;
+ 3 : siren IR 12; 452.3;
+ 4 : nautophone IR 13; 452.4;
+ 5 : reed IR 13; 452.4;
+ 6 : tyfon IR 13; 452.4;
+ 7 : bell IR 14; 452.5;
+ 8 : whistle IR 15; 452.6;
+ 9 : gong IR 16; 452.7;
+ 10 : horn IR 13; 452.4;
+
+Definitions:
+
+explosive: a signal produced by the firing of explosive charges. (Admiralty List of Lights and Fog Signals)
+
+diaphone: a diaphone uses compressed air and generally emits a powerful low-pitched sound, which often concludes with a brief sound of suddenly lowered pitch, termed the grunt'. (Admiralty List of Lights and Fog Signals)
+
+siren: a siren uses compressed air and exists in a variety of types which differ considerably in their sound and power. (Admiralty List of Lights and Fog Signals)
+
+nautophone: a horn having a diaphragm oscillated by electricity (IHO Dictionary, S-32, 5th Edition, 3371).
+
+reed: a reed uses compressed air and emits a weak, high pitched sound. (Admiralty List of Lights and Fog Signals)
+
+tyfon: a diaphragm horn which operates under the influence of compressed air or steam (IHO Dictionary, S-32, 5th Edition, 5717).
+
+bell:  a ringing sound with a short range. The apparatus may be operated automatically, by hand or by wave action. (IHO Chart Specifications, M-4, 452.5)
+
+whistle: a distinctive sound made by a jet of air passing through an orifice.  The apparatus may be operated automatically, by hand or by air being forced up a tube by waves acting on a buoy. (IHO Chart Specifications, M-4, 452.6)
+
+gong: a sound produced by vibration of a disc when struck.  The apparatus may be operated automatically, by hand or by wave action. (IHO Chart Specifications, M-4, 452.7)
+
+horn: a horn uses compressed air or electricity to vibrate a diaphragm and exists in a variety of types which differ greatly in their sound and power. (Admiralty List of Lights and Fog Signals)
+
+Remarks:
+
+ The attribute `category of fog signal` encodes the various means of generating the signal.
+
+ The classification `horn` is the generic term for fog signals `nautophone`, `reed` and `tyfon`. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATFOR  Code: 28
+Category of fortified structure   CATFOR 28
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : castle IE 34.2; 379.1-2;
+ 2 : fort IE 34.1; 379.1-2;
+ 3 : battery IE 34.3; 379.2;
+ 4 : blockhouse IE 34.2; 379.1-2;
+ 5 : Martello tower
+
+Definitions:
+
+castle: a large fortified building or structure (adapted from The Collins Dictionary).
+
+fort:  a fortified enclosure, building, or position able to be defended against an enemy (The Collins Dictionary).
+
+battery: a fortified structure on which artillery is mounted (The Collins Dictionary).
+
+blockhouse: a concrete structure strengthened to give protection against enemy fire, with apertures to allow defensive gunfire (The Collins Dictionary).
+
+Martello tower: a round fort for coastal defence.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATGAT  Code: 29
+Category of gate  CATGAT 29
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : gate in general
+ 2 : flood barrage gate IF 43; 326.7;
+ 3 : caisson IF 42; 326.5;
+ 4 : lock gate IF 41.1-2; 326.6;
+ 5 : dyke gate
+
+Definitions:
+
+flood barrage gate:  an opening gate used to control flood water.
+
+caisson: a steel structure used for closing the entrance of locks, wet and dry docks. (IHO Dictionary, S-32, 5th Edition, 602)
+
+lock gate: lock gates are the massive hinged doors at each end of a lock. (adapted from IHO Dictionary, S-32, 5th Edition, 2882)
+
+dyke gate: an opening gate in a dyke.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+acronym: CATHAF  Code: 30
+Category of harbour facility  CATHAF 30
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : RoRo-terminal IF 50; 321.5;
+ 2 : timber yard
+ 3 : ferry terminal
+ 4 : fishing harbour IF 10; 320.1;
+ 5 : yacht harbour/marina IU 1.1; 320.2;
+ 6 : naval base
+ 7 : tanker terminal
+ 8 : passenger terminal
+ 9 : shipyard
+ 10 : container terminal
+ 11 : bulk terminal
+
+Definitions:
+
+ A terminal provides facilities for handling particular forms of cargo (IHO Dictionary, S-32, 5th Edition, 5343).
+
+RoRo-terminal: a terminal for roll-on roll-off ferries.
+
+ferry terminal: a terminal for passenger and vehicle ferries.
+
+fishing harbour: a harbour with facilities for fishing boats.
+
+yacht harbour/marina: a harbour with facilities for small boats and yachts (IHO Dictionary, S-32, 5th Edition, 3095).
+
+naval base: a centre of operations for naval vessels (adapted from The Collins Dictionary).
+
+tanker terminal: a terminal for the bulk handling of liquid cargoes.
+
+passenger terminal: a terminal for the loading and unloading of passengers.
+
+shipyard: a place where ships are built or repaired (IHO Dictionary, S-32, 5th Edition, 4686).
+
+container terminal: a terminal for container ships.
+
+bulk terminal: a terminal for the handling of bulk materials such as iron ore, coal, etc.
+
+.Remark:
+
+ Value number 2 (timber yard) has been transferred to the attribute category of production area (CATPRA). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATHLK  Code: 31
+Category of hulk  CATHLK 31
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : floating restaurant
+ 2 : historic ship
+ 3 : museum
+ 4 : accommodation
+ 5 : floating breakwater
+
+Definitions:
+
+floating restaurant: a permanently moored floating structure, such as an old ship, used as a restaurant.
+
+historic ship: a ship of historical interest permanently moored as a tourist attraction.
+
+museum: a permanently moored floating structure, such as an old ship, used as a museum.
+
+accommodation: a permanently moored floating structure, such as an old ship, used for accommodation.
+
+floating breakwater: a permanently moored floating structure, often constructed from old ships, used as a breakwater.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATICE  Code: 32
+Category of ice   CATICE 32
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : fast ice IN 60.1; 449.1;
+ 2 : sea ice IN 60.2; 449.1;
+ 3 : growler area
+ 4 : pancake ice
+ 5 : glacier IC 25; 353.8;
+ 6 : ice peak
+ 7 : pack ice
+ 8 : polar ice
+
+Definitions:
+
+fast ice: sea ice which remains fast, generally in the position where originally formed, and which may attain a considerable thickness. It is found along coasts, where it is attached to the shore, or over shoals, where it may be held in position by islands, grounded icebergs  or  grounded  polar  ice. (IHO Dictionary, S-32, 5th Edition, 1772)
+
+sea ice: any form of ice which has originated from sea water. Generally any ice in the sea. (IHO Dictionary, S-32, 5th Edition, 4566)
+
+growler: a low-lying mass of flow ice which is not easily seen by approaching vessels owing to its dark indigo colour. It is therefore a menace to shipping. It is usually caused by the capsizing and disintegration of an iceberg.
+
+pancake ice:  pieces of new ice, usually approximately circular, about 30 cm to 3 m across, and with raised rims, due to the pieces striking against each other as the result of wind and swell. (IHO Dictionary, S-32, 5th Edition, 3643)
+
+glacier: a mass of snow and ice continuously moving from higher to lower ground or, if afloat, continuously spreading. (IHO Dictionary, S-32, 5th Edition, 2041)
+
+pack ice: term used in a wide sense to include any area of sea ice, other than fast ice, no matter what form it takes or how it is disposed. (IHO Dictionary, S-32, 5th Edition, 3639)
+
+polar ice: sea ice that is more than one year old (in contrast to winter ice).  The WMO code defines polar ice as any sea ice more than one year old and more than 3 metres thick. (IHO Dictionary, S-32, 5th Edition, 3928)
+
+Remarks:
+
+ Ice is the topic of another group and is subject to a future extension to this document. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATINB  Code: 33
+Category of installation buoy     CATINB 33
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : catenary anchor leg mooring (CALM) IL 16; 445.4;
+ 2 : single buoy mooring (SBM or SPM) IL 16; 445.4;
+
+Definitions:
+
+catenary anchor leg mooring(CALM):
+  incorporates a large buoy which remains on the surface at all times and is moored by 4 or more anchors.  Mooring hawsers and cargo hoses lead from a turntable on top of the buoy, so that the buoy does not turn as the ship swings to wind and stream.
+
+single buoy mooring (SBM):
+  a mooring structure used by tankers to load and unload in port approaches or in offshore oil and gas fields. The size of the structure can vary between a large mooring buoy and a manned floating structure. Also known as single point mooring (SPM) (IHO Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATLND  Code: 34
+Category of land region   CATLND 34
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : fen
+ 2 : marsh IC 33; 312.2;
+ 3 : moor/bog
+ 4 : heathland
+ 5 : mountain range
+ 6 : lowlands
+ 7 : canyon lands
+ 8 : paddy field
+ 9 : agricultural land
+ 10 : savanna/grassland
+ 11 : parkland
+ 12 : swamp IC 33; 312.2;
+ 13 : landslide
+ 14 : lava flow IC 26; 355;
+ 15 : salt pan IC 24; 353.7;
+ 16 : moraine
+ 17 : crater
+ 18 : cave
+ 19 : rock column or pinnacle
+
+Definitions:
+
+fen:  a type of bog, especially a low-lying area, wholly or partly covered with water and dominated by grasslike plants, grasses, sedges and reeds. (The New Encyclopaedia Britannica, 15th Edition 1991)
+
+marsh: an area of wet, often spongy ground that is subject to frequent flooding or tidal inundations, but not considered to be continually under water. It is characterized by the growth of non woody plants and by the lack of trees. (Nautical Chart Manual, US National Oceanic and Atmospheric Administration - NOAA, 1992).
+
+moor/bog: wet spongy ground consisting of decaying vegetation, which retains stagnant water, too soft to bear the weight of any heavy body.(IHO Dictionary, S-32, 5th Edition, 504)
+
+heathland: a tract of wasteland; peat bog, usually covered by a low scrubby growth, but may have scattered small open water holes. (Nautical Chart Manual, US National Oceanic and Atmospheric Administration - NOAA, 1992)
+
+mountain range: a series of connected and aligned mountains or mountain ridges. (US National Oceanic and Atmospheric Administration - NOAA, 1992).
+
+lowlands: low and relatively level land at a lower elevation than adjoining areas. (US National Oceanic and Atmospheric Administration - NOAA, 1992)
+
+canyon lands: a relatively narrow, deep depression with steep sides, the bottom of which generally has a continuous slope. (IHO Dictionary, S-32, 5th Edition, 638)
+
+paddy field: a piece of land set aside for crops which are periodically flooded (e.g. rice paddy).
+
+agricultural land:
+  areas used for cultivation of the soil, the breeding of livestock etc. and general farming.
+
+savanna/grassland: a large area of relatively flat natural pasture.
+
+parkland: a piece of ground kept for ornament and/or recreation or maintained in its natural state as a public property or area. (Websters New Collegiate Dictionary 1975)
+
+swamp: an area of spongy land saturated with water. It may have a shallow covering of water, usually with a considerable amount of vegetation appearing above the surface. (IHO Dictionary, S-32, 5th Edition, 5240)
+
+landslide: (or landslip). The sliding down of a mass of land on a mountain or cliff-side; land which has so fallen. (IHO Dictionary, S-32, 5th Edition, 2646)
+
+lava flow: the substance that results from the cooling of molten rock. (adapted IHO Dictionary, S-32, 5th Edition, 2680)
+
+saltpan: shallow pools of brackish water used for the natural evaporation of sea water to obtain salt. (IHO Dictionary, S-32, 5th Edition, 4494)
+
+moraine: any accumulation of loose material deposited by a glacier.  (Marine Chart Manual, US National Oceanic and Atmospheric Administration - NOAA, 1992)
+
+crater: bowl-shaped cavity, at the summit or on the side of a volcano.(IHO Dictionary, S-32, 5th Edition, 1115)  Also a hole formed by the impact of a meteor. (Nautical Chart Manual, US National Oceanic and Atmospheric Administration - NOAA, 1992).
+
+cave: a natural subterranean chamber or series of chambers open to the earths surface. (Digital Geographic Information Standard - DIGEST)
+
+rock column or pinnacle:
+  any high tower or spire-shaped pillar of rock, alone or cresting a summit. (IHO Dictionary, S-32, 5th Edition, 3852)
+
+Remarks:
+
+ The attribute `category of land region` encodes general terms for describing landscapes. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATLMK  Code: 35
+Category of landmark  CATLMK 35
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : cairn IQ 100; 456.2;
+ 2 : cemetery IE 19; 373.6;
+ 3 : chimney IE 22; 374.1;
+ 4 : dish aerial IE 31; 375.4;
+ 5 : flagstaff (flagpole) IE 27; 374.7;
+ 6 : flare stack IE 23; IL 11; 374.1; 445.6;
+ 7 : mast
+ 8 : windsock
+ 9 : monument IE 24; 374.4;
+ 10 : column (pillar)
+ 11 : memorial plaque
+ 12 : obelisk
+ 13 : statue
+ 14 : cross IE 12;
+ 15 : dome IE 30.4; 487.3;
+ 16 : radar scanner IE 30.3; 487.3;
+ 17 : tower IE 20; 374.3;
+ 18 : windmill IE 25.1-2; 374.5;
+ 19 : windmotor IE 26; 374.6;
+ 20 : spire/minaret IE 10.3, 17;
+
+Definitions:
+
+cairn: a mound of stones, usually conical or pyramidal, raised as a landmark or to designate a point of  importance in surveying. (IHO Dictionary, S-32, 5th Edition, 601)
+
+cemetery: an area of land for burying the dead.
+
+chimney: a vertical structure containing a passage or flue for discharging smoke and gases. (Digital Geographic Information Standard - DIGEST)
+
+dish aerial: a parabolic aerial for the receipt and transmission of high frequency radio signals. (IHO Dictionary, S-32, 5th Edition, 1400)
+
+flagstaff(flagpole): a staff or pole on which flags are raised. (Digital Geographic Information Standard - DIGEST 1.28)
+
+flare stack: a tall structure used for burning-off waste oil or gas. (IHO Dictionary, S-32, 5th Edition, 1836). Normally showing a flame and located at refineries (IHO Chart specifications, M-4).
+
+mast: a straight vertical piece of timber or a hollow cylinder. (adapted from Digital Geographic Information Standard - DIGEST)
+
+wind sock: a tapered fabric sleeve mounted so as to catch and swing with the wind, thus indicating the wind direction. (Navigation dictionary, US National Oceanic and Atmospheric Administration - NOAA, 1969)
+
+monument: a structure erected or maintained as a memorial to a person or event. (Digital Geographic Information Standard - DIGEST)
+
+column (pillar): a cylindrical or slightly tapering body of considerably greater length than diameter erected vertically. (Oxford English Dictionary)
+
+memorial plaque: a slab of metal, usually ornamented, erected as a memorial to a person or event.
+
+obelisk: a tapering shaft usually of stone or concrete, square or rectangular in section, with a pyramidal apex. (Adapted from Oxford English Dictionary)
+
+statue: a representation of a human, animal or fantasy figure in marble, bronze, etc.
+
+cross: a monument, or other structure in form of a cross. (Funk & Wagnalls Dictionary)
+
+dome: a landmark comprising a hemispherical or spheroidal shaped structure (adapted from the Macquarie Dictionary).
+
+radar scanner: a device used for directing a radar beam through a search pattern (adapted from Navigation Dictionary, US National Oceanic and Atmospheric Administration - NOAA, 1969)
+
+tower: a relatively tall structure which may be used for observation, support, storage or communication etc. (Digital Geographic Information Working Group -DGIWG, Oct. 1987)
+
+windmill: a wind driven system of vanes attached to a tower like structure (excluding wind- generated power plants). (Digital Geographic Information Standard - DIGEST)
+
+windmotor: a modern structure for the use of windpower. (IHO Chart Specifications, M-4)
+
+spire/minaret: a tall conical or pyramid-shaped structure often built on the roof or tower of a building, especially a church or mosque. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATLAM    Code: 36
+Category of lateral mark  CATLAM 36
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : port-hand lateral mark IQ 91-92,130.1; 456.1;
+ 2 : starboard-hand lateral mark IQ 91-92,130.1; 456.1;
+ 3 : preferred channel to starboard lateral mark IQ 130.1;
+ 4 : preferred channel to port lateral mark IQ 130.1;
+
+Definitions:
+
+port-hand lateral mark: indicates the port boundary of a navigational channel or suggested route when proceeding in the conventional direction of buoyage'.
+
+starboard-hand lateral mark:
+  indicates the starboard boundary of a navigational channel or suggested route when proceeding in the conventional direction of buoyage'.
+
+preferred channel to starboard lateral mark:
+  at a point where a channel divides, when proceeding in the conventional direction of buoyage', the preferred channel (or primary route) is indicated by a modified port-hand lateral mark.
+
+preferred channel to port lateral mark:
+  at a point where a channel divides, when proceeding in the conventional direction of buoyage', the preferred channel (or primary route) is indicated by a modified starboard-hand lateral mark.
+
+Note: the conventional direction of buoyage' may be either the general direction taken by the mariner when approaching a harbour, river, estuary or other waterway from seaward, or the direction determined by the proper authority, which in principle follows a clockwise direction around land masses.
+
+Remarks:
+
+ There are two international buoyage regions, A and B, between which lateral marks differ.  The buoyage region is encoded using the separate attribute MARSYS.  When top-marks, retro reflectors and/or lights are fitted to these marks, they are encoded as separate objects. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATLIT  Code: 37
+Category of light     CATLIT 37
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : directional function IP 30.1-3; 475.7;
+ 2 : rear/upper light
+ 3 : front/lower light
+ 4 : leading light IP 20.1-3; 475.6;
+ 5 : aero light IP 60; 476.1;
+ 6 : air obstruction light IP 61; 476.2;
+ 7 : fog detector light IP 62; 477;
+ 8 : flood light IP 63; 478.2;
+ 9 : strip light IP 64; 478.5;
+ 10 : subsidiary light IP 42; 471.8;
+ 11 : spotlight
+ 12 : front
+ 13 : rear
+ 14 : lower
+ 15 : upper
+ 16 : moirè effect IP 31; 475.8;
+ 17 : emergency
+ 18 : bearing light 478.1;
+ 19 : horizontally disposed
+ 20 : vertically disposed
+
+Definitions:
+
+directional function: a light illuminating a sector of very narrow angle and intended to mark a direction to follow. (IHO Dictionary, S-32, 5th Edition, 2778)
+
+leading light: a light associated with other lights so as to form a leading line to be followed. (adapted from IHO Dictionary, S-32, 5th Edition, 2794)
+
+aero light: an aero light is established for aeronautical navigation and may be of higher power than marine lights and visible from well offshore. (IHO Chart Specifications, M-4, 476.1)
+
+air obstruction light: a light marking an obstacle which constitutes a danger to air navigation. (IHO Dictionary, S-32, 5th Edition, 2767)
+
+fog detector light: a light used to automatically determine conditions of visibility which warrant the turning on or off of a sound signal. (IHO Dictionary, S-32, 5th Edition, 1885)
+
+flood light: a broad beam light used to illuminate a structure or area. (adapted from The Collins Dictionary)
+
+strip light: a light whose source has a linear form generally horizontal, which can reach a length of several metres. (IHO Chart Specifications, M-4, 478.5)
+
+subsidiary light: a light placed on or near the support of a main light and having a special use in navigation. (Admiralty List of Radio Signals, UK Hydrographic Office)
+
+spotlight: a powerful light focused so as to illuminate a small area. (The Collins Dictionary)
+
+front, rear, upper, lower:
+  terms used with leading lights to describe the position of the light on the lead as viewed from seaward.
+
+moirè effect: a short range (up to 2km) type of directional light.  Sodium lighting gives a yellow background to a screen on which a vertical black line will be seen by an observer on the centre line. (IHO Chart Specifications, M-4, 475.8)
+
+emergency light: a light available as a backup to a main light which will be illuminated should the main light fail.
+
+bearing light: a light which enables its approximate bearing to be obtained without the use of a compass. (IHO Chart Specifications, M-4, 478.1)
+
+horizontally disposed: a group of lights of identical character and almost identical position, that are disposed horizontally.
+
+vertically disposed: a group of lights of identical character and almost identical position, that are disposed vertically.
+
+Remarks:
+
+ Marine light (a light intended primarily for marine navigation) is not included in the above list. All lights are considered to be marine lights unless the attribute category of light' indicates otherwise. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATMFA  Code: 38
+Category of marine farm/culture   CATMFA 38
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : crustaceans IK 47-48.2; 447.4,6;
+ 2 : oysters/mussels IK 47-48.2; 447.4,6;
+ 3 : fish IK 47-48.2; 447.4,6;
+ 4 : seaweed
+
+Definitions:
+
+crustaceans: hard shelled animals, for example crabs or lobsters.
+
+oysters/mussels: edible bivalve molluscs.
+
+fish:  vertebrate cold blooded animal with gills, living in water.
+
+seaweed: the general name for marine plants of the Algae class which grow in long narrow ribbons. (International Maritime Dictionary, 2nd Ed.)
+
+Remarks:
+
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATMST
+Category of mast  CATMST
+Attribute type: E
+
+INT 1 Reference:  IE 28, 30.1;
+
+Chart Specification:  375.1; 487;
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : radio mast / television mast
+ 2 : mooring mast
+ 3 : radar mast
+ 4 : wind sock
+
+
+Remarks:
+
+ The attribute `category of mast` encodes the various types of mast.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attribute category of landmark (CATLMK).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATMPA    Code: 39
+Category of military practice area    CATMPA 39
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : practice area in general 441.1;
+ 2 : torpedo exercise area
+ 3 : submarine exercise area IN 33; 441.5;
+ 4 : firing danger area IN 30; 441.2-3;
+ 5 : mine-laying practice area IN 32; 441.4;
+ 6 : small arms firing range
+
+Definitions:
+
+torpedo exercise area: an area within which exercises are carried out with torpedoes.
+
+submarine exercise area:
+  an area within which submarine exercises are carried out.
+
+firing danger area: areas for bombing and missile exercises. (Adapted from IHO Chart Specifications, M-4, 441)
+
+mine laying practice area:
+  an area within which mine laying exercises are carried out.
+
+small arms firing range: an area for shooting pistols, rifles and machine guns etc. at a target.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATMNT
+Category of monument  CATMNT
+Attribute type: E
+
+INT 1 Reference:   IE 24;
+
+Chart Specification:   374.4;
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  column
+    2 :  memorial plaque
+    3 :  obelisk
+    4 :  pillar
+    5 :  statue
+
+
+Remarks:
+
+ The attribute `category of monument` encodes the various types of monument.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attribute category of landmark (CATLMK).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATMOR    Code: 40
+Category of mooring/warping facility  CATMOR 40
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : dolphin IF 20; 327.1;
+ 2 : deviation dolphin IF 21; 327.2;
+ 3 : bollard
+ 4 : tie-up wall
+ 5 : post or pile IF 22;
+ 6 : chain/wire/cable IQ 42; 431.6;
+ 7 : mooring buoy IQ 40-43; 431.5;
+
+Definitions:
+
+dolphin: a post or group of posts, which may support a deck, used for mooring or warping a vessel. (IHO Dictionary, S-32, 5th Edition, 1433)
+
+deviation dolphin: a post or group of posts, which a vessel may swing around for compass adjustment.
+
+bollard: small shaped post, mounted on a wharf or dolphin used to  secure ship's lines.
+
+tie-up wall: a tie-up wall is a section of wall designated for tying-up vessels awaiting transit. Bollards and mooring devices are available for both large and small ships.
+
+post or pile: a long heavy timber or section of steel, wood, concrete, etc., forced into the seabed to serve as a mooring facility. (IHO Dictionary, S-32, 5th Edition, 3840)
+
+chain/wire/cable: a connection between two independent objects e.g. a buoy and pile or between two buoys used as a mooring facility.
+
+mooring buoy: a buoy secured to the bottom by permanent moorings with means for mooring a vessel by use of its anchor chain or mooring lines. (IHO Dictionary, S-32, 5th Edition, 575)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATNAV    Code: 41
+Category of navigation line   CATNAV 41
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : clearing line IM 2; 433;
+ 2 : transit line IM 2;
+ 3 : leading line bearing a recommended track  IM 1, 3; 433;
+
+Definitions:
+
+clearing line: a straight line that marks the boundary between a safe and a dangerous area or that passes clear of a navigational danger.  (adapted from IHO Dictionary, S-32, 5th Edition, 826)
+
+transit line:  a line passing through one or more fixed marks.
+
+leading line:  a line passing through one or more clearly defined objects, along the path of which a vessel can approach safely up to a certain distance off. (Adapted from IHO Dictionary, S-32, 5th Edition, 2696)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATOBS    Code: 42
+Category of obstruction   CATOBS 42
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : snag/stump IK 43; 327.5;
+ 2 : wellhead IL 21, 23; 445.1;
+ 3 : diffuser IL 43;
+ 4 : crib IL 43;
+ 5 : fish haven IK 46; 447.5;
+ 6 : foul area IK 1; 420.1;
+ 7 : foul ground IK 31; 422.8;
+ 8 : ice boom
+ 9 : ground tackle IQ 42; 431.6;
+
+Definitions:
+
+snag/stump: a tree, branch or broken pile embedded in the ocean floor, river or lake bottom and not visible on the surface, forming thereby a hazard to vessels. (IHO Dictionary, S-32, 5th Edition, 4794)
+
+wellhead: a submarine structure projecting some distance above the seabed and capping a temporarily abandoned or suspended oil or gas well. (IHO Dictionary, S-32, 5th Edition, 5976)
+
+diffuser: a structure on an outfall through which liquids are discharged. The structure will usually project above the level of the outfall and can be an obstruction to navigation.
+
+crib:  a permanent structure set in the water, framed with wooden beams and filled with rocks or boulders. They are used to anchor log booms or support other constructions, e.g. submerged outfalls, diffusers etc.. They may always be dry, submerged or cover and uncover.
+
+fish haven: areas established by private interests, usually sport fishermen, to simulate natural reefs and wrecks that attract fish. The reefs are constructed by dumping assorted junk in areas which may be of very small extent or may stretch a considerable distance along a depth contour. Also called fishery reefs.
+
+foul area: an area of numerous unidentified dangers to navigation. The area serves as a warning to the mariner that all dangers are not identified individually and that navigation through the area may be hazardous.  Commonly used to encode areas behind danger lines on navigation charts. (adapted from IHO Dictionary, S-32, 5th Edition, 1915)
+
+foul ground: areas over which it is safe to navigate but which should be avoided for anchoring, taking the ground or ground fishing. (IHO Chart Specifications, M-4, 442.8)
+
+ice boom: floating barriers, anchored to the bottom,  used to deflect the path of floating ice in order to prevent the obstruction of locks, intakes, etc., and to prevent damage to bridge piers and other structures. (Canadian Hydrographic Service, Chart specifications).
+
+ground tackle: equipment such as anchors, concrete blocks, chains and cables, etc., used to position floating structures such as trot and mooring buoys etc. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATOFP    Code: 43
+Category of offshore platform     CATOFP 43
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : oil derrick/rig IL 10; 445.2;
+ 2 : production platform IL 10; 445.2;
+ 3 : observation/research platform IL 13;
+ 4 : articulated loading platform (ALP)
+ 5 : single anchor leg mooring (SALM) IL 12; 445.2,4;
+ 6 : mooring tower
+ 7 : artificial island IL 15;
+ 8 : floating production, storage and off-loading vessel (FPSO) IL 17;
+ 9 : accommodation platform
+ 10 : navigation, communication and control buoy (NCCB)
+
+Definitions:
+
+oil derrick/rig:  a temporary mobile structure, either fixed or floating, used in the exploration stages of oil and gas fields. (IHO Dictionary, S-32, 5th Edition,
+
+production platform: a term used to indicate a permanent offshore structure equipped to control the flow of oil or gas.  It does not include entirely submarine structures. (IHO Dictionary, S-32, 5th Edition, 4037)
+
+observation/research platform:
+  a platform from which one's surroundings or events can be observed, noted or recorded such as for scientific study. (adapted from IHO Dictionary, S-32, 5th Edition, 3493/3500)
+
+articulated loading platform (ALP):
+  a metal lattice tower, buoyant at one end and attached at the other by a universal joint to a concrete filled base on the sea bed.  The platform may be fitted with a helicopter platform, emergency accommodation and hawser/hose retrieval. (adapted from United Kingdom Hydrographic Office CSDO 607.2 (12), May 1994)
+
+single anchor leg mooring (SALM):
+  a rigid frame or tube with a buoyancy device at its upper end , secured at its lower end to a universal joint on a large steel or concrete base resting on the sea bed, and at its upper end to a mooring buoy by a chain or wire. (adapted from United Kingdom Hydrographic Office CSDO 607.2 (12), May 1994)
+
+mooring tower: a platform secured to the sea bed and surmounted by a turntable to which ships moor. (adapted from United Kingdom Hydrographic Office CSDO 607.2 (12), May 1994)
+
+artificial island: a man-made structure usually built for the exploration or exploitation of marine resources, marine scientific research, tidal observations, etc. (adapted from IHO Dictionary, S-32, 5th Edition, 240)
+
+floating production, storage and offloading vessel (FPSO):
+  an offshore oil/gas facility consisting of a moored tanker/barge by which the product is extracted, stored and exported. (adapted from United Kingdom Hydrographic Office CSDO 607.2 (13), May 1994)
+
+accommodation platform:
+  a platform used primarily for eating, sleeping and recreation purposes.
+
+navigation, communication and control buoy (NCCB):
+  a floating structure with control room, power and storage facilities, attached to the sea bed by a flexible pipeline and cables.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATOLB    Code: 44
+Category of oil barrier   CATOLB 44
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : oil retention (high pressure pipe) IF 29.2;
+ 2 : floating oil barrier IF 29.1;
+
+Definitions:
+
+oil retention (high pressure pipe):
+  a pipe with holes from which air blows.  When the air bubbles reach the surface they form a barrier which prevents the spread of oil. (Kort- og Matrikelstyrelsen, Denmark)
+
+floating oil barrier: a floating tube shaped structure, with a curtain (2 metre) hanging under it, below the surface, which prevents the spread of oil. (Kort- og Matrikelstyrelsen, Denmark)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATPLE    Code: 45
+Category of pile  CATPLE 45
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : stake
+ 2 : snag
+ 3 : post IF 22; 327.3;
+ 4 : tripodal
+
+Definitions:
+
+stake: an elongated wood or metal pole embedded in the bottom to serve as a marker or support. (adapted from IHO Dictionary, S-32, 5th Edition, 4960)
+
+post: a vertical piece of timber, metal or concrete forced into the earth or sea bed.
+
+tripodal: a single structure comprising 3 or more piles held together (sections of heavy timber, steel or concrete), and forced into the earth or sea bed. (adapted from IHO Dictionary, S-32, 5th Edition, 3840)
+
+Remarks:
+
+ No remarks.
+
+Value number 2 (snag) has been transferred to the attribute category of obstruction (CATOBS). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATPIL    Code: 46
+Category of pilot boarding place  CATPIL 46
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : boarding by pilot-cruising vessel IT 1.1-3; 491.1;
+ 2 : boarding by helicopter IT 1.4; 491.2;
+ 3 : pilot comes out from shore IT 1.1-3; 491.1;
+
+Definitions:
+
+boarding by pilot-cruising vessel:
+  pilot boards from a cruising vessel.
+
+boarding by helicopter: pilot boards by helicopter which comes out from the shore.
+
+pilot comes out from shore:
+  pilot boards from a vessel which comes out from the shore on request.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATPIP    Code: 47
+Category of pipeline/pipe   CATPIP 47
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : pipeline in general
+ 2 : outfall pipe IL 41.1; 444.2,4;
+ 3 : intake pipe IL 41.1; 444.2,4;
+ 4 : sewer IL 41.1; 444.2,4;
+ 5 : bubbler system
+     6   :    supply pipe           IL 40.1;        444;
+
+Definitions:
+
+outfall pipe: a pipe (generally a sewer or drainage pipe) discharging in to the sea or a river.
+
+intake pipe: a pipe taking water from a river or other body of water, to drive a mill or supply a canal, waterworks, etc. (IHO Dictionary, S-32, 5th Edition, 2468)
+
+sewer: a pipe in a sewage system for carrying water or sewage to a disposal area.
+
+bubbler system: a submerged pipe from which warm water bubbles, preventing the surrounding water from freezing.
+
+supply pipe: a pipe used for supplying of gas or liquid product.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATPRA    Code: 48
+Category of production area     CATPRA 48
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : quarry IE 35.1-2; 367.1;
+ 2 : mine IE 36; 367.2;
+ 3 : stockpile
+ 4 : power station area
+ 5 : refinery area  367;
+ 6 : timber yard IF 52; 328.2;
+ 7 : factory area
+ 8 : tank farm  376.2;
+ 9 : wind farm
+
+Definitions:
+
+quarry: an excavation in solid rock from which building stone, limestone, etc. is removed.
+
+mine: an excavation in the earth for the purpose of extracting earth materials.
+
+stockpile: a reserve stock of material, equipment or other supplies.
+
+power station area: a stationary plant containing apparatus for large-scale conversion of some form of energy (hydraulic, steam, chemical, nuclear, etc.) into electrical energy.
+
+refinery area: a system of process units used to convert crude petroleum into fuels, lubricants and other petroleum-derived products.
+
+timber yard: a storage area for wood used for building, carpentry or joinery.
+
+factory area: a group of buildings where goods are manufactured.
+
+tank farm: an area in which a number of large-capacity storage tanks are located, generally used for crude oil or petroleum products.
+
+wind farm: an area in which numerous wind motors are located.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATPRI   Attribute type: E
+Category of production installation     CATPRI
+INT 1 Reference:  IE 35.1-2, 36; IL 20, 21.1-3;
+
+Chart Specification:  367.1-2; 445; 445.1; 445.5;
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  quarry
+    2 :  mine
+    3 :  wellhead
+    4 :  production well
+
+
+Remarks:
+
+ The attribute `category of production installation` encodes the various types of production installation.
+
+ Definitions of attribute values:
+
+ Mine: An excavation in the earth for the purpose of extracting earth materials.
+
+ Quarry:  An excavation in solid rock from which building stone, limestone, etc. is removed.
+
+ Submerged wellhead:
+  A submarine structure projecting some distance above the seabed and capping a temporarily abandoned, or suspended, oil or gas well. (IHO Dictionary, S-32, 4th Edition)
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attributes category of production area (CATPRA) and category of obstructions (CATOBS).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATPYL    Code: 49
+Category of pylon   CATPYL 49
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : power transmission pylon/pole
+ 2 : telephone/telegraph pylon/pole
+ 3 : aerial cableway/sky pylon
+ 4 : bridge pylon/tower
+ 5 : bridge pier
+
+Definitions:
+
+power transmission pylon/pole:
+  a vertical construction consisting, for example, of a steel framework or of pre-stressed concrete, to support a power transmission cable or line. (adapted from Digital Geographic Information Standard - DIGEST FACC 1.2)
+
+telephone/telegraph pylon/pole:
+  a pylon or pole used to support a telephone or telegraph line. (Digital Geographic Information Standard - DIGEST FACC 1.2)
+
+aerial cableway/sky pylon:
+  a tower or pylon supporting steel cables which convey cars, buckets, or other suspended carrier units. (adapted from Digital Geographic Information Standard - DIGEST FACC 1.2)
+
+bridge pylon/tower: a tower, abutment or pylon from which a bridge deck is suspended. (adapted from Digital Geographic Information Standard - DIGEST FACC 1.2)
+
+bridge pier: a support in the form of a pillar or pier for the spans of a bridge. (adapted from Digital Geographic Information Standard - DIGEST FACC 1.2)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATQUA   Code: 50
+Category of quality of data CATQUA
+Attribute type: E
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : data quality A
+ 2 : data quality B
+ 3 : data quality C
+ 4 : data quality D
+ 5 : data quality E
+ 6 : quality not evaluated
+
+Definitions:
+
+CategoryPositional
+Accuracy 1Sounding
+TechniqueCoverage
+DatumA 5mecho sounder/
+sweepfull 2WGS84B 20mecho sounder/
+laser/sweepfull 2transformed to
+WGS844C 50mecho sounder/
+lead linesystematic 3transformed to
+WGS844D 500mlead linenot systematicother datumEunknownunknownnot systematicunknown datum
+Remarks:
+
+Footnote numbers quoted in the table have the following meanings:
+
+1 accuracy specified at 2 drms.  Accuracy is quoted with respect to the given datum of the data. The quoted accuracy is the maximum value of the cumulative error in the production of the data.  It should take account of survey errors, transformation errors, digitising errors, etc.
+
+2 full coverage is defined as 100% coverage using systematic controlled surveys providing full sea floor coverage or full coverage to a defined depth and an investigation of all contacts.
+
+3 systematic is defined as a controlled survey but full coverage may not have been achieved.
+
+4 parameters for the transformation of various datums to or from WGS84 can be found in IHO publication S-60 (User's Handbook on Datum Transformations involving WGS-84).
+
+. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATRAS    Code: 51
+Category of radar station   CATRAS 51
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : radar surveillance station IM 30; 487;
+ 2 : coast radar station IS 1; 485.1;
+
+Definitions:
+
+radar surveillance station:
+  a radar station established for traffic surveillance. (IHO Dictionary, S-32, 5th Edition, 4144)
+
+coast radar station:  a shore-based station which the mariner can contact by radio to obtain a position. IHO Chart Specifications, M-4
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATRTB    Code: 52
+Category of radar transponder beacon    CATRTB 52
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : ramark, radar beacon transmitting continuously IS 2; 486.1;
+ 2 : racon, radar transponder beacon IS 3.1-6; 486.2-4;
+ 3 : leading racon/radar transponder beacon IS 3.5;
+
+Definitions:
+
+ramark: a radar marker beacon which continuously transmits a signal appearing as a radial line on a radar screen, the line indicating the direction of the beacon. Ramarks are intended primarily for marine use. The name "ramark" is derived from the words radar marker. (IHO Dictionary, S-32, 5th Edition, 4208)
+
+racon:  a radar beacon which returns a coded signal which provides identification of the beacon, as well as range and bearing. The range and bearing are indicated by the location of the first character received on the radar screen. The name "racon" is derived from the words radar beacon. (IHO Dictionary, S-32, 5th Edition, 4132)
+
+leading racon/radar transponder beacon:
+  a radar beacon that may be used (in conjunction with at least one other radar beacon) to indicate a leading line.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATROS    Code: 53
+Category of radio station   CATROS 53
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : circular (non-directional) marine or
+   aero-marine radiobeacon IS 10; 481.1;
+ 2 : directional radiobeacon IS 11; 481.2;
+ 3 : rotating-pattern radiobeacon IS 12; 481.1;
+ 4 : Consol beacon IS 13; 481.3;
+ 5 : radio direction-finding station IS 14; 483;
+ 6 : coast radio station providing QTG service IS 15; 484;
+ 7 : aeronautical radiobeacon IS 16; 482;
+ 8 : Decca
+ 9 : Loran C
+ 10 : Differential GPS
+ 11 : Toran
+ 12 : Omega
+ 13 : Syledis
+ 14 : Chaika (Chayka)
+
+Definitions:
+
+ A radiobeacon is a radio transmitter which emits a distinctive or characteristic signal on which a bearing may be taken (IHO Dictionary, S-32, 5th Edition, 4168).
+
+circular (non-directional) marine or aero-marine radiobeacon:
+  a radio station which need not necessarily be manned, the emissions of which, radiated around the horizon, enable its bearing to be determined by means of the radio direction finder of a ship. (IHO Dictionary, S-32, 5th Edition, 802)
+
+directional radiobeacon:
+  a special type of radiobeacon station the emissions of which are intended to provide a definite track for guidance. (IHO Dictionary, S-32, 5th Edition, 1378)
+
+rotating pattern radiobeacon:
+  a special type of radiobeacon station emitting a beam of waves to which a uniform turning movement is given, the bearing of the station being determined by means of an ordinary listening receiver and a stop watch.  Also referred to as a rotating loop radiobeacon. (IHO Dictionary, S-32, 5th Edition, 4444)
+
+Consol beacon: a type of long range position fixing beacon.
+
+radio direction-finding station:
+  a radio station intended to determine only the direction of other stations by means of transmission from the latter. (IHO Dictionary, S-32, 5th Edition, 4174)
+
+coast radio station providing QTG service:
+  a radio station which is prepared to provide QTG service, that is to say, to transmit upon request from a ship, a radio signal, the bearing of which can be taken by that ship. (IHO Dictionary, S-32, 5th Edition, 4108)
+
+aeronautical radiobeacon:
+  a radio beacon designed for aeronautical use.
+
+Decca: the Decca Navigator System is a high accuracy, short to medium range radio navigational aid intended for coastal and landfall navigation. (Admiralty List of Radio Signals, UK Hydrographic Office, Volume 2, 1994)
+
+Loran C: Loran-C is a low frequency electronic position fixing system using pulsed transmissions at 100 Khz. (Admiralty List of Radio Signals, UK Hydrographic Office, Volume 2, 1994)
+
+Differential GPS: a radiobeacon transmitting DGPS correction signals.
+
+Toran: Toran is an electronic position fixing system used mainly by aircraft.
+
+Omega: Omega is a long-range radio navigational aid which operates within the VLF frequency band.  The system comprises eight land based stations. (Admiralty List of Radio Signals, UK Hydrographic Office, Volume 2, 1994)
+
+Syledis: Syledis is a ranging position fixing system operating at 420-450MHz over a range of up to 400Km.
+
+Chiaka (Chayka): Chiaka is a low frequency electronic position fixing system using pulsed transmissions at 100 Khz. (Admiralty List of Radio Signals, UK Hydrographic Office, Volume 2, 1995)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATTRK    Code: 54
+Category of recommended track   CATTRK 54
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : based on a system of fixed marks IM 3; 434.1-2;
+ 2 : not based on a system of fixed marks IM 4; 434.1-2;
+
+Definitions:
+
+based on a system of fixed marks:
+  a straight route (known as a recommended track, range or leading line), which comprises at least two structures (usually beacons or daymarks) and/or natural features, which may carry lights and/or top-marks.  The structures/features are positioned so that when observed to be in line, a vessel can follow a known bearing with safety. (adapted from International Association of Lighthouse Authorities - IALA Aids to Navigation Guide, 1990)
+
+not based on a system of fixed marks:
+  a route (known as a recommended track or preferred route) which is not based on a series of structures or features in line.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATREB   Attribute type: E
+Category of religious building  CATREB
+INT 1 Reference:  IE 10.1, 13-18;
+
+Chart Specification:  373.1-5;
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  church
+    2 :  chapel
+    3 :  cross; calvary
+    4 :  temple
+    5 :  pagoda
+    6 :  shinto-shrine
+    7 :  buddhist temple
+    8 :  mosque
+    9 :  marabout
+
+
+Remarks:
+
+ The attribute `category of religious building` encodes the various types of religious building.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attributes function (FUNCTN) and category of landmark (CATLMK).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATRSC    Code: 55
+Category of rescue station  CATRSC 55
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : rescue station with lifeboat IT 12; 493;
+ 2 : rescue station with rocket IT 12; 493;
+ 3 : rescue station with lifeboat and rocket
+ 4 : refuge for shipwrecked mariners IT 14; IQ 124; 456.4;
+ 5 : refuge for intertidal area walkers IT 14; IQ 124; 456.4;
+ 6 : lifeboat lying at a mooring IT 13; 493.2;
+
+Definitions:
+
+rescue station with lifeboat:
+  a place where equipment for saving life at sea is maintained; the type of lifeboat may vary from fast, long distance boats to inflatable inshore boats. (IHO Chart Specifications, M-4)
+
+rescue station with rocket:
+  rocket - a pyrotechnic projectile used for signalling or for life-saving purposes. (IHO Dictionary, S-32, 5th Edition, 4418)
+
+refuge for ship-wrecked mariners:
+  shelter or protection from danger or distress at sea.
+
+refuge for intertidal area walkers:
+  shelter or protection from danger in areas exposed to extreme and sudden tides or tidal streams.
+
+lifeboat lying at a mooring:
+  a place where a lifeboat is moored ready for use.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATREA    Code: 56
+Category of restricted area     CATREA 56
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : offshore safety zone IL 3;
+ 2 : anchoring prohibition area
+ 3 : fishing prohibition area
+ 4 : nature reserve  IN 22;
+ 5 : bird sanctuary  IN 22;
+ 6 : game reserve IN 22;
+ 7 : seal sanctuary IN 22;
+ 8 : degaussing range  IN 25; 448.1-3;
+ 9 : military area IN 31;
+ 10 : historic wreck area IN 26; 449.5;
+ 11 : inshore traffic zone
+ 12 : navigational aid safety zone IM 29.1; 435.7;
+ 13 : danger of stranding area
+ 14 : minefield IN 34; 441.8;
+ 15 : diving prohibition area
+ 16 : area to be avoided
+ 17 : Prohibited area
+ 18 : swimming area
+ 19 : waiting area
+ 20 : research area
+ 21 : dredging area IN 63; 446.4;
+ 22 : fish sanctuary
+ 23 : ecological reserve
+ 24 : no wake area
+  25    :    swinging area
+
+Definitions:
+
+
+offshore safety zone: the area around an offshore installation within which vessels are prohibited from entering without permission; special regulations protect installations within a safety zone and vessels of all nationalities are required to respect the zone. (IHO Dictionary, S-32, 5th Edition, 4471)
+
+nature reserve: a tract of land managed so as to preserve it's flora, fauna, physical features, etc.
+
+bird sanctuary: a place where birds are bred and protected.
+
+game reserve: a place where wild animals or birds hunted for sport or food are kept undisturbed for private use.
+
+seal sanctuary: a place where seals are protected.
+
+degaussing range: an area, usually about two cables diameter, within which ships' magnetic fields may be measured; sensing instruments and cables are installed on the sea bed in the range and there are cables leading from the range to a control position ashore. (IHO Chart Specifications, M-4)
+
+military area: an area controlled by the military in which restrictions may apply. (Hydrographic Service, Royal Australian Navy)
+
+historic wreck area: an area around certain wrecks of historical importance to protect the wrecks from unauthorized interference by diving, salvage or deposition (including anchoring). (IHO Chart Specifications, M-4)
+
+navigational aid safety zone:
+  an area around a navigational aid which vessels are prohibited from entering.
+
+minefield: an area laid and maintained with explosive mines for defence or practice purposes.
+
+swimming area: an area in which people may swim and therefore vessel movement may be restricted.
+
+waiting area: an area reserved for vessels waiting to enter a harbour.
+
+research area: an area where marine research takes place.
+
+dredging area: an area where dredging is taking place.
+
+fish sanctuary: a place where fish are protected.
+
+ecological reserve: a tract of land managed so as to preserve the relation of plants and living creatures to each other and to their surroundings.
+
+no wake area: an area in which a vessels' speed must be reduced in order to reduce the size of the wake it produces.
+
+swinging area: an area where vessels turn. (Service Hydrographique et Ocèanographique de la Marine, France)
+
+
+
+Remarks:
+
+ The official legal status of each kind of restricted area defines the kind of restriction(s), e.g. the restriction for a `game preserve'  may be entering prohibited', the restriction for an anchoring prohibition area' is anchoring prohibited'.
+
+Values 2, 3, 15, 16 and 17 have been transferred to the attribute restriction (RESTRN).  Value number 11 has been replaced by the object class inshore traffic zone (ISTZNE).  Value 13 should be encoded using object class caution area (CTNARE). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATROD    Code: 57
+Category of road    CATROD 57
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : motorway ID 10; 365.1;
+ 2 : major road ID 11; 365.2;
+ 3 : minor road ID 11; 365.2;
+ 4 : track/path ID 12; 365.3;
+ 5 : major street ID 12; 365.3;
+ 6 : minor street
+ 7 : crossing
+
+Definitions:
+
+motorway: a main road with separate carriageways and limited access, specially constructed and controlled for fast motor traffic.
+
+major road: a hard surfaced (metalled) road; a main through route.
+
+minor road: a secondary road for local traffic.
+
+track/path: track - a rough path or way formed by use.
+  path - a way or track laid down for walking or made by continual treading.
+
+major street: a main road, in an urban area, for through traffic.
+
+minor street: a secondary road, in an urban area, for local traffic.
+
+crossing: a place where roads, etc. intersect.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATRUN  Code: 58
+Category of runway  CATRUN 58
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : aeroplane runway
+ 2 : helicopter landing pad
+
+Definitions:
+
+aeroplane runway: a level stretch of land where aeroplanes take of and land.
+
+helicopter landing pad: a site on which helicopters may land and take off. (IHO Dictionary, S-32, 5th Edition, 2232)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSEA    Code: 59
+Category of sea area    CATSEA 59
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : sea area in general
+ 2 : gat
+ 3 : bank
+ 4 : deep
+ 5 : bay
+ 6 : trench
+ 7 : basin
+ 8 : mud flats
+ 9 : reef
+ 10 : ledge
+ 11 : canyon
+ 12 : narrows
+ 13 : shoal
+ 14 : knoll
+ 15 : ridge
+ 16 : seamount
+ 17 : pinnacle
+ 18 : abyssal plain
+ 19 : plateau
+ 20 : spur
+ 21 : shelf
+ 22 : trough
+ 23 : saddle
+ 24 : abyssal hills
+ 25 : apron
+ 26 : archipelagic apron
+ 27 : borderland
+ 28 : continental margin
+ 29 : continental rise
+ 30 : escarpment
+ 31 : fan
+ 32 : fracture zone
+ 33 : gap
+ 34 : guyot
+ 35 : hill
+ 36 : hole
+ 37 : levee
+ 38 : median valley
+ 39 : moat
+ 40 : mountains
+ 41 : peak
+ 42 : province
+ 43 : rise
+ 44 : sea channel
+ 45 : seamount chain
+ 46 : shelf-edge
+ 47 : sill
+ 48 : slope
+ 49 : terrace
+ 50 : valley
+ 51 : canal
+ 52 : lake
+ 53 : river
+
+Definitions:
+
+gat:  a natural or artificial passage or channel through shoals or steep banks, or across a line of banks lying between two channels. (IHO Hydrographic Dictionary, S-32, 5th Edition)
+
+bank: an elevation over which the depth of water is relatively shallow, but normally sufficient for safe surface navigation. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+deep: in oceanography, an obsolete term which was generally restricted to depths greater than 6,000 m. (IHO Hydrographic Dictionary, S-32, 5th Edition)
+
+bay:  an indentation in the coastline.
+
+trench: a long narrow, characteristically very deep and asymmetrical depression of the sea floor, with relatively steep sides. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+basin: a depression, characteristically in the deep sea floor, more or less equidimensional in plan and of variable extent. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+flat:  a level tract of land, as the bed of a dry lake or an area frequently uncovered at low tide. Usually in plural.
+
+reef: rock lying at or near the sea surface that may constitute a hazard to surface navigation. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+ledge: a rocky formation continuous with and fringing the shore. (IHO Hydrographic Dictionary, S-32, 5th Edition)
+
+canyon: a relatively narrow, deep depression with steep sides, the bottom of which generally has a continuous slope, developed characteristically on some continental slopes. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+narrows: a navigable narrow part of a bay, strait, river, etc. (IHO Hydrographic Dictionary, S-32, 5th Edition)
+
+shoal: an offshore hazard to surface navigation that is composed of unconsolidated material. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+knoll: a relatively small isolated elevation of a rounded shape. (IHO-IOC Publication B- 6, Standardization of Undersea Feature Names, 2ndEdition)
+
+ridge: (a) A long, narrow elevation with steep sides. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2nd Edition)
+  (b) A long, narrow elevation often separating ocean basins. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2nd Edition)
+  (c) The linked major mid-oceanic mountain systems of global extent. Also called mid-oceanic ridge. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+seamount: a large isolated elevation, greater than 1000m in relief above the sea floor, characteristically of conical form. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+pinnacle: any high tower or spire-shaped pillar or rock or coral, alone or cresting a summit. It may extend above the surface of the water. It may or may not be a hazard to surface navigation. (IHO Hydrographic Dictionary, S-32, 5th Edition)
+
+abyssal plain: an extensive, flat, gently sloping or nearly level region at abyssal depths. (IHO- IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+plateau: a flat or nearly flat area of considerable extent, dropping off abruptly on one or more sides. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+spur: a subordinate elevation, ridge or rise projecting outward from a larger feature. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+shelf: a zone adjacent to a continent (or around an island) and extending from the low water line to a depth at which there is usually a marked increase of slope towards oceanic depths. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+trough: a long depression of the sea floor characteristically flat bottomed and steep sided and normally shallower than a trench. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+saddle: a broad pass, resembling in shape a riding saddle, in a ridge or between contiguous seamounts. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+abyssal hills: a tract, on occasion extensive, of low (100-500m) elevations on the deep sea floor. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+apron: a gently dipping featureless surface, underlain primarily by sediment, at the base of any steeper slope. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2nd Edition)
+
+archipelagic apron: a gentle slope with a generally smooth surface on the sea floor, characteristically found around groups of islands or seamounts. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+borderland: a region adjacent to a continent, normally occupied by or bordering a shelf, that is highly irregular with depths well in excess of those typical of a shelf. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+continental margin: the zone, generally consisting of shelf, slope and rise, separating the continent from the abyssal plain or deep sea floor. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+continental rise: a gentle slope rising from the oceanic depths towards the foot of a continental slope. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+escarpment: an elongated and comparatively steep slope separating or gently sloping areas. Also called: scarp. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+fan:  a relatively smooth, fan-like, depositional feature normally sloping away from the outer termination of a canyon or canyon system. Also called: cone. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+fracture zone: an extensive linear zone of irregular topography of the sea floor, characterized by steep-sided or asymmetrical ridges, troughs or escarpments. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+gap: a narrow break in a ridge or a rise. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+guyot: a seamount having a comparatively smooth flat top. Also called tablemount. (IHO Hydrographic Dictionary, S-32, 5th Edition and IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+hill:  a small isolated elevation (see also abyssal hills). (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+hole: a local depression, often steep sided, of the sea floor. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+levee: a depositional embankment bordering a canyon, valley or deep-sea channel. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+median valley: the axial depression of the mid-oceanic ridge system. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+moat: an annular depression that may not be continuous, located at the base of many seamounts, islands and other isolated elevations. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+mountains: a large and complex grouping of ridges and seamounts. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+peak: a prominent elevation either pointed or of a very limited extent across the summit. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+province: a region identifiable by a group of similar physiographic features whose characteristics are markedly in contrast with surrounding areas. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+rise:  (a) A broad elevation that rises gently and generally smoothly from the sea floor.
+  (b) The linked major mid-oceanic mountain systems of global extent. Also called mid-oceanic ridge. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2nd Edition)
+
+sea channel: a continuously sloping, elongated narrow depression commonly found in fans or abyssal plains and customarily bordered by levees on one or both sides. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+seamount chain: several seamounts in linear or orcuate alignment. Also called: seamounts. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+shelf-edge: a narrow zone at the seaward margin of a shelf along which is a marked increase of slope. Also called: shelf break. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEd.)
+
+sill:  a sea floor barrier of relatively shallow depth restricting water movement between basins. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+slope: the slope seaward from the shelf edge to the upper edge of a continental rise or the point where there is a general reduction in slope. (adapted from IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+terrace: a relatively flat horizontal or gently inclined surface, sometimes long and narrow, which is bounded by a steeper ascending slope on one side and by a steeper descending slope on the opposite side. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2nd Edition)
+
+valley: a relatively shallow, wide depression, the bottom of which usually has a continuous gradient. This term is generally not used for features that have canyon-like characteristics for a significant portion of their extent. Also called: submarine valley; sea valley. (IHO-IOC Publication B-6, Standardization of Undersea Feature Names, 2ndEdition)
+
+canal: an artificial water course used for navigation.
+
+lake: a large body of water entirely surrounded by land. (IHO Dictionary, S-32, 5th Edition, 2629)
+
+river: a relatively large natural stream of water.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSLC    Code: 60
+Category of shoreline construction  CATSLC 60
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT                     M-4
+
+ 1 : breakwater IF 4.1-3; 322.1;
+ 2 : groyne (groin) IF 6.1-3; 313.4;
+ 3 : mole IF 12; 321.3;
+ 4 : pier (jetty) IF 14; 321.2,4;
+ 5 : promenade pier IF 15; 321.2;
+ 6 : wharf (quay) IF 13; 321.1;
+ 7 : training wall IF 5; 322.2;
+ 8 : rip rap
+ 9 : revetment
+ 10 : sea wall IF 2; 313.2;
+ 11 : landing steps  IF 18;
+ 12 : ramp IF 23;
+ 13 : slipway IF 23; 324.1;
+ 14 : fender
+   15   :    solid face wharf
+   16   :    open face wharf
+
+Definitions:
+
+breakwater: a structure protecting a shore area, harbour, anchorage, or  basin from waves. (IHO Dictionary, S-32, 5th Edition, 542)
+
+groyne (groin): a low artificial wall-like structure of durable material extending from the land to seaward for a particular purpose, such as to prevent coast erosion (adapted from IHO Dictionary, S-32, 5th Edition, 2525 and IHO Chart Specifications, M-4)
+
+mole:  a form of breakwater alongside which vessels may lie on the sheltered side only; in some cases it may lie entirely within an artificial harbour, permitting vessels to lie along both sides. (IHO Chart Specifications, M-4)
+
+pier (jetty): a long, narrow structure extending into the water to afford a berthing place for vessels, to serve as a promenade, etc. (IHO Dictionary, S-32, 5th Edition, 3833)
+
+promenade pier:  a pier built only for recreational purposes. (IHO Chart Specifications, M-4)
+
+wharf (quay): a structure serving as a berthing place for vessels. (IHO Dictionary, S-32, 5th Edition, 5985)
+
+training wall:   a wall or bank, often submerged, built to direct or confine the flow of a river or tidal current, or to promote a scour action. (Adapted from IHO Dictionary, S-32, 5th Edition, 5586 and IHO Chart Specifications, M-4).
+
+rip rap: A layer of broken rock, cobbles, boulders, or fragments of sufficient size to resist the erosive forces of flowing water and wave action. (Adapted from Marine Chart Manual, US National Oceanic and Atmospheric Administration - NOAA, 1992)
+
+revetment: facing of stone or other material, either permanent or temporary, placed along the edge of a stream, river or canal to stabilize the bank and to protect it from the erosive action of the stream. (Adapted from IHO Dictionary, S-32, 5th Edition, 4379)
+
+sea wall: an embankment or wall for protection against waves or tidal action along a shore or water front. (IHO Dictionary, S-32, 5th Edition, 4584)
+
+landing steps: steps at the shoreline as the connection between land and water on different levels.
+
+ramp: a sloping structure that can either be used, as a landing place, at variable water levels, for small  vessels, landing ships, or a ferry boat, or for hauling a cradle carrying a vessel, which may include rails. (Adapted from IHO Dictionary, S-32, 5th Edition, 4209)
+
+slipway: the prepared and usually reinforced inclined surface on which keel- and bilge-blocks are laid for  supporting a vessel under construction. (IHO Dictionary, S-32, 5th Edition, 4775)
+
+fender: a protective structure designed to cushion the impact of a vessel and prevent damage.
+
+solid face wharf: a wharf consisting of a solid wall of concrete, masonry, wood etc., such that the water cannot circulate freely under the wharf. The type of construction affects ship-handling; for example, a solid face wharf may give shelter from tidal streams, but under certain circumstances a cusion of water may build up between such a wharf and a ship attempting to berth at it, causing difficulties in ship handling. (Capt. A. Rae, pilot, Port of Halifax & Mr. R. Morash, wharf building engineer, Transport Canada)
+
+
+open face wharf: a wharf supported on piles or other structures which allow free circulation of water under the wharf. (Capt. A. Rae, pilot, Port of Halifax & Mr. R. Morash, wharf building engineer, Transport Canada)
+
+
+Remarks:
+
+ The attribute category of shoreline construction' encodes the usage of a shoreline construction. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSIT    Code: 61
+Category of signal station, traffic     CATSIT 61
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : port control IT 23; 495.1;
+ 2 : port entry and departure
+ 3 : International Port Traffic IT 21; 495.5;
+ 4 : berthing
+ 5 : dock
+ 6 : lock IT 24; 495.2;
+ 7 : flood barrage
+ 8 : bridge passage IT 25.1; 495.3;
+ 9 : dredging
+
+Definitions:
+
+port control: a signal station for the control of vessels within a port.
+
+port entry and departure:
+  a signal station for the control of vessels entering or leaving a port.
+
+International Port Traffic:
+  a signal station displaying International Port Traffic signals.
+
+berthing: a signal station for the control of vessels when berthing.
+
+dock: a signal station for the control of vessels entering or leaving a dock.
+
+lock: a signal station for the control of vessels entering or leaving a lock.
+
+flood barrage: a signal station for the control of vessels wishing to pass through a flood control barrage.
+
+bridge passage: a signal station for the control of vessels wishing to pass under a bridge.
+
+dredging: a signal station indicating when dredging is in progress.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSIW    Code: 62
+Category of signal station, warning     CATSIW 62
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : danger IT 35; 490.1;
+ 2 : maritime obstruction
+ 3 : cable
+ 4 : military practice IT 36; 490.1;
+ 5 : distress IT 26; 497;
+ 6 : weather IT 29; 494.1;
+ 7 : storm IT 28; 494.1;
+ 8 : ice IT 30; 494.1;
+ 9 : time IT 31; 494.2;
+ 10 : tide IT 33; 496.2;
+ 11 : tidal stream IT 34; 496.3;
+ 12 : tide gauge IT 32.2; 496.1;
+ 13 : tide scale IT 32.1; 496.1;
+   14   :   diving
+
+Definitions:
+
+danger: a signal or message warning of the presence of a danger to navigation.
+
+maritime obstruction: a signal or message warning of the presence of a maritime obstruction.
+
+cable: a signal or message warning of the presence of a cable.
+
+military practice: a signal or message warning of activity in a military practice area.
+
+distress: a station that may receive or transmit distress signals.
+
+weather: a visual signal displayed to indicate a weather forecast. (IHO Dictionary, S-32, 5th Edition, 4740)
+
+storm: a signal or message conveying information about storm conditions.
+
+ice:  a signal or message conveying information about ice conditions.
+
+time: an accurate signal marking a specified time or time interval. It is used primarily for determining errors of timepieces. Such signals are usually sent from an observatory by radio or telegraph, but visual signals are used at some ports. (IHO Dictionary, S-32, 5th Edition, 4735)
+
+tide:  a signal or message conveying information on tidal conditions in the area in question. (IHO Dictionary, S-32, 5th Edition, 4734)
+
+tidal stream: a signal or message conveying information on condition of tidal currents in the area in question. (IHO Dictionary, S-32, 5th Edition, 4733)
+
+tide gauge: a device for measuring the height of tide. A graduated staff in a sheltered area where visual observations can be made; or it may consist of an elaborate recording instrument making a continuous graphic record of tide height against time. Such an instrument is usually actuated by a float in a pipe communicating with the sea through a small hole which filters out shorter waves. (IHO Dictionary, S-32, 5th Edition, 1984)
+
+tide scale: a visual scale which directly shows the height of the water above chart datum or a local datum. (IHO Chart Specifications, M-4, 496)
+
+diving: a signal or message warning of diving activity
+
+
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSIL    Code: 63
+Category of silo/tank    CATSIL 63
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : silo in general IE 33; 376.3;
+ 2 : tank in general IE 32; 376.1-2;
+ 3 : grain elevator
+ 4 : water tower
+
+Definitions:
+
+silo in general: a generally cylindrical tower used for storing fodder or grain.
+
+tank in general: a fixed structure for storing liquids. (IHO Dictionary, S-32, 5th Edition, 5290)
+
+grain elevator: a storage building for grain.  Usually a tall frame, metal or concrete structure with an especially compartmented interior. (The New Encyclopaedia Britannica Micropaedia, 15th Edition).
+
+water tower: a tower with an elevated container used to hold water.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSLO    Code: 64
+Category of slope   CATSLO 64
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : cutting ID 14; 363.2;
+ 2 : embankment ID 15; 364.1;
+ 3 : dune IC 8; 312.3;
+ 4 : hill
+ 5 : pingo
+ 6 : cliff IC 3; 312.1;
+ 7 : scree
+
+Definitions:
+
+cutting: an excavation through high ground for a road, canal, etc.
+
+embankment: an artificial elevation constructed from earth, stone, etc. carrying a road, railway or similar or serving to dam water.
+
+dune: a mound, ridge or hill of drifted material on the sea coast or in a desert. (adapted from IHO Dictionary, S-32, 5th Edition, 1496)
+
+hill:  a small isolated elevation, smaller than a mountain. (IHO Dictionary, S-32, 5th Edition, 2262)
+
+pingo: a dome-shaped hill formed in a permafrost area when the hydrostatic pressure of freezing ground water causes the upheaval of a layer of frozen ground. (Encyclopaedia Britannica Mycropaedia, 15th Edition)
+
+cliff:  land rising abruptly for a considerable distance above the water or surrounding land. (IHO Dictionary, S-32, 5th Edition, 829)
+
+scree: rocky debris on the side or at the foot of a mountain forming a steep stony slope.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSCF    Code: 65
+Category of small craft facility    CATSCF 65
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1
+
+ 1 : visitor's berth IU 2;
+ 2 : nautical club IU 4;
+ 3 : boat hoist IU 6;
+ 4 : sailmaker IU 8;
+ 5 : boatyard IU 9;
+ 6 : public inn IU 10;
+ 7 : restaurant IU 11;
+ 8 : chandler IU 12;
+ 9 : provisions IU 13;
+ 10 : doctor IU 15;
+ 11 : pharmacy IU 16;
+ 12 : water tap IU 17;
+ 13 : fuel station IU 18;
+ 14 : electricity IU 19;
+ 15 : bottle gas IU 20;
+ 16 : showers IU 21;
+ 17 : launderette IU 22;
+ 18 : public toilets IU 23;
+ 19 : post box IU 24;
+ 20 : public telephone IU 25;
+ 21 : refuse bin IU 26;
+ 22 : car park IU 27;
+ 23 : parking for boats and trailers IU 28;
+ 24 : caravan site IU 29;
+ 25 : camping site IU 30;
+ 26 : sewerage pump-out station
+ 27 : emergency telephone
+ 28 : landing/launching place for boats IF 17; IU 7;
+ 29 : visitors mooring IU 3;
+ 30 : scrubbing berth
+ 31 : picnic area
+
+Definitions:
+
+visitor's berth: a berth set aside for the use of visiting vessels.
+
+nautical club: a club for mariners generally associated with other small craft facilities.
+
+boat hoist: a hoist for lifting boats out of the water.
+
+sailmaker: a place where sails are made or may be taken for repair.
+
+boatyard: a place on shore where boats may be built, stored and repaired.
+
+public inn: a public house providing food, drink and accommodation. (The Collins Reference English Dictionary, 1992)
+
+restaurant: a commercial establishment serving food. (The Collins Reference Dictionary, 1992)
+
+chandler: a dealer in ships' supplies. (The Collins Reference Dictionary, 1992)
+
+provisions: a place where food and other such supplies are available.
+
+doctor: a place where a doctor is available to provide medical attention.
+
+pharmacy a place where medical drugs are dispensed.
+
+water tap: a place where fresh water is available.
+
+fuel station: a place where fuel is available.
+
+electricity: a place where a connection to an electrical supply is available.
+
+bottle gas: a place where bottled gas is available.
+
+showers: a place where showers are available.
+
+launderette: a place where there are facilities for washing clothes.
+
+public toilets: a place where toilets are available for public use.
+
+post box: a place where mail may be posted.
+
+public telephone: a place where a telephone is available for public use.
+
+refuse bin: a place where refuse may be dumped.
+
+car park: a place where cars may be parked.
+
+parking for boats and trailers:  a place on shore where boats and/or trailers may be parked.
+
+caravan site: a place where caravans may be parked or where caravan accommodation is provided.
+
+camping site: a place where visitors may pitch tents and camp.
+
+sewerage pump-out station:  a place where sewerage may be pumped off a vessel.
+
+emergency telephone: a place where a telephone is available for emergency use only.
+
+landing/launching place for boats:  a place where boats may be landed or launched.
+
+visitors mooring: a mooring set aside for the use of visiting vessels.
+
+scrubbing berth: a place where vessels may berth for the purpose of careening.
+
+picnic area: a place where people may go to eat a picnic.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATSPM    Code: 66
+Category of special purpose mark    CATSPM 66
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : firing danger area mark IQ 125; 441.2;
+ 2 : target mark IQ 51;
+ 3 : marker ship mark IQ 52;
+ 4 : degaussing range mark IQ 54; 448.3;
+ 5 : barge mark IQ 53;
+ 6 : cable mark IQ 55, 123; 443.6; 458;
+ 7 : spoil ground mark IQ 56; 446.3;
+ 8 : outfall mark IQ 57; 444.4;
+ 9 : ODAS (Ocean-Data-Acquisition-System) IQ 58; 462.9;
+ 10 : recording mark IQ 59;
+ 11 : seaplane anchorage mark IQ 60;
+ 12 : recreation zone mark IQ 62;
+ 13 : private mark IQ 70;
+ 14 : mooring mark  431.5;
+ 15 : LANBY (Large Automatic Navigational Buoy) IQ 26; 474.4-5;
+ 16 : leading mark IQ 120; 458;
+ 17 : measured distance mark IQ 122; 458;
+ 18 : notice mark IQ 126; 456.8;
+ 19 : TSS mark (Traffic Separation Scheme) IQ 61;
+ 20 : anchoring prohibited mark
+ 21 : berthing prohibited mark
+ 22 : overtaking prohibited mark
+ 23 : two-way traffic prohibited mark
+ 24 : reduced wake' mark
+ 25 : speed limit mark  456.2;
+ 26 : stop mark
+ 27 : general warning mark
+ 28 : sound ship's siren' mark
+ 29 : restricted vertical clearance mark
+ 30 : maximum vessel's draught mark
+ 31 : restricted horizontal clearance mark
+ 32 : strong current warning mark
+ 33 : berthing permitted mark
+ 34 : overhead power cable mark
+ 35 : channel edge gradient' mark
+ 36 : telephone mark
+ 37 : ferry crossing mark
+ 38 : marine traffic lights
+ 39 : pipeline mark
+ 40 : anchorage mark
+ 41 : clearing mark IQ 121; 458;
+ 42 : control mark
+ 43 : diving mark
+ 44 : refuge beacon IQ 124;
+ 45 : foul ground mark
+ 46 : yachting mark
+ 47 : heliport mark
+ 48 : GPS mark
+ 49 : seaplane landing mark
+ 50 : entry prohibited mark
+ 51 : work in progress mark
+   52   :   mark with unknown purpose
+
+Definitions:
+
+firing danger mark: a mark used to indicate a firing danger area, usually at sea.
+
+target mark: any object toward which something is directed.
+  the distinctive marking or instrumentation of a ground point to aid its identification on a photograph. (Adapted from IHO Dictionary, S-32, 5th Edition, 5309)
+
+marker ship mark: a mark marking the position of a ship which is used as a target during some military exercise. (Bundesamt für Seeschiffahrt und Hydrographie, Germany)
+
+degaussing range mark:
+  a mark used to indicate a degaussing range.
+
+barge mark: a mark of relevance to barges.
+
+cable mark: a mark used to indicate the position of submarine cables or the point at which they run on to the land.
+
+spoil ground mark: a mark used to indicate the limit of a spoil ground (adapted from IHO Dictionary, S-32, 5th Edition, 4931).
+
+outfall mark: a mark used to indicate the position of an outfall or the point at which it leaves the land.
+
+ODAS: Ocean Data Acquisition System (IHO Dictionary, S-32, 5th Edition, 5953)
+
+recording mark: a mark used to record data for scientific purposes.
+
+seaplane anchorage mark:
+  a mark used to indicate a seaplane anchorage.
+
+recreation zone mark: a mark used to indicate a recreation zone.
+
+private mark: a privately maintained mark.
+
+mooring mark: a mark indicating a mooring or moorings.
+
+LANBY: a large buoy designed to take the place of a lightship where construction of an offshore light station is not feasible. (IHO Dictionary, S-32, 5th Edition, 2656)
+
+leading mark: aids to navigation or other indicators so located as to indicate the path to be followed. Leading marks identify a leading line when they are in transit. (IHO Dictionary, S-32, 5th Edition, 2697)
+
+measured distance mark:
+  a mark forming part of a transit indicating one end of a measured distance.
+
+notice mark: a notice board or sign indicating information to the mariner.
+
+TSS mark: a mark indicating a traffic separation scheme.
+
+
+anchoring prohibited mark:
+  a mark indicating an anchoring prohibited area.
+
+berthing prohibited mark:
+  a mark indicating that berthing is prohibited.
+
+overtaking prohibited mark:
+  a mark indicating that overtaking is prohibited.
+
+two-way traffic prohibited mark:
+  a mark indicating a one-way route.
+
+reduced wake' mark: a mark indicating that vessels must not generate excessive wake.
+
+speed limit mark: a mark indicating that a speed limit applies.
+
+stop mark: a mark indicating the place where the bow of a ship must stop when traffic lights show red.
+
+general warning mark: a mark indicating that special caution must be exercised in the vicinity of the mark.
+
+sound ships siren' mark:
+  a mark indicating that a ship should sound its siren or horn.
+
+restricted vertical clearance mark:
+  a mark indicating the minimum vertical space available for passage.
+
+maximum vessel's draught mark:
+  a mark indicating the maximum draught of vessel permitted.
+
+restricted horizontal clearance mark:
+  a mark indicating the minimum horizontal space available for passage.
+
+strong current warning mark:
+  a mark warning of strong currents.
+
+berthing permitted mark:
+  a mark indicating that berthing is allowed.
+
+overhead power cable mark:
+  a mark indicating an overhead power cable.
+
+channel edge gradient' mark:
+  a mark indicating the gradient of the slope of a dredge channel edge.
+
+telephone mark: a mark indicating the presence of a telephone.
+
+ferry crossing mark: a mark indicating that a ferry route crosses the ship route; often used with a sound ship's siren' mark.
+
+pipeline mark: a mark used to indicate the position of submarine pipelines or the point at which they run on to the land.
+
+anchorage mark: a mark indicating an anchorage area.
+
+clearing mark: a mark used to indicate a clearing line.
+
+control mark: a mark indicating the location at which a restriction or requirement exists.
+
+diving mark: a mark indicating that diving may take place in the vicinity.
+
+refuge beacon: a mark providing or indicating a place of safety.
+
+foul ground mark: a mark indicating a foul ground.
+
+yachting mark: a mark installed for use by yachtsmen.
+
+heliport mark: a mark indicating an area where helicopters may land.
+
+GPS mark: a mark indicating a location at which a GPS position has been accurately determined.
+
+seaplane landing mark: a mark indicating an area where sea-planes land.
+
+entry prohibited mark: a mark indicating that entry is prohibited.
+
+work in progress mark: a mark indicating that work (generally construction) is in progress.
+
+mark with unknown purpose:
+  a mark whose detailed characteristics are unknown.
+
+
+Remarks:
+
+ A mark may be a beacon, a buoy, a signpost or may take another form.
+
+Value number 38 should be encoded using object class signal station, traffic (SISTAT). FEATURE OBJECT ATTRIBUTES
+
+
+
+Acronym: CAT_TS    Code: 188
+
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : flood stream IH 40; 407.4;
+ 2 : ebb stream IH 41; 407.4;
+     3   :    Other tidal flow
+
+Definitions:
+
+flood stream: the horizontal movement of water associated with the rising tide. Flood streams generally set towards the shore, or in the direction of the tide progression. Also called flood, flood current or ingoing stream. (Adapted from IHO Dictionary, S-32, 5th Edition)
+
+ebb stream: the horizontal movement of water associated with falling tide. Ebb streams generally set seaward, or in the opposite direction to the tide progression. Also called ebb, ebb current or outgoing stream. (Adapted from IHO Dictionary, S-32, 5th Edition)
+
+Other tidal flow: any other horizontal movement of water associated with tides, eg. rotary flow.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATTOW   Attribute type: E
+Category of tower   CATTOW
+INT 1 Reference:  IE 20, 21, 29; 30.2;
+
+Chart Specification:  374.2-3; 375.2; 487.3;
+
+Expected input:
+
+   ID Meaning
+
+    1 :  light tower
+    2 :  water tower
+    3 :  radio/television tower
+    4 :  cooling tower
+    5 :  radar tower
+    6 :  lookout tower
+    7 :  observation tower
+
+Remarks:
+
+ The attribute category of tower' encodes the various types of tower.
+
+ Definitions of attribute values:
+
+ Light tower: A tower carrying a light as a navigational aid.
+
+ Water tower:  A tower with an elevated container used to hold water.
+
+ Radio/television tower:
+  A tower used for transmitting and/or receiving radio/television signals.
+
+ Cooling tower: A tower to cool liquids. (Digital Geographic Information Working Group -DGIWG, Oct.87)
+
+ Radar tower:  A tower carrying radar equipment.
+
+ Lookout tower: A tower from which a watch is habitually kept.
+
+ Observation tower: A tower from which a watch is not habitually kept.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attribute category of landmark (CATLMK).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATTSS    Code: 67
+Category of Traffic Separation Scheme   CATTSS 67
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : IMO - adopted
+ 2 : not IMO - adopted
+
+Definitions:
+
+IMO - adopted: a defined Traffic Separation Scheme that has been adopted as an IMO routing measure.
+
+not IMO - adopted: a defined Traffic Separation Scheme that has not been adopted as an IMO routing measure.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: CATTRE   Attribute type: E
+Category of tree    CATTRE
+INT 1 Reference:  IC 31.1-8;
+
+Chart Specification:  354.2;
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  evergreen
+    2 :  conifer
+    3 :  palm
+    4 :  nipa palm
+    5 :  casuarina
+    6 :  filao
+    7 :  eucalypt
+    8 :  deciduous
+    9 :  mangrove
+
+
+Remarks:
+
+ The attribute category of tree' encodes the various types of tree.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attribute category of vegetation (CATVEG).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATVEG    Code: 68
+Category of vegetation  CATVEG 68
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : grass
+ 2 : paddy field
+ 3 : bush
+ 4 : deciduous wood
+ 5 : coniferous wood
+ 6 : wood in general (inc mixed wood) IC 30; 354.1;
+ 7 : mangroves IC 32; 312.4;
+ 8 : park
+ 9 : parkland
+ 10 : mixed crops
+ 11 : reed
+ 12 : moss
+ 13 : tree in general IC 31; 354.2;
+ 14 : evergreen tree IC 31.2; 354.2;
+ 15 : coniferous tree IC 31.3; 354.2;
+ 16 : palm tree IC 31.4; 354.2;
+ 17 : nipa palm tree IC 31.5; 354.2;
+ 18 : casuarina tree IC 31.6; 354.2;
+ 19 : eucalypt tree IC 31.8; 354.2;
+ 20 : deciduous tree IC 31.1; 354.2;
+ 21 : mangrove tree IC 32; 312.4;
+ 22 : filao tree IC 31.7; 354.2;
+
+Definitions:
+
+grass: vegetation belonging to a group of plants with green blades that are eaten by cattle, sheep, etc. (The Concise Oxford Dictionary)
+
+bush: a shrub or clump of shrubs with stems of moderate length. (The Concise Oxford Dictionary)
+
+deciduous wood: a wood with trees that shed their leaves annually. (Bundesamt für Seeschiffahrt und Hydrographie, Germany)
+
+coniferous wood: a wood with evergreen trees of a group usually bearing cones, including yews, cedars and redwoods. (Bundesamt für Seeschiffahrt und Hydrographie, Germany)
+
+wood in general (including mixed wood):
+  growing trees densely occupying a tract of land. (The Concise Oxford Dictionary)
+
+mangroves: one of several genera of tropical trees or shrubs which produce many prop roots and grow along low lying coasts into shallow water. (IHO Dictionary, S-32, 5th Edition, 3064)
+
+mixed crops: a mixture of arable crops.
+
+reed: any of various water or marsh plants with a firm stem. (The Concise Oxford Dictionary)
+
+moss: any small cryptogamous plant of the class Musci, growing in dense clusters on the surface of the ground in bogs, on trees, stones, etc. (The Concise Oxford Dictionary)
+
+tree in general: a woody perennial plant, having a self supporting  main stem or trunk.
+
+evergreen tree: a tree which keeps its foliage all year round.
+
+coniferous tree: a cone-bearing, needle-leaved or scale-leaved evergreen tree. (adapted from The New Encyclopaedia Britannica, 15th Edition 1991)
+
+palm tree: a tropical or sub-tropical tree, shrub or vine having a tall, unbranched, columnar trunk.  The trunk is crowned by a tuft or large, pleated fan or feather shaped leaves with stout sheathing and often prickly petioles (stalks), the persistent bases of which frequently clothe the trunk. (adapted from The New Encyclopaedia Britannica, 15th Edition 1991)
+
+nipa palm tree: (also called Nypa palm) a rare palm tree with regular branching involving equal or sub-equal division of the apex that results in forking. (adapted from The New Encyclopaedia Britannica, 15th Edition 1991)
+
+casuarina tree: (also called beefwood, Australian pine, ironwood, she-oak, swamp oak, whistling pine) a tree characterized by slender, green, often drooping branches that are deeply grooved and that bear, at intervals, whorls of tine leaves. (adapted from The New Encyclopaedia Britannica, 15th Edition 1991)
+
+eucalypt tree: an instance of a large genus of mostly very large trees (90 metres). (adapted from The New Encyclopaedia Britannica, 15th Edition 1991)
+
+deciduous tree: a tree which sheds its foliage for part of the year (generally in winter).
+
+mangrove tree: one of several genera of tropical trees or shrubs which produce many prop roots and grow along low lying coasts in to shallow waters. (IHO Dictionary, S-32, 5th Edition, 3064)
+
+filao tree: a variety of tropical or sub-tropical tree.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATWAT    Code: 69
+Category of water turbulence    CATWAT 69
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : breakers IK 17; 423.2;
+ 2 : eddies IH 45; 423.3;
+ 3 : overfalls IH 44; 423.1;
+ 4 : tide rips IH 44; 423.1;
+ 5 : bombora
+
+Definitions:
+
+breaker: a wave breaking on the shore, over a reef, etc. Breakers may be roughly classified into three kinds, although the categories may overlap: spilling breakers break gradually over a considerable distance; plunging breakers tend to curl over and break with a crash; and surging breakers peak up, but then instead of spilling or plunging they surge up on the beach face. The French word brisant' is also used for the obstacle causing the breaking of the wave. (IHO Dictionary, S-32, 5th Edition, 540)
+
+eddies:  circular movements of water usually formed where currents pass obstructions, between two adjacent currents flowing counter to each other, or along the edge of a permanent current. (IHO Dictionary, S-32, 5th Edition, 1560)
+
+overfalls:  short, breaking waves occurring when a strong current  passes over a shoal or other submarine obstruction or meets a contrary current or wind. (IHO Dictionary, S-32, 5th Edition, 3631)
+
+tide rips: small waves formed on the surface of water by the meeting  of opposing tidal currents or by a tidal current crossing an  irregular  bottom. (IHO Dictionary, S-32, 5th Edition, 5494)
+
+bombora: a wave that forms over a submerged offshore reef or rock, sometimes (in very calm weather or at high tide) nearly swelling but in other conditions breaking heavily and producing a dangerous stretch of broken water; the reef or rock itself. Also called bumbora or bomborah. (Australian National Dictionary)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATWED    Code: 70
+Category of weed/kelp    CATWED 70
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : kelp IJ 13.2; 428.2;
+ 2 : sea weed IJ 13.1; 425.5;
+ 3 : sea grass
+ 4 : saragasso
+
+Definitions:
+
+kelp: a giant plant sometimes 60 metres long with no roots, it is anchored by hold-fasts or tendrils up to 10 metres long, that cling to rock.  Gas filled bubbles on fronds act as floats keeping the kelp just below the surface. (Earth Sciences References, Mary McNeil)
+
+sea weed: general name for marine plants of the algae class which grow in long narrow ribbons.  Also called sea grass. (International Maritime Dictionary, 2nd Edition)
+
+sea grass: any grasslike marine alga.  Eelgrass is one of the best known seagrasses. (IHO Dictionary, S-32, 5th Edition, 4565)
+
+sargasso: a certain type of sea weed, or more generally, a large floating mass of this sea weed. (IHO Dictionary, S-32, 5th Edition, 4501)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATWRK    Code: 71
+Category of wreck   CATWRK 71
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : non-dangerous wreck IK 29; 422.6;
+ 2 : dangerous wreck IK 28; 422.5;
+ 3 : distributed remains of wreck IK 31; 422.8;
+ 4 : wreck showing mast/masts IK 25; 422.2;
+ 5 : wreck showing any portion of hull or superstructure IK 24; 422.2;
+
+Definitions:
+
+non-dangerous wreck: a wreck which is not considered to be dangerous to surface navigation.
+
+dangerous wreck: a wreck which is considered to be dangerous to surface navigation.
+
+distributed remains of wreck:
+  (foul ground) an area over which it is safe to navigate but which should be avoided for anchoring, taking the ground or ground fishing. (IHO Chart Specifications, M-4)
+
+wreck showing mast/masts:
+  wreck of which only the mast(s) is visible at the sounding datum indicated.
+
+wreck showing any portion of hull or superstructure:
+  wreck of which any portion of the hull or superstructure is visible at the sounding datum indicated.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CATZOC    Code: 72
+Category of zone of confidence in data CATZOC
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : zone of confidence A1
+ 2 : zone of confidence A2
+ 3 : zone of confidence B
+ 4 : zone of confidence C
+ 5 : zone of confidence D
+ 6 : zone of confidence U (data not assessed)
+
+Definitions:
+
+See ZOC Table on following page.
+
+ZOC Table:
+
+
+12345ZOC 1
+Position
+Accuracy
+Depth Accuracy 3
+
+Seafloor CoverageTypical Survey Characteristics 5
+
+
+
+A1
+
+
+
+
+   5 m  a = 0.5
+
+ b = 1   Full seafloor ensonification or sweep.  All significant seafloor features detected 4 and depths measured.Controlled,
+systematic
+high accuracy Survey on
+WGS 84 datum; using DGPS or a minimum three lines of position (LOP) with  multibeam, channel or mechanical
+sweep system.
+
+
+
+. Depth (m) Accuracy (m)10
+30
+100
+1000 0.6
+ 0.8
+ 1.5
+ 10.5
+
+
+
+A2
+
+
+
+  20 m a = 1.0
+b = 2Full seafloor ensonification or sweep. All significant  seafloor features detected 4  and depths measured.Controlled,
+systematic survey  to standard accuracy; using modern survey echosounder with sonar or mechanical sweep. Depth (m) Accuracy (m)10
+30
+100
+1000 1.2
+ 1.6
+ 3.0
+ 21.0
+
+
+
+B
+
+
+
+  50 ma = 1.0
+b = 2Full seafloor coverage not achieved; uncharted features, hazardous to surface navigation are not expected but may exist.Controlled,
+systematic survey  to standard accuracy. Depth (m) Accuracy (m)10
+30
+100
+1000  1.2
+ 1.6
+ 3.0
+ 21.0
+
+
+
+C
+
+
+
+    500 ma = 2.0
+b = 5Full seafloor coverage not achieved, depth anomalies may be expected.Low accuracy survey or data collected on an opportunity basis such as soundings on passage. Depth (m) Accuracy (m)10
+30
+100
+1000  2.5
+ 3.5
+ 7.0
+ 52.0
+D worse
+ than
+ ZOC C worse
+ than
+ ZOC CFull seafloor coverage not achieved, large depth anomalies may be expected.Poor quality data or data that cannot be quality asses-sed due to lack of information.
+Note: The CATZOC attribute definitions are currently the subject of review and the results of this review will be promulgated as soon as possible in the S-57 Corrections Document.
+Remarks:
+
+To decide on a ZOC Category, all conditions outlined in columns 2 to 4 of the table must be met.
+
+Footnote numbers quoted in the table have the following meanings:
+
+1 The allocation of a ZOC indicates that particular data meets minimum criteria for position and depth accuracy and seafloor coverage defined in this Table. Data may be further qualified by Object Class "Quality of Data" (M_QUAL) sub-attributes as follows:
+
+ a) Positional Accuracy (POSACC) and Sounding Accuracy (SOUACC) may be used to indicate that a higher position or depth accuracy has been achieved than defined in this Table (e.g. a survey where full seafloor coverage was not achieved could not be classified higher that ZOC B; however, if the position accuracy was, for instance,  15 metres, the sub-attribute POSACC could be used to indicate this).
+
+ b) Swept areas where the clearance depth is accurately known but the actual seabed depth is not accurately known may be accorded a "higher" ZOC (i.e. A1 or A2) providing positional and depth accuracies of the swept depth meets the criteria in this Table. In this instance, Depth Range Value 1 (DRVAL1) may be used to specify the swept depth. The position accuracy criteria apply to the boundaries of swept areas.
+
+ c) SURSTA, SUREND and TECSOU may be used to indicate the start and end dates of the survey and the technique of sounding measurement.
+
+2 Position Accuracy of depicted soundings at 95% CI (2.45 sigma) with respect to the given datum. It is the cumulative error and includes survey, transformation and digitizing errors etc. Position accuracy need not be rigorously computed for ZOCs B, C and D but may be estimated based on type of equipment, calibration regime, historical accuracy etc.
+
+3 Depth accuracy of depicted soundings = a + (bd)/100 at 95% CI (2.00 sigma), where d = depth in metres at the critical depth. Depth accuracy need not be rigorously computed for ZOCs B, C and D but may be estimated  based on type of equipment, calibration regime, historical accuracy etc.
+
+4 Significant seafloor features are defined as those rising above depicted depths by more than:
+
+  Depth  Significant Feature
+
+ a. <10 metres  >0.1depth,
+ b. 10 to 30 metres  >1.0 metre,
+ c. >30 metres  >(0.1depth) minus 2.0 metres
+
+5 Controlled, systematic (high accuracy) survey (ZOC A1, A2 and B) - a survey comprising planned survey lines, on a geodetic datum that can be transformed to WGS 84.
+
+ Position fixing (ZOC A1) must be strong with at least three high quality Lines of Position (LOP) or Differential GPS.
+
+ Modern survey echosounder - a high precision surveying depth measuring equipment, generally including all survey echosounders designed post 1970.
+
+
+
+
+Acronym: $SPACE    Code: 73
+Character spacing  $SPACE 73
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : expanded/condensed
+ 2 : standard
+
+
+Definitions:
+
+expanded/condensed: string expanded or condensed to fit between the first and last positions.
+
+standard: character spacing in accordance with the typeface in use.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $CHARS    Code: 74
+Character specification   $CHARS 74
+Attribute type: A
+
+
+Expected input: SEWBB, where:
+
+ S  style =  U : Univers
+  T : Times
+ E  weight =  4 : light
+  5 : medium
+  6 : bold
+ W  width = 1 : upright
+  2 : italic
+ BB body size =  XX (body size in pica points)
+
+References:
+
+ INT 1: not specified;
+
+ M-4: not specified;
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: COLOUR    Code: 75
+Colour  COLOUR 75
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : white IP 11.1; 450.2-3;
+ 2 : black
+ 3 : red IP 11.2; 450.2-3;
+ 4 : green IP 11.3; 450.2-3;
+ 5 : blue IP 11.4; 450.2-3;
+ 6 : yellow IP 11.6; 450.2-3;
+ 7 : grey
+ 8 : brown
+ 9 : amber IP 11.8; 450.2-3;
+ 10 : violet IP 11.5; 450.2-3;
+ 11 : orange IP 11.7; 450.2-3;
+ 12 : magenta
+   13   :    pink
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: COLMAR   Attribute type: E
+Colour of navigational mark     COLMAR
+INT 1 Reference:  IQ 2-5, 130.1, 130.3-6;
+
+Chart Specification:  455.4; 464; 464.1-3;
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  green
+    2 :  black
+    3 :  red
+    4 :  yellow
+    5 :  white
+    6 :  orange
+    7 :  black/yellow
+    8 :  black/yellow/black
+    9 :  yellow/black
+   10 :  yellow/black/yellow
+   11 :  red/white
+   12 :  green/red/green
+   13 :  red/green/red
+   14 :  black/red/black
+   15 :  yellow/red/yellow
+   16 :  green/red
+   17 :  red/green
+   18 :  green/white
+
+Remarks:
+
+ The attribute colour of navigational mark' encodes the various colours and the combinations for navigational marks.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attribute colour (COLOUR).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: COLPAT    Code: 76
+Colour pattern  COLPAT 76
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : horizontal stripes
+ 2 : vertical stripes
+ 3 : diagonal stripes
+ 4 : squared
+ 5 : stripes (direction unknown)
+ 6 : border stripe
+
+Definitions:
+
+horizontal stripes: straight bands or stripes of differing colours painted horizontally.
+
+vertical stripes: straight bands or stripes of differing colours painted vertically.
+
+diagonal stripes: straight bands or stripes of differing colours painted diagonally (ie not horizontally or vertically).
+
+squared: often referred to as checker plate, where alternate colours are used to create squares similar to a chess or draught board.  The pattern may be straight or diagonal.
+
+stripes (direction unknown):
+  straight bands or stripes of differing colours painted in an unknown direction.
+
+border stripe: a band or stripe of colour which is displayed around the outer edge of the object, which may also form a border to an inner pattern or plain colour.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: COMCHA    Code: 77
+Communication channel   COMCHA 77
+Attribute type: A
+
+
+Definition:
+
+ A channel number assigned to a specific radio frequency, frequencies or frequency band.
+
+Expected input:
+
+ enter specific VHF-Channel
+
+
+References:
+
+ INT 1: IM 40;
+
+ M-4: 488;
+
+ The attribute communication channel' encodes the various VHF-channels used for communication.
+
+Indication:
+
+ Each VHF-channel should be indicated by 2 digits and up to 2 characters (A-Z);
+
+  e.g. VHF-channel 7  -> 07'
+  VHF-channel 16 -> 16';
+
+ The indication of several VHF-channels is possible;
+
+Format:
+
+ [XXXX];[XXXX];...
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $CSIZE    Code: 78
+Compass size   $CSIZE 78
+Attribute type: F
+
+
+Expected input:
+
+ the radius of the compass.
+
+
+Definition:
+
+ specifies the display radius for a cartographic compass rose.
+
+Indication:
+
+ Unit: millimetre (mm)
+ Resolution: 0.1 mm
+
+Format:
+
+ xx.xx
+
+Example:
+
+ 68  for a compass rose radius of 68 millimetres.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CPDATE    Code: 79
+Compilation date    CPDATE 79
+Attribute type: A
+
+
+Definition:
+
+ The date on which the compilation of the data was completed.
+
+
+Indication:
+
+ The compilation date  should be encoded using 4 digits for the calendar year (CCYY), 2  digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD), according to ISO 8601: 1988.
+
+
+Format:
+
+ CCYYMMDD (mandatory)
+
+Example:
+
+ 19871021 for 21 October 1987 as compilation date.
+
+Remarks:
+
+ No remarks.
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CSCALE    Code: 80
+Compilation scale   CSCALE 80
+Attribute type: I
+
+
+Definition:
+
+ The scale at which the data was originally compiled.
+
+
+Minimum value:  0
+
+Indication:
+
+ The modulus of the scale is indicated, that is 1:75 000 is encoded as 75000.
+
+ Unit: none
+ Resolution: 1
+
+
+Format:
+
+ xxxxxxxx
+
+
+Example:
+
+ 75000  for a scale of 1:75 000.
+
+Remarks:
+
+ For example, the scale of the paper chart that was used for the ENC compilation. This attribute is only used in conjunction with the meta-object Compilation Scale of data' (M_CSCL) which is used to define polygons of equal compilation scale. CSCALE should therefore not be confused with the attributes SCAMIN and SCAMAX. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CONDTN    Code: 81
+Condition   CONDTN 81
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : under construction IF 30; 329.1;
+ 2 : ruined ID 8; IF 33.1-2; 378.1-2;
+ 3 : under reclamation IF 31; 329.2;
+ 4 : wingless IE 25.2; 374.5;
+ 5 : planned construction
+
+Definitions:
+
+under construction: a structure that is in the process of being built.
+
+ruined: a structure in a decayed or deteriorated condition resulting from neglect or disuse, or a damaged structure in need of repair. (IHO Dictionary, S-32, 5th Edition, 4456)
+
+under reclamation: an area of the sea that is being reclaimed as land, usually by the dumping of earth and other material.
+
+wingless: a windmill or windmotor from which the turbine blades are missing.
+
+planned construction: an area where a future construction is planned.
+
+Remarks:
+
+ The attribute condition' encodes the various conditions of buildings and other constructions. The default condition' should be considered to be completed, undamaged and working normally. This attribute should, therefore, only be used to indicate objects whose condition is anything other than normal'. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CONRAD    Code: 82
+Conspicuous, radar  CONRAD 82
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : radar conspicuous IS 5; 485.2;
+ 2 : not radar conspicuous
+ 3 : radar conspicuous (has radar reflector)
+
+Definitions:
+
+radar conspicuous: an object which returns a strong radar echo. (IHO Dictionary, S-32, 5th Edition, 4142)
+
+not radar conspicuous: an object which does not return a particularly strong radar echo.
+
+radar conspicuous (has radar reflector):
+  an object which returns a strong radar echo, having a radar reflector.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CONVIS    Code: 83
+Conspicuous, visually   CONVIS 83
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : visually conspicuous  340.1;
+ 2 : not visually conspicuous
+
+Definitions:
+
+visually conspicuous: term applied to an object either natural or artificial which is distinctly and notably visible from seaward. (IHO Dictionary, S-32, 5th Edition, 984)
+
+not visually conspicuous:
+  an object which is visible from seaward, but is not conspicuous.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: CURVEL    Code: 84
+Current velocity  CURVEL 84
+Attribute type: F
+
+
+Definition:
+
+ The rate of travel of a current.
+
+
+References:
+
+ INT 1: IH 40, 43;
+
+ M-4: 407.1; 407.4; 408.3;
+
+Indication:
+
+ Unit: knot (kt)
+ Resolution: 0.1kt
+
+Format:
+
+ xx.x
+
+Example:
+
+ 1.6  for a velocity of 1.6 knots.
+
+Remarks:
+
+ The attribute current velocity' indicates the speed of the current in knots. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: DATEND    Code: 85
+Date end    DATEND 85
+Attribute type: A
+
+
+Indication:
+
+ the date, end' should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD), according to ISO 8601: 1988.
+
+Format:
+
+ CCYYMMDD (mandatory)
+
+Example:
+
+19961007 for 07 October 1996 as ending date.
+
+
+Remarks:
+
+ The attribute date end' indicates the latest date on which an object (e.g. a buoy) will be present.
+
+ This attribute is to be used to indicate the removal or cancellation of an object at a specific date in the future. See also periodic date end'
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: DATSTA    Code: 86
+Date start  DATSTA 86
+Attribute type: A
+
+
+Indication:
+
+ The date, start' should be encoded using 4 digits for the calendar year (CCYY), 2  digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD), according to ISO 8601: 1988.
+
+Format:
+
+ CCYYMMDD (mandatory)
+
+Example:
+
+ 19960822 for 22 August 1996 as starting date.
+
+Remarks:
+
+ The attribute date, start' indicates the earliest date on which an object (e.g. a buoy) will be present.
+
+ This attribute is to be used to indicate the deployment or implementation of an object at a specific date in the future. See also periodic date start'.
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: DRVAL1    Code: 87
+Depth range value 1     DRVAL1 87
+Attribute type: F
+
+
+Definition:
+
+ The minimum (shoalest) value of a depth range.
+
+References:
+
+ INT 1: II 21; IM 6;
+
+ M-4:  414; 432.4; 434.3-4;
+
+Indication:
+
+ Unit: defined in the DUNI subfield of the DSPM record or in the DUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 fm or 0.1 ft
+
+
+Format:
+
+ sxxxxx.x
+ s: sign, negative values only.
+
+Example:
+
+ 50  for a minimum depth of 50 metres.
+
+Remarks:
+
+ Where the area dries, the value is negative.
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: DRVAL2    Code: 88
+Depth range value 2     DRVAL2 88
+Attribute type: F
+
+
+Definition:
+
+ The maximum (deepest) value of a depth range.
+
+References:
+
+ INT 1: II 21; IM 6;
+
+ M-4: 414; 432.4; 434.3-4;
+
+
+Indication:
+
+ Unit: defined in the DUNI subfield of the DSPM record or in the DUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 fm or 0.1 ft
+
+
+Format:
+
+ sxxxxx.x
+ s: sign, negative values only.
+
+Example:
+
+ 100  for a maximum depth of 100 metres
+
+Remarks:
+
+ Where the area dries, the value is negative. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: DUNITS    Code: 89
+Depth units     DUNITS 89
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : metres
+ 2 : fathoms and feet
+ 3 : feet
+ 4 : fathoms and fractions
+
+Definitions:
+
+metres: depths are specified in metres (SI units of length).
+
+fathoms and feet: depths are specified in fathoms (units of six feet of depth) and feet.
+
+feet: depths are specified in feet (imperial units of length).
+
+fathoms and fractions: depths are specified in fathoms (units of six feet of depth) and fractions of fathoms.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: ELEVAT    Code: 90
+Elevation   ELEVAT 90
+Attribute type: F
+
+
+Definition:
+
+ The altitude of the ground level of an object, measured from a specified vertical datum.
+
+Minimum Value: 0
+
+References:
+
+ INT 1: IC 10-13; IH 20;
+
+ M-4: 352.1-2; 302.2; 405;
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 47  for an elevation of 47 metres
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: ESTRNG   Code: 91
+Estimated range of transmission    ESTRNG 91
+Attribute type: F
+
+
+Definition:
+
+ The estimated range of a non-optical electromagnetic transmission.
+
+Minimum Value: 0
+
+References:
+
+ INT 1: none specified;
+
+ M-4: none specified;
+
+Indication:
+
+ Unit: nautical mile (M)
+ Resolution: 0.1M
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 45  for a range of 45 nautical miles.
+
+Remarks:
+
+ The estimated range (distance) assumes in vacuo' transmission  and a standard antenna height of 5 metres. Thus it gives a hint to the mariner whether he is likely to receive transmission at a certain distance from an object carrying this attribute.
+
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: EXCLIT    Code: 92
+Exhibition condition of light   EXCLIT 92
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : light shown without change of character
+ 2 : daytime light IP 51; 473.4;
+ 3 : fog light IP 52; 473.5;
+ 4 : night light
+
+Definition:
+
+light shown without change of character:
+  a light shown throughout the 24 hours without change of character. IHO Chart Specifications, M-4
+
+daytime light: a light which is only exhibited by day.
+
+fog light a light which is exhibited in fog or conditions of reduced visibility.
+
+night light: a light which is only exhibited at night.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: EXPSOU    Code: 93
+Exposition of sounding  EXPSOU 93
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : within the range of depth of the surrounding depth area
+ 2 : shoaler than the range of depth of the surrounding depth area
+ 3 : deeper than the range of depth of the surrounding depth area
+
+Definitions:
+
+within the range of depth of the surrounding depth area:
+  the depth corresponds to the depth range of the surrounding depth area. i.e. the depth is not shoaler than the minimum depth of the surrounding depth area or deeper than the maximum depth of the surrounding depth area.
+
+shoaler than the range of depth of the surrounding depth area:
+  the depth is shoaler than the minimum depth of the surrounding depth area.
+
+deeper than the range of depth of the surrounding depth area:
+  the depth is deeper than the maximum depth of the surrounding depth area.
+
+Remarks:
+
+ This attribute indicates objects with a value of sounding' not within the range of depth of the surrounding depth area. These objects could be a potential danger for navigation.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: FUNCTN  Code: 94
+Function+ FUNCTN
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : no function/service of major interest
+ 2 : harbour-master's office IF 60; 325.1;
+ 3 : custom office IF 61; 325.2;
+ 4 : health office IF 62.1; 325.3;
+ 5 : hospital IF 62.2; 325.3;
+ 6 : post office IF 63; 372.1;
+ 7 : hotel
+ 8 : railway station ID 13; 362.2;
+ 9 : police station
+ 10 : water-police station
+ 11 : pilot office IT 3; 491.4;
+ 12 : pilot lookout IT 2; 491.3;
+ 13 : bank office
+ 14 : headquarters for district control
+ 15 : transit shed/warehouse IF 51; 328.1;
+ 16 : factory
+ 17 : power station
+ 18 : administrative IG 72;
+ 19 : educational facility
+ 20 : church IE 10.1; 373.1-2;
+ 21 : chapel IE 11;
+ 22 : temple IE 13 373.2;
+ 23 : pagoda IE 14; 373.3;
+ 24 : shinto shrine IE 15; 373.3;
+ 25 : buddhist temple IE 16; 373.3;
+ 26 : mosque IE 17; 373.4;
+ 27 : marabout IE 18; 373.5;
+ 28 : lookout
+ 29 : communication
+ 30 : television
+ 31 : radio
+ 32 : radar
+ 33 : light support
+ 34 : microwave
+ 35 : cooling
+ 36 : observation
+ 37 : timeball
+ 38 : clock
+ 39 : control
+ 40 : airship mooring
+   41   :   stadium
+   42   :   bus station
+
+Definitions:
+
+harbour-master's office: the office of the local official who has charge of mooring and berthing of vessels, collecting harbour fees, etc. (adapted from IHO Dictionary, S-32, 5th Edition, 2191)
+
+customs office: an office which is charged with enforcing customs regulations.
+
+health office: the office which is charged with the administration of health laws and sanitary inspections. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+hospital: an institution or establishment providing medical or surgical treatment for the ill or wounded. (The New Shorter Oxford English Dictionary, 1993)
+
+post office: the public department, agency or organisation responsible primarily for the collection, transmission and distribution of mail. (The New Shorter Oxford English Dictionary, 1993)
+
+hotel: an establishment, especially of a comfortable or luxurious kind, where paying visitors are provided with accommodation, meals and other services. (The New Shorter Oxford English Dictionary, 1993)
+
+railway station: a building with platforms where trains arrive, load, discharge and depart. (The New Shorter Oxford English Dictionary, 1993)
+
+police station: the office of the local police force.
+
+water-police station: the headquarters of a local water-police force.
+
+pilot office: the office or headquarters of pilots; the place where the services of a pilot may be obtained. (IHO Dictionary, S-32, 5th Edition, 3845)
+
+pilot lookout: a distinctive structure on shore from which personnel keep watch upon events at sea or along the coast. (IHO Dictionary, S-32, 5th Edition, 2917)
+
+bank office: an office for custody, deposit, loan, exchange or issue of money. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+headquarters for district control:
+  the quarters of an executive officer (director, manager, etc.) with responsibility for an administrative area.
+
+transit shed/warehouse:
+  a building or part of a building for storage of wares or goods. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+factory: a building or buildings with equipment for manufacturing; a workshop. (The New Shorter Oxford English Dictionary, 1993)
+
+power station: a stationary plant containing apparatus for large scale conversion of some form of energy (such as hydraulic, steam, chemical or nuclear energy) into electrical energy. (McGraw-Hill Dictionary of Scientific and Technical Terms, 3rd Edition, 1984)
+
+administrative: a building for the management of affairs. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+educational facility: a building concerned with education (eg. school, college, university, etc.)
+
+church: a building for public Christian worship. (The New Shorter Oxford English Dictionary, 1993)
+
+chapel: a place for Christian worship other than a parish, cathedral or church, especially one attached to a private house or institution. (The New Shorter Oxford English Dictionary, 1993)
+
+temple: a building for public Jewish worship. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+pagoda: a Hindu or Buddhist temple or sacred building. (The New Shorter Oxford English Dictionary, 1993)
+
+shinto shrine: a building for public Shinto worship. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+buddhist temple: see pagoda.
+
+mosque: a Muslim place of worship. (The New Shorter Oxford English Dictionary, 1993)
+
+marabout: a shrine marking the burial place of a Muslim holy man. (The New Shorter Oxford English Dictionary, 1993)
+
+lookout: keeping a watch upon events at sea or along the coast. (adapted from IHO Dictionary, S-32,5th Edition,2917)
+
+communication: transmitting and/or receiving electronic communication signals. (adapted from Digital Geographic Information Standard - DIGEST)
+
+television:  broadcast of television signals.
+
+radio: broadcast of radio signals.
+
+radar: a method, system or technique of using beamed, reflected, and timed radio waves for detecting, locating, or tracking objects, and for measuring altitudes. (IHO Dictionary, S-32, 5th Edition,4158)
+
+light support: supporting a light
+
+microwave: broadcasting and receiving signals using microwaves.
+
+cooling: dissipating heat.
+
+observation: a place from which the surroundings can be observed but at which a watch is not habitually maintained. (adapted from IHO Dictionary, S-32, 5th Edition,2917)
+
+time ball: a visual time signal in form of a ball
+
+clock: visual time signal. (adapted from S-32, 5th Edition, 5536)
+
+control: used to control the flow of air, rail, or marine traffic. (Digital Geographic Information Standard - DIGEST)
+
+airship mooring: a facility to secure an airship. (adapted from Digital Geographic Information Standard - DIGEST)
+
+stadium: a large usually unroofed building with tiers of seats for spectators
+
+bus station: a location at which buses arrive and from which they depart.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HEIGHT    Code: 95
+Height  HEIGHT 95
+Attribute type: F
+
+
+Definition:
+
+ The value of the vertical distance to the highest point of  the object, measured from a specified vertical datum.
+
+Minimum Value: 0
+
+References:
+
+ INT 1: IC 14; IE 4; IK 10-11;
+
+ M-4:  302; 352.4; 421.1-2;
+
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 73  for a height of 73 metres.
+
+
+Remarks:
+
+ Height must not be used for floating objects. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HUNITS    Code: 96
+Height/length units    HUNITS 96
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : metres
+ 2 : feet
+
+Definitions:
+
+metres: heights/lengths are specified in metres (SI units of length).
+
+feet: heights/lengths are specified in feet (imperial units of length).
+
+Remarks:
+
+ This attribute encodes the units of measurement for heights and lengths, but not depths for which the attribute depth units (DUNITS) is used. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HORACC    Code: 97
+Horizontal accuracy     HORACC 97
+Attribute type: F
+
+
+Definition:
+
+ The best estimate of the horizontal accuracy of horizontal clearance and distances.
+
+Minimum value:  0
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 0.5  for an error of 0.5 metre.
+
+
+Remarks:
+
+ The expected input is the radius of the two-dimensional error.
+
+ The error is assumed to be positive and negative. The plus/minus character shall not be encoded. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HORCLR    Code: 98
+Horizontal clearance    HORCLR 98
+Attribute type: F
+
+
+Definition:
+
+ The width of an object, such as a canal or a tunnel, which is available for safe navigation. This may, or may not, be the same as the total physical width of the object.
+
+Minimum Value: 0
+
+References:
+
+ INT 1: ID 21;
+
+ M-4:  380.2;
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 125  for a width of 125 metres.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HORLEN    Code: 99
+Horizontal length   HORLEN 99
+Attribute type: F
+
+
+Definition:
+
+ A measurement of the longer of two linear axis. (Digital Geographic Information Working Group -DGIWG, Oct.87)
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 95  for a length of 95 metres.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HORWID    Code: 100
+Horizontal width    HORWID 100
+Attribute type: F
+
+
+Definition:
+
+ A measurement of the shorter of two linear axis. (Digital Geographic Information Working Group -DGIWG, Oct.87)
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 12.6  for a width of 12.6 metres.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: ICEFAC    Code: 101
+Ice factor  ICEFAC 101
+Attribute type: F
+
+
+Definition:
+
+ The value of the maximum variation in the vertical clearance of an overhead cable due to an accumulation of ice.
+
+Minimum Value: 0
+
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 2.5  for a reduction of 2.5 metres in the vertical clearance.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: INFORM    Code: 102
+Information     INFORM 102
+Attribute type: S
+
+
+Definition:
+
+ Textual information about the object.
+
+References:
+
+ INT 1: IA 16;
+
+ M-4: 242.3-5;
+
+Remarks:
+
+ The textual information could be, for example, a list, a table or a text.
+
+ This attribute should be used, for example, to hold the information that is shown on paper charts by cautionary and explanatory notes.
+
+ No formatting of text is possible within INFORM.  If formatted text is required, then the attribute TXTDSC must be used. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: JRSDTN    Code: 103
+Jurisdiction    JRSDTN 103
+Attribute type: E
+
+
+Definition:
+
+ The jurisdiction applicable to an administrative area.
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : international
+ 2 : national
+ 3 : national sub-division
+
+Definitions:
+
+international: involving more than one country; covering more than one national area.
+
+national: an area administered or controlled by a single nation.
+
+national sub-division: an area smaller than the nation in which it lies.
+
+Remarks:
+
+ No remarks; FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $JUSTH    Code: 104
+Justification - horizontal   $JUSTH 104
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : centre justified
+ 2 : right justified
+ 3 : left justified
+
+Definitions:
+
+centre justified: position refers to the centre of the text string.
+
+right justified: position refers to the right side of the last character in the text string.
+
+left justified: position refers to the left side of the first character in the text string.
+
+Remarks:
+
+ No remarks; FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $JUSTV    Code: 105
+Justification - vertical   $JUSTV 105
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : bottom justified
+ 2 : centre justified
+ 3 : top justified
+
+Definitions:
+
+bottom justified: position refers to the bottom of the text string.
+
+centre justified: position refers to the centre of the text string.
+
+top justified: position refers to the top of the text string.
+
+Remarks:
+
+ No remarks; FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: LIFCAP    Code: 106
+Lifting capacity    LIFCAP 106
+Attribute type: F
+
+
+Definition:
+
+ The specific safe lifting capacity of an object.
+
+References:
+
+ INT 1: IF 53.1-2;
+
+ M-4: 328.3;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit:  tonne (t)
+ Resolution:  0.1 t
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 120  for a lifting capacity of 120 tonnes.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: LITCHR    Code: 107
+Light characteristic    LITCHR 107
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : fixed IP 10.1;
+ 2 : flashing IP 10.4;
+ 3 : long-flashing IP 10.5;
+ 4 : quick-flashing IP 10.6;
+ 5 : very quick-flashing IP 10.7;
+ 6 : ultra quick-flashing IP 10.8;
+ 7 : isophased IP 10.3;
+ 8 : occulting IP 10.2;
+ 9 : interrupted quick-flashing IP 10.6;
+ 10 : interrupted very quick-flashing IP 10.7;
+ 11 : interrupted ultra quick-flashing IP 10.8;
+ 12 : morse IP 10.9;
+ 13 : fixed/flash IP 10.10;
+ 14 : flash/long-flash
+ 15 : occulting/flash
+ 16 : fixed/long-flash
+ 17 : occulting alternating
+ 18 : long-flash alternating
+ 19 : flash alternating
+ 20 : group alternating
+ 21 : 2 fixed (vertical)
+ 22 : 2 fixed (horizontal)
+ 23 : 3 fixed (vertical)
+ 24 : 3 fixed (horizontal)
+ 25 : quick-flash plus long-flash
+ 26 : very quick-flash plus long-flash
+ 27 : ultra quick-flash plus long-flash
+ 28 : alternating
+ 29 : fixed and alternating flashing
+
+Definitions:
+
+fixed: a signal light that shows continuously, in any given direction, with constant luminous intensity and colour. (IHO Dictionary, S-32, 5th Edition, 2780)
+
+flashing: a rhythmic light in which the total duration of light in a period is clearly shorter than the total duration of darkness and all the appearances of light are of equal duration. (IHO Dictionary, S-32, 5th Edition, 2783)
+
+long-flashing: a flashing light in which a single flash of not less than two seconds duration is regularly repeated. (IHO Dictionary, S-32, 5th Edition, 2796)
+
+quick-flashing: a light exhibiting without interruption very rapid regular alternations of light and darkness. (IHO Dictionary, S-32, 5th Edition, 2803)
+
+very quick flashing: a flashing light in which flashes are repeated at a rate of not less than 80 flashes per minute but less than 160 flashes per minute.
+
+ultra quick flashing: a flashing light in which flashes are repeated at a rate of not less than 160 flashes per minute.
+
+isophased: a light with all durations of light and darkness equal. (IHO Dictionary, S-32, 5th Edition, 2779)
+
+occulting: a rhythmic light in which the total duration of light in a period is clearly longer than the total duration of darkness and all the eclipses are of equal duration. (IHO Dictionary, S-32, 5th Edition, 2801)
+
+interrupted quick flashing:
+  a quick light in which the sequence of flashes is interrupted by regularly repeated eclipses of constant and long duration. (IHO Dictionary, S-32, 5th Edition, 2790)
+
+interrupted very quick flashing:
+  a light in which the very rapid alterations of light and darkness are interrupted at regular intervals by eclipses of long duration. (IHO Dictionary, S-32, 5th Edition, 2792)
+
+interrupted ultra quick flashing:
+  a light in which the ultra quick flashes (160 or more per minute) are interrupted at regular intervals by eclipses of long duration. (IHO Dictionary, S-32, 5th Edition, 2791)
+
+morse: a rhythmic light in which appearances of light of two clearly different durations are grouped to represent a character or characters in the Morse code. (IHO Dictionary, S-32, 5th Edition, 2798)
+
+alternating: a signal light that shows, in any given direction, two or more colours in a regularly repeated sequence with a regular periodicity. (IHO Dictionary, S-32, 5th Edition, 2770)
+
+Remarks:
+
+ A selection of the above characteristics is defined and illustrated diagrammatically in IHO Chart Specifications, M-4,  471.2.
+
+Values 21-24 are no longer used.  They are only included here for reasons of backward compatibility.  Horizontally or vertically disposed lights should be encoded using the attributes category of light (CATLIT) and multiplicity of lights (MLTYLT). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: LITVIS  Code: 108
+Light visibility    LITVIS 108
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : high intensity IP 61.1; 476.2;
+ 2 : low intensity IP 61.2; 476.2;
+ 3 : faint IP 45; 475.3;
+ 4 : intensified IP 46; 475.5;
+ 5 : unintensified
+ 6 : visibility deliberately restricted IP 44; 475.3;
+ 7 : obscured IP 43; 475.3;
+ 8 : partially obscured
+
+Definitions:
+
+high intensity: non-marine lights with a higher power than marine lights and visible from well off shore (often Aero' lights). (adapted from IHO Chart Specifications, M-4)
+
+low intensity: non-marine lights with lower power than marine lights. (Bundesamt für Seeschiffahrt und Hydrographie, Germany)
+
+faint:  a decrease in the apparent intensity of a light which may occur in the case of partial obstructions. (IHO Chart Specifications, M-4)
+
+intensified: a light in a sector is intensified (i.e. has longer range than other sectors). (Bundesamt für Seeschiffahrt und Hydrographie, Germany)
+
+unintensified: a light in a sector is unintensified (i.e. has shorter range than other sectors). (Bundesamt für Seeschiffahrt und Hydrographie, Germany)
+
+visibility deliberately restricted:
+  a light sector is deliberately reduced in intensity, for example to reduce its effect on a built-up area.
+
+obscured:  said of the arc of a light sector designated by its limiting bearings in which the light is not  visible  from  seaward. (IHO Dictionary, S-32, 5th Edition, 3492)
+
+partially obscured: this value specifies that parts of the sector are obscured.
+
+Remarks:
+
+ The attribute light visibility'  encodes the specific visibility of a light, with respect to the light's intensity and ease of recognition. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: MARSYS    Code: 109
+Marks navigational - System of  MARSYS 109
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : IALA A IQ 130; 461;
+ 2 : IALA B IQ 130; 461;
+ 3 : modified US
+ 4 : old US
+ 5 : US intracoastal waterway
+ 6 : US uniform state
+ 7 : US western rivers
+ 8 : SIGNI
+ 9 : no system
+ 10 : other system  461;
+
+Definitions:
+
+IALA A: navigational aids conform to the International Association of Lighthouse Authorities -  IALA A system.
+
+IALA B: navigational aids conform to the International Association of Lighthouse Authorities - IALA  B system.
+
+no system: navigational aids do not conform to any defined system.
+
+other system: navigational aids conform to a defined system other than International Association of Lighthouse Authorities -IALA.
+
+Remarks:
+
+ No remarks.  GEO AND META OBJECT ATTRIBUTES
+
+
+
+
+Acronym: MLTYLT    Code: 110
+Multiplicity of lights  MLTYLT 110
+Input type: I
+
+
+Definition:
+
+ The number of lights of identical character that exist as a co-located group.
+
+References:
+
+ INT 1: not specified;
+
+ M4: not specified
+
+Minimum Value: 2
+
+
+Indication:
+
+ Unit:  none
+ Resolution:  1
+
+Format:
+
+ xx
+
+Example:
+
+ 5
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: NATION    Code: 111
+Nationality     NATION 111
+Attribute type: A
+
+
+Indication:
+
+ the nationality is encoded by a 2 character- code following ISO 3166 (refer to Annex A to S-57 Appendix A);
+
+Format:
+
+ c2 (mandatory)
+
+Remarks:
+
+ The attribute nationality' indicates the nationality of the specific object.
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: NATCON    Code: 112
+Nature of construction  NATCON 112
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : masonry IF 4.3; 322.1;
+ 2 : concreted IF 4.3; 322.1;
+ 3 : loose boulders IF 4.2; 322.1;
+ 4 : hard surfaced ID 11; 365.2;
+ 5 : unsurfaced ID 12; 365.3;
+ 6 : wooden
+ 7 : metal
+ 8 : glass reinforced plastic (GRP)
+ 9 : painted IQ 101; 456.2;
+
+Definitions:
+
+masonry: constructed of brick or stone.
+
+concreted: constructed of concrete, a material made of sand and gravel that is united by cement into a hardened mass used for roads, foundations, etc. (adapted from the Illustrated Contemporary Dictionary, Encyclopaedic Edition, 1978)
+
+loose boulders: constructed from large stones or blocks of concrete, often placed loosely for protection against waves or water turbulence.
+
+hard surface: constructed with a surface of hard material, usually a term applied to roads surfaced with asphalt or concrete.
+
+unsurfaced: constructed with no extra protection, usually a term applied to roads not surfaced with a hard material.
+
+wooden: constructed from wood.
+
+metal: constructed from metal.
+
+glass reinforced plastic (GRP):
+  constructed from a plastic material strengthened with fibres of glass.
+
+painted: the application of paint to some other construction or natural feature.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: NATSUR    Code: 113
+Nature of surface   NATSUR 113
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : mud IJ 2,20;
+ 2 : clay IJ 3;
+ 3 : silt IJ 4;
+ 4 : sand IC 6; IJ 1,20; 312.2;
+ 5 : stone IC 7; IJ 5,20; 312.2; 425.5-6;
+ 6 : gravel IJ 6,20;
+ 7 : pebbles IJ 7;
+ 8 : cobbles IJ 8;
+ 9 : rock IJ 9,21; 426.2
+ 10 : marsh
+ 11 : lava
+ 12 : snow
+ 13 : ice
+ 14 : coral IJ 10,22; 425.5; 426.3;
+ 15 : swamp
+ 16 : bog/moor
+ 17 : shells IJ 11; 425.5-6;
+ 18 : boulder
+
+Definitions:
+
+mud: soft, wet earth.
+
+clay: (particles of less than 0.002mm); stiff, sticky earth that becomes hard when baked.
+
+silt:  (particles of 0.002-0.0625mm); when dried on hand will rub off easily.
+
+sand: (particles of 0.0625-2.0mm); tiny grains of crushed or worn rock.
+
+stone: a general term for rock fragments ranging in size from pebbles and gravel to boulders or a large rock mass. (IHO Dictionary, S-32, 5th Edition, 5059)
+
+gravel: (particles of 2.0-4.0mm); small stones with coarse sand.
+
+pebbles: (particles of 4.0-64.0mm); small stones made smooth and round by being rolled in water.
+
+cobbles: (particles of 64.0-256.0mm); stones worn round and smooth by water and used for paving.
+
+rock:   any formation of natural origin that constitutes an integral part of the lithosphere. The natural occurring material that forms firm, hard, and solid masses. (adapted from IHO Dictionary, S-32, 5th Edition, 4415)
+
+lava: the fluid or semi-fluid matter flowing from a volcano. The substance that results from the cooling of the molten rock. Part of the ocean bed  is  composed  of  lava. (IHO Dictionary, S-32, 5th Edition, 2680)
+
+coral:  hard calcareous skeletons of many tribes of marine polyps. (IHO Dictionary, S- 32, 5th Edition, 1061)
+
+shells: exoskeletons of various water dwelling animals. (adapted from IHO Dictionary, S-32, 5th Edition, 4680)
+
+boulder a rounded rock with diameter of 256 mm or larger. (adapted from IHO Dictionary, S-32, 5th Edition, 527)
+
+
+Remarks:
+
+ The attribute nature of surface' encodes the general nature of the material of which the land surface or the sea bed is composed.
+
+ Mixed bottom: where the seabed comprises a mixture of material, the main constituent is given first e.g. fine sand with mud and shells would be indicated as 4,1,17.
+
+ Mud, sand, stone, rock are terms used for the general description.
+
+ Clay, silt, gravel, pebbles, cobbles are more specific terms related to particle size. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: NATQUA    Code: 114
+Nature of surface - qualifying terms    NATQUA 114
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : fine IJ 30; 425.5-6;
+ 2 : medium IJ 31; 425.5-6;
+ 3 : coarse IJ 32; 425.5-6;
+ 4 : broken IJ 33; 425.5-6;
+ 5 : sticky IJ 34; 425.5-6;
+ 6 : soft IJ 35; 425.5-6;
+ 7 : stiff IJ 36; 425.5-6;
+ 8 : volcanic IJ 37; 425.5-6;
+ 9 : calcareous IJ 38; 425.5-6;
+ 10 : hard IJ 39; 425.5-6;
+
+Definitions:
+
+fine:  falls within the smallest size continuum for a particular nature of surface term. (M-4 425.6)
+
+medium: falls within the moderate size continuum for a particular nature of surface term. (M-4 425.6)
+
+coarse: falls within the largest size continuum for a particular nature of surface term. (M-4 425.6)
+
+broken: fractured or in pieces. (adapted from Webster's II New Riverside Dictionary, 1984)
+
+sticky: having an adhesive or glue like property. (adapted from Webster's II New Riverside Dictionary, 1984)
+
+soft:  not hard or firm. (adapted from Webster's II New Riverside Dictionary, 1984)
+
+stiff:  not pliant; thick, resistant to flow. (adapted from Webster's II New Riverside Dictionary, 1984)
+
+volcanic: composed of or containing material ejected from a volcano. (adapted from Webster's II New Riverside Dictionary, 1984)
+
+calcareous: composed of or containing calcium or calcium carbonate. (IHO Dictionary, S-32, 5th Edition, 603)
+
+hard: firm; usually refers to an area of the sea floor not covered by unconsolidated sediment. (IHO Dictionary, S-32, 5th Edition, 2194 and adapted from Webster's II New Riverside Dictionary, 1984)
+
+
+Remarks:
+
+ The attribute nature of surface - qualifying terms' encodes the nature of various forms of natural surface materials in terms of their size, morphology and consistency. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: NMDATE    Code: 115
+Notice to Mariners date     NMDATE 115
+Attribute type: A
+
+
+Indication:
+
+ The Notice to Mariners date should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the months (MM) (e.g. April = 04) and 2 digits for the day (DD), according to ISO 8601: 1988.
+
+Format:
+
+ CCYYMMDD (mandatory)
+
+Example:
+
+ 19950615 for 15 June 1995 as Notice to Mariners date.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: OBJNAM   Code: 116
+Object name     OBJNAM 116
+Attribute type: S
+
+
+Definition:
+
+ The individual name of an object.
+
+References:
+
+ INT 1: ID 7, IF 19, IN 12.2-3;
+
+ M-4: 371; 323.1-2; 431.2-3; 431.5;
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: ORIENT    Code: 117
+Orientation     ORIENT 117
+Attribute type: F
+
+
+Definition:
+
+ The angular distance measured from true north to the major axis of the object. (Digital Geographic Information Working Group -DGIWG, Oct.87)
+
+References:
+
+ INT 1: IM 1-4, 40; IP 20.1-2, 21, 30.1-2, 31; IS 3.5, 11;
+
+ M-4: 433.2-6; 434.1-2; 475.6-8; 487.2; 488;
+
+Minimum Value: 0
+
+Maximum Value: 360
+
+Indication:
+
+ Unit:  degree ()
+ Resolution:  0.01 degree
+
+ Conversion factor:  one tenth of a second = 0.000028 degree
+
+Format:
+
+ xxx.xx
+
+Example:
+
+ 246.7  for an orientation of 246.7 degrees
+
+Remarks
+
+ No remarks.
+
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PEREND    Code: 118
+Periodic date end   PEREND 118
+Attribute type: A
+
+
+Definition:
+
+ The end of the active period for a seasonal object (e.g. a buoy). See also date end'.
+
+References:
+
+ INT 1: IQ71;
+
+ M-4: 460.5;
+
+Indication:
+
+ the periodic date end' should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD). When no specific year is required (ie the object is removed at the same time each year) the following two cases may be considered:
+  - same day each year:   --MMDD
+  - same month each year:  --MM
+
+ This conforms to ISO 8601: 1988.
+
+Format:
+
+ CCYYMMDD  (full date, mandatory)
+        --MMDD  (same day each year, mandatory)
+        --MM  (same month each year, mandatory)
+
+Example:
+
+ --1015  for an ending date of 15 October each year.
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PERSTA    Code: 119
+Periodic date start     PERSTA 119
+Attribute type: A
+
+
+Definition:
+
+ The start of the active period for a seasonal object (e.g. a buoy). See also date start'.
+
+References:
+
+ INT 1: IQ71;
+
+ M-4: 460.5;
+
+Indication:
+
+ the periodic date, start' should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD). When no specific year is required (ie the object is deployed at the same time each year) the following two cases may be considered:
+  - same day each year:   --MMDD
+  - same month each year:  --MM
+
+ This conforms to ISO 8601: 1988.
+
+
+Format:
+
+ CCYYMMDD  (full date, mandatory)
+        --MMDD  (same day each year, mandatory)
+        --MM  (same month each year, mandatory)
+
+Example:
+
+--04  for an operation starting in April each year.
+
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PICREP    Code: 120
+Pictorial representation    PICREP 120
+Attribute type: S
+
+
+Definition:
+
+ Indicates whether a pictorial representation of the object is available.
+
+References:
+
+ INT 1: IE 3.1-2;
+
+ M-4: 456.5; 457.3;
+
+Indication:
+
+ the string encodes the file name of an external graphic file (pixel/vector)
+
+Remarks:
+
+ The pictorial representation' could be a drawing or a photo. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PILDST    Code: 121
+Pilot district  PILDST 121
+Attribute type: S
+
+
+Definition:
+
+ The area within which a particular pilotage service operates.
+
+References:
+
+ INT 1: IT 1.2;
+
+ M-4: 491.1-2;
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PUNITS    Code: 189
+
+Attribute type: E
+
+
+Expected input:
+
+ ID Meaning
+
+ 1 :  metres
+ 2 :  degrees of arc
+ 3 : millimeters
+ 4 : feet
+
+
+Definition:
+
+metres: Positional accuracy is specified in metres (SI units of positional accuracy).
+
+degrees of arc: Positional accuracy is specified in degrees of arc.
+
+millimeters: Positional accuracy is specified in millimeters.
+
+feet: Positional accuracy is specified in feet (imperial units of positional accuracy).
+
+
+Remarks:
+
+ This attribute encodes the units for positional accuracy which may be different from the unit for coordinates. The latter is specified at the dataset level in the COUN subfield of the DSPM record.  FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PRCTRY    Code: 122
+Producing country   PRCTRY 122
+Attribute type: A
+
+
+Definition:
+
+ The country responsible for data production.
+
+Indication:
+
+ country (c2): Two letter code according to ISO 3166 (refer to Annex A to S-57 Appendix A)
+
+Format:
+
+ c2 (mandatory)
+
+
+Example:
+
+ DK (Denmark)
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PRODCT    Code: 123
+Product     PRODCT 123
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : oil IE 32;IL 40.1-2; 376.1-2; 444.1;
+ 2 : gas IE 32;IL 40.1-2; 376.1-2; 444.1;
+ 3 : water IL 40.1; 444.4;
+ 4 : stone
+ 5 : coal
+ 6 : ore
+ 7 : chemicals IE 32; 376.1-2; 444.1;
+ 8 : drinking water
+ 9 : milk
+ 10 : bauxite
+ 11 : coke
+ 12 : iron ingots
+ 13 : salt
+ 14 : sand
+ 15 : timber
+ 16 : sawdust/wood chips
+ 17 : scrap metal
+   18   :    liquified natural gas (LNG)
+   19   :    liquified petroleum gas (LPG)
+    20  :    wine
+    21  :    cement
+    22  :    grain
+
+Definitions:
+
+oil:  a thick, slippery liquid that will not dissolve in water, usually petroleum based in the context of storage tanks. (adapted from the Oxford Minidictionary, Third Edition)
+
+gas:  a substance with particles that can move freely, usually a fuel substance in the context of storage tanks. (adapted from the Oxford Minidictionary, Third Edition)
+
+water: a colourless, odourless, tasteless liquid that is a compound of hydrogen and oxygen. (adapted from the Oxford Minidictionary, Third Edition)
+
+stone: a general term for rock fragments. (IHO Dictionary, S-32, 5th Edition, 5059)
+
+coal: a hard black mineral that is burned as fuel. (adapted from the Oxford Minidictionary, Third Edition)
+
+ore:  a solid rock or mineral from which metal is obtained. (adapted form the Oxford Minidictionary, Third Edition)
+
+chemicals: any substance obtained by or used in a chemical process. (adapted from the Oxford Minidictionary, Third Edition)
+
+drinking water: water that is suitable for human consumption. (adapted from the Oxford Minidictionary, Third Edition)
+
+milk: a white fluid secreted by female mammals as food for their young. (adapted from the Oxford Minidictionary, Third Edition)
+
+bauxite: a mineral from which aluminum is obtained. (adapted from the Oxford Minidictionary, Third Edition)
+
+coke: a solid substance obtained after gas and tar have been extracted from coal, used as a fuel. (adapted from the Oxford Minidictionary, Third Edition)
+
+iron ingots: an oblong lump of cast iron metal. (adapted from the Oxford Minidictionary, Third Edition)
+
+salt:  sodium chloride obtained from mines or by the evaporation of sea water. (adapted from the Oxford Minidictionary, Third Edition)
+
+sand: tiny grains of crushed or worn rock. (adapted from the Oxford Minidictionary, Third Edition)
+
+timber: wood prepared for use in building or carpentry. (adapted from the Oxford Minidictionary, Third Edition)
+
+sawdust/wood chips: powdery fragments of wood made in sawing timber or coarse chips produced for use in manufacturing pressed board. (adapted from the Oxford Minidictionary, Third Edition)
+
+scrap metal: discarded metal suitable for being reprocessed. (adapted from the Oxford Minidictionary, Third Edition)
+
+liquified natural gas (LNG):
+  a compressed gas consisting of flammable light hydrocarbons and derived from natural gas.
+
+liquified petroleum gas (LPG):
+  a compressed gas consisting of flammable light hydrocarbons and derived from petroleum. (adapted from Websters Third New)
+
+wine: the fermanted juice of grapes. (adapted from the  Websters New World Dictionary)
+
+cement: a substance made of powdered lime and clay, mixed with water. (adapted from the  Websters New World Dictionary)
+
+grain: a small hard seed, especially that of any cereal plant such as wheat, rice, corn, rye etc. (adapted from the  Websters New World Dictionary)
+
+
+
+Remarks:
+
+ The attribute product' encodes the various substances which are transported, stored or exploited. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: PUBREF    Code: 124
+Publication reference   PUBREF 124
+Attribute type: S
+
+
+Definition:
+
+ A reference to a nautical publication.
+
+Indication:
+
+ The string encodes the reference to a specific paragraph from a nautical publication
+
+Example:
+
+ United States Coast Pilot No 1 1992 (27th) edition, Atlantic Coast, Eastport to Cape Cod, Chapter 3, Paragraph 2' FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: QUASOU    Code: 125
+Quality of sounding measurement     QUASOU 125
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : depth known
+ 2 : depth unknown IK 40; 422.9;
+ 3 : doubtful sounding II 2; 417; 424.4;
+ 4 : unreliable sounding II 14; 412.4;
+ 5 : no bottom found at value shown II 13; 412.3;
+ 6 : least depth known IK 26-27; 422.3-4;
+ 7 : least depth unknown, safe clearance at value shown IK 30; 422.7;
+ 8 : value reported (not surveyed) II 3.1; 417, 424.5;
+ 9 : value reported (not confirmed) II 4;
+ 10 : maintained depth II 23; 414.2;
+ 11 : not regularly maintained
+
+Definitions:
+
+depth known: the depth from chart datum to the bottom is a known value.
+
+depth unknown: the depth from chart datum to the bottom is unknown.
+
+doubtful sounding: a depth that may be less than indicated. (adapted from IHO Dictionary, S-32, 5th Edition, 4840)
+
+unreliable sounding: a depth that is considered to be an unreliable value.
+
+no bottom found at value shown:
+  upon investigation the bottom was not found at this depth. (adapted from IHO Dictionary, S-32, 5th Edition, 4848)
+
+least depth known: the shoalest depth over a feature is of known value. (adapted from IHO Dictionary, S-32, 5th Edition, 2705)
+
+least depth unknown, safe clearance at depth shown:
+  the least depth over a feature is unknown, but there is considered to be safe clearance at this depth.
+
+value reported (not surveyed):
+  depth value obtained from a report, but not fully surveyed.
+
+value reported (not confirmed):
+  depth value obtained from a report, which it has not been possible to confirm.
+
+maintained depth: the depth at which a channel is kept by human influence, usually by dredging. (IHO Dictionary, S-32, 5th Edition, 3057)
+
+not regularly maintained:
+  depths may be altered by human influence, but will not be routinely  maintained.
+
+Remarks:
+
+ The attribute quality of sounding measurement' indicates the reliability of the value of sounding. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: QUAVEM  Attribute type: E
+Quality of vertical measurement   QUAVEM
+INT 1 Reference:  not specified
+
+Chart Specification:  not specified
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  measured
+    2 :  estimated
+
+
+Remarks:
+
+ The attribute quality of vertical measurement' indicates the quality of a vertical measurement.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: RADWAL    Code: 126
+Radar wave length     RADWAL 126
+Attribute type: A
+
+
+Definition:
+
+ The distance between two successive peaks (or other points of identical phase) on an electromagnetic wave in the radar band of the electromagnetic spectrum.
+
+References:
+
+ INT 1: IS 3.1-4;
+
+ M-4: 486.3-4;
+
+Indication:
+
+ the wavelength and the band code character is indicated;
+
+ In the case where two bands should be encoded, these should be separated by a comma.
+
+ Unit :m
+ resolution: 0.01 m
+
+Format:
+
+ V.VV-B
+ V.VV-B,V.VV-B
+
+ VV.VV' encodes the value of wavelength.
+
+ B' encodes the band;
+ each separated by a hyphen (-')
+
+Example:
+
+ the radar transponder beacon wavelength 3cm (X) - Band' is indicated as 0.03-X'
+
+Remarks:
+
+ The attribute radar transponder beacon wavelength' encodes the specific wavelength at which a radar transponder beacon transmits.
+
+ Radar transponder beacons generally work on the following wavelengths:
+
+ -  3cm (X) - Band
+ - 10cm (S) - Band
+
+ Nevertheless, wavelengths outside the marine band are used.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: RADIUS    Code: 127
+Radius  RADIUS 127
+Attribute type: F
+
+
+Definition:
+
+ The vector extending from the centre to the periphery of a circular or spherical object.
+
+References:
+
+ INT 1: IN 11.2;
+
+ M-4: not specified
+
+Minimum Value: 0
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 26  for a radius of 26 metres.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: RECDAT    Code: 128
+Recording date  RECDAT 128
+Attribute type: A
+
+
+Definition:
+
+ The date when the specific object or cartographic primitive was captured, edited or deleted.
+
+Reference:
+
+ INT 1: II 22;
+
+ M-4: 414.1;
+
+Indication:
+
+ The recording date should be encoded using 4 digits for the calendar year (CCYY), 2  digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD), according to ISO 8601: 1988.
+
+
+Format:
+
+ CCYYMMDD (mandatory)
+
+Example:
+
+19930112 for 12 January 1993 as recording date.
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: RECIND    Code: 129
+Recording indication    RECIND 129
+Attribute type: A
+
+
+Definition:
+
+ The procedure for the encoding and entering of data.
+
+Indication:
+
+ country (c2): Two letter code according to ISO 3166 (refer to Annex A to S-57 Appendix A)
+
+ authority (c2): A string of two alphanumeric characters  (refer to Annex A to S-57 Appendix A), e.g. German Bundesamt für Seeschiffahrt und Hydrographie = DE; US National Imagery and Mapping Agency = U1.
+
+ procedure (c4): digitized =  digi
+  scanned =  scan
+  alpha/numeric input =  alph
+
+Format:
+
+ c2,c2,c4 (mandatory)
+
+Example:
+
+ DK,D1,digi FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: RYRMGV    Code: 130
+Reference year for magnetic variation   RYRMGV 130
+Attribute type: A
+
+
+Definition:
+
+ The reference calendar year for magnetic variation values.
+
+References:
+
+ INT 1: IB 68.1, 70-71;
+
+ M-4: 270;
+
+Indication:
+
+ the reference calendar year for magnetic variation' should be encoded using a 4 digit year-indication (CCYY).
+
+Format:
+
+ CCYY (mandatory) FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: RESTRN  Code: 131
+Restriction     RESTRN 131
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : anchoring prohibited IN 20; 439.3-4;
+ 2 : anchoring restricted
+ 3 : fishing prohibited IN 21; 439.3-4;
+ 4 : fishing restricted
+ 5 : trawling prohibited
+ 6 : trawling restricted
+ 7 : entry prohibited IN 2.2; 439.3;
+ 8 : entry restricted
+ 9 : dredging prohibited
+ 10 : dredging restricted
+ 11 : diving prohibited
+ 12 : diving restricted
+ 13 : no wake
+ 14 : area to be avoided IM 29.1; 435.7;
+   15   :   construction prohibited
+
+Definitions:
+
+anchoring prohibited: an area within which anchoring is not permitted.
+
+anchoring restricted: a specified area designated by appropriate authority, within which anchoring is restricted in accordance with certain specified conditions.
+
+fishing prohibited: an area within which fishing is not permitted.
+
+fishing restricted: a specified area designated by appropriate authority, within which fishing is restricted in accordance with certain specified conditions.
+
+trawling prohibited: an area within which trawling is not permitted.
+
+trawling restricted: a specified area designated by appropriate authority, within which trawling is restricted in accordance with certain specified conditions.
+
+entry prohibited: an area within which navigation and/or anchoring is prohibited. (adapted from IHO Dictionary, S-32, 5th Edition, 4044)
+
+entry restricted: a specified area designated by appropriate authority, within which navigation is restricted in accordance with certain specified conditions. (adapted from IHO Dictionary, S-32, 5th Edition, 4366)
+
+dredging prohibited: an area within which dredging is not permitted.
+
+dredging restricted: a specified area designated by appropriate authority, within which dredging is restricted in accordance with certain specified conditions.
+
+diving prohibited: an area within which diving is not permitted.
+
+diving restricted: a specified area designated by appropriate authority, within which diving is restricted in accordance with certain specified conditions.
+
+no wake: mariners must adjust the speed of their vessels to reduce the wave or wash which may cause erosion or disturb moored vessels.
+
+area to be avoided: an IMO designated area to be avoided, defined as a routeing measure. (adapted from IHO Chart Specifications, M-4, 435.7)
+
+Construction prohibited: the erection of permanent or temporary fixed structures or artificial islands is prohibited.
+
+Remarks:
+
+ The official legal statue of each kind of restricted area defines the kind of restriction(s), e.g. the restriction for a game preserve' may be entry prohibited', the restriction for an anchoring prohibition' is anchoring prohibited'.
+
+ The complete information about the restriction(s), actually held in handbooks or other publications, may be encoded by the attribute TXTDSC'. A short explanation may be given by the use of the attribute INFORM'. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SCAMAX    Code: 132
+Scale maximum   SCAMAX 132
+Attribute type: I
+
+
+Definition:
+
+ The maximum scale at which the object may be used e.g. for ECDIS presentation.
+
+Minimum Value: 1
+
+Indication:
+
+ the modulus of the scale is indicated, that is 1:25 000 is encoded as 25000;
+
+ Unit: none
+ resolution: 1
+
+Format:
+
+ xxxxxxxx
+
+Example:
+
+ If a particular maximum scale is specified as 1:25 000 (encoded as 25000), an example of a larger scale would be 1:20 000 (encoded as 20000);
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SCAMIN    Code: 133
+Scale minimum   SCAMIN 133
+Attribute type: I
+
+
+Definition:
+
+ The minimum scale at which the object may be used e.g. for ECDIS presentation.
+
+Minimum Value: 1
+
+Indication:
+
+ the modulus of the scale is indicated, that is 1:1 250 000 is encoded as 1250000;
+
+ Unit: none
+ resolution: 1
+
+Format:
+
+ xxxxxxxx
+
+Example:
+
+ If a particular minimum scale is specified as 1:1 250 000 (encoded as 1250000), and example of a smaller scale would be 1:2 000 000 (encoded as 2000000);
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SCVAL1    Code: 134
+Scale value one     SCVAL1 134
+Attribute type: I
+
+
+Definition:
+
+ The largest scale for the range of survey scale as used in source diagram information.
+
+Minimum Value: 1
+
+Indication:
+
+ the modulus of the scale is indicated, that is 1:25 000 is encoded as 25000.
+
+ Unit: none
+ Resolution: 1
+
+Format:
+
+ xxxxxxxx
+
+Example:
+
+ 25000  for a scale of 1:25 000.
+
+Remarks.
+
+ No remarks.
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SCVAL2   Code: 135
+Scale value two     SCVAL2 135
+Attribute type: I
+
+
+Definition:
+
+ The smallest scale for the range of survey scale as used in source diagram information.
+
+Minimum Value: 1
+
+Indication:
+
+ The modulus of the scale is indicated, that is 1:250 000 is encoded as 250000.
+
+ Unit: none
+ Resolution: 1
+
+Format:
+
+ xxxxxxxx
+
+Example:
+
+ 250000  for a scale of 1:250 000.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SECTR1    Code: 136
+Sector limit one     SECTR1 136
+Attribute type: F
+
+
+Definition:
+
+ A sector is the part of a circle between two straight lines drawn from the centre to the circumference. (Advanced Learner's Dictionary, 2nd Edition)
+
+ Sector limit 1 specifies the first limit of the sector. The order of sector limit 1 and sector limit 2 is clockwise around the central object (e.g. a light).
+
+References:
+
+ INT 1: IP 40;
+
+ M-4: 475; 475.1;
+
+Minimum Value: 0
+
+Maximum Value: 360
+
+Indication:
+
+ Unit:  degree ()
+ Resolution: 0.01 degree
+
+ Conversion factor: one tenth of a second = 0.000028 degree.
+
+Format:
+
+ xxx.xx
+
+Example:
+
+ 125  for a sector orientation of 125 degrees.
+
+Remarks:
+
+ The values given to the common limits of adjacent sectors should be identical.
+
+ The orientation of bearing is from seaward to the central object. This conforms with the method used in List of Lights' publications.
+
+ A generic term such as to shore' cannot be used; a specific bearing must be encoded. Where a light sector limit is defined as to the shore', it should be encoded using a value that ensures that, when the limit is drawn, it will fall entirely on land. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SECTR2   Code: 137
+Sector limit two     SECTR2 137
+Attribute type: F
+
+
+Definition:
+
+ A sector is the part of a circle between two straight lines drawn from the centre to the circumference. (Advanced Learner's Dictionary, 2nd Edition)
+
+ The sector limit 2 specifies the second limit of the sector. The order of sector limit 1 and sector limit 2 is clockwise around the central object (e.g. a light).
+
+References:
+
+ INT 1: IP 40;
+
+ M-4: 475; 475.1;
+
+Minimum Value: 0
+
+Maximum Value: 360
+
+Indication:
+
+ Unit:  degree.
+ Resolution: 0.01 degree
+
+ Conversion factor: one tenth of a second = 0.000028 degree.
+
+Format:
+
+ xxx.xx
+
+Example:
+
+ 220  for a sector orientation of 220 degrees.
+
+
+Remarks:
+
+ The values given to the common limits of adjacent sectors should be identical.
+
+ The orientation of bearing is from seaward to the central object. This to the method used in List of Lights' publications.
+
+ A generic term such as to shore' cannot be used; a specific bearing must be encoded. Where a light sector limit is defined as to the shore', it should be encoded using a value that ensures that, when the limit is drawn, it will fall entirely on land. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SHIPAM   Code: 138
+Shift parameters     SHIPAM
+Attribute type: A
+
+
+Definition:
+
+ Latitude and longitude offsets required to shift a position from one geodetic datum to another.
+
+Indication:
+
+ Lat: Shift parameter in latitude from the specified horizontal datum to the horizontal datum of the data.
+
+ Unit:  minutes (), negative south.
+ Resolution: 0.001 minute.
+
+ Lon: Shift parameter in longitude from the specified horizontal datum to the horizontal datum of the data.
+
+ Unit:  minutes (), negative west.
+ Resolution: 0.001 minute.
+
+Format:
+
+ sxx.xxx, syy.yyy
+
+ sxx.xxx: lat
+ syy.yyy: lon
+ s: sign, negative values only.
+
+Example:
+
+ -0.03,0.07 in the following case:
+
+
+ Position on specified datum: 20 40.36 (N) 085 20.05 (E)
+ Shift parameters (-0.03,0.07):     -0.03 (S)   0.07 (E)
+  ============= ==============
+ Position on datum of data: 20 40.33 (N) 085 20.12 (E)
+
+Remarks
+
+ All necessary information for conversion of geographic coordinates from most of the Geodetic Datums in the above list to WGS-84 is contained in the "User's Handbook on Datum Transformations involving WGS-84", prepared by the US Defense Mapping Agency and which is available from the IHB as IHO Publication S-60 (English and French Versions), along with an associated standard datum transformation software on floppy disk called "MADTRAN". The resulting latitude and longitude offsets can be encoded in the attribute SHIPAM.
+
+Additional information on the transformation as indicated in IHO Publication S-60, e.g. TOY-M (Mean Solution), TOY-A (Japan), TOY-B (South Korea) or TOY-C (Okinawa) in relation to Tokyo Datum, may be encoded in the attribute INFORM or NINFORM. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SIGFRQ   Code: 139
+Signal frequency    SIGFRQ 139
+Attribute type: I
+
+
+Definition:
+
+ The frequency of a signal.
+
+Indication:
+
+ Unit: Hz
+ resolution: 1 Hz FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SIGGEN   Code: 140
+Signal generation   SIGGEN 140
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : automatically
+ 2 : by wave action IR 21-22; 454.1-2;
+ 3 : by hand
+ 4 : by wind
+
+Definitions:
+
+automatically: signal generation is initiated by a self regulating mechanism such as a timer or light sensor.
+
+by wave action: the signal is generated by the motion of the sea surface such as a bell in a buoy.
+
+by hand: the signal is generated by a manually operated mechanism such as a hand cranked siren.
+
+by wind: the signal is generated by the motion of air such as a wind driven whistle.
+
+Remarks:
+
+ The attribute signal generation' encodes the mechanism used to generate a fog signal. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SIGGRP   Code: 141
+Signal group    SIGGRP 141
+Attribute type: A
+
+
+Definition:
+
+ The number of signals, the combination of signals or the morse character(s) within one period of full sequence.
+
+References:
+
+ INT 1: IP 10.2-9; IR 20, 22;
+
+ M-4: 453; 453.1-4; 471.2;
+
+Indication:
+
+ The signal group of a light is encoded using brackets to separate the individual groups. A group of signals may be a single number, a chain of numbers separated by "+", a sequence of up to 4 letters or a letter and a number.
+
+ A fixed light has no signal group.
+
+ Where no specific signal group is given for one of the light characteristics, this should be shown by an empty pair of brackets.
+
+Format:
+
+ (c)(c)...
+
+Examples:
+
+ Light characteristic  SIGGRP Indication
+
+ VQ(6)+LFl -> (6)(1)
+ Fl+LFl (2+3) -> (1)(2+3)
+ Fl(2)+Lfl(3) -> (2)(3)
+ FFl -> ()(1)
+ Mo(AA) -> (AA)
+ AlFl(2W+1R) -> (2+1)
+ AlLFlWR -> (2)
+ FOcW -> ()(1)
+ AlOc(4)WR -> (4) FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SIGPER   Code: 142
+Signal period   SIGPER 142
+Attribute type: F
+
+
+Definition:
+
+ The time occupied by an entire cycle of intervals of light and eclipse.
+
+References:
+
+ INT 1: IP 12; IR 20, 22;
+
+ M-4: 453.5; 471.5;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit:  second (s)
+ Resolution: 0.01 s
+
+Format:
+
+ xx.xx
+
+Example:
+
+ 12  for an interval of 12 seconds.
+
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SIGSEQ   Code: 143
+Signal sequence     SIGSEQ 143
+Attribute type: A
+
+
+Definition:
+
+ The sequence of times occupied by intervals of light and eclipse for all light characteristics' except for occulting where the sequence of times is occupied by intervals of eclipse and light.
+
+Indication:
+
+ Unit for value of intervals:  second (s)
+ resolution:   0.01 s
+
+Format:
+
+ LL.L + (EE.E)
+
+Example:
+
+ 00.8+(02.2)+00.8+(05.2)
+
+ The above example encodes a signal sequence with two intervals of light and two intervals of eclipse.
+
+ For occulting lights, the signal sequence' is indicated using a fixed format to encode the values of intervals of eclipse (E) and (L).
+
+Format:
+
+ (EE.E)+LL.L
+
+Example:
+
+ (00.8)+02.2+(00.8)+05.2)
+
+ The above example encodes a signal sequence with two intervals of eclipse and two intervals of light.
+
+Remarks:
+
+ The signal sequence' for all light characteristics' except for occulting is indicated using a fixed format to encode the value of intervals of light (L) and eclipse (E). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SOUACC    Code: 144
+Sounding accuracy   SOUACC 144
+Attribute type: F
+
+Expected input:
+
+ The maximum of the one-dimensional error.
+
+ The error is assumed to be positive and negative. The plus/minus character shall not be encoded.
+
+
+Definition:
+
+ The best estimate of the accuracy of the sounding data.
+
+Minimum value: 0
+
+Indication:
+
+ Unit: defined in the DUNI subfield of the DSPM record or in the DUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 fm or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 0.3  for a maximum error of 0.3 metre.
+
+
+Remarks:
+
+ No remarks
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SDISMX   Code: 145
+Sounding distance - maximum     SDISMX 145
+Attribute type: I
+
+
+Definition:
+
+ The maximum spacing of the principal sounding lines of a survey.
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 1 m or 1 ft
+Format:
+
+ xxxx
+
+Example:
+
+ 150  for a maximum spacing of 150 metres.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SDISMN   Code: 146
+Sounding distance - minimum     SDISMN 146
+Attribute type: I
+
+
+Definition:
+
+ The minimum spacing of the principal sounding lines of a survey.
+
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 1 m or 1 ft
+
+Format:
+
+ xxxx
+
+Example:
+
+ 50  for a minimum spacing of 50 metres.
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SORDAT   Code: 147
+Source date     SORDAT 147
+Attribute type: A
+
+
+Definition:
+
+ The production date of the source, e.g. the date of measurement.
+
+Indication:
+
+ The source should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the months (MM) and 2 digits for the Day (DD), according to ISO 8601: 1988.
+
+Format:
+
+ CCYYMMDD (mandatory)
+
+Example:
+
+ 19820506 for 6 May 1982 as source date.
+
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SORIND   Code: 148
+Source indication   SORIND 148
+Attribute type: A
+
+
+Definition:
+
+ Information about the source of the object.
+
+Indication:
+
+ Country (c2): (mandatory)
+ Two letter code from ISO 3166 (refer to Annex A to S-57 Appendix A)
+
+ Authority (c2): (mandatory)
+ A string of two alphanumeric characters  (refer to Annex A to S-57 Appendix A), e.g. German Bundesamt für Seeschiffahrt und Hydrographie = DE; US National Imagery and Mapping Agency = U1.
+
+ Source (c5): Graphic e.g. plotting sheet, paper chart = graph
+  Report  e.g. wreck report = reprt
+
+ ID-Code (c...): e.g. Code of paper chart
+
+Format:
+
+ c2,c2,c5,c...
+
+Example:
+
+ DK,D1,graph,chart196 FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: STATUS   Code: 149
+Status  STATUS 149
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : permanent
+ 2 : occasional IP 50; 473.2;
+ 3 : recommended IN 10; 431.1;
+ 4 : not in use IL 14, 44; 444.7;
+ 5 : periodic/intermittent IC 21; IQ 71; 353.3; 460.5;
+ 6 : reserved IN 12.9;
+ 7 : temporary IP 54;
+ 8 : private IQ 70;
+ 9 : mandatory
+ 10 : destroyed/ruined
+ 11 : extinguished
+ 12 : illuminated
+ 13 : historic
+ 14 : public
+ 15 : synchronized
+ 16 : watched
+ 17 : un-watched
+ 18 : existence doubtful
+
+Definitions:
+
+permanent: intended to last or function indefinitely. (The Concise Oxford Dictionary, 7th Edition)
+
+occasional: acting on special occasions; happening irregularly. (The Concise Oxford Dictionary, 7th Edition)
+
+recommended: presented as worthy of confidence, acceptance, use, etc. (The Macquarie Dictionary, 1988)
+
+not in use: no longer used for the purpose intended; disused.
+
+periodic/intermittent: recurring at intervals. (The Concise Oxford Dictionary, 7th Edition)
+
+reserved: set apart for some specific use. (adapted from The Concise Oxford Dictionary, 7th Edition)
+
+temporary: meant to last only for a time. (The Concise Oxford Dictionary)
+
+private: not in public ownership or operation.
+
+mandatory: compulsory; enforced. (The Concise Oxford Dictionary, 7th Edition)
+
+extinguished: no longer lit
+
+illuminated: lit by floodlights, strip lights, etc.
+
+historic: famous in history; of historical interest. (The Concise Oxford Dictionary, 7th Edition)
+
+public: belonging to, available to, used or shared by, the community as a whole and not restricted to private use. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+synchronized: occur at a time, coincide in point of time, be contemporary or simultaneous. (The New Shorter Oxford English Dictionary, 1993)
+
+watched: looked at or observed over a period of time especially so as to be aware of any movement or change. (adapted from The New Shorter Oxford English Dictionary, 1993)
+
+un-watched: usually automatic in operation, without any permanently-stationed personnel to superintend it. (adapted from IHO Dictionary, S-32, 5th Edition, 2814)
+
+existence doubtful: an object that has been reported but has not been definitely determined to exist.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+ DELETED - DO NOT USE
+
+
+Acronym: SUPLIT  Attribute type: E
+Supervision of light    SUPLIT
+INT 1 Reference:  IP 53;
+
+Chart Specification:  473.1;
+
+
+Expected input:
+
+   ID Meaning
+
+    1 :  watched light
+    2 :  unwatched light
+
+
+Remarks:
+
+ The attribute supervision of light' encodes whether the light is watched or not.
+
+This attribute is obsolete.  It is only shown here for reasons of backward compatibility.  These values have been transferred to the attribute status (STATUS).
+
+ DELETED - DO NOT USE FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SURATH   Code: 150
+Survey authority    SURATH 150
+Attribute type: S
+
+
+Definition:
+
+ The authority which was responsible for the survey.
+
+Example:
+
+ Hydrographic Service, Royal Australian Navy
+ Port of Melbourne Authority
+
+Remarks:
+
+ The attribute survey authority' encodes the name of the source survey authority. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SUREND   Code: 151
+Survey date - end   SUREND 151
+Attribute type: A
+
+
+Definition:
+
+ The end date of the survey.
+
+Indication:
+
+ The survey date, end' should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD). When no specific month and/or day is required/known, indication of the month and/or the day is omitted. This conforms to ISO 8601: 1988.
+
+
+Format:
+
+ CCYYMMDD   (full date, mandatory)
+ CCYYMM (no specific day required, mandatory)
+ CCYY (no specific month required, mandatory)
+
+Example:
+
+ 19781127 for a survey ending on 27 November 1978.
+
+
+Remarks;
+
+ No remarks.
+
+
+
+
+  FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SURSTA   Code: 152
+Survey date - start     SURSTA 152
+Attribute type: A
+
+
+Definition:
+
+ The start date of the survey.
+
+Indication:
+
+ The survey date, start' should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD). When no specific month and/or day is required/known, indication of the month and/or the day is omitted. This conforms to ISO8601: 1988.
+
+Format:
+
+ CCYYMMDD  (full date, mandatory)
+ CCYYMM (no specific day required, mandatory)
+ CCYY (no specific month required, mandatory)
+
+Example:
+
+ 198403 for a survey starting in March 1984.
+
+Remarks.
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: SURTYP  Code: 153
+Survey type     SURTYP 153
+Attribute type: L
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : reconnaissance/sketch survey
+ 2 : controlled survey
+ 3 : unsurveyed
+ 4 : examination survey
+ 5 : passage survey
+ 6 : remotely sensed
+
+Definitions:
+
+reconnaissance/sketch survey:
+  a survey made to a lower degree of accuracy and detail than the chosen scale would normally indicate. (IHO Dictionary, S-32, 5th Edition, 5219)
+
+controlled survey: a thorough survey usually conducted with reference to guidelines.
+
+examination survey: a survey principally aimed at the investigation of underwater obstructions and dangers.
+
+passage survey: a survey where soundings are acquired by vessels on passage.
+
+remotely sensed: a survey where features have been positioned and delimited using remote sensing techniques.
+
+Remarks:
+
+ No remarks.
+
+Value number 3 (unsurveyed) should now be encoded using the object unsurveyed area (UNSARE). FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $SCALE Code: 154
+Symbol scaling factor $SCALE 154
+Attribute type: F
+
+
+Expected input:
+
+ a scaling factor relative to the standard symbol size.
+
+Indication:
+
+ Unit:  none
+ Resolution: 0.1
+
+Format:
+
+ x.x
+
+Example:
+
+ 1.5
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $SCODE Code: 155
+Symbolization code $SCODE 155
+Attribute type: A
+
+
+Expected input:
+
+ see the applicable application profile.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TECSOU   Code: 156
+Technique of sounding measurement   TECSOU 156
+Attribute type: L
+
+INT 1 Reference:  II 24; IK 2, 27, 42;
+
+Chart Specification:  415; 415.1-2; 422.3-4; 422.9;
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : found by echo-sounder
+ 2 : found by side scan sonar
+ 3 : found by multi-beam
+ 4 : found by diver
+ 5 : found by lead-line
+ 6 : swept by wire-drag II 24;IK 2,27,42; 415; 422.3; 422.9;
+ 7 : found by laser
+ 8 : swept by vertical acoustic system
+ 9 : found by electromagnetic sensor
+ 10 : photogrammetry
+ 11 : satellite imagery
+ 12 : found by levelling
+ 13 : swept by side-scan sonar
+ 14 : computer generated
+
+Definitions:
+
+found by echo-sounder: the depth was determined by using an instrument that determines depth of water by measuring the time interval between emission of a sonic or ultrasonic signal and return of its echo from the bottom. (adapted from IHO Dictionary, S-32, 1547)
+
+found by side-scan-sonar:
+  the depth was computed from a record produced by active sonar in which fixed acoustic beams are directed into the water perpendicularly to the direction of travel to scan the bottom and generate a record of the bottom configuration. (adapted from IHO Dictionary, S-32, 4710)
+
+found by multi-beam: the depth was determined by using a wide swath echo sounder that uses multiple beams to measure depths directly below and transverse to the ship's track. (adapted from IHO Dictionary, S-32, 3339)
+
+found by diver: the depth was determined by a person skilled in the practice of diving. (adapted from IHO Dictionary, S-32, 1422)
+
+found by lead-line: the depth was determined by using a line, graduated with attached marks and fastened to a sounding lead. (adapted from IHO Dictionary, S-32, 2698)
+
+swept by wire-drag: the given area was determined to be free from navigational dangers to a certain depth by towing a buoyed wire at the desired depth by two launches, or a least depth was identified using the same technique. (adapted from IHO Dictionary, S-32, 5248, 6013)
+
+found by laser: the depth was determined by using an instrument that measures distance by emitting timed pulses of laser light and measuring the time between emission and reception of the reflected pulses. (adapted from IHO Dictionary, S-32, 2763)
+
+swept by vertical acoustic system:
+  the given area has been swept using a system comprised of multiple echo sounder transducers attached to booms deployed from the survey vessel.
+
+found by electromagnetic sensor:
+  the depth was determined by using an instrument that compares electromagnetic signals. (adapted from IHO Dictionary, S-32, 1571)
+
+photogrammetry: the depth was determined by applying mathematical techniques to photographs. (adapted from IHO Dictionary, S-32, 3791)
+
+satellite imagery: the depth was determined by using instruments placed aboard an artificial satellite. (adapted from IHO Dictionary, S-32, 4509)
+
+found by levelling: the depth was determined by using levelling techniques to find the elevation of the point relative to a datum. (adapted from IHO Dictionary, S-32, 2741)
+
+swept by side-scan-sonar:
+  the given area was determined to be free from navigational dangers to a certain depth by towing a side-scan-sonar. (adapted from IHO Dictionary, S-32, 5248, 4710) [415.2]
+
+computer generated: the sounding was determined from a bottom model constructed using a computer.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $TXSTR Code: 157
+Text string $TXSTR 157
+Attribute type: S
+
+
+Expected input:
+
+ the content of the legend to be displayed.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TXTDSC Code: 158
+Textual description TXTDSC 158
+Attribute type: S
+
+
+Indication:
+
+ the string encodes the file name of an external text file that contains the text in English.
+
+Remarks:
+
+ The attribute textual description' indicates that a file containing text extracted from relevant pilot books or navigational publications is available. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TS_TSP    Code: 159
+Tidal stream - panel values    TS_TSP 159
+Attribute type: A
+
+
+Indication:
+
+ The direction in degrees and velocity in knots are encoded in pairs.  Each value separated by a comma.
+
+Example:
+
+ 63230,Darwin,HW,124,2.2,128,2.1,125,2.9,116,2.8,110,2.0,095,0.6,020,0.2,320,1.9,315,2.1,300,2. 8,268,2.6,200,2.4,165,2.5
+
+Remarks:
+
+ The attribute Tidal stream - panel values' encodes the identification of the reference station with reference water level and the direction of the flow and the springs rate from 6 hours before to 6 hours after high water (HW) or low water (LW) at the reference station, at hourly intervals.
+
+ The relationship to a reference station is encoded using a collection object. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TS_TSV    Code: 160
+Tidal stream - time series values    TS_TSV 160
+Attribute type: A
+
+
+Indication:
+
+ The direction in degrees and velocity in knots are encoded in pairs.  Each value separated by a comma.
+
+Example:
+
+ 135,1.5,156,1.9,301,1.1,342,0.9
+
+Remarks:
+
+ The attribute Tidal stream, current - time series values' encodes values for a direction and velocity time series. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_ACWL    Code: 161
+Tide - accuracy of water level  T_ACWL 161
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : better than 0.1 m and 10 minutes
+ 2 : worse than 0.1 m or 10 minutes
+
+Remarks:
+
+ The attribute Tide - accuracy of water level' encodes the accuracy of the water level, to the confidence level of 95%. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_HWLW    Code: 162
+Tide - high and low water values    T_HWLW 162
+Attribute type: A
+
+
+Indication:
+
+ Dates/times and heights are to be encoded in pairs, each value separated by a comma.
+
+ The date/time should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (eg April = 04) and 2 digits for the day (DD), separated by a capital "T" from the hour (hh) and minutes (mm) which should each be encoded using 2 digits. This conforms  to ISO 8601: 1988. Seconds should not be used.
+
+ The height should be given in metres (xx.x) with a resolution of 0.1 metre.
+
+Format:
+
+ CCYYMMDDThhmm,xx.x,CCYYMMDDThhmm,xx.x
+
+Example:
+
+ 19950428T1020,1.2,19950428T1455,4.8,...
+
+Remarks:
+
+ The attribute tide - high and low water values' encodes information on the times and heights of high and low waters for each day of the duration of the time series.
+
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_MTOD    Code: 163
+Tide - method of tidal prediction   T_MTOD 163
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : simplified harmonic method of tidal prediction
+ 2 : full harmonic method of tidal prediction
+ 3 : time and height difference non-harmonic method
+
+Definitions:
+
+simplified harmonic method of tidal prediction:
+  prediction of tidal heights by combining a simplified set of harmonic constituents into a single time/height curve.
+
+full harmonic method of tidal prediction:
+  prediction of tidal heights by combining a complete set of harmonic constituents into a single time/height curve.
+
+time and height difference non-harmonic method:
+  prediction of high and low water times and heights by modification of the high and low water times and heights of a known time/height curve.
+
+Remarks:
+
+ The attribute Tide - method of tidal prediction' encodes the various methods of tidal prediction. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_THDF    Code: 164
+Tide - time and height differences    T_THDF 164
+Attribute type: A
+
+
+Indication:
+
+ time difference in hours and minutes:   hhmm (according to ISO 8106: 1988)
+ height difference:  metres  (preceded with "-" if negative value)
+ rate difference:  knots  (preceded with "-" if negative value)
+
+Example:
+
+ Tidal height: 63230, Darwin,-0040,-0.7,0.9
+
+ Tidal stream: 59060, Cairns,+0130,1.2,-0.7
+
+Remarks:
+
+ The attribute tide - time and height differences' encodes the time and tidal height or tidal stream rate difference comparative to a reference station.
+
+ The format is the same for tides and tidal streams, with height difference being replaced by rate difference.  The relation to a reference station is encoded by the use of a collection object.
+
+ The attribute is used to contain the identification of the reference station and , encoded in triplets, mean time difference (+ or -), height or rate difference for mean high water or mean high rate (preceded with "-" if negative value), height or rate difference for mean low water or mean low rate (preceded with "-" if negative value), each value separated by a comma. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_TSVL    Code: 166
+Tide - time series values   T_TSVL 166
+Attribute type: A
+
+
+Indication:
+
+ the height above or below (-ve) datum. Each value separated by a comma.
+
+Example:
+
+ 0.2,0.1,0.0,-0.1,-0.2,-0.1,0.0,0.1
+
+Remarks:
+
+ The attribute tide - time series values' encodes the values of a time series. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_VAHC    Code: 167
+Tide - value of harmonic constituents   T_VAHC 167
+Attribute type: A
+
+
+Definition:
+
+ Harmonic constituents are the harmonic elements in a mathematical expression for the tide producing force and in the corresponding formula for the tidal curve.  Each constituent represents a periodic change or variation in the relative positions of the earth, moon and sun.
+
+Indication:
+
+ the first is the number of columns (C, always 2) and the second is the number of rows (R). The next value(s) (C times) indicates the name(s) of the columns, and the next value(s) (R times) indicates the name(s) of the rows (ie constituents). Here after follow the values (C x R times) of amplitude and phase.
+
+Example:
+
+ the following example encodes the amplitude and the phase for M2, S2, K1 and O1.
+
+ 2,4,amplitude,phase,M2,S2,K1,O1,0.962,165,0.361,243,1.223,097,0.875,143
+
+
+amplitudephaseM20.962165S20.361243K11.223097O10.875143
+
+Remarks:
+
+ The attribute tide - value of harmonic constituents' contains a 2 dimensional array of harmonic constituents. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: T_TINT    Code: 165
+Tide - time interval of values  T_TINT 165
+Attribute type: I
+
+
+Indication:
+
+ Unit: minutes
+
+Remarks:
+
+ The attribute Tide, current - time interval of values' encodes the interval between the values in any  time series, e.g. tidal, current or other data. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TIMEND    Code: 168
+Time end    TIMEND 168
+Attribute type: A
+
+
+Indication:
+
+ The time end' will consist of a date and a time separated by a capital "T". The date should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD). The time should be encoded using 2 digits for the hour (hh), 2 digits for the minutes (mm) and 2 digits for the seconds (ss). This conforms to ISO 8601: 1988.
+
+Format:
+
+ CCYYMMDDThhmmss (mandatory)
+
+
+Example:
+
+ 19940426T094500  for a period ending at 09:45 am on 26 April 1994.
+
+
+Remarks:
+
+ The attribute time end' indicates the end of a active period. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TIMSTA    Code: 169
+Time start  TIMSTA 169
+Attribute type: A
+
+
+Indication:
+
+ The time start' will consist of a date and a time separated by a capital "T". The date should be encoded using 4 digits for the calendar year (CCYY), 2 digits for the month (MM) (e.g. April = 04) and 2 digits for the day (DD). The time should be encoded using 2 digits for the hour (hh), 2 digits for the minutes (mm) and 2 digits for the seconds (ss). This conforms to ISO 8601: 1988.
+
+
+Format:
+
+ CCYYMMDDThhmmss (mandatory)
+
+Example:
+
+ 19940212T162000  for a period starting at 04:20 pm on 12 February 1994.
+
+
+Remarks:
+
+ The attribute time start' indicates the start of an active period. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: $TINTS   Code: 170
+Tint   $TINTS 170
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : darkest blue
+ 2 : medium blue
+ 3 : lightest blue
+
+Remarks:
+
+ The attribute tint' is used to indicate that a polygon should be filled with a given colour tint. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TOPSHP   Code: 171
+Topmark/daymark shape   TOPSHP 171
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : cone, point up
+ 2 : cone, point down
+ 3 : sphere
+ 4 : 2 spheres
+ 5 : cylinder (can)
+ 6 : board
+ 7 : x-shape (St. Andrew's cross)
+ 8 : upright cross (St George's cross)
+ 9 : cube, point up
+ 10 : 2 cones, point to point
+ 11 : 2 cones, base to base
+ 12 : rhombus (diamond)
+ 13 : 2 cones (points upward)
+ 14 : 2 cones (points downward)
+ 15 : besom, point up (broom or perch)
+ 16 : besom, point down (broom or perch)
+ 17 : flag
+ 18 : sphere over rhombus
+ 19 : square
+ 20 : rectangle, horizontal
+ 21 : rectangle, vertical
+ 22 : trapezium, up
+ 23 : trapezium, down
+ 24 : triangle, point up
+ 25 : triangle, point down
+ 26 : circle
+ 27 : two upright crosses (one over the other)
+ 28 : T-shape
+ 29 : triangle pointing up over a circle
+ 30 : upright cross over a circle
+ 31 : rhombus over a circle
+ 32 : circle over a triangle pointing up
+ 33 : other shape (see INFORM)
+
+Definitions:
+
+cone: a solid figure generated by straight lines drawn from a fixed point (the vertex) to a circle in a plane not containing the vertex. (The New Shorter Oxford English Dictionary. 1993. vol 2)
+  cones are commonly used as International Association of Lighthouse Authorities - IALA topmarks (lateral).
+cone, point up: is where the vertex points up.
+cone, point down: is where the vertex points down.
+
+sphere: a body the surface of which is at all points equidistant from the centre. (The New Shorter Oxford English Dictionary. 1993. vol 2)
+  spheres are commonly used as International Association of Lighthouse Authorities - IALA topmarks (safe water).
+
+2 spheres: two black spheres are commonly used as an International Association of Lighthouse Authorities - IALA topmark (isolated danger).
+
+cylinder: a solid geometrical figure generated by straight lines fixed in direction and describing with one of point a closed curve, especially a circle (in which case the figure is circular cylinder, it's ends being parallel circles). (The New Shorter Oxford English Dictionary. 1993. vol 2).
+  cylinders are commonly used as International Association of Lighthouse Authorities - IALA topmarks (lateral).
+
+board: usually of rectangular shape, made from timber or metal and used to provide a contrast with the natural background of a daymark. The actual daymark is often painted on to this board.
+
+x-shape: having a shape or a cross-section like the capital letter X. (The New Shorter Oxford English Dictionary. 1993. vol 2)
+  an x-shape as an International Association of Lighthouse Authorities - IALA topmark should be 3 dimensional in shape. It is made of at least three crossed bars.
+
+upright cross: a cross with one vertical member and one horizontal member, i.e. similar in shape to the character +'.
+
+cube: a solid contained by six equal squares; a regular hexahedron (The New Shorter Oxford English Dictionary. 1993. vol 2)
+cube, point up: a cube standing on one of its vertexes.
+
+2 cones, point to point: 2 cones, one above the other, with their vertices together in the centre.
+
+2 cones, base to base: 2 cones, one above the other, with their bases together in the centre and their vertices pointing up and down.
+
+rhombus: a plane figure having four equal sides and equal opposite angles (two acute and two obtuse); an oblique equilateral parallelogram. (The New Shorter Oxford English Dictionary. 1993. vol 2)
+
+besom: a bundle of rods or twigs. (The New Shorter Oxford English Dictionary. 1993. vol 1)
+perch: a staff placed on top of a buoy, rock or shoal as a mark for navigation. (IHO Dictionary, S-32, 5th Edition, 3734)
+
+flag:  a flag mounted on a short pole.
+
+sphere over rhombus: A sphere located above a rhombus.
+
+square: a plane figure with four right angles and four equal straight sides (The New Shorter Oxford English Dictionary. 1993. vol 2)
+
+rectangle: a plane figure with four right angles and four straight sides, opposite sides being parallel and equal in length (The New Shorter Oxford English Dictionary. 1993. vol 2)
+horizontal rectangle: where the two longer opposite sides are standing horizontally.
+vertical rectangle: where the two longer opposite sides are standing vertically.
+
+trapezium: a quadrilateral having one pair of opposite sides parallel. (The New Shorter Oxford English Dictionary. 1993. vol 2)
+trapezium, up: which stands on its longer parallel side.
+trapezium, down: which stands on its shorter parallel side.
+
+triangle: a figure having three angles and three sides. (New Shorter Oxford English Dictionary. 1993. vol 2)
+
+circle: a perfectly round plane figure whose circumference is everywhere equidistant from its centre. (The New Shorter Oxford English Dictionary. 1993. vol 1)
+
+two upright crosses: two upright crosses, generally vertically disposed one above the other.
+
+T-shape: having a shape like the capital letter T.
+
+triangle pointing up over a circle:
+  a triangle, vertex uppermost, located above a circle.
+
+upright cross over a circle:
+  an upright cross located above a circle.
+
+rhombus over a circle: a rhombus located above a circle.
+
+circle over a triangle pointing up
+  a circle located over a triangle, vertex uppermost.
+
+References:
+
+ INT 1: IQ 9;
+
+ M-4: 463.1 (for International Association of Lighthouse Authorities - IALA systems only)
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: TRAFIC   Code: 172
+Traffic flow    TRAFIC 172
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : inbound
+ 2 : outbound
+ 3 : one-way
+ 4 : two-way
+
+Definitions:
+
+inbound: traffic flow in a general direction toward a port or similar destination.
+
+outbound: traffic flow in a general direction away from a port or similar point of origin.
+
+one-way: traffic flow in one general direction only.
+
+two-way: traffic flow in two generally opposite directions.
+
+References:
+
+ INT 1: IM 40;
+
+ M-4: 488;
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALACM    Code: 173
+Value of annual change in magnetic variation VALACM 173
+Attribute type: F
+
+
+Definition:
+
+ The annual change in magnetic variation values.
+
+References:
+
+ INT 1: IB 68.1, 71;
+
+ M-4: 272.1;
+
+Indication:
+
+ Unit:  minute (), negative west
+ Resolution: 0.1'
+
+Format:
+
+ sxx.x
+ s: sign, negative values only
+
+Example:
+
+ -7.1  for an annual change of 7.1 minutes in the westerly direction.
+
+
+Remarks:
+
+ A positive value, i.e. unsigned, indicates a change in an easterly direction and a negative value indicates a change in a westerly direction.
+
+ Distinction: value of magnetic variation; FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALDCO   Code: 174
+Value of depth contour  VALDCO 174
+Attribute type: F
+
+
+Definition:
+
+ The depth of a sea bottom contour.
+
+References:
+
+ INT 1: II 30;
+
+ M-4: 410; 411;
+
+Indication:
+
+ Unit: defined in the DUNI subfield of the DSPM record or in the DUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 fm or 0.1 ft
+
+Format:
+
+ sxxxxx.x
+ s: sign, negative values only.
+
+Example:
+
+ 50  for a depth contour of 50 metres.
+
+
+Remarks:
+
+ Drying contours are indicated by a negative value. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALLMA    Code: 175
+Value of local magnetic anomaly     VALLMA 175
+Attribute type: F
+
+
+Definition:
+
+ The value of the deviation from the normal magnetic variation.
+
+References:
+
+ INT 1: IB 82.1-2;
+
+ M-4: 274;
+
+Indication:
+
+ Unit:  minute ()
+ Resolution: 0.1'
+
+Format:
+
+ xx.x
+
+Example:
+
+ 2.3 for a deviation of 2.3 minutes.
+
+Remarks:
+
+ The deviation is assumed to be positive and negative. The plus/minus character shall not be encoded. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALMAG    Code: 176
+Value of magnetic variation     VALMAG 176
+Attribute type: F
+
+
+Definition:
+
+ The magnetic variation value.
+
+References:
+
+ INT 1: IB 68.1, 71;
+
+ M-4: 272.1;
+
+Indication:
+
+ Unit:  degree (), negative west.
+ Resolution: 0.01 degree
+
+ Conversion factor: one tenth of a second = 0.000028 degree.
+
+Format:
+
+ sxx.xx
+ s: sign, negative values only.
+
+Example:
+
+ 2.3 for a magnetic north oriented at 2.3 degrees east from the geographic (true)   north.
+
+Remarks:
+
+ A positive value, i.e. unsigned, indicates variation in an easterly direction and a negative value indicates variation in a westerly direction.
+
+ Distinction: value of annual change in magnetic variation; FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALMXR   Code: 177
+Value of maximum range  VALMXR 177
+Attribute type: F
+
+
+Definition:
+
+ The extreme distance at which an object can be seen or a signal detected.
+
+References:
+
+ INT 1: not specified;
+
+ M-4: not specified;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit:  nautical mile (M)
+ Resolution: 0.1 M
+
+Format:
+
+ xx.x
+
+Example:
+
+ 17  for a maximum range of 17 nautical miles.
+
+Remarks:
+
+ This attribute does not apply to lights where the attribute value of nominal range' should be used. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALNMR   Code: 178
+Value of nominal range  VALNMR 178
+Attribute type: F
+
+
+Definition:
+
+ The nominal range at which an object can be seen or a signal detected.
+
+References:
+
+ INT 1: IB 45; IP 14;
+
+ M-4: 451.1; 471.7;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit:  nautical mile (M)
+ Resolution: 0.1 M
+
+Format:
+
+ xx.x
+
+Example:
+
+ 14  for a nominal range of 14 nautical miles.
+
+Remarks:
+
+ The nominal range is normally the luminous range of a light in a homogeneous atmosphere in which the meteorological visibility is 10 sea miles. (IHO Hydrographic Dictionary, S-32, 5th Edition, 4218) FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VALSOU   Code: 179
+Value of sounding   VALSOU 179
+Attribute type: F
+
+
+Definition:
+
+ The value of the measurement of a sounding relative to the chart datum.
+
+References:
+
+ INT 1: II 10, 11, 14, 15;
+
+ M-4: 410; 412 413.1;
+
+Indication:
+
+ Unit:  defined in the DUNI subfield of the DSPM record or in the DUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 fm or 0.1 ft
+
+Format:
+
+ sxxxxx.xx
+ s:  sign, negative values only.
+
+Examples:
+
+ 18.2  for a sounding  of 18.2 metres.
+ -2.4  for a drying height of 2.4 metres.
+
+
+Remarks:
+
+ A drying height is indicated by a negative value. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERACC    Code: 180
+Vertical accuracy   VERACC 180
+Attribute type: F
+
+Expected input:
+
+ The one-dimensional error.
+
+ The error is assumed to be positive and negative.  The plus/minus character shall not be encoded.
+
+Definition:
+
+ The best estimate of the vertical accuracy of heights, vertical distances and vertical clearances, excluding sounding measurements.
+
+Minimum value:  0
+
+
+Indication:
+
+ Unit:  defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 1.2  for an error of 1.2 metres.
+
+
+Remarks:
+
+ No remarks.
+ FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERCLR   Code: 181
+Vertical clearance  VERCLR 181
+Attribute type: F
+
+
+Definition:
+
+ The vertical clearance measured from the plane towards the object overhead.
+
+References:
+
+ INT 1: ID 25-28;
+
+ M-4: 380; 380.1; 382; 383;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 7.6  for a vertical clearance of 7.6 metres.
+
+Remarks:
+
+ In the case of cables carrying high voltages an additional clearance of from 2 to 5 metres may be needed to avoid an electrical discharge. When known, the authorized safe clearance (known in the UK as the Safe Overhead Clearance) which is the physical clearance minus a safety margin shall be stated. (IHO Chart Specifications, M-4).
+
+ See also vertical clearance safe'. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERCCL   Code: 182
+Vertical clearance, closed  VERCCL 182
+Attribute type: F
+
+
+Definition:
+
+ The vertical clearance of an object in closed condition (e.g. a closed lifting bridge) measured from the plane towards the object overhead.
+
+References:
+
+ INT 1: ID 23.3;
+
+ M-4: 380; 380.1; 381.3;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 11.2  for a vertical clearance of 11.2 metres.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERCOP   Code: 183
+Vertical clearance, open    VERCOP 183
+Attribute type: F
+
+
+Definition:
+
+ The vertical clearance of an object in opened condition (e.g. an opened lifting bridge) measured from the plane towards the object overhead.
+
+References:
+
+ INT 1: ID 23.3;
+
+ M-4: 380; 380.2; 381.3;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 17.8  for a vertical clearance of 17.8 metres.
+
+Remarks:
+
+ No remarks. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERCSA   Code: 184
+Vertical clearance, safe    VERCSA 184
+Attribute type: F
+
+
+Definition:
+
+ The safe vertical clearance measured from the plane towards the object overhead.
+
+References:
+
+ INT 1: ID 26;
+
+ M-4: 382.1;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xx.x
+
+Example:
+
+ 7.2  for a vertical clearance of 7.2 metres.
+
+Remarks:
+
+ In the case of cables carrying high voltages, the quoted vertical clearance (VERCLR) may have to be reduced by 2-5m to avoid electrical discharge. When known, this authorized safe clearance (known in the UK as the Safe Overhead Clearance) which is the physical clearance minus a safety margin shall, be stated. (IHO Chart Specifications, M-4).
+
+ See also Vertical Clearance' FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERDAT   Code: 185
+Vertical datum  VERDAT 185
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : Mean low water springs
+ 2 : Mean lower low water springs
+ 3 : Mean sea level
+ 4 : Lowest low water
+ 5 : Mean low water
+ 6 : Lowest low water springs
+ 7 : Approximate mean low water springs
+ 8 : Indian spring low water
+ 9 : Low water springs
+ 10 : Approximate lowest astronomical tide
+ 11 : Nearly lowest low water
+ 12 : Mean lower low water
+ 13 : Low water
+ 14 : Approximate mean low water
+ 15 : Approximate mean lower low water
+ 16 : Mean high water
+ 17 : Mean high water springs
+ 18 : High water
+ 19 : Approximate mean sea level
+ 20 : High water springs
+ 21 : Mean higher high water
+ 22 : Equinoctial spring low water
+ 23 : Lowest astronomical tide
+ 24 : Local datum
+ 25 : International Great Lakes Datum 1985
+ 26 : Mean water level
+ 27 : Lower low water large tide
+ 28 : Higher high water large tide
+   29   :   Nearly highest high water
+
+Definitions:
+
+mean low water springs:
+  (MLWS) - the average height of the low waters of spring tides. Also called spring low water. (IHO Dictionary, S-32, 5th Edition, 3150)
+
+mean lower low water springs:
+  (MLLWS) - the average height of lower low water springs at a place. (IHO Dictionary, S-32, 5th Edition, 3146)
+
+
+
+mean sea level: (MSL) - the average height of the surface of the sea at a tide station for all stages of the tide over a 19-year period, usually determined from hourly height readings measured from a fixed predetermined reference level. (IHO Dictionary, S-32, 5th Edition, 3156)
+
+lowest low water: an arbitrary level conforming to the lowest tide observed at a place, or some what lower.
+
+mean low water: (MLW) - the average height of all low waters at a place over a 19-year period. (IHO Dictionary, S-32, 5th Edition, 3147)
+
+lowest low water springs:
+  an arbitrary level conforming to the lowest water level observed at a place at spring tides during a period of time shorter than 19 years. (Hydrographic Service, Royal Australian Navy)
+
+approximate mean low water springs:
+  an arbitrary level, usually within  0.3m from that of mean low water springs (MLWS). (Hydrographic Service, Royal Australian Navy)
+
+Indian spring low water: (ISLW) - an arbitrary tidal datum approximating the level of the mean of the lower low water at spring tides.  Also called Indian tidal plane. (IHO Dictionary, S-32, 5th Edition, 2427)
+
+  A tidal datum approximating the lowest water level observed at a place, originated by G.H. Darwin for the tides of India at a level below MSL being equal to the sum of amplitudes of the harmonic constituents M2, S2, K1 and O1; usually below that of the lower low water at spring tides. Also called Indian tide plane. (Hydrographic Service, Royal Australian Navy).
+
+low water springs: an arbitrary level, approximating that of mean low water springs (MLWS). (Hydrographic Service, Royal Australian Navy)
+
+approximate lowest astronomical tide:
+  an arbitrary level, usually within  0.3m from that of lowest astronomical tide (LAT). (Hydrographic Service, Royal Australian Navy)
+
+nearly lowest low water:
+  an arbitrary level approximating the lowest water level observed at a place, usually equivalent to the Indian spring low water (ISLW). (Hydrographic Service, Royal Australian Navy)
+
+mean lower low water: (MLLW) - the average height of the lower low waters at a place over a 19-year period. (IHO Dictionary, S-32, 5th Edition, 3145)
+
+low water: an approximation of mean low water adopted as the reference level for a limited area, irrespective of better determinations at a later date.  Used mostly in harbour and river engineering.
+
+  used in inland (non-tidal) waters.  It is generally defined as a level which the daily mean water level would fall below less than 5% of the time and by no more than 0.2 metres during the navigation season.  A single level surface is usually chosen as the low water datum for a whole lake.  On a river, low water datum is a sloping surface which approximates the river surface at a low state. (Canadian Hydrographic Service)
+
+approximate mean low water:
+  an arbitrary level, usually within  0.3m from that of mean low water (MLW). (Hydrographic Service, Royal Australian Navy)
+
+
+approximate mean lower low water:
+  an arbitrary level, usually within  0.3m from that of mean lower low water (MLLW). (Hydrographic Service, Royal Australian Navy)
+
+mean high water: (MHW) - the average height of all high waters at a place over a 19-year period. (IHO Dictionary, S-32, 5th Edition, 3141)
+
+mean high water springs:
+  (MHWS) - the average height of the high waters of spring tides.  Also called spring high water. (IHO Dictionary, S-32, 5th Edition, 3144)
+
+high water: the highest level reached at a place by the water surface in one tidal cycle.  Also called high tide. (IHO Dictionary, S-32, 5th Edition, 2251)
+
+  when used on inland (non-tidal) waters it is generally defined as a level which the daily mean water level exceeds less than 5% of the time.
+
+approximate mean sea level:
+  an arbitrary level, usually within  0.3m from that of mean sea level (MSL). (Hydrographic Service, Royal Australian Navy)
+
+high water springs: an arbitrary level, approximating that of mean high water springs (MHWS). (Hydrographic Service, Royal Australian Navy)
+
+mean higher high water:
+  (MHHW) - the average height of higher high waters at a place over a 19-year period. (IHO Dictionary, S-32, 5th Edition, 3140)
+
+equinoctial spring low water:
+  the level of low water springs near the time of an equinox.
+
+lowest astronomical tide:
+  (LAT) - the lowest tide level which can be predicted to occur under average meterological conditions and under any combination of astronomical conditions. (IHO Dictionary, S-32, 5th Edition, 2936)
+
+local datum: an arbitrary datum defined by a local harbour authority, from which levels and tidal heights are measured by this authority.
+
+international great lakes datum 1985:
+  (IGLD 1985) - a vertical reference system with its zero based on the mean water level at Rimouski/Pointe-au-Pére, Quebec, over the period 1970 to 1988.
+
+mean water level: the average of all hourly water levels over the available period of record.
+
+lower low water large tide:
+  (LLWLT) - the average of the lowest low waters, one from each of 19 years of observations.
+
+higher high water large tide:
+  (HHWLT) - the average of the highest high waters, one from each of 19 years of observations.
+
+nearly highest high water:
+  an arbitrary level approximating the highest water level observed at a place, usually equivalent to the high water springs.
+
+
+
+
+
+Remarks:
+
+ This attribute is used to specify the datum to which both heights (vertical datum, see S-57 Part 3) and soundings (sounding datum, see S-57 Part 3) are referred.
+
+ When the vertical datum is unknown, such as water areas above locks, the value local datum' is to be used, and further details may be encoded using INFORM'.
+
+ The  0.3m approximation quoted in the "approximate" levels is somehow arbitrary and follows the British example of their definition for "approximate LAT". FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: VERLEN   Code: 186
+Vertical length     VERLEN 186
+Attribute type: F
+
+
+Definition:
+
+ The total vertical length of an object.
+
+References:
+
+ INT 1: IE 5; IL 21.3;
+
+ M-4: 303;
+
+Minimum Value: 0
+
+Indication:
+
+ Unit: defined in the HUNI subfield of the DSPM record or in the HUNITS attribute of the M_UNIT meta object class, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 ft
+
+Format:
+
+ xxx.x
+
+Example:
+
+ 24.5  for a vertical length of 24.5 metres.
+
+Remarks:
+
+ For floating objects:
+  the vertical distance from the surface of water to the highest point of that object.
+
+ For fixed objects: the vertical distance from seabed or ground to the highest point of that object.
+
+ For objects on top of other objects:
+  the vertical distance from the lowest to the highest point of that object.
+
+ Vertical length measurements do not require a datum. FEATURE OBJECT ATTRIBUTES
+
+
+
+
+Acronym: WATLEV   Code: 187
+Water level effect  WATLEV 187
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : partly submerged at high water IF 33.2; 378.1;
+ 2 : always dry IF 6.1; IK 10; 313.4; 421.1;
+ 3 : always under water/submerged IF 6.3; IK 13; 421.4;
+ 4 : covers and uncovers  IF 6.2; IK 11; 421.2;
+ 5 : awash IK 12; 421.3;
+ 6 : subject to inundation or flooding
+
+Definitions:
+
+partly submerged at high water:
+  partially covered and partially dry at high water.
+
+always dry: not covered at high water under average meteorological conditions.
+
+always under water/submerged:
+  remains covered by water at all times under average meteorological conditions.
+
+covers and uncovers: expression intended to indicate an area of a reef or other projection from the bottom of a body of water  which periodically extends above and is submerged below the surface. Also referred to as dries or uncovers. (IHO Dictionary, S-32, 5th Edition, 1111)
+
+awash: flush with, or washed by the waves at low water under average meteorological conditions. (adapted from IHO Dictionary, S-32, 5th Edition, 308)
+
+subject to inundation or flooding:
+  an area periodically covered by flood water, excluding tidal waters. (Digital Geographic Information Standard - DIGEST 1.2)
+
+Remarks:
+
+ The attribute water level effect' encodes the effect of the surrounding water on an object. 2.3 National Language Attributes
+ NATIONAL LANGUAGE ATTRIBUTES
+
+
+
+
+Acronym: NINFOM   Code: 300
+Information in national language   NINFOM 300
+Attribute type: S
+
+
+References:
+
+ INT 1: IA 16;
+
+ M-4: 242.3-5;
+
+Indication:
+
+ Text (c...):  Textual information in national language characters
+
+Format:
+
+ c...
+
+Remarks:
+
+ The attribute information in national language' encodes  any textual information about an object using a specified national language.
+
+ The textual information could be, for example, a list, a table or a text.
+
+ This attribute should be used, for example, to hold the information that is shown on paper charts by cautionary and explanatory notes. NATIONAL LANGUAGE ATTRIBUTES
+
+
+
+
+Acronym: NOBJNM   Code: 301
+Object name in national language   NOBJNM 301
+Attribute type: S
+
+
+References:
+
+ INT 1: ID 7, IF 19, IN 12.2-3;
+
+ M-4: 371; 323.1-2; 431.2-3; 431.5;
+
+Indication:
+
+ Name of object (c...): string of national language characters
+
+Format:
+
+ c...
+
+Remarks:
+
+ The attribute object name in national language' encodes the individual name of an object in the specified national language. NATIONAL LANGUAGE ATTRIBUTES
+
+
+
+
+Acronym: NPLDST   Code: 302
+Pilot district in national language    NPLDST 302
+Attribute type: S
+
+
+References:
+
+ INT 1: IT 1.2;
+
+ M-4: 491.1-2;
+
+Indication:
+
+ Pilot district (c...): string of national language characters
+
+Format:
+
+ c...
+
+Remarks:
+
+ The attribute pilot district in national language' encodes the pilot district for which a pilot station is responsible in the specified national language. NATIONAL LANGUAGE ATTRIBUTES
+
+
+
+
+Acronym: $NTXST Code: 303
+Text string in national language $NTXST 303
+Attribute type: S
+
+
+Expected input: the content of the legend to be displayed in a national language other than English.
+Remarks:
+
+ No remarks. NATIONAL LANGUAGE ATTRIBUTES
+
+
+
+
+Acronym: NTXTDS Code: 304
+Textual description in national language NTXTDS 304
+Attribute type: S
+
+
+Indication:
+
+ the string encodes the file name of an external text file that contains the text in a national language.
+
+Remarks:
+
+ The attribute textual description in national language' indicates whether a text file containing text extracted from relevant pilot books or navigational publications is available. 2.4 Spatial and Meta Object Attributes
+
+
+
+Some attributes qualify the location of an object, as opposed to defining the characteristics of the individual object itself.
+
+Attributes specifying the accuracy and quality of a position (x,y - coordinates) and the reference datum for horizontal measurement are considered to be attributes of spatial objects.
+
+Within a data set encoded according to S-57, the attributes of spatial objects are held in the Spatial Record Attribute field (refer to S-57 Part 3).
+ SPATIAL AND META OBJECT ATTRIBUTES
+
+
+
+
+Acronym: HORDAT  Code: 400
+Horizontal datum    HORDAT 400
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning
+
+ 1 : WGS 72
+ 2 : WGS 84
+ 3 : European 1950
+ 4 : Potsdam Datum
+ 5 : Adindan
+ 6 : Afgooye
+ 7 : Ain el Abd 1970
+ 8 : Anna 1 Astro 1965
+ 9 : Antigua Island Astro 1943
+ 10 : Arc 1950
+ 11 : Arc 1960
+ 12 : Ascension Island 1958
+ 13 : Astro beacon "E" 1945
+ 14 : Astro DOS 71/4
+ 15 : Astro Tern Island (FRIG) 1961
+ 16 : Astronomical Station 1952
+ 17 : Australian Geodetic 1966
+ 18 : Australian Geodetic 1984
+ 19 : Ayabelle Lighthouse
+ 20 : Bellevue (IGN)
+ 21 : Bermuda 1957
+ 22 : Bissau
+ 23 : Bogota Observatory
+ 24 : Bukit Rimpah
+ 25 : Camp Area Astro
+ 26 : Campo Inchauspe 1969
+ 27 : Canton Astro 1966
+ 28 : Cape
+ 29 : Cape Canaveral
+ 30 : Carthage
+ 31 : Chatam Island Astro 1971
+ 32 : Chua Astro
+ 33 : Corrego Alegre
+ 34 : Dabola
+ 35 : Djakarta (Batavia)
+ 36 : DOS 1968
+ 37 : Easter Island 1967
+ 38 : European 1979
+ 39 : Fort Thomas 1955
+ 40 : Gan 1970
+ 41 : Geodetic Datum 1949
+ 42 : Graciosa Base SW 1948
+ 43 : Guam 1963
+ 44 : Gunung Segara
+ 45 : GUX 1 Astro
+ 46 : Herat North
+ 47 : Hjorsey 1955
+ 48 : Hong Kong 1963
+ 49 : Hu-Tzu-Shan
+ 50 : Indian
+ 51 : Indian 1954
+ 52 : Indian 1975
+ 53 : Ireland 1965
+ 54 : ISTS 061 Astro 1968
+ 55 : ISTS 073 Astro 1969
+ 56 : Johnston Island 1961
+ 57 : Kandawala
+ 58 : Kerguelen Island 1949
+ 59 : Kertau 1948
+ 60 : Kusaie Astro 1951
+ 61 : L. C. 5 Astro 1961
+ 62 : Leigon
+ 63 : Liberia 1964
+ 64 : Luzon
+ 65 : Mahe 1971
+ 66 : Massawa
+ 67 : Merchich
+ 68 : Midway Astro 1961
+ 69 : Minna
+ 70 : Montserrat Island Astro 1958
+ 71 : M'Poraloko
+ 72 : Nahrwan
+ 73 : Naparima, BWI
+ 74 : North American 1927
+ 75 : North American 1983
+ 76 : Observatorio Meteorologico 1939
+ 77 : Old Egyptian 1907
+ 78 : Old Hawaiian
+ 79 : Oman
+ 80 : Ordnance Survey of Great Britain 1936
+ 81 : Pico de las Nieves
+ 82 : Pitcairn Astro 1967
+ 83 : Point 58
+ 84 : Pointe Noire 1948
+ 85 : Porto Santo 1936
+ 86 : Provisional South American 1956
+ 87 : Provisional South Chilean 1963 (also known as Hito XVIII 1963)
+ 88 : Puerto Rico
+ 89 : Qatar national
+ 90 : Qornoq
+ 91 : Reunion
+ 92 : Rome 1940
+ 93 : Santo (DOS) 1965
+ 94 : Sao Braz
+ 95 : Sapper Hill 1943
+ 96 : Schwarzeck
+ 97 : Selvagem Grande 1938
+ 98 : South American 1969
+ 99 : South Asia
+ 100 : Tananarive  Observatory 1925
+ 101 : Timbalai 1948
+ 102 : Tokyo
+ 103 : Tristan Astro 1968
+ 104 : Viti Levu 1916
+ 105 : Wake-Eniwetok 1960
+ 106 : Wake Island Astro 1952
+ 107 : Yacare
+ 108 : Zanderij
+ 109 : American Samoa 1962
+ 110 : Deception Island
+ 111 : Indian 1960
+ 112 : Indonesian 1974
+ 113 : North Sahara 1959
+ 114 : Pulkovo 1942
+ 115 : S-42 (Pulkovo 1942)
+ 116 : S-JYSK
+ 117 : Voirol 1950
+118  : Average Terrestrial System 1977
+ 119 : Compensation Gèodèsique du Quèbec 1977
+ 120 : Finnish (KKJ)
+121   :   Ordnance Survey of Ireland
+122   :   Revised Kertau
+123   :   Revised Nahrwan
+124  : GGRS 76 (Greece)
+ 125 : Nouvelle Triangulation de France
+ 126 : RT 90 (Sweden)
+127    :    Geocentric Datum of Australia (GDA)
+128    :    BJZ54 (A954 Beijing Coordinates)
+129    :    Modified BJZ54
+130    :    GDZ80
+131    :    Local datum
+
+
+References:
+
+ INT 1: IS 50;
+
+ M-4: not specified;
+
+Remarks:
+
+ All necessary information for conversion of geographic coordinates from most of the Geodetic Datums in the above list to WGS-84 is contained in the "User's Handbook on Datum Transformations involving WGS-84", prepared by the US Defense Mapping Agency and which is available from the IHB as IHO Publication S-60 (English and French Versions), along with an associated standard datum transformation software on floppy disk called "MADTRAN". The resulting latitude and longitude offsets can be encoded in the attribute SHIPAM.
+
+ SPATIAL AND META OBJECT ATTRIBUTES
+
+
+
+
+Acronym: POSACC    Code: 401
+Positional Accuracy     POSACC 401
+Attribute type: F
+
+Expected input:
+
+ The expected input is the maximum of the two-dimensional error.
+
+ The error is assumed to be positive and negative. The plus/minus character shall not be encoded.
+
+Definition:
+
+ The best estimate of the accuracy of a position.
+
+Minimum value: 0
+
+
+Indication:
+
+ Unit: defined in the PUNI subfield of the DSPM record, e.g. metre (m)
+ Resolution: 0.1 m or 0.1 mm
+
+Format:
+
+ xxxx.x
+
+Example:
+
+ 25  for an error of 25 metres.
+
+Remarks
+
+ No remarks SPATIAL  AND META OBJECT ATTRIBUTES
+
+
+
+
+Acronym: QUAPOS    Code: 402
+Quality of position     QUAPOS 402
+Attribute type: E
+
+
+Expected input:
+
+ ID  Meaning INT 1 M-4
+
+ 1 : surveyed IC 1; 310.1;
+ 2 : unsurveyed IC 2; II 25; 311; 410;
+ 3 : inadequately surveyed II 25; 410;
+ 4 : approximate IB 7, 33; IC 12; II 31; 305.1; 351.4; 411.2;
+ 5 : position doubtful II 1; 424.3;
+ 6 : unreliable
+ 7 : reported (not surveyed)
+ 8 : reported (not confirmed) II 3.1-2, 4;
+ 9 : estimated
+ 10 : precisely known
+ 11 : calculated
+
+Definitions:
+
+surveyed: the position(s) was(were) determined by the operation of making measurements for determining the relative position of points on, above or beneath the earth's surface.  Survey implies a regular, controlled survey of any date. (adapted from IHO Dictionary, S-32, 5195, & IHO Chart Specifications, M-4, 175.2)
+
+unsurveyed: survey data is does not exist or is very poor.  (adapted from IHO Dictionary, S- 32, 5732)
+
+inadequately surveyed: position data is of a very poor quality. (adapted from IHO Dictionary, S-32, 5732)
+
+approximate: a position that is considered to be less than third-order accuracy, but is generally considered to be within 30.5 metres of its correct geographic location.  Also may apply to an object whose position does not remain fixed.  (adapted from IHO Dictionary, S-32, 213, 3967, & IHO Specifications, M-4, 424.1)
+
+position doubtful: an object whose position has been reported but which is considered to be doubtful.
+
+unreliable: an object's position obtained from questionable or unreliable data.
+
+reported (not surveyed):
+  an object whose position has been reported and its position confirmed by some means other than a formal survey such as an independent report of the same object.
+
+reported (not confirmed):
+  an object whose position has been reported and its position has not been confirmed.
+
+estimated: the most probable position of an object determined from incomplete data or data of questionable accuracy. (adapted from IHO Dictionary, S-32, 3960)
+
+precisely known: a position that is of a known value, such as the position of an anchor berth or other defined object.
+
+calculated: a position that is computed from data.
+
+Remarks:
+
+ No remarks.
\ No newline at end of file

Added: packages/openev/branches/upstream/current/contrib/S52/doc/boylat24.dia
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/contrib/S52/doc/boylat24.dia
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/contrib/S52/doc/boylat24.png
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/contrib/S52/doc/boylat24.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/contrib/S52/doc/libS52-layout.dia
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/doc/libS52-layout.dia	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/doc/libS52-layout.dia	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1830 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.54"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.54"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.54"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.54"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:object type="Standard - Box" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-5,-5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-5.05,-5.05;24.05,28.9912"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="-5,-5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="29"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="33.9412"/>
+      </dia:attribute>
+      <dia:attribute name="inner_color">
+        <dia:color val="#ffc0cb"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="4"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="12,2"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="12,0.9675;21.6825,4.165"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#libS52 dependency
+layout --SEP2004#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1.4"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="12,2"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="11.6,11.55"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11.6,10.9;11.6,11.9"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="11.6,11.55"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Magnetic Disk" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="12,10"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11.95,9.95;14.9571,13.2642"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="12,10"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.90711"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.21421"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.1"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#S52raz#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="13.4536,12.125"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="flip_horizontal">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="flip_vertical">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8,5.35;8,6.35"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="8,6"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,8.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.93805,8.03805;20.1873,11.4941"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,8.1"/>
+        <dia:point val="20,11"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O10" connection="6"/>
+        <dia:connection handle="1" to="O16" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2,4.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="1.9398,4.0398;10.1642,6.49802"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="2,4.1"/>
+        <dia:point val="10,6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O12" connection="6"/>
+        <dia:connection handle="1" to="O10" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Polygon" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16,30"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="15.95,-11.05;32.05,35.05"/>
+      </dia:attribute>
+      <dia:attribute name="poly_points">
+        <dia:point val="16,30"/>
+        <dia:point val="26,30"/>
+        <dia:point val="26,-6"/>
+        <dia:point val="16,-6"/>
+        <dia:point val="16,-11"/>
+        <dia:point val="32,-11"/>
+        <dia:point val="32,35"/>
+        <dia:point val="16,35"/>
+      </dia:attribute>
+      <dia:attribute name="inner_color">
+        <dia:color val="#add8e6"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,-9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20,-9.93;27.43,-8.34"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#libgv.a (OpenEV)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1.2"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="20,-9"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,13.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.6127,13.0293;20.0707,23.3905"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20,13.1"/>
+        <dia:point val="10,23"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O16" connection="6"/>
+        <dia:connection handle="1" to="O37" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.95,5.95;12.05,8.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8,6"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,7.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8,6.4;10.1,7.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52PL#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="8,7.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O10" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,2"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.05,1.95;4.05,4.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="0,2"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,3.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0,2.4;2.15,3.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52GL#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0,3.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O12" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,33"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.05,32.95;4.05,35.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="0,33"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,34.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0,33.4;2.6,34.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># OpenGL#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="0,34.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O14" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,11"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="17.95,10.95;22.05,13.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="18,11"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,12.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="18,11.4;20.2,12.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52CS#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="18,12.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O16" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,15"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.95,14.95;12.05,17.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8,15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,16.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8,15.4;10.1,16.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S57gv#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="8,16.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O18" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="11,-8"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="10.95,-8.05;15.05,-5.85"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="11,-8"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="11,-6.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11,-7.6;14.75,-6.6"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># gvS57Layer#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="11,-6.95"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O20" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-2,25.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-2.5,25.05;-1.5,30.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="-2,25.1"/>
+        <dia:point val="-2,30"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O35" connection="6"/>
+        <dia:connection handle="1" to="O49" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O23">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,23"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="17.95,22.95;22.05,25.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="18,23"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O24">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="18,23.4;20.75,24.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52type#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="18,24.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O25">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,8.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.5,8.05;10.5,15.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,8.1"/>
+        <dia:point val="10,15"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O10" connection="6"/>
+        <dia:connection handle="1" to="O18" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O26">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,-16"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.05,-16.05;15.05,-12.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="0,-16"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O27">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,-15"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3,-15.6713;13.2713,-13.8075"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#s52plugin (to test default build)
+s52test &amp; s52osg (debug/static)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="3,-15"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O28">
+      <dia:attribute name="obj_pos">
+        <dia:point val="15,-14.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="14.9382,-14.5618;16.4945,-10.8146"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="15,-14.5"/>
+        <dia:point val="16,-11"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O26" connection="4"/>
+        <dia:connection handle="1" to="O7" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O29">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,19"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="17.95,18.95;22.05,21.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="18,19"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O30">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,20.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="18,19.4;20.7,20.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52utils#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="18,20.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O29" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O31">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,30"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.95,29.95;12.05,32.05"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8,30"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O32">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,31"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8,30.35;11.6,31.35"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># PROJ (OGR)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="8,31"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O31" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="0" id="O33">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2,4.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="1.5,4.05;2.5,33.05"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="2,4.1"/>
+        <dia:point val="2,8"/>
+        <dia:point val="2,8"/>
+        <dia:point val="2,33"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O12" connection="6"/>
+        <dia:connection handle="1" to="O14" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O34">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,8.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.93209,8.03209;13.7384,10.4622"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,8.1"/>
+        <dia:point val="13.4536,10"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O10" connection="6"/>
+        <dia:connection handle="1" to="O3" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O35">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-4,23"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-4.05,22.95;0.05,25.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="-4,23"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O36">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-4,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-4,23.4;-1.3,24.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52OSG#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-4,24.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O35" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O37">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,23"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.95,22.95;12.05,25.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8,23"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O38">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8,23.4;10.85,24.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S57data#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="8,24.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O37" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O39">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,19"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.95,18.95;7.05,21.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="3,19"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O40">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,20.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3,19.4;5.45,20.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S57ogr#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="3,20.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O39" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O41">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5,21.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.9355,21.0355;10.2243,23.4852"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="5,21.1"/>
+        <dia:point val="10,23"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O39" connection="6"/>
+        <dia:connection handle="1" to="O37" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O42">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,-2"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.95,-2.05;12.05,0.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8,-2"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O43">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,0.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.5,0.05;10.5,6.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,0.1"/>
+        <dia:point val="10,6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O42" connection="6"/>
+        <dia:connection handle="1" to="O10" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O44">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,0.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="1.83582,0.0397996;10.0602,2.49802"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,0.1"/>
+        <dia:point val="2,2"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O42" connection="6"/>
+        <dia:connection handle="1" to="O12" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O45">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2,4.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-2.49952,4.04073;2.05927,23.1524"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="2,4.1"/>
+        <dia:point val="-2,23"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O12" connection="6"/>
+        <dia:connection handle="1" to="O35" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O46">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8,-0.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8,-1.6;9.4,-0.6"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="8,-0.95"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O42" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O47">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,17.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.5,17.05;10.5,23.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,17.1"/>
+        <dia:point val="10,23"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O18" connection="6"/>
+        <dia:connection handle="1" to="O37" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O48">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,17.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.77565,17.0355;10.0645,19.4852"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,17.1"/>
+        <dia:point val="5,19"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O18" connection="6"/>
+        <dia:connection handle="1" to="O39" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O49">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-4,30"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-4.05,29.95;0.05,32.15"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="-4,30"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O50">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-4,31.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-4,30.4;-2.5,31.4"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># OSG#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-4,31.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O49" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="0" id="O51">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,31.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.05,30.95;2.5,33.05"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="0,31.05"/>
+        <dia:point val="0,31"/>
+        <dia:point val="2,31"/>
+        <dia:point val="2,33"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O49" connection="4"/>
+        <dia:connection handle="1" to="O14" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O52">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,-8"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.95,-8.05;8.05,-5.85"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="3,-8"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2.1"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O53">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,30"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.95,29.95;7.05,32.05"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="3,30"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="2"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O54">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,31"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3,30.35;4.5,31.35"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># OGR#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="3,31"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O53" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O55">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5,21.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.5,21.05;5.5,30.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="5,21.1"/>
+        <dia:point val="5,30"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O39" connection="6"/>
+        <dia:connection handle="1" to="O53" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O56">
+      <dia:attribute name="obj_pos">
+        <dia:point val="3,-6.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3,-7.6;7.25,-6.6"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string># S52glxsimple#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.8"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="3,-6.95"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O52" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O57">
+      <dia:attribute name="obj_pos">
+        <dia:point val="13,-5.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.5732,-5.97012;13.0701,-1.65551"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="13,-5.9"/>
+        <dia:point val="10,-2"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O20" connection="6"/>
+        <dia:connection handle="1" to="O42" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O58">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5,-5.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.42947,-5.97053;10.3652,-1.58941"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="5.5,-5.9"/>
+        <dia:point val="10,-2"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O52" connection="6"/>
+        <dia:connection handle="1" to="O42" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O59">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16,-8.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="12.8685,-8.55754;16.0575,-7.49858"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16,-8.5"/>
+        <dia:point val="13,-8"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O7" connection="7"/>
+        <dia:connection handle="1" to="O20" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O60">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,25.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.5,25.05;10.5,30.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,25.1"/>
+        <dia:point val="10,30"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O37" connection="6"/>
+        <dia:connection handle="1" to="O31" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O61">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,17.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.92951,17.0295;21.4129,30.3625"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,17.1"/>
+        <dia:point val="21,30"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O18" connection="6"/>
+        <dia:connection handle="1" to="O7" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O62">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,33"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20,32.07;27.43,33.66"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#libgv.a (OpenEV)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1.2"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="20,33"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+  </dia:layer>
+</dia:diagram>

Added: packages/openev/branches/upstream/current/contrib/S52/doc/obj.txt
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/doc/obj.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/doc/obj.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8848 @@
+
+
+
+ S-57 Appendix A
+ IHO Object Catalogue
+
+ Edition 3.0
+ Page intentionally left blank
+ CONTENTS
+
+
+
+Introduction
+
+Object Classes Chapter 1
+
+Attributes Chapter 2
+
+IHB Codes for Producing Agency Annex A
+
+Attributes/Object Classes Cross Reference Annex B
+ Page intentionally left blank
+Introduction
+
+The Object Catalogue is the data schema for "S-57 - The IHO Transfer 
+Standard for Digital Hydrographic Data". Its primary function is to 
+provide a means of describing real world entities. That is entities 
+which actually exist (either physically such as a beacon or legally 
+such as an anchorage area) in the real world. The Object Catalogue is 
+based on the theoretical model described in Part 2 of this Standard. 
+The model assumes that real world entities can be categorized into a 
+finite number of types, such as lights, wrecks, built up areas etc. 
+These entity types are termed feature object classes in the Object 
+Catalogue. An instance of a feature object class, referred to as a 
+feature object, (that is one specific light or wreck or built up area) 
+can be more precisely described by assigning to it a number of 
+attributes and then specifying values for those attributes. A 
+particular real world entity is encoded by specifying the appropriate 
+feature object class, attributes and attribute values. For example, a 
+red lateral buoy would be encoded as follows:- feature object class: 
+buoy lateral; attribute: colour; attribute value: red.
+
+The data model defines four types of feature object:
+
+Geo containing the descriptive characteristics of a real world 
+ entity.
+
+Meta containing information about other objects (eg. 
+ compilation scale, vertical datum).
+
+Collection containing information about the relationships 
+ between other objects. 
+
+Cartographic containing information about the cartographic 
+ representation of a real world entity.
+
+Chapter 1 contains a description of each feature object class. This 
+includes a definition of the class and a list of the attributes that 
+are allowed for that class. Instructions on how to interpret the 
+information associated with each feature object class are given in the 
+introduction to Chapter 1.
+
+The Object Catalogue does not mandate the use of any attributes. 
+However, for each instance of a feature object, a particular attribute 
+may only be used once. In general terms it is up to the encoder to 
+select from the appropriate list the attributes that are relevant to a 
+particular object instance. However, for some applications, certain 
+attributes may be designated as mandatory for specific object classes. 
+These attributes will be listed in the appropriate product 
+specification (see S-57 Appendix B).
+
+A description of each attribute is contained in Chapter 2. This 
+includes a definition of the attribute and, where appropriate, a list 
+of allowable values, also with definitions. Instructions on how to 
+interpret the information associated with each attribute are given in 
+the introduction to Chapter 2.
+
+For the purposes of backward compatibility, changes from edition 2.0 
+of S-57 have been emphasized in the table of contents by striking out 
+object classes or attributes that have been deleted and by marking 
+those that have been added in the margin.  In addition, pages in the 
+Object Catalogue relating to deleted object classes or attributes have 
+been retained with the remark "DELETED - DO NOT USE".  Where a deleted 
+object class or attribute has been replaced by another one, this is 
+specified at the bottom of the page in bold characters.
+ Page intentionally left blank
+
+
+
+ S-57 Appendix A
+ Chapter 1 - Object Classes
+
+ Edition 3.0
+ Page intentionally left blank
+ CONTENTS
+Acronym Code
+
+1.1 Introduction 1.1
+
+1.2 Geo Object Classes 1.2
+Administration Area (Named)             ADMARE 1 1.3
+Airport/airfield                        AIRARE 2 1.4
+Anchor                                  ACHPNT 1.5
+Anchor berth                            ACHBRT 3 1.6
+Anchorage area                          ACHARE 4 1.7
+Beacon, cardinal                        BCNCAR 5 1.8
+Beacon, isolated danger                 BCNISD 6 1.9
+Beacon, lateral                         BCNLAT 7 1.10
+Beacon, safe water                      BCNSAW 8 1.11
+Beacon, special purpose/general         BCNSPP 9 1.12
+Berth                                   BERTHS 10 1.13
+Berthing facility                       BRTFAC 1.14
+Bridge                                  BRIDGE 11 1.15
+Building, religious                     BUIREL 1.16
+Building, single                        BUISGL 12 1.17
+Built-up area                           BUAARE 13 1.18
+Buoy, cardinal                          BOYCAR 14 1.19
+Buoy, installation                      BOYINB 15 1.20
+Buoy, isolated danger                   BOYISD 16 1.21
+Buoy, lateral                           BOYLAT 17 1.22
+Buoy, safe water                        BOYSAW 18 1.23
+Buoy, special purpose/general           BOYSPP 19 1.24
+Cable area                              CBLARE 20 1.25
+Cable, overhead                         CBLOHD 21 1.26
+Cable, submarine                        CBLSUB 22 1.27
+Cairn                                   CAIRNS 1.28
+Canal                                   CANALS 23 1.29
+Canal bank                              CANBNK 24 1.30
+Cargo transhipment area                 CTSARE 25 1.31
+Causeway                                CAUSWY 26 1.32
+Caution area                            CTNARE 27 1.33
+Cemetery                                CEMTRY 1.34
+Chain/Wire                              CHNWIR 1.35
+Checkpoint                              CHKPNT 28 1.36
+Chimney                                 CHIMNY 1.37
+Coastguard station                      CGUSTA 29 1.38
+Coastline                               COALNE 30 1.39
+Contiguous zone                         CONZNE 31 1.40
+Continental shelf area                  COSARE 32 1.41
+Control point                           CTRPNT 33 1.42
+Conveyor                                CONVYR 34 1.43
+Crane                                   CRANES 35 1.44
+Current - non-gravitational             CURENT 36 1.45
+Custom zone                             CUSZNE 37 1.46
+Dam                                     DAMCON 38 1.47
+Daymark                                 DAYMAR 39 1.48
+Deep water route centerline             DWRTCL 40 1.49
+Deep water route part                   DWRTPT 41 1.50
+Depth area                              DEPARE 42 1.51
+Depth contour                           DEPCNT 43 1.52
+Diffuser                                DIFFUS 1.53
+Dish aerial                             DSHAER 1.54
+Distance mark                           DISMAR 44 1.55
+Dock area                               DOCARE 45 1.56
+Dredged area                            DRGARE 46 1.57
+Dry dock                                DRYDOC 47 1.58
+Dumping ground                          DMPGRD 48 1.59
+Dune                                    DUNARE 1.60
+Dyke                                    DYKCON 49 1.61
+Dyke area                               DYKARE 1.62
+Dyke crown                              DYKCRW 1.63
+Exclusive economic zone                 EXEZNE 50 1.64
+Fairway                                 FAIRWY 51 1.65
+Fence/wall                              FNCLNE 52 1.66
+Ferry route                             FERYRT 53 1.67
+Fish haven                              FSHHAV 1.68
+Fishery zone                            FSHZNE 54 1.69
+Fishing facility                        FSHFAC 55 1.70
+Fishing ground                          FSHGRD 56 1.71
+Flagstaff/Flagpole                      FLGSTF 1.72
+Flare stack                             FLASTK 1.73
+Floating dock                           FLODOC 57 1.74
+Fog signal                              FOGSIG 58 1.75
+Fortified structure                     FORSTC 59 1.76
+Free port area                          FRPARE 60 1.77
+Gate                                    GATCON 61 1.78
+Gridiron                                GRIDRN 62 1.79
+Harbour area (administrative)           HRBARE 63 1.80
+Harbour facility                        HRBFAC 64 1.81
+Hill                                    HILARE 1.82
+Hulk                                    HULKES 65 1.83
+Ice area                                ICEARE 66 1.84
+Incineration area                       ICNARE 67 1.85
+Inshore traffic zone                    ISTZNE 68 1.86
+Intertidal area                         ITDARE 1.87
+Lake                                    LAKARE 69 1.88
+Lake shore                              LAKSHR 70 1.89
+Land area                               LNDARE 71 1.90
+Land elevation                          LNDELV 72 1.91
+Land region                             LNDRGN 73 1.92
+Landing place                           LNDPLC 1.93
+Landing stairs                          LNDSTS 1.94
+Landmark                                LNDMRK 74 1.95
+Light                                   LIGHTS 75 1.96
+Light float                             LITFLT 76 1.97
+Light, moiré effect                     LITMOI 1.98
+Light vessel                            LITVES 77 1.99
+Local magnetic anomaly                  LOCMAG 78 1.100
+Lock basin                              LOKBSN 79 1.101
+Log pond                                LOGPON 80 1.102
+Magnetic variation                      MAGVAR 81 1.103
+Marine farm/culture                     MARCUL 82 1.104
+Mast                                    MSTCON 1.105
+Military practice area                  MIPARE 83 1.106
+Monument                                MONUMT 1.107
+Mooring/Warping facility                MORFAC 84 1.108
+National territorial area               NATARE 1.109
+Navigation line                         NAVLNE 85 1.110
+Obstruction                             OBSTRN 86 1.111
+Offshore platform                       OFSPLF 87 1.112
+Offshore production area                OSPARE 88 1.113
+Oil barrier                             OILBAR 89 1.114
+Pile                                    PILPNT 90 1.115
+Pilot boarding place                    PILBOP 91 1.116
+Pingo                                   PINGOS 1.117
+Pipeline area                           PIPARE 92 1.118
+Pipeline, overhead                      PIPOHD 93 1.119
+Pipeline, submarine/on land             PIPSOL 94 1.120
+Pontoon                                 PONTON 95 1.121
+Precautionary area                      PRCARE 96 1.122
+Production installation                 PRDINS 1.123
+Production/storage area                 PRDARE 97 1.124
+Pylon/bridge support                    PYLONS 98 1.125
+Radar dome                              RADDOM 1.126
+Radar line                              RADLNE 99 1.127
+Radar range                             RADRNG 100 1.128
+Radar reflector                         RADRFL 101 1.129
+Radar station                           RADSTA 102 1.130
+Radar transponder beacon                RTPBCN 103 1.131
+Radio calling-in point                  RDOCAL 104 1.132
+Radio station                           RDOSTA 105 1.133
+Railway                                 RAILWY 106 1.134
+Ramp                                    RMPARE 1.135
+Rapids                                  RAPIDS 107 1.136
+Recommended route centerline            RCRTCL 108 1.137
+Recommended track                       RECTRC 109 1.138
+Recommended traffic lane part           RCTLPT 110 1.139
+Rescue station                          RSCSTA 111 1.140
+Restricted area                         RESARE 112 1.141
+Retro-reflector                         RETRFL 113 1.142
+River                                   RIVERS 114 1.143
+River bank                              RIVBNK 115 1.144
+Road                                    ROADWY 116 1.145
+Road crossing                           RODCRS 1.146
+Road part                               ROADPT 1.147
+Runway                                  RUNWAY 117 1.148
+Salt pan                                SLTPAN 1.149
+Sand waves                              SNDWAV 118 1.150
+Sea area/named water area               SEAARE 119 1.151
+Sea-plane landing area                  SPLARE 120 1.152
+Seabed area                             SBDARE 121 1.153
+Shoreline construction                  SLCONS 122 1.154
+Signal station, traffic                 SISTAT 123 1.155
+Signal station, warning                 SISTAW 124 1.156
+Silo                                    SILBUI 1.157
+Silo/tank                               SILTNK 125 1.158
+Slipway                                 SLIPWY 1.159
+Slope topline                           SLOTOP 126 1.160
+Sloping ground                          SLOGRD 127 1.161
+Small craft facility                    SMCFAC 128 1.162
+Sounding                                SOUNDG 129 1.163
+Spoil ground                            SPOGRD 1.164
+Spring                                  SPRING 130 1.165
+Square                                  SQUARE 131 1.166
+Straight territorial sea baseline       STSLNE 132  1.167
+Submarine transit lane                  SUBTLN 133 1.168
+Swept Area                              SWPARE 134 1.169
+Tank                                    TNKCON 1.170
+Telepheric                              TELPHC 1.171
+Territorial sea area                    TESARE 135 1.172
+Tidal stream - flood/ebb                TS_FEB 160 1.173
+Tidal stream - harmonic prediction      TS_PRH 136  1.174
+Tidal stream - non-harmonic prediction  TS_PNH 137  1.175
+Tidal stream panel data                 TS_PAD 138 1.176
+Tidal stream - time series              TS_TIS 139 1.177
+Tide - harmonic prediction              T_HMON 140 1.178
+Tide - non-harmonic prediction          T_NHMN 141  1.179
+Tide - time series                      T_TIMS 142 1.180
+Tideway                                 TIDEWY 143 1.181
+Topmark                                 TOPMAR 144 1.182
+Tower                                   TOWERS 1.183
+Traffic separation line                 TSELNE 145 1.184
+Traffic separation scheme boundary      TSSBND 146  1.185
+Traffic separation scheme crossing      TSSCRS 147  1.186
+Traffic separation scheme lane part     TSSLPT 148  1.187
+Traffic separation scheme roundabout    TSSRON 149  1.188
+Traffic separation zone                 TSEZNE 150 1.189
+Tree                                    TREPNT 1.190
+Tunnel                                  TUNNEL 151 1.191
+Tunnel entrance                         TNLENT 1.192
+Two-way route part                      TWRTPT 152 1.193
+Underwater/awash rock                   UWTROC 153 1.194
+Unsurveyed area                         UNSARE 154 1.195
+Vegetation                              VEGATN 155 1.196
+Vegetation area                         VEGARE 1.197
+Water turbulence                        WATTUR 156 1.198
+Waterfall                               WATFAL 157 1.199
+Weed/Kelp                               WEDKLP 158 1.200
+Weir                                    WIRLNE 1.201
+Windmill                                WNDMIL 1.202
+Windmotor                               WIMCON 1.203
+Wreck                                   WRECKS 159 1.204
+Zero metre - contour                    ZEMCNT 1.205
+
+1.3 Meta Object Classes 1.207
+Accuracy of data                        M_ACCY 300 1.208
+Compilation scale of data               M_CSCL 301 1.209
+Coverage                                M_COVR 302 1.210
+Horizontal datum of data                M_HDAT 303 1.211
+Horizontal datum shift parameters       M_HOPA 304  1.212
+Nautical publication information        M_NPUB 305      1.213
+Navigational system of marks            M_NSYS 306 1.214
+Production information                  M_PROD 307 1.215
+Quality of data                         M_QUAL 308 1.216
+Sounding datum                          M_SDAT 309 1.217
+Survey reliability                      M_SREL 310 1.218
+Survey source                           M_SSOR 1.219
+Units of measurement of data            M_UNIT 311      1.220
+Vertical datum of data                  M_VDAT 312 1.221
+
+1.4 Collection Object Classes 1.223
+Aggregation                             C_AGGR 400 1.224
+Association                             C_ASSO 401 1.225
+Stacked on/stacked under                C_STAC 402 1.226
+
+1.5 Cartographic Object Classes 1.227
+Cartographic area                       $AREAS 500 1.228
+Cartographic line                       $LINES 501 1.229
+Cartographic symbol                     $CSYMB 502 1.230
+Closing line                            $CLOLN 1.231
+Compass                                 $COMPS 503 1.232
+Shallow water blue                      $SHABL 1.233
+Text                                    $TEXTS 504 1.234
+
+
+
+
+
+
+ Page intentionally left blank
+1.1 Introduction
+
+
+Each object class is specified in a standardized way, under the 
+following headings:
+
+· Object Class:  object class name
+
+· Acronym:  six_character code for the object class
+
+· Code: integer code to be used in the coding of data
+
+· For each object class the set of relevant attributes is 
+ defined. This set is divided into three subsets:
+
+* subset `Attribute_A': Attributes in this subset define the 
+ individual characteristics of an object;
+
+* subset `Attribute_B': Attributes in this subset provide 
+ information relevant to the use of the data, e.g. for 
+ presentation or for an information system;
+
+* subset `Attribute_C': Attributes in this subset provide 
+ administrative information about the object and the data 
+ describing it;
+
+Each subset shows a list of ASCII attribute acronyms. For the 
+description of each attribute see Chapter 2.
+
+· Definition: Where possible each object class is 
+ defined and the source of the definition is quoted.
+
+· References:
+
+* INT 1: reference to the number of the paper chart feature in 
+ the `International Chart Series INT 1 _ Symbols, 
+ Abbreviations, Terms used on Charts'. INT 1 was one of the 
+ major guidelines for the definition of object classes.
+
+* M-4: reference to the paragraph number in the `Chart 
+ Specifications  of   the   IHO',  publication M-4. This was 
+ another guideline used in the definition and description of 
+ object classes.
+
+· Remarks: Under `Remarks' further comments and notes 
+ are given. Related but separate object classes are listed 
+ under the heading `Distinction'.
+
+ 1.2 Geo Object Classes
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Administration Area (Named)
+
+Acronym: ADMARE   Code: 1
+Administration Area (Named) ADMARE 1
+
+ Set Attribute_A: JRSDTN; NATION; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A defined (and possibly named) administrative area.
+
+References:
+
+INT 1: not specified;
+
+M-4: not specified;
+
+Remarks:
+
+Distinction : land region; contiguous zone; continental shelf 
+ area; exclusive economic zone; fishery zone; territorial sea 
+ area;
+
+ 
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Airport/airfield     
+
+Acronym: AIRARE   Code: 2
+Airport/airfield      AIRARE 2
+
+Set Attribute_A: CATAIR; CONDTN; CONVIS; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area containing at least one runway, used for landing, take_off, 
+and movement of aircraft. 
+
+References:
+
+INT 1: ID 17;
+
+M-4:  366;
+
+Remarks:
+
+Distinction : runway; sea-plane landing area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Anchor          
+
+Acronym: ACHPNT 
+Anchor           ACHPNT
+INT 1 Reference: IQ 42;
+
+Chart Specification:  431.6;
+
+
+Set Attribute_A: DATEND; DATSTA; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; QUASOU; STATUS; TECSOU; VALSOU; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:  Point;
+
+
+Definition:
+-----------
+
+A heavy forging or casting comprising a shank with large shackle or 
+ring at one end and two arms with palms at the other, so shaped as to 
+grip the sea bottom, and by means of a cable or rope hold a vessel, 
+boat, or any other floating structure in a desired position regardless 
+of wind and current. (International Maritime Dictionary, 2nd Ed.)
+
+
+Remarks:
+
+Distinction: chain/wire;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. An anchor should be encoded as an obstruction (OBSTRN) 
+with a category of obstruction (CATOBS) value 9.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class:     Anchor berth         
+
+Acronym: ACHBRT   Code: 3
+Anchor berth          ACHBRT 3
+
+Set Attribute_A: CATACH; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; RADIUS; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC; 
+
+Set Attribute_C:     RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A designated area of water where a single vessel, sea plane, etc... 
+may anchor.
+
+Reference:
+
+INT 1:  IN 11.1_2;
+
+M-4:  431.2;
+
+Remarks:
+
+      In general the anchor berth is defined by the centre 
+ point and a swinging circle.
+
+Distinction : anchorage area; berth; mooring/warping facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class:     Anchorage area      
+
+Acronym: ACHARE   Code: 4
+Anchorage area       ACHARE 4
+
+Set Attribute_A: CATACH; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area in which vessels anchor or may anchor. (IHO Dictionary, S-32, 
+5th Edition, 130)
+
+References:
+
+INT 1: IN 12.1-9;
+
+M-4:  431.3;
+
+Remarks:
+
+Distinction:  anchor berth; mooring/warping facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Beacon, cardinal        
+
+Acronym: BCNCAR   Code: 5
+Beacon, cardinal         BCNCAR 5
+
+Set Attribute_A: BCNSHP; CATCAM; COLOUR; COLPAT; CONDTN; 
+ CONVIS; CONRAD; DATEND; DATSTA; ELEVAT; HEIGHT; MARSYS; 
+ NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; STATUS; VERACC; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A beacon is a prominent, specially constructed object forming a 
+conspicuous mark as a fixed aid to navigation or for use in 
+hydrographic survey (IHO Dictionary, S-32, 5th Edition, 420). 
+
+A cardinal beacon is used in conjunction with the compass to indicate 
+where the mariner may find the best navigable water. It is placed in 
+one of the four quadrants (North, East, South and West), bounded by 
+inter-cardinal bearings from the point marked. (UKHO NP 735, 5th 
+Edition)
+
+References:
+
+INT 1: IQ 130.3;
+
+M-4:  461;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: daymark; beacon lateral; beacon safe water; beacon 
+ isolated danger; beacon special purpose/general;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Beacon, isolated danger    
+
+Acronym: BCNISD   Code: 6
+Beacon, isolated danger     BCNISD 6
+
+Set Attribute_A: BCNSHP; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; ELEVAT; HEIGHT; MARSYS; NATCON; 
+ NOBJNM; OBJNAM; PEREND; PERSTA; STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A beacon is a prominent specially constructed object forming a 
+conspicuous mark as a fixed aid to navigation or for use in 
+hydrographic survey (IHO Dictionary, S-32, 5th Edition, 420). 
+
+An isolated danger beacon is a beacon erected on an isolated danger of 
+limited extent, which has navigable water all around it. (UKHO NP735, 
+5th Edition)
+
+References:
+
+INT 1:  IQ 130.4;
+
+M-4:  463.1;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: daymark; beacon lateral; beacon safe water; beacon 
+ cardinal; beacon special purpose/general;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Beacon, lateral        
+
+Acronym: BCNLAT   Code: 7
+Beacon, lateral         BCNLAT 7
+
+Set Attribute_A: BCNSHP; CATLAM; COLOUR; COLPAT; CONDTN; 
+ CONRAD; CONVIS; DATEND; DATSTA; ELEVAT; HEIGHT; MARSYS; 
+ NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; STATUS; VERACC; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A beacon is a prominent specially constructed object forming a 
+conspicuous mark as a fixed aid to navigation or for use in 
+hydrographic survey (IHO Dictionary, S-32, 5th Edition, 420). 
+
+A lateral beacon is used to indicate the port or starboard hand side 
+of the route to be followed. They are generally used for well defined 
+channels and are used in conjunction with a conventional direction of 
+buoyage. (UKHO NP 735, 5th Edition)
+
+References:
+
+INT 1:  IQ 91-92, 130.1;
+
+M-4:  not specified;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: daymark; beacon cardinal; beacon safe water; 
+ beacon isolated danger; beacon special purpose/general;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Beacon, safe water     
+
+Acronym: BCNSAW  Code: 8
+Beacon, safe water      BCNSAW 8
+
+Set Attribute_A: BCNSHP; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; ELEVAT; HEIGHT; MARSYS; NATCON; 
+ NOBJNM; OBJNAM; PEREND; PERSTA; STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A safe water beacon is a prominent specially constructed object 
+forming a conspicuous mark as a fixed aid to navigation or for use in 
+hydrographic survey (IHO Dictionary, S-32, 5th Edition, 420). 
+
+A safe water beacon may be used to indicate that there is navigable 
+water around the mark. (UKHO NP735, 5th Edition)
+
+References:
+
+INT 1:  IQ 130.5;
+
+M-4:  456.4;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: daymark; beacon cardinal; beacon lateral; beacon 
+ isolated danger; beacon special purpose/general;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Beacon, special purpose/general  
+
+Acronym: BCNSPP  Code: 9
+Beacon, special purpose/general   BCNSPP 9
+
+Set Attribute_A: BCNSHP; CATSPM; COLOUR; COLPAT; CONDTN; 
+ CONRAD; CONVIS; DATEND; DATSTA; ELEVAT; HEIGHT; MARSYS; 
+ NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; STATUS; VERACC; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A beacon is a prominent specially constructed object forming a 
+conspicuous mark as a fixed aid to navigation or for use in 
+hydrographic survey (IHO Dictionary, S-32, 5th Edition, 420). 
+
+A special purpose beacon is primarily used to indicate an area or 
+feature, the nature of which is apparent from reference to a chart, 
+Sailing Directions or Notices to Mariners. (UKHO NP 735, 5th Edition)
+
+Beacon in general: A beacon whose appearance or purpose is not 
+adequately known.
+
+References:
+
+INT 1:  IQ 130.6;
+
+M-4:  456.4;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: daymark; beacon lateral; beacon safe water; beacon 
+ isolated danger; beacon cardinal; distance mark;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Berth        
+
+Acronym: BERTHS  Code: 10
+Berth         BERTHS 10
+
+Set Attribute_A: DATEND; DATSTA; DRVAL1; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; QUASOU; SOUACC; STATUS; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A named or numbered place where a vessel is moored at a wharf. (IHO 
+Dictionary, S-32, 5th Edition, 470)
+
+References:
+
+INT 1:  IF 19;
+
+M-4:  321.1;
+
+Remarks:
+
+Distinction:  anchor berth; dock area; mooring/warping 
+ facility; shoreline construction;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Berthing facility      
+
+Acronym: BRTFAC
+Berthing facility       BRTFAC
+INT 1 Reference:      IF 13;
+
+Chart Specification:  321.1;
+
+
+Set Attribute_A: CONDTN; DATEND; DATSTA; NATCON; NOBJNM; 
+ OBJNAM; STATUS; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Line; Area;
+
+
+Definition:
+-----------
+
+The designated
+- length along the limit of land area or along a shoreline construction
+_ area at a dolphin
+     where a ship may be tied on and may safely lie.
+
+
+Remarks:
+
+Distinction:  dock area;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A berthing facility should be encoded as a berth 
+(BERTHS).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Bridge       
+
+Acronym: BRIDGE  Code: 11
+Bridge        BRIDGE 11
+
+Set Attribute_A: CATBRG; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; HORACC; HORCLR; NATCON; NOBJNM; 
+ OBJNAM;; VERACC; VERCCL; VERCLR; VERCOP; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A structure erected over a depression or an obstacle such as a body of 
+water, railroad, etc... to provide a roadway for vehicles, pedestrians 
+or to carry utility services. (IHO Dictionary, S-32, 5th Edition, 544)
+
+References:
+
+INT 1:  ID 20, 21, 22, 23.1-6, 24;
+
+M-4:  381.1-3;
+
+ Remarks:
+
+A bridge may consist of portions which cover the land and the water.
+
+The bridge supports are encoded as pylon/bridge supports (PYLONS).
+
+Distinction: pylon/bridge support;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Building, religious    
+
+Acronym: BUIREL
+Building, religious     BUIREL
+INT 1:  IE 10.1-10.4, 13-18;
+
+M-4:  373.1-5;
+
+
+Set Attribute_A: BUISHP; CATREB; COLOUR; CONDTN; CONRAD; 
+ CONVIS; HEIGHT; NATCON; NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+A structure designed for religious use.
+
+
+Remarks:
+
+Distinction:  building, single;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A religious building should be encoded as a single 
+building (BUISGL) with an appropriate function (FUNCTN) value.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Building, single       
+
+Acronym: BUISGL  Code: 12
+Building, single        BUISGL 12
+
+Set Attribute_A: BUISHP; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; ELEVAT; FUNCTN; HEIGHT; NATCON; NOBJNM; OBJNAM; 
+ STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A relatively permanent structure, roofed and usually walled. It is 
+designed for some particular use which it may be important to 
+indicate. (Digital Geographic Information Working Group, Oct.87)
+
+References:
+
+INT 1: ID 5-6, 13; IE 10.1, 10.3, 11, 13-18, 28-30.1; IF 51, 
+ 60-63;
+
+M-4:  325.1-3; 328.1; 362.2; 370.3,5; 372.1; 373.1-4; 375.1,2; 
+ 487.3;
+
+Remarks:
+
+This object class is used to encode single buildings, including those 
+with a particular function or service of major interest.
+
+Distinction:  built-up area; coastguard station; landmark; 
+ rescue station;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Built_up area     
+
+Acronym: BUAARE  Code: 13
+Built_up area      BUAARE 13
+
+Set Attribute_A: CATBUA; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NOBJNM; OBJNAM;  VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area containing a concentration of buildings and the supporting 
+road or rail infrastructure.
+
+References:
+
+INT 1:  ID 1-4;
+
+M-4:  370.3-4; 370.6-7;
+
+ Remarks:
+
+Distinction: building, single; road; square;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Buoy, cardinal         
+
+Acronym: BOYCAR  Code: 14
+Buoy, cardinal          BOYCAR 14
+
+Set Attribute_A: BOYSHP; CATCAM; COLOUR; COLPAT; CONRAD; 
+ DATEND; DATSTA; MARSYS; NATCON; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A buoy is a floating object moored to the bottom in a particular 
+place, as an aid to navigation or for other specific purposes. (IHO 
+Dictionary S-32 5th Edition, 565).
+
+A cardinal buoy is used in conjunction with the compass to indicate 
+where the mariner may find the best navigable water. It is placed in 
+one of the four quadrants (North, East, South and West), bounded by 
+inter-cardinal bearings from the point marked. (UKHO NP 735, 5th 
+Edition)
+
+References:
+
+INT 1:  IQ 130.3;
+
+M-4:  461; 462.5, 462.6;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction:  buoy lateral; buoy safe water; buoy isolated 
+ danger;  buoy special purpose/general; mooring/warping 
+ facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Buoy, installation     
+
+Acronym: BOYINB  Code: 15
+Buoy, installation      BOYINB 15
+
+Set Attribute_A: BOYSHP; CATINB; COLOUR; COLPAT; CONRAD; 
+ DATEND; DATSTA; MARSYS; NATCON; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; PRODCT;  STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A buoy is a floating object moored to the bottom in a particular 
+place, as an aid to navigation or for other specific purposes. (IHO 
+Dictionary, S-32, 5th Edition, 565).
+
+An installation buoy is a buoy used for loading tankers with gas or 
+oil. (IHO Chart Specifications, M-4)
+
+References:
+
+INT 1:  IL 16
+
+M-4:  445.4;
+
+ Remarks:
+
+Distinction: buoy special purpose/general; mooring/warping 
+ facility; offshore platform;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Buoy, isolated danger       
+
+Acronym: BOYISD  Code: 16
+Buoy, isolated danger        BOYISD 16
+
+Set Attribute_A: BOYSHP; COLOUR; COLPAT; CONRAD; DATEND; 
+ DATSTA; MARSYS; NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; 
+ STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A buoy is a floating object moored to the bottom in a particular 
+place, as an aid to navigation or for other specific purposes. (IHO 
+Dictionary, S-32, 5th Edition, 565).
+
+A isolated danger buoy is a buoy moored on or above an isolated danger 
+of limited extent, which has navigable water all around it. (UKHO 
+NP735, 5th Edition)
+
+References:
+
+INT 1:  IQ 130.4;
+
+M-4:  461;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: buoy lateral; buoy safe water; buoy cardinal; buoy 
+ special purpose/general; mooring/warping facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Buoy, lateral     
+
+Acronym: BOYLAT  Code: 17
+Buoy, lateral      BOYLAT 17
+
+Set Attribute_A: BOYSHP; CATLAM; COLOUR; COLPAT; CONRAD; 
+ DATEND; DATSTA; MARSYS; NATCON; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A buoy is a floating object moored to the bottom in a particular 
+place, as an aid to navigation or for other specific purposes. (IHO 
+Dictionary, S-32, 5th Edition, 565).
+
+A lateral buoy is used to indicate the port or starboard hand side of 
+the route to be followed. They are generally used for well defined 
+channels and are used in conjunction with a conventional direction of 
+buoyage. (UKHO NP 735, 5th Edition)
+
+References:
+
+INT 1:  IQ 130.1;
+
+M-4:  461;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction: buoy cardinal; buoy safe water; buoy isolated 
+ danger; buoy special purpose/general; mooring/warping facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Buoy, safe water       
+
+Acronym: BOYSAW  Code: 18
+Buoy, safe water        BOYSAW 18
+
+Set Attribute_A: BOYSHP; COLOUR; COLPAT; CONRAD; DATEND; 
+ DATSTA; MARSYS; NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; 
+ STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A buoy is a floating object moored to the bottom in a particular 
+place, as an aid to navigation or for other specific purposes. (IHO 
+Dictionary, S-32, 5th Edition, 565).
+
+A safe water buoy is used to indicate that there is navigable water 
+around the mark. (UKHO NP735, 5th Edition)
+
+References:
+
+INT 1:  IQ 130.5;
+
+M-4:  461;
+
+Remarks:
+
+A safe water mark may be used as a centerline, mid-channel or landfall 
+buoy, or to indicate the best point of passage under a fixed bridge.
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction:  buoy cardinal; buoy lateral; buoy isolated 
+ danger; buoy special purpose/general; mooring/warping facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Buoy, special purpose/general    
+
+Acronym: BOYSPP  Code: 19
+Buoy, special purpose/general     BOYSPP 19
+
+Set Attribute_A: BOYSHP; CATSPM; COLOUR; COLPAT; CONRAD; 
+ DATEND; DATSTA; MARSYS; NATCON; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A buoy is a floating object moored to the bottom in a particular 
+place, as an aid to navigation or for other specific purposes. (IHO 
+Dictionary, S-32, 5th Edition, 565).
+
+A special purpose buoy is primarily used to indicate an area or 
+feature, the nature of which is apparent from reference to a chart, 
+Sailing Directions or Notices to Mariners. (UKHO NP 735, 5th Edition)
+
+Buoy in general: A buoy whose appearance or purpose is not adequately 
+known.
+
+References:
+
+INT 1:  IQ 130.6;
+
+M-4:  461;
+
+Remarks:
+
+Topmark, light, fog signal, radar reflector and retro-reflector are 
+separate objects.
+
+Distinction:  buoy lateral; buoy safe water; buoy isolated 
+ danger; buoy cardinal; buoy installation; mooring/warping 
+ facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Cable area        
+
+Acronym: CBLARE  Code: 20
+Cable area         CBLARE 20
+
+Set Attribute_A: CATCBL; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area which contains one or more submarine cables.
+
+References:
+
+INT 1:  IL 30.2, 31.2
+
+M-4:  439.3; 443.2;
+
+Remarks:
+
+Distinction:  cable, overhead; cable, submarine;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Cable, overhead        
+
+Acronym: CBLOHD  Code: 21
+Cable, overhead         CBLOHD 21
+
+Set Attribute_A: CATCBL; CONDTN; CONRAD; CONVIS; DATEND; 
+ DATSTA; ICEFAC; NOBJNM; OBJNAM; STATUS; VERACC; VERCLR; 
+ VERCSA; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An overhead cable is an assembly of wires or fibres, or a wire rope or 
+chain, which is supported by structures such as poles or pylons and 
+passing over or nearby navigable waters. (Hydrographic Service, Royal 
+Australian Navy).
+
+References:
+
+INT 1:  ID 26, 27
+
+M-4:  382; 382.1-2;
+
+Remarks:
+
+The cable supports are encoded as pylon/bridge supports (PYLONS).
+
+Distinction:  cable area; cable, submarine; conveyor; 
+ pylon/bridge support;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Cable, submarine       
+
+Acronym: CBLSUB  Code: 22
+Cable, submarine        CBLSUB 22
+
+Set Attribute_A: BURDEP; CATCBL; CONDTN; DATEND; DATSTA; 
+ DRVAL1; DRVAL2; NOBJNM; OBJNAM; STATUS; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An assembly of  wires or fibres, or a wire rope or chain which has 
+been laid underwater or buried beneath the seabed (Hydrographic 
+Service, Royal Australian Navy)
+
+
+References:
+
+INT 1:  IL 30.1, 31.1, 32
+
+M-4:  443.1; 443.3; 443.7;
+
+Remarks:
+
+Distinction:  cable, overhead; cable area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Cairn        
+
+Acronym: CAIRNS
+Cairn         CAIRNS
+INT 1:  IQ 100;
+
+M-4:  456.2;
+
+
+Set Attribute_A: COLOUR; COLPAT; CONDTN; CONRAD; CONVIS; 
+ DATEND; DATSTA; HEIGHT; NOBJNM; OBJNAM; QUAVEM; STATUS; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+A mound of stones, usually conical or pyramidal, raised as a landmark 
+or to designate a point of importance in surveying. (IHO Dictionary, 
+S-32, 4th Edition)
+
+Remarks:
+
+If a cairn bears the colour(s) specified by a navigational mark 
+system, it is to be encoded as a beacon.
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A cairn should be encoded as a landmark (LNDMRK) with a 
+category of landmark (CATLMK) value 1.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Canal        
+
+Acronym: CANALS  Code: 23
+Canal         CANALS 23
+
+Set Attribute_A: CATCAN; CONDTN; DATEND; DATSTA; HORACC; 
+ HORCLR; HORWID; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An artificial waterway with no flow, or a controlled flow, used for 
+navigation, or for draining or irrigating land (ditch). (United States 
+Geological Survey, Jan.89)
+
+References:
+
+INT 1:  IF 40;
+
+M-4:  361.6;
+
+Remarks:
+
+The object `canal' describes the area of the canal, the object `canal 
+bank' the banks.
+
+Distinction:  canal bank; river; lake; tideway;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Canal bank       
+
+Acronym: CANBNK  Code: 24
+Canal bank        CANBNK 24
+
+Set Attribute_A: CONDTN; DATEND; DATSTA; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The limit line between the water area of a canal and the area of land.
+
+References:
+
+INT 1:   IF 40;
+
+M-4:  361.6;
+
+Remarks:
+
+Distinction:  canal; coastline; lake shore; river bank; 
+ shoreline construction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Cargo transhipment area     
+
+Acronym: CTSARE  Code: 25
+Cargo transhipment area      CTSARE 25
+
+Set Attribute_A:      DATEND; DATSTA; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area designated for the transfer of cargo from one vessel to 
+another. (adapted from IHO Dictionary, S-32, 5th Edition, 5593).
+
+References:
+
+INT 1:  IN 64;
+
+M-4:  449.4;
+
+ Remarks:
+
+The transhipment of cargo is often known as `lightering' and the area 
+may be known as `lightering area' or `cargo transfer area'. (IHO Chart 
+Specifications, M-4)
+
+Distinction:  dock area; harbour area (administrative); harbour 
+ facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Causeway          
+
+Acronym: CAUSWY  Code: 26
+Causeway           CAUSWY 26
+
+Set Attribute_A: CONDTN; NATCON; NOBJNM; OBJNAM; STATUS; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A raised way across low or wet ground or water. (IHO Dictionary, S-32, 
+5th Edition, 662)
+
+References:
+
+INT 1:  IF 3;
+
+M-4:  313.3;
+
+ Remarks:
+
+Distinction:  dam; road;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Caution area      
+
+Acronym: CTNARE  Code: 27
+Caution area       CTNARE 27
+
+Set Attribute_A: DATEND; DATSTA; PEREND; PERSTA;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Generally, an area where the mariner has to be made aware of 
+circumstances influencing the safety of navigation.
+
+References:
+
+INT 1:  IM 29.2;
+
+M-4:  not specified;
+
+Remarks:
+
+This object class may be required to identify:
+
+_ a danger
+_ a risk
+_ a rule
+_ advice
+
+which is not directly related to a specific object.
+
+Distinction:  wrecks; underwater rocks; obstructions; 
+ unsurveyed area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Cemetery          
+
+Acronym: CEMTRY
+Cemetery           CEMTRY
+
+Set Attribute_A: CONDTN; CONVIS; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of land for burying the dead with two or more graves. (United 
+States Geological Survey, Jan.89)
+
+References:
+
+INT 1: IE 19;
+
+M-4: 373.6;
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A cemetery should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 2.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Chain/Wire        
+
+Acronym: CHNWIR
+Chain/Wire         CHNWIR
+INT 1:  IQ 42;
+
+M-4:  431.6;
+
+
+Set Attribute_A: DATEND; DATSTA; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Line;
+
+
+Definition:
+-----------
+
+A connection between two independent objects, e.g.
+       - between an anchor and a mooring buoy
+   _ between an anchor and an offshore platform
+   _ between a hulk and a bollard on land
+   _ etc.
+
+ Remarks:
+
+Distinction:  anchor;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A chain/wire should be encoded either as a cable 
+(CBLSUB) with a category of cable (CATCBL) value 6, or as a 
+mooring/warping facility (MORFAC) with a category of mooring/warping 
+facility (CATMOR) value 6.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Checkpoint        
+
+Acronym: CHKPNT  Code: 28
+Checkpoint         CHKPNT 28
+
+Set Attribute_A:      CATCHP; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An official place to register, declare or check goods and people.
+
+References:
+
+INT 1: not specified;
+
+M-4: not specified;
+
+Remarks:
+
+The object `checkpoint' does not include facilities such as buildings, 
+gates or other installations.
+
+Distinction: custom zone;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Chimney      
+
+Acronym: CHIMNY
+Chimney       CHIMNY
+INT 1:  IE 22;
+
+M-4:  374.1;
+
+
+Set Attribute_A: COLOUR; COLPAT; CONDTN; CONRAD; CONVIS; 
+ HEIGHT; NATCON; NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+A chimney is a vertical structure containing a passage or flue for 
+discharging smoke and gases of combustion. (Digital Geographic 
+Information Working Group, Oct.87)
+
+Remarks:
+
+Where a chimney carries a light, the light should be encoded as a 
+separate object.
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A chimney should be encoded as a landmark (LNDMRK) with 
+a category of landmark (CATLMK) value 3.
+
+ DELETED - DO NOT USE 
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Coastguard station     
+
+Acronym: CGUSTA  Code: 29
+Coastguard station      CGUSTA 29
+
+Set Attribute_A:      DATEND; DATSTA; NOBJNM; OBJNAM; PEREND; 
+ PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Watch keeping stations at which a watch is kept either continuously, 
+or at certain times only. (IHO Chart Specifications, M-4)
+
+References:
+
+INT 1:  IT 10;
+
+M-4:  492;
+
+Remarks:
+
+This object class is used to describe the function of the coastguard 
+rather than the building in which the coastguard is sited.
+
+Distinction:  building, single; rescue station;
+  GEO OBJECT CLASSES
+
+
+
+Object Class: Coastline         
+
+Acronym: COALNE  Code: 30
+Coastline          COALNE 30
+
+Set Attribute_A: CATCOA; COLOUR; CONRAD; CONVIS; ELEVAT; 
+ NOBJNM; OBJNAM;  VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The line where shore and water meet. Although the terminology of 
+coasts and shores is rather confused, shoreline and coastline are 
+generally used as synonyms. (IHO Dictionary, S-32, 5th Edition, 
+858,4695)
+
+References:
+
+INT 1:  IC 1-8, 32-33;
+
+M-4:  310; 312.1-4;
+
+Remarks:
+
+Distinction:  canal bank; lake shore; river bank; shoreline 
+ construction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Contiguous zone        
+
+Acronym: CONZNE  Code: 31
+Contiguous zone         CONZNE 31
+
+Set Attribute_A: DATEND; DATSTA; NATION; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A zone contiguous to a coastal State's territorial sea, which may not 
+extend beyond 24 nautical miles from the baselines from which the 
+breadth of the territorial sea is measured. The coastal state may 
+exercise certain control in this zone subject to the provisions of 
+International Law. (IHO Dictionary, S-32, 5th Edition, 993)
+
+References:
+
+INT 1:  IN 44;
+
+M-4:  440.6;
+
+Remarks:
+
+Distinction: administrative area; continental area; exclusive 
+ economic zone; fishing zone; territorial sea area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Continental shelf area      
+
+Acronym: COSARE  Code: 32
+Continental shelf area       COSARE 32
+
+Set Attribute_A:      NATION; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The continental shelf of a coastal State comprises the sea bed and 
+subsoil of the submarine areas that extend beyond its territorial sea 
+throughout the natural prolongation of its land territory to the outer 
+edge of the continental margin, or to a distance of 200 nautical miles 
+from the baselines from which the breadth of the territorial sea is 
+measured where the outer edge of the continental margin does not 
+extend out to that distance. (IHO Publication S-51)
+
+References:
+
+INT 1:  IN 46;
+
+M-4:  440.8;
+
+Remarks:
+
+Distinction: administrative area; contiguous zone; exclusive 
+ economic zone; fishery zone; territorial sea area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Control point     
+
+Acronym: CTRPNT  Code: 33
+Control point      CTRPNT 33
+
+Set Attribute_A: CATCTR; DATEND; DATSTA; ELEVAT; NOBJNM; 
+ OBJNAM; VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A point on the ground where position (horizontal and vertical) is used 
+as a base for a dependent survey. Also referred to as a control 
+station. (IHO Dictionary, S-32, 5th Edition, 1026)
+
+References:
+
+INT 1:  IB 20-24;
+
+M-4:  304.1-3; 305.1; 306;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Conveyor          
+
+Acronym: CONVYR  Code: 34
+Conveyor           CONVYR 34
+
+Set Attribute_A: CATCON; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; HEIGHT; LIFCAP; NOBJNM; OBJNAM; 
+ PRODCT; STATUS; VERACC; VERCLR; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A mechanical apparatus for moving bulk material or people from place 
+to place (as by a moving belt or chain of receptacles).
+
+References:
+
+INT 1:  ID25;
+
+M-4:  382.3;
+
+Remarks:
+
+The conveyor supports are encoded as pylon/bridge supports (PYLONS).
+
+Distinction: cable, overhead; pylon/bridge support;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Crane        
+
+Acronym: CRANES  Code: 35
+Crane         CRANES 35
+
+Set Attribute_A: CATCRN; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; HEIGHT; LIFCAP; NOBJNM; OBJNAM; ORIENT; RADIUS; 
+ STATUS; VERACC; VERCLR; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A machine for lifting, shifting and lowering objects or materials by 
+means of a swinging boom or with a lifting apparatus supported on an 
+overhead track. (Digital Geographic Information Working Group, Oct.87)
+
+References:
+
+INT 1:  IF 53.1-3;
+
+M-4:  328.3;
+
+Remarks:
+
+The position of a sheerlegs or a travelling crane is defined as its 
+resting position.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Current - non-gravitational
+
+Acronym: CURENT  Code: 36
+Current - non-gravitational CURENT 
+
+Set Attribute_A: CURVEL; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ ORIENT; PEREND; PERSTA;
+
+Set Attribute_B:      INFORM; NINFOM; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Currents (non-gravitational) include either singly or in combination: 
+ocean currents (wind and/or density driven), inter-oceanic equalising 
+currents, currents of navigable rivers, river outflow effects offshore 
+and other non-tidal flows.
+
+References:
+
+INT 1:  IH 42-43;
+
+M-4:  408.2-3;
+
+Remarks:
+
+Distinction: tidal stream - harmonic prediction; tidal stream - 
+ non-harmonic prediction; tidal stream panel data; tidal stream 
+ - time series;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Custom zone       
+
+Acronym: CUSZNE  Code: 37
+Custom zone        CUSZNE 37
+
+Set Attribute_A:      NATION;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The area within which national custom regulations are in force.
+
+References:
+
+INT 1:  IN 48;
+
+M-4:  440.2;
+
+Remarks:
+
+Distinction: check point; free port area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Dam          
+
+Acronym: DAMCON  Code: 38
+Dam           DAMCON 38
+
+Set Attribute_A:  CATDAM; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; HEIGHT; NATCON; NOBJNM; OBJNAM; 
+ VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A barrier to check or confine anything in motion; particularly one 
+constructed to hold back water and raise its level to form a 
+reservoir, or to prevent flooding. (IHO Dictionary, S-32, 5th Edition, 
+1196)
+
+References:
+
+INT 1:   IF 44
+
+M-4:  364.2;
+
+Remarks:
+
+Distinction: causeway; dyke; road;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Daymark      
+
+Acronym: DAYMAR  Code: 39
+Daymark       DAYMAR 39
+
+Set Attribute_A:  CATSPM; COLOUR; COLPAT; DATEND; DATSTA; 
+ ELEVAT; HEIGHT; NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; 
+ STATUS; TOPSHP; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The identifying characteristics of an aid to navigation which serve to 
+facilitate its recognition against a daylight viewing background. On 
+those structures that do not by themselves present an adequate viewing 
+area to be seen at the required distance, the aid is made more visible 
+by affixing a daymark to the structure. A daymark so affixed has a 
+distinctive colour and shape depending on the purpose of the aid. (IHO 
+Dictionary, S-32, 5th Edition, 1248)
+
+References:
+
+INT 1:  IQ 101;
+
+M-4:  456.2;
+
+Remarks:
+
+Distinction:  beacon, lateral; beacon, safe water; beacon, 
+ isolated danger; beacon, cardinal; beacon special 
+ purpose/general; topmark;
+  GEO OBJECT CLASSES
+
+
+
+Object Class: Deep water route centerline      
+
+Acronym: DWRTCL  Code: 40
+Deep water route centerline       DWRTCL 40
+
+Set Attribute_A: CATTRK; DATEND; DATSTA; DRVAL1; DRVAL2; 
+ NOBJNM; OBJNAM; ORIENT; QUASOU; SOUACC; STATUS; TECSOU; 
+ TRAFIC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A deep water route is a route in a designated area, within defined 
+limits, which has been accurately surveyed for clearance of sea bottom 
+and submerged obstacles to a minimum indicated depth of water. (IHO 
+Dictionary, S-32, 5th Edition, 1280)
+
+The deep water route centerline indicates the centerline of a route, 
+the width of which is not explicitly defined.
+
+References:
+
+INT 1:  IM 27.3;
+
+M-4:  435.3;
+
+Remarks:
+
+Additional information can be found in IHO Technical Resolution A1.17.
+
+Distinction:  deep water route part;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Deep water route part       
+
+Acronym: DWRTPT  Code: 41
+Deep water route part        DWRTPT 41
+
+Set Attribute_A: DATEND; DATSTA; DRVAL1; DRVAL2; NOBJNM; 
+ OBJNAM; ORIENT; QUASOU; RESTRN; SOUACC; STATUS; TECSOU; 
+ TRAFIC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A deep water route is a route in a designated area, within defined 
+limits, which has been accurately surveyed for clearance of sea bottom 
+and submerged obstacles to a minimum indicated depth of water. (IHO 
+Dictionary, S-32, 5th Edition, 1280)
+
+References: 
+
+INT 1:   IM 27.1_2;
+
+M-4:  435, 435.3; 436.3;
+
+Remarks:
+
+The complete deep water route consists of one or more parts depending 
+on the shape of the deep water route.
+
+The orientation of the route part is defined by the middle line of the 
+part relating to the general direction of the deep water route.
+
+Additional information can be found in IHO Technical Resolution A1.17.
+
+Distinction: deep water route centerline; two way route part;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Depth area        
+
+Acronym: DEPARE  Code: 42
+Depth area         DEPARE 42
+
+Set Attribute_A: DRVAL1; DRVAL2; QUASOU; SOUACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A depth area is a water area whose depth is within a defined range of 
+values.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Intertidal areas are encoded as depth areas. These do not have to 
+include soundings. 
+
+The depth range within a depth area is defined by the attributes 
+`DRVAL1' and `DRVAL2'.
+
+Distinction:  depth contour; dredged area; sounding; 
+ obstruction;  sea area/named water area; unsurveyed area; 
+ wreck;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Depth contour     
+
+Acronym: DEPCNT  Code: 43
+Depth contour      DEPCNT 43
+
+Set Attribute_A:      VALDCO; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A line connecting points of equal water depth which is sometimes 
+significantly displaced outside of soundings, symbols and other chart 
+detail for clarity as well as generalization. Depth contours, 
+therefore, often represent an approximate location of the line of 
+equal depth as related to the surveyed line delineated on the source. 
+Also referred to as depth curve. (IHO Dictionary, S-32, 5th Edition, 
+1314, 1315)
+
+References:
+
+INT 1:  II 15, 30, 31;
+
+M-4:  404.2; 410; 411, 411.2; 413-413.2;
+
+Remarks:
+
+Drying contours are encoded with negative values.
+
+Distinction:  sounding; depth area; coastline;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Diffuser          
+
+Acronym: DIFFUS
+Diffuser           DIFFUS
+INT 1:  IL 43;
+
+M-4:  not specified
+
+
+Set Attribute_A: CONDTN; DATEND; DATSTA; EXPSOU; PRODCT; 
+ QUASOU; SOUACC; TECSOU; VALSOU; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+An artificial installation at or below water level, where liquids 
+(e.g. cooling water, spillage) are spread out.
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A diffuser should be encoded as an obstruction (OBSTRN) 
+with a category of obstruction (CATOBS) value 3.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Dish aerial       
+
+Acronym: DSHAER
+Dish aerial        DSHAER
+INT 1:  IE 31;
+
+M-4:  375.4;
+
+
+Set Attribute_A: COLOUR; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+A parabolic aerial for the receipt and transmission of high frequency 
+radio signals. (IHO Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A dish aerial should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 4.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Distance mark     
+
+Acronym: DISMAR  Code: 44
+Distance mark      DISMAR 44
+
+Set Attribute_A:      CATDIS; DATEND; DATSTA; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A distance mark indicates the distance measured from an origin and 
+consists of either a solid visible structure or a distinct location 
+without special installation. Usually found on canals.
+
+References:
+
+INT 1:  IF 40;
+
+M-4:  361.3; 307;
+
+Remarks:
+
+Distinction: beacon, special purpose;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Dock area         
+
+Acronym: DOCARE  Code: 45
+Dock area          DOCARE 45
+
+Set Attribute_A:      CATDOC; CONDTN; DATEND; DATSTA; HORACC; 
+ HORCLR; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A dock is an artificially enclosed area within which ships may moor 
+and which may have gates to regulate water level (adapted from IHO 
+Chart Specifications, M-4).
+
+Reference:
+
+INT 1:    IF 27,28;
+
+M-4:  326.3-4;
+
+Remarks:
+
+Distinction:  harbour area (administrative); cargo transhipment 
+ area; berth; harbour facility; gate; floating dock; dry dock;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Dredged area      
+
+Acronym: DRGARE  Code: 46
+Dredged area       DRGARE 46
+
+Set Attribute_A: DRVAL1; DRVAL2; NOBJNM; OBJNAM; QUASOU; 
+ RESTRN; SOUACC; TECSOU; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of the bottom of a body of water which has been deepened by 
+dredging. (IHO Dictionary, S-32, 5th Edition, 1462)
+
+References:
+
+INT 1:  II 20-23;
+
+M-4:  414.1-2; 414.4;
+
+Remarks:
+
+Distinction:  depth area; dumping ground; swept area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Dry dock          
+
+Acronym: DRYDOC  Code: 47
+Dry dock           DRYDOC 47
+
+Set Attribute_A:      CONDTN; DRVAL1; HORACC; HORCLR; HORLEN; 
+ HORWID; NOBJNM; OBJNAM; QUASOU; SOUACC; STATUS; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An artificial basin fitted with a gate or caisson, into which vessels 
+can be floated and the water pumped out to expose the vessel's 
+bottom.  Also called graving dock. (IHO Dictionary, S-32, 5th Edition, 
+1426)
+
+Remarks:
+
+INT 1:  IF 25;
+
+M-4:  326.1;
+
+Distinction:  floating dock; gate; dock area; shoreline 
+ construction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Dumping ground         
+
+Acronym: DMPGRD  Code: 48
+Dumping ground          DMPGRD 48
+
+Set Attribute_A:      CATDPG; NOBJNM; OBJNAM; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A sea area where dredged material or other potentially more harmful 
+material, e.g. explosives, chemical waste, is deliberately deposited. 
+(Derived from IHO Chart Specifications, M-4).
+
+References:
+
+INT 1:  IN 23-24, 62.1-2;
+
+M-4:  442.1-4; 446a;
+
+Remarks:
+
+ Distinction:  dredged area; incineration area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Dune         
+
+Acronym: DUNARE 
+Dune          DUNARE
+
+Set Attribute_A: COLOUR; CONRAD; CONVIS; HEIGHT; NATSUR; 
+ NATQUA; NOBJNM; OBJNAM; QUAVEM; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A mound, ridge or hill of drifted material on the sea coast or in a 
+desert. (IHO Dictionary, S-32, 4th Edition)
+
+References:
+
+INT 1: IC 8;
+
+M-4:  312.3;
+
+Remarks:
+
+Distinction:  sand waves;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A dune should be encoded as sloping ground (SLOGRD) 
+with a category of slope (CATSLO) value 3.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Dyke         
+
+Acronym: DYKCON  Code: 49
+Dyke          DYKCON 49
+
+Set Attribute_A:      CONDTN; CONRAD; DATEND; DATSTA; HEIGHT; 
+ NATCON; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A dyke (or dike) is an artificial embankment to contain or hold back 
+water.(IHO Dictionary, S-32, 5th Edition, 1361)
+
+References:
+
+INT 1:  IF 1;
+
+M-4:  313.1;
+
+ Remarks:
+
+Distinction:  dam; sloping ground; slope top line;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Dyke area
+
+Acronym: DYKARE
+Dyke area DYKARE
+INT 1 Reference: IF 1;
+
+Chart Specification: 313.1;
+
+Set Attribute_A: CATDYK; CONDTN; NATCON;
+
+Set Attribute_B: INFORM; NINFOM; SCAMAX; SCAMIN;
+
+Set Attribute_C: RECDAT; RECIND; SORDAT; SORIND;
+
+Geometric Primitive: Area;
+
+Definition:
+-----------
+
+A dyke (or dike) is an artificial embankment to contain or hold back 
+water. (IHO Dictionary, S-32, 4th Edition)
+
+The dyke area is the base of the dyke.
+
+Remarks:
+
+A system of winter and summer dykes may form an area of polder or koog.
+
+It is necessary to record the dyke base as an area for a complete 
+two-dimensional description of reality.
+
+Distinction: dam; dyke crown;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A dyke area should be encoded as a dyke (DYKCON).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Dyke crown        
+
+Acronym: DYKCRW
+Dyke crown         DYKCRW
+INT 1:      IF 1;
+
+M-4:  313.1;
+
+
+Set Attribute_A:      CATDYK; CONDTN; CONRAD; NATCON; NOBJNM; 
+ OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:  Line;
+
+
+Definition:
+-----------
+
+A dyke (or dike) is an artificial embankment to contain or hold back 
+water.
+(IHO Dictionary, S-32, 4th Edition)
+
+The dyke crown is the top line of the dyke.
+
+ Remarks:
+
+A system of winter and summer dykes may form an area of polder or koog.
+
+The dyke crown records the third dimension of the dyke. Only the crown 
+of the dyke construction is relevant to Radar.
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A dyke crown should be encoded as a slope top line 
+(SLOTOP).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Exclusive economic zone     
+
+Acronym: EXEZNE  Code: 50
+Exclusive economic zone      EXEZNE 50
+
+Set Attribute_A:      NATION;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area, not exceeding 200 nautical miles from the baselines from 
+which the breadth of the territorial sea is measured, subject to a 
+specific legal regime established in the United Nations Convention on 
+the Law of the Sea under which the coastal state has certain rights 
+and jurisdiction. (IHO Dictionary, S-32, 5th Edition, 1723)
+
+References:
+
+INT 1:  IN 47;
+
+M-4:  440.9;
+
+Remarks:
+
+Distinction:  administrative area; contiguous zone; continental 
+ shelf area; fishery zone; territorial sea area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fairway      
+
+Acronym: FAIRWY  Code: 51
+Fairway       FAIRWY 51
+
+Set Attribute_A: DATEND; DATSTA; DRVAL1; NOBJNM; OBJNAM; 
+ ORIENT; QUASOU; RESTRN; SOUACC; STATUS; TRAFIC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+That part of a river, harbour and so on, where the main navigable 
+channel for vessels of larger size lies. It is also the usual course 
+followed by vessels entering or leaving harbours, called `ship 
+channel'. (International Maritime Dictionary, 2nd Ed.)
+
+References:
+
+INT 1: not specified;
+
+M-4: not specified;
+
+Remarks:
+
+Distinction:  deep water route centerline; deep water route 
+ part; traffic separation scheme lane part;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fence/wall
+
+Acronym: FNCLNE Code: 52
+Fenceline        FNCLNE 52
+
+
+Set Attribute_A: CATFNC; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; ELEVAT; HEIGHT; NATCON; NOBJNM; OBJNAM; STATUS; 
+ VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+Definition:
+-----------
+
+A natural or man_made barrier used as an enclosure or boundary or for 
+protection. (adapted from Didital Geographic Information Working 
+Group, Oct.1987)
+
+References:
+
+INT 1:  not specified
+
+M-4:  not specified
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Ferry route       
+
+Acronym: FERYRT  Code: 53
+Ferry route        FERYRT 53
+
+Set Attribute_A: CATFRY; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A route in a body of water where a ferry crosses from one shoreline to 
+another. (Digital Geographic Information Working Group, Oct.87)
+
+References:
+
+INT 1:  IM 50, 51;
+
+M-4:  438.1, 438.2;
+
+Remarks:
+
+No remarks
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Fish haven        
+
+Acronym: FSHHAV 
+Fish haven         FSHHAV
+
+Set Attribute_A: EXPSOU; NOBJNM; OBJNAM; QUASOU; TECSOU; 
+ VALSOU; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Areas established by private interests, usually sport fishermen, to 
+simulate natural reefs and wrecks that attract fish. The reefs are 
+constructed by dumping assorted junk in areas which may be of very 
+small extent or may stretch considerable distance along a depth 
+contour. Also called fishery reefs. (IHO Dictionary, S-32, 5th 
+Edition, 1812).
+
+References:
+
+INT 1:   IK 46.1-2;
+
+M-4:  447.5;
+
+
+Remarks:
+
+Distinction:  fishing facility; marine farm/culture;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A fish haven should be encoded as an obstruction 
+(OBSTRN) with a category of obstruction (CATOBS) value 5.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fishery zone      
+
+Acronym: FSHZNE  Code: 54
+Fishery zone       FSHZNE 54
+
+Set Attribute_A:      NATION; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The offshore zone in which exclusive fishing rights and management are 
+held by the coastal nation. (IHO Dictionary, S-32, 5th Edition, 1816)
+
+References:
+
+INT 1:  IN 45;
+
+M-4:  440.7;
+
+Remarks:
+
+The fishery zone commonly coincides with other zones such as:
+   _ Continental Shelf
+   _ Exclusive Economic Zone.
+
+Distinction:  administrative area; contiguous zone; continental 
+ shelf area; exclusive economic zone; fishing ground; 
+ restricted area; territorial sea area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fishing facility       
+
+Acronym: FSHFAC  Code: 55
+Fishing facility        FSHFAC 55
+
+Set Attribute_A:      CATFIF; NOBJNM; OBJNAM; PEREND; PERSTA; 
+ STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A structure in shallow water for fishing purposes which can be an 
+obstruction to ships in general. The position of these structures may 
+vary frequently over time.
+
+References: 
+
+INT 1:  IK 44.1-2, 45;
+
+M-4:  447.1-3;
+
+Remarks:
+
+Distinction:  marine farm/culture; obstruction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fishing ground         
+
+Acronym: FSHGRD  Code: 56
+Fishing ground          FSHGRD 56
+
+Set Attribute_A:      NOBJNM; OBJNAM; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A water area in which fishing is frequently carried on. (IHO 
+Dictionary, S-32, 5th Edition, 1814)
+
+References: 
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction:  fishery zone;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Flagstaff/Flagpole     
+
+Acronym: FLGSTF
+Flagstaff/Flagpole      FLGSTF
+INT 1:  IE 27;
+
+M-4:  374.7;
+
+
+Set Attribute_A: COLOUR; CONRAD; CONVIS; HEIGHT; NOBJNM; OBJNAM;
+QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+A pole on which a flag is hoisted and displayed. (International 
+Maritime Dictionary, 2nd Ed.)
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A flagstaff should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 5.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Flare stack       
+
+Acronym: FLASTK
+Flare stack        FLASTK
+INT 1:  IE 23; IL 11;
+
+M-4:  374,1; 445,6;
+
+
+Set Attribute_A: COLOUR; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NATCON; NOBJNM; OBJNAM; QUAVEM; STATUS; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+A tall structure used for burning-off waste oil or gas. (IHO 
+Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+A Flare stack is generally located at refineries or at other 
+production installations and it is normally showing a flame.
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A flare stack should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 6.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Floating dock     
+
+Acronym: FLODOC  Code: 57
+Floating dock      FLODOC 57
+
+Set Attribute_A: COLOUR; COLPAT; CONDTN; CONRAD; CONVIS; 
+ DATEND; DATSTA; DRVAL1; HORACC; HORCLR; HORLEN; HORWID; 
+ LIFCAP; NOBJNM; OBJNAM; STATUS; VERACC; VERDAT; VERLEN; 
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A form of dry dock consisting of a floating structure of one or more 
+sections which can be partly submerged by controlled flooding to 
+receive a vessel, then raised by pumping out the water so that the 
+vessel's bottom can be exposed. (IHO Dictionary, S-32, 5th Edition, 
+1427)
+
+References:
+
+INT 1:  IF 26
+
+M-4:  326.2;
+
+Remarks:
+
+Distinction:  dry dock; dock area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fog signal        
+
+Acronym: FOGSIG  Code: 58
+Fog signal         FOGSIG 58
+
+Set Attribute_A: CATFOG; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ SIGFRQ; SIGGEN; SIGGRP; SIGPER; SIGSEQ; STATUS; VALMXR;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A  warning signal transmitted by a vessel, or aid to navigation, 
+during periods of low visibility. Also, the device producing such a 
+signal. (IHO Dictionary, S-32, 5th Edition, 1890)
+
+References:
+
+INT 1:  IR 1, 10-16, 20-22;
+
+M-4:  452-452.8;
+
+Remarks:
+
+Distinction: signal station, warning;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Fortified structure    
+
+Acronym: FORSTC  Code: 59
+Fortified structure     FORSTC 59
+
+Set Attribute_A: CATFOR; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NATCON; NOBJNM; OBJNAM; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A structure for the military defence of a site. 
+
+References:
+
+INT 1:  IE 34.1-3;
+
+M-4:  379.1-2;
+
+Remarks:
+
+A fortified structure is often disused, decayed or used for 
+non_defence purpose. Such structures range from major castles and 
+forts to minor lookout posts. (IHO Chart Specifications, M-4)
+
+Distinction: building single;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Free port area         
+
+Acronym: FRPARE  Code: 60
+Free port area          FRPARE 60
+
+Set Attribute_A:      NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A port where certain import and export duties are waived (unless goods 
+pass into the country) to facilitate reshipment to other countries. 
+(IHO Dictionary, S-32, 5th Edition, 1927)
+
+References:
+
+INT 1:   not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction:  custom zone; production/storage area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Gate         
+
+Acronym: GATCON  Code: 61
+Gate          GATCON 61
+
+Set Attribute_A: CATGAT; CONDTN; DRVAL1; HORACC; HORCLR; 
+ NATCON; NOBJNM; OBJNAM; QUASOU; SOUACC; STATUS; VERACC; 
+ VERCLR; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A structure that may be swung, drawn, or lowered to block an entrance 
+or passageway. (United States Geological Survey, Jan.89)
+
+References:
+
+INT 1:   IF 27, 41.1-2, 42-43;
+
+M-4:  326.3; 326.5; 326.6; 326.7;
+
+Remarks:
+
+This object class is used to encode gates that control the flow of 
+water.
+
+Distinction:  dry dock; floating dock;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Gridiron          
+
+Acronym: GRIDRN  Code: 62
+Gridiron           GRIDRN 62
+
+Set Attribute_A:  HORACC; HORLEN; HORWID; NATCON; NOBJNM; 
+ OBJNAM; STATUS; VERACC; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A timber structure in the intertidal zone serving as a support for 
+vessels at low stages of the tide to permit work on the exposed 
+portion of  the  vessel's  hull. Also  called  careening grid. 
+(adapted from IHO Dictionary, S-32, 5th Edition, 649)
+
+References:
+
+INT 1:  IF 24;
+
+M-4:  326.8;
+
+Remarks:
+
+No remarks
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Harbour area (administrative)    
+
+Acronym: HRBARE  Code: 63
+Harbour area (administrative)     HRBARE 63
+
+Set Attribute_A:      NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The area over which a harbour authority has jurisdiction.
+
+References:
+
+INT 1:  IN 49;
+
+M-4:  430.1;
+
+Remarks:
+
+Distinction:  dock area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Harbour facility       
+
+Acronym: HRBFAC  Code: 64
+Harbour facility        HRBFAC 64
+
+Set Attribute_A:      CATHAF; CONDTN; DATEND; DATSTA; NATCON; 
+ NOBJNM; OBJNAM; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A harbour installation with a service or commercial operation of 
+public interest.
+
+References:
+
+INT 1:  IF 10, 50; IU 1.1,
+
+M-4:  320.1-2; 321.5; 328.2;
+
+Remarks:
+
+Distinction:  small craft facility;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Hill         
+
+Acronym: HILARE
+Hill          HILARE
+INT 1:  IC 4;
+
+M-4:  312.1;
+
+
+Set Attribute_A: CONRAD; CONVIS; HEIGHT; NATQUA; NATSUR; 
+ NOBJNM; OBJNAM; QUAVEM; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Area;
+
+
+Definition:
+-----------
+
+A small isolated elevation, smaller than a mountain. (IHO Dictionary, 
+S-32, 4th Edition)
+
+Remarks:
+
+Distinction:  dune;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A hill should be encoded as sloping ground (SLOGRD) 
+with a category of slope (CATSLO) value 4.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Hulk         
+
+Acronym: HULKES  Code: 65
+Hulk          HULKES 65
+
+Set Attribute_A:      CATHLK; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; HORACC; HORLEN; HORWID; NOBJNM; OBJNAM; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A permanently moored ship.
+
+References:
+
+INT 1:  IF 34;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction: wreck;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Ice area          
+
+Acronym: ICEARE  Code: 66
+Ice area           ICEARE 66
+
+Set Attribute_A: CATICE; CONVIS; ELEVAT; HEIGHT; NOBJNM; 
+ OBJNAM; PEREND; PERSTA; STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of ice over land or water.
+
+References:
+
+INT 1:  IC 25; IN 60.1-2;
+
+M-4:  353.8; 449.1;
+
+Remarks:
+
+Distinction: depth area; land area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Incineration area      
+
+Acronym: ICNARE  Code: 67
+Incineration area       ICNARE 67
+
+Set Attribute_A:      NOBJNM; OBJNAM; PEREND; PERSTA; RESTRN; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An offshore area officially designated as suitable for the burning of 
+chemical waste by specially equipped ships. (IHO Dictionary, S-32, 5th 
+Edition, 2408)
+
+References:
+
+INT 1:  IN 65;
+
+M-4:  449.3;
+
+Remarks:
+
+Distinction:  dumping ground;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Inshore traffic zone  
+
+Acronym: ISTZNE  Code: 68
+Inshore traffic zone  ISTZNE 68
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A routeing measure comprising a designated area between the landward 
+boundary of a traffic separation scheme and the adjacent coast, to be 
+used in accordance with the provisions of the International  
+Regulations for Preventing Collisions at Sea. (IHO Dictionary, S-32, 
+5th Edition, 2457)
+
+References:
+
+INT 1:  IM 25.1;
+
+M-4:  435.1;
+
+Remarks:
+
+Distinction: traffic separation scheme crossing; traffic 
+ separation scheme lane part; traffic separation scheme 
+ roundabout; traffic separation zone; precautionary area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Intertidal area        
+
+Acronym: ITDARE
+Intertidal area         ITDARE
+INT 1: IJ 20 _ 22;
+
+M-4:  426.1 _ 3;
+
+
+Set Attribute_A:      NATQUA; NATSUR; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Line; Area;
+
+
+Definition:
+-----------
+
+An intertidal zone is the zone generally considered to be between mean 
+high water and mean low water levels. (IHO Dictionary, S-32, 4th 
+Edition)
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. Intertidal areas should be coded as depth areas 
+(DEPARE) with negative DRVAL1 and DRVAL2 attributes. The bottom 
+characteristics of intertidal areas should be coded as sea bed areas 
+(SBDARE) using the attribute nature of surface (NATSUR) and qualifying 
+terms of nature of surface (NATQUA).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Lake         
+
+Acronym: LAKARE  Code: 69
+Lake          LAKARE 69
+
+Set Attribute_A:      ELEVAT; NOBJNM; OBJNAM; VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A  large  body  of  water entirely surrounded by land. (IHO 
+Dictionary, S-32, 5th Edition, 2629)
+
+References:
+
+INT 1:  IC 23;
+
+M-4:  353.6;
+
+Remarks:
+
+Distinction:  canal; depth area; lake shore; river;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Lake shore        
+
+Acronym: LAKSHR  Code: 70
+Lake shore         LAKSHR 70
+
+Set Attribute_A:      NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The limit line between the water area of a lake and the area of land.
+
+References:
+
+INT 1:  IC 23;
+
+M-4:  353, 353.6;
+
+Remarks:
+
+Distinction:  canal bank; coastline; lake area; river bank; 
+ shoreline construction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Land area         
+
+Acronym: LNDARE  Code: 71
+Land area          LNDARE 71
+
+ Set Attribute_A: CONDTN; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The solid portion of the Earth's surface, as opposed to sea, water. 
+(IHO Dictionary, S-32, 5th Edition, 2635)
+
+References:
+
+INT 1:  IK 10;
+
+M-4:  421.1;
+
+Remarks:
+
+Distinction:  canal; coastline; depth area; lake; land region; 
+ river; sea bed area; shoreline construction; vegetation;
+ GEO OBJECT CLASSES
+
+
+
+Object Class:  Land elevation        
+
+Acronym: LNDELV  Code: 72
+Land elevation         LNDELV 72
+
+Set Attribute_A:      CONVIS; ELEVAT; NOBJNM; OBJNAM; VERACC; 
+ VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An elevation is the vertical distance of a point or a level, on, or 
+affixed to, the surface of the earth, measured from a specified 
+vertical datum. (IHO Dictionary, S-32, 5th Edition, 1590)
+
+References:
+
+INT 1:  IC 10-13;
+
+M-4:  351; 352.1_2;
+
+Remarks:
+
+This object class is used to encode both spot heights and land 
+(height) contours.
+
+Distinction: slope top line;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Land region       
+
+Acronym: LNDRGN  Code: 73
+Land region        LNDRGN 73
+
+Set Attribute_A:      CATLND; NATQUA; NATSUR; NOBJNM; OBJNAM; 
+ WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of natural scenery on land. It is defined by its geographical 
+characteristics and may be known by its proper name.
+
+References:
+
+INT 1:  IC 24, 26, 33;
+
+M-4:  312.1-4; 355;
+
+Remarks:
+
+Distinction:  sea area; land area; vegetation;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Landing place     
+
+Acronym: LNDPLC 
+Landing place      LNDPLC
+
+Set Attribute_A:      CONDTN; NATCON; NOBJNM; OBJNAM; STATUS; 
+ WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A landing is a place where boats receive or discharge passengers, 
+freight, etc.. (IHO Dictionary, S-32, 4th Edition)
+
+References:
+
+INT 1:  IF 17;
+
+M-4:  324.2;
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A landing place should be encoded as a small craft 
+facility (SMCFAC) with a category of small craft facility (CATSCF) 
+value 28.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Landing stairs         
+
+Acronym: LNDSTS 
+Landing stairs          LNDSTS
+
+Set Attribute_A:      CONDTN; NATCON; QUAVEM; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Steps at the shoreline as the connection between land and water on 
+different levels.
+
+References:
+
+INT 1:  IF 18
+
+M-4:  not specified
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. Landing stairs should be encoded as a shoreline 
+construction (SLCONS) with a category of shoreline construction 
+(CATSLC) value 11.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Landmark          
+ 
+Acronym: LNDMRK  Code: 74
+Landmark           LNDMRK 74
+
+Set Attribute_A: CATLMK; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; ELEVAT; FUNCTN; HEIGHT; NATCON; NOBJNM; OBJNAM; 
+ STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A prominent object at a fixed location which can be used in 
+determining a location or a direction. (adapted from IHO Dictionary, 
+S-32, 5th Edition, 2643).
+
+References:
+
+INT 1: ID 5-6, 13; IE 10.1-20, 22-30.1, 30.3-4, 31; IL 11; IQ 
+ 100;
+
+M-4: 373.6; 374.1; 374.4; 374.5; 374.6; 374.7; 375.1-2; 375.4; 
+ 445.6; 456.2; 487.3;
+
+Remarks:
+
+Distinction:  beacon, special purpose/general; building single; 
+ daymark; pylon/bridge support; topmark;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Light        
+
+Acronym: LIGHTS  Code: 75
+Light         LIGHTS 75
+
+Set Attribute_A: CATLIT; COLOUR; DATEND; DATSTA; EXCLIT; 
+ HEIGHT; LITCHR; LITVIS; MARSYS; MLTYLT; NOBJNM; OBJNAM; 
+ ORIENT; PEREND; PERSTA;  SECTR1; SECTR2; SIGGRP; SIGPER; 
+ SIGSEQ; STATUS; VERACC; VALNMR; VERDAT;
+
+Set Attribute_B: INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; TXTDSC;
+
+Set Attribute_C: RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A luminous or lighted aid to navigation. (adapted from IHO Dictionary, 
+S-32, 5th Edition, 2766)
+
+References:
+
+INT 1:  IP 1-30.3, 40-65;
+
+M-4: 470-473.5; 475-475.7; 476-478,5;
+
+Remarks:
+
+A light may be fixed on a buoy, beacon, tower etc. These are separate 
+objects.
+
+Distinction:  beacon, cardinal; beacon, isolated danger; 
+ beacon, lateral; beacon, safe water; beacon special 
+ purpose/general; buoy, cardinal; buoy, installation; buoy, 
+ isolated danger; buoy, lateral; buoy, safe water; buoy, 
+ special purpose/general; light vessel; light float;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Light float       
+
+Acronym: LITFLT  Code: 76
+Light float        LITFLT 76
+
+Set Attribute_A: COLOUR; COLPAT; CONRAD; CONVIS; DATEND; 
+ DATSTA; HORACC; HORLEN; HORWID; NATCON; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A boat-like structure used instead of a light buoy in waters where 
+strong streams or currents are experienced, or when a greater 
+elevation than that of a light buoy is necessary (IHO Dictionary, 
+S-32, 5th Edition, 2821).
+
+References:
+
+INT 1:  IQ 30-31;
+
+M-4:  462.8;
+
+Remarks:
+
+The light of a light float is a separate object, handled as with 
+buoys, beacons, etc.
+
+Distinction:  buoy, cardinal; buoy, installation; buoy, 
+ isolated danger; buoy, lateral; buoy, safe water; buoy, 
+ special purpose/general; light vessel;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Light, moiré effect    
+
+Acronym: LITMOI
+Light, moiré effect     LITMOI
+INT 1:  IP 31;
+
+M-4:  475.8;
+
+
+Set Attribute_A: COLOUR; DATEND; DATSTA; HEIGHT; NOBJNM; 
+ OBJNAM; ORIENT; PEREND; PERSTA; QUAVEM; STATUS; VALNMR; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+The moiré effect is the effect created by transmitting light through 
+two separate, overlapping families of parallel lines. (IHO Dictionary, 
+S-32, 4th Edition)
+
+Remarks:
+
+The attribute `orientation' indicates the orientation of the leading 
+line of the moiré effect light measured from the water towards the 
+light.
+
+Distinction:  light;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A moiré effect light should be encoded as a light 
+(LIGHTS) with a category of light (CATLIT) value 16.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Light vessel      
+
+Acronym: LITVES  Code: 77
+Light vessel       LITVES 77
+
+Set Attribute_A: COLOUR; COLPAT; CONRAD; CONVIS; DATEND; 
+ DATSTA; HORACC; HORLEN; HORWID; NATCON; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A distinctively marked vessel anchored or moored at a charted point, 
+to serve as an aid to navigation. By night, it displays a 
+characteristic light(s) and is usually equipped with other devices, 
+such as fog signal, submarine sound signal, and radio-beacon, to 
+assist navigation. Also called light ship. (IHO Dictionary, S-32, 5th 
+Edition, 2828,2829)
+
+References:
+
+INT 1:   IP 6;
+
+M-4:  474.1_3; 474.5_6;
+
+Remarks:
+
+The light(s), fog signal etc of a light vessel is a separate object, 
+handled as with buoys, beacons etc.
+
+Distinction:  beacon, cardinal; beacon, isolated danger; 
+ beacon, lateral; beacon, safe water; beacon special 
+ purpose/general; buoy, cardinal; buoy, installation; buoy, 
+ isolated danger; buoy, lateral; buoy, safe water; buoy, 
+ special purpose/general; light float;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Local magnetic anomaly      
+
+Acronym: LOCMAG  Code: 78
+Local magnetic anomaly       LOCMAG 78
+
+Set Attribute_A:      NOBJNM; OBJNAM; VALLMA;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An anomaly of the magnetic field of the Earth, extending over a 
+relatively small area, due to local magnetic influences. (IHO 
+Dictionary, S-32, 5th Edition, 2874, 2984)
+
+References:
+
+INT 1:  IB 82.1_2;
+
+M-4:  274;
+
+Remarks:
+
+The value of the deviation from the normal magnetic variation is 
+stored in the VALLMA attribute.
+
+Distinction: magnetic variation;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Lock basin        
+
+Acronym: LOKBSN  Code: 79
+Lock basin         LOKBSN 79
+
+Set Attribute_A: DATEND; DATSTA; HORACC; HORCLR; HORLEN; 
+ HORWID; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A lock basin is a wet dock in a waterway, permitting a ship to pass 
+from one level to another. (adapted from IHO Dictionary, S-32, 5th 
+Edition, 2881)
+
+References:
+
+INT 1:  IF 41.1;
+
+M-4:  326.6;
+
+Remarks:
+
+The lock gates are encoded as separate gate objects (GATCON).
+
+Distinction:  gate;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Log pond          
+
+Acronym: LOGPON  Code: 80
+Log pond           LOGPON 80
+
+Set Attribute_A:      NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A maritime area enclosed with connected floating timbers used as a 
+staging area for sawn logs.
+
+References:
+
+INT 1:  IN 61;
+
+M-4:  449.2;
+
+Remarks:
+
+Also known as booming ground.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Magnetic variation       
+
+Acronym: MAGVAR  Code: 81
+Magnetic variation        MAGVAR 81
+
+Set Attribute_A: DATEND; DATSTA; RYRMGV; VALACM; VALMAG;
+
+Set Attribute_B:      INFORM; NINFOM; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The angle between the magnetic and geographic (true) north at a 
+location, expressed in degrees east or west from the direction of true 
+north.
+
+References:
+
+INT 1:  IB 64-66, 71;
+
+M-4:  261; 272.1,3;
+
+Remarks:
+
+No remarks.
+
+Distinction: local magnetic anomaly;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Marine farm/culture    
+
+Acronym: MARCUL  Code: 82
+Marine farm/culture     MARCUL 82
+
+Set Attribute_A: CATMFA; DATEND; DATSTA; EXPSOU; NOBJNM; 
+ OBJNAM; PEREND; PERSTA; QUASOU; RESTRN; SOUACC; STATUS; 
+ VALSOU; VERACC; VERDAT; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An assemblage of cages, nets, rafts and floats or posts where fish, 
+including shellfish, are artificially cultivated. Also called fish 
+farm. (IHO Dictionary, S-32, 5th Edition, 1811)
+
+References:
+
+INT 1:  IK 47, 48.1_2;
+
+M-4:  447.4,6;
+
+Remarks:
+
+Distinction:  fishing facility; obstruction;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Mast         
+
+Acronym: MSTCON
+Mast          MSTCON
+INT 1:  IE 28, 30.1;
+
+M-4:  375.1-2;
+
+
+Set Attribute_A: CATMST; COLOUR; COLPAT; CONRAD; CONVIS; 
+ HEIGHT; NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A straight piece of timber or a hollow cylinder of wood or metal set 
+up vertically or nearly so. (International Maritime Dictionary, 2nd 
+Ed.)
+
+Remarks:
+
+The object `mast' is independent of associated equipment e.g. radar 
+station.
+
+A mast could be constructed of any material, including those mentioned 
+above.
+
+Distinction:  pylon;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A mast should be encoded as a landmark (LNDMRK) with 
+category of landmark (CATLMK) value 7.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Military practice area      
+
+Acronym: MIPARE  Code: 83
+Military practice area       MIPARE 83
+
+Set Attribute_A:      CATMPA; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which naval, military or aerial exercises are carried 
+out. Also called an exercise area. (adapted from IHO Dictionary, S-32, 
+5th Edition, 1722)
+
+References:
+
+INT 1:  IN 30-33;
+
+M-4:  441.1_6;
+
+Remarks:
+
+Distinction:  caution area; restricted area; submarine transit 
+ lane;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Monument          
+
+Acronym: MONUMT
+Monument           MONUMT
+INT 1:  IE 24;
+
+M-4:  374.4;
+
+
+Set Attribute_A: BUISHP; CATMNT; COLOUR; CONDTN; CONRAD; 
+ CONVIS; HEIGHT; NATCON; NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+A structure erected or maintained as a memorial to a person or event. 
+(Digital Geographic Information Working Group, Oct.87)
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A monument should be encoded as a landmark (LNDMRK) 
+with category of landmark (CATLMK) value 9.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Mooring/Warping facility    
+
+Acronym: MORFAC  Code: 84
+Mooring/Warping facility     MORFAC 84
+
+Set Attribute_A: BOYSHP; CATMOR; COLOUR; COLPAT; CONDTN; 
+ CONRAD; CONVIS; DATEND; DATSTA; HEIGHT; NATCON; NOBJNM; 
+ OBJNAM; PEREND; PERSTA; STATUS; VERACC; VERDAT; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The equipment or structure used to secure a vessel (adapted from IHO 
+Dictionary, S-32, 5th Edition, 3322)
+
+References:
+
+INT 1:  IF 20,21,22; IG 181; IQ 40-43;
+
+M-4:  327.1-2,3; 431.5-6;
+
+Remarks:
+
+Distinction: buoy, special purpose/general;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: National territorial area   
+
+Acronym: NATARE 
+National territorial area    NATARE
+
+Set Attribute_A:      NATION;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The whole area of a nation defined by authorities. The area is 
+delimited by boundaries established by agreement between adjacent or 
+opposite states.
+
+References:
+
+INT 1:  IN 40, 41;
+
+M-4:  440.1, 440.3;
+
+Remarks:
+
+Distinction: territorial sea area;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A national territorial area should be encoded as an 
+administrative area (ADMARE) with jurisdiction (JRSDTN) value 2.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Navigation line        
+
+Acronym: NAVLNE  Code: 85
+Navigation line         NAVLNE 85
+
+Set Attribute_A: CATNAV; DATEND; DATSTA; ORIENT; PEREND; 
+ PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A navigation line is a straight line extending towards an area of 
+navigational interest and generally generated by two navigational aids 
+or one navigational aid and a bearing. (Service Hydrographique et 
+Océanographique de la Marine, France)
+
+References:
+
+INT 1:  IM 1-3;
+
+M-4:  433-433.5;
+
+Remarks:
+
+The portion of a navigation line that a ship should use for navigation 
+is known as a recommended track.
+
+The extent of the navigation line depends on the visibility of the 
+navigational aid(s).
+
+The attribute `orientation' (ORIENT) specifies the orientation of the 
+navigation line measured from the water towards the navigational 
+aid(s).
+
+The recommended track is that portion of a  `navigation line' that a 
+ship should use for navigation. (see below)
+
+  RECTRC
+<­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­>
+    Navaid    Navaid 
+X--------------X---------­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
+
+NAVLNE
+<­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­>
+
+Distinction:  recommended route; recommended track;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Obstruction       
+
+Acronym: OBSTRN  Code: 86
+Obstruction        OBSTRN 86
+
+Set Attribute_A: CATOBS; CONDTN; EXPSOU; HEIGHT; NATCON; 
+ NATQUA; NATSUR; NOBJNM; OBJNAM; PRODCT; QUASOU; SOUACC; 
+ STATUS; TECSOU; VALSOU; VERACC; VERDAT; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+In marine navigation, anything that hinders or prevents movement, 
+particularly anything that endangers or prevents passage of a vessel. 
+The term is usually used to refer to an isolated danger to 
+navigation... (IHO Dictionary, S-32, 5th Edition, 3503)
+
+References:
+
+INT 1:  IK 1, 31, 40-43, 46.1-2; IL 21, 23; IQ 42;
+
+M-4:  422.8-9; 431.6; 445.1; 447.5;
+
+Remarks:
+
+Distinction:  wreck; fishing facility; marine farm/culture; 
+ depth area; underwater/awash rock; water turbulence;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Offshore platform      
+
+Acronym: OFSPLF  Code: 87
+Offshore platform       OFSPLF 87
+
+Set Attribute_A: CATOFP; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; HEIGHT; NATCON; NOBJNM; OBJNAM; 
+ PRODCT; STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A permanent offshore structure, either fixed or floating, used in the 
+production of oil or natural gas. (IHO Dictionary, S-32, 5th Edition, 
+3895)
+
+References:
+
+INT 1:  IL 2, 10, 11-15, 17;
+
+M-4:  445.2; 445.3; 445.4; 445.6;
+
+Remarks:
+
+Distinction:  buoy, installation; offshore production area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Offshore production area    
+
+Acronym: OSPARE  Code: 88
+Offshore production area     OSPARE 88
+
+Set Attribute_A: CATPRA; CONDTN; CONRAD; CONVIS; DATEND; 
+ DATSTA; HEIGHT; NOBJNM; OBJNAM; PRODCT; RESTRN; STATUS; 
+ VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area at sea within which there are production facilities.
+
+References:
+
+INT 1:  IL 4;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction:  offshore platform; exclusive economic zone;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Oil barrier       
+
+Acronym: OILBAR  Code: 89
+Oil barrier        OILBAR 89
+
+Set Attribute_A:      CATOLB; CONDTN; DATEND; DATSTA; NOBJNM; 
+ OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A construction to dam oil flow on water.
+
+References:
+
+INT 1:  IF 29.1-2;
+
+M-4:  not specified;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pile         
+
+Acronym: PILPNT  Code: 90
+Pile          PILPNT 90
+
+Set Attribute_A: CATPLE; COLOUR; COLPAT; CONDTN; CONVIS; 
+ DATEND; DATSTA; HEIGHT; NOBJNM; OBJNAM; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A long heavy timber or section of steel, wood, concrete, etc.. forced 
+into the earth which may serve as a support, as for a pier, or a free 
+standing pole within a marine environment. (Adapted from IHO 
+Dictionary, S-32, 5th Edition, 3840)
+
+References:
+
+INT 1:  IF 22;
+
+M-4:  327.3;
+
+Remarks:
+
+Distinction:  beacon, cardinal; beacon, isolated danger; 
+ beacon, lateral; beacon, safe water; beacon special 
+ purpose/general; mooring/warping facility;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pilot boarding place        
+
+Acronym: PILBOP  Code: 91
+Pilot boarding place         PILBOP 91
+
+Set Attribute_A: CATPIL; COMCHA; DATEND; DATSTA; NOBJNM; 
+ NPLDST; OBJNAM; PEREND; PERSTA; PILDST; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The meeting place to which the pilot comes out. (IHO Chart 
+Specifications, M-4)
+
+References:
+
+INT 1:  IT 1.1-4;
+
+M-4:  491.1_2;
+
+Remarks:
+
+No remarks
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Pingo        
+
+Acronym: PINGOS
+Pingo         PINGOS
+INT 1:  not specified
+
+M-4:  not specified
+
+
+Set Attribute_A: CONRAD; CONVIS; EXPSOU; HEIGHT; NATQUA; 
+ NATSUR; NOBJNM; OBJNAM; QUASOU; QUAVEM; TECSOU; VALSOU; 
+ VERDAT; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+Geometric Primitive:   Point, Area;
+
+
+Definition:
+-----------
+
+Small conical hills having a large central core of ice formed from the 
+encroachment of permafrost and the resulting hydrostatic pressure. 
+(IHO Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+Distinction:  hill;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A pingo should be encoded as sloping ground (SLOGRD) 
+with a category of slope (CATSLO) value 5.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pipeline area     
+
+Acronym: PIPARE  Code: 92
+Pipeline area      PIPARE 92
+
+Set Attribute_A:      CATPIP; CONDTN; DATEND; DATSTA; NOBJNM; 
+ OBJNAM; PRODCT; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area containing one or more pipelines.
+
+References:
+
+INT 1:  IL 40.2, 41.2;
+
+M-4:  439.3; 444.2;
+
+Remarks:
+
+Distinction:  pipeline, overhead; pipeline, submarine/on land;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pipeline, overhead     
+
+Acronym: PIPOHD  Code: 93
+Pipeline, overhead      PIPOHD 93
+
+Set Attribute_A: CATPIP; CONDTN; CONRAD; CONVIS; DATEND; 
+ DATSTA; NOBJNM; OBJNAM; PRODCT; STATUS; VERACC; VERCLR; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A pipeline is a string of interconnected pipes used for the transport 
+of matter, nowadays mainly oil or gas. (IHO Dictionary, S-32, 5th 
+Edition, 3857)
+
+An overhead pipeline is a pipeline supported by pylons and passing 
+over or nearby navigable waters.
+
+References:
+
+INT 1:  ID 28;
+
+M-4:  383;
+
+Remarks:
+
+Distinction:  pipeline area; pipeline, submarine/on land;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pipeline, submarine/on land      
+
+Acronym: PIPSOL  Code: 94
+Pipeline, submarine/on land       PIPSOL 94
+
+Set Attribute_A: BURDEP; CATPIP; CONDTN; DATEND; DATSTA; 
+ DRVAL1; DRVAL2; NOBJNM; OBJNAM; PRODCT; STATUS; VERACC; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A pipeline is a string of interconnected pipes used for the transport 
+of matter, nowadays mainly oil or gas. (IHO Dictionary, S-32, 5th 
+Edition, 3857)
+
+A submarine or land pipeline is a pipeline lying on or buried under 
+the seabed or the land.
+
+References:
+
+INT 1:   ID 29; IL 40.1, 41.1, 42, 44;
+
+M-4:  377; 444.1; 444.4-5; 444.7;
+
+Remarks:
+
+It must be assumed that the pipes are vulnerable to damage from 
+anchoring or trawling.... They may be a potential danger to 
+navigation. (IHO Chart Specifications, M-4)
+
+Distinction:  pipeline area; pipeline, overhead;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pontoon      
+
+Acronym: PONTON  Code: 95
+Pontoon       PONTON 95
+
+Set Attribute_A: CONDTN; CONRAD; CONVIS; DATEND; DATSTA; 
+ NATCON; NOBJNM; OBJNAM; PEREND; PERSTA; STATUS; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A floating structure, usually rectangular in shape which serves as 
+landing, pier head or bridge support. (IHO Dictionary, S-32, 5th 
+Edition, 3947)
+
+References:
+
+INT 1:  IF 16;
+
+M-4:  326.9;
+
+Remarks:
+
+Distinction:  bridge; mooring/warping facility; shoreline 
+ construction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Precautionary area     
+
+Acronym: PRCARE  Code: 96
+Precautionary area      PRCARE 96
+
+Set Attribute_A:      DATEND; DATSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A routeing measure comprising an area within defined limits where 
+ships must navigate with particular caution and within which the 
+direction of traffic flow may be recommended. (IHO Dictionary, S-32, 
+5th Edition, 3982)
+
+References:
+
+INT 1:  IM 16, 24;
+
+M-4:  435.2;
+
+Remarks:
+
+Distinction:  caution area; inshore traffic zone; restricted 
+ area; all traffic separation scheme elements;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Production installation     
+
+Acronym: PRDINS
+Production installation      PRDINS
+INT 1 Reference:      IL 20, 21.1-3;
+
+Chart Specification:  445; 445.1; 445.5;
+
+
+Set Attribute_A: CATPRI; CONDTN; CONRAD; CONVIS; DATEND; 
+ DATSTA; EXPSOU; HEIGHT; NOBJNM; OBJNAM; PRODCT; QUASOU; 
+ QUAVEM; STATUS; TECSOU; VALSOU; VERDAT; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; PICREP; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+An installation for the exploitation of natural resources.
+
+Remarks:
+
+Distinction:  offshore platform; Exclusive Economic Zone;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A production installation should be encoded as either 
+an obstruction (OBSTRN) with an appropriate category of obstruction 
+(CATOBS) value, or as a production area (PRDARE) with an appropriate 
+category of production area (CATPRA) value.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Production/storage area     
+
+Acronym: PRDARE  Code: 97
+Production/storage area      PRDARE 97
+
+Set Attribute_A: CATPRA; CONDTN; CONRAD; CONVIS; DATEND; 
+ DATSTA; ELEVAT; HEIGHT; NOBJNM; OBJNAM; PRODCT; STATUS; 
+ VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area on land for the exploitation or storage of natural resources.
+
+References:
+
+INT 1:  IE 35.1-2, 36; IF 52
+
+M-4:  367.1-2;
+
+Remarks:
+
+Distinction: free port area; offshore production area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Pylon/bridge support        
+
+Acronym: PYLONS  Code: 98
+Pylon/bridge support         PYLONS 98
+
+Set Attribute_A: CATPYL; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; HEIGHT; NATCON; NOBJNM; OBJNAM; 
+ VERACC; VERDAT; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A vertical construction consisting, for example, of a steel framework 
+or pre-stressed concrete to carry cables, a bridge, etc.
+
+References:
+
+INT 1:  ID 26;
+
+M-4:  382.1;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Radar dome        
+
+Acronym: RADDOM
+Radar dome         RADDOM
+INT 1:  IE 30.4;
+
+M-4:  487.3;
+
+
+Set Attribute_A: COLOUR; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NOBJNM; OBJNAM; QUAVEM; RADIUS; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+A dome shaped structure used to protect the antenna of a radar 
+installation. (IHO Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+Distinction:  radar station;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A radar dome should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 15.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radar line        
+
+Acronym: RADLNE  Code: 99
+Radar line         RADLNE 99
+
+Set Attribute_A:      NOBJNM; OBJNAM; ORIENT; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A track along which ships may be guided by coastal radar stations in 
+the event of bad visibility. Also known as a radar guided track. (IHO 
+Dictionary, S-32, 5th Edition, 4146).
+
+References:
+
+INT 1:  IM 32.1-2;
+
+M-4:  487.2;
+
+Remarks:
+
+Distinction:  radar range; recommended track;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radar range       
+
+Acronym: RADRNG  Code: 100
+Radar range        RADRNG 100
+
+Set Attribute_A:      COMCHA; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Indicates the coverage of a sea area by a radar surveillance station. 
+Inside this area a vessel may request shore-based radar assistance, 
+particularly in poor visibility.
+
+References:
+
+INT 1:   IM 31;
+
+M-4:  487.1;
+
+Remarks:
+
+Many large ports have a radar surveillance system covering their 
+approaches to provide guidance for vessels, particularly in poor 
+visibility...
+
+The maximum range of the system forms an arc or series of overlapping 
+arcs... (IHO Chart Specifications, M-4)
+
+Distinction:  radar line;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radar reflector        
+
+Acronym: RADRFL  Code: 101
+Radar reflector         RADRFL 101
+
+Set Attribute_A:      HEIGHT; STATUS; VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A device capable of, or intended for, reflecting radar signals. (IHO 
+Dictionary, S-32, 5th Edition, 4147)
+
+A radar reflector is usually a `tetrahedron or pentagonal corner 
+reflector (...) to facilitate reflection towards the sender'. 
+(International Maritime Dictionary, 2nd Ed.)
+
+References:
+
+INT 1:  IS 4;
+
+M-4:  465.1_2;
+
+Remarks:
+
+The object `radar reflector' is only used to encode a device 
+specifically intended  to reflect radar signals. If any other object, 
+e.g. topmark, buoy, beacon etc.. is radar conspicuous, because of its 
+construction, the attribute `CONRAD' must be used.
+
+Distinction: retro-reflector;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radar station     
+
+Acronym: RADSTA  Code: 102
+Radar station      RADSTA 102
+
+Set Attribute_A: CATRAS; COMCHA; DATEND; DATSTA; HEIGHT; 
+ NOBJNM; OBJNAM; STATUS; VALMXR; VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A station with a transmitter emitting pulses of ultra_high frequency 
+radio waves which are reflected by solid objects and are detected upon 
+their return to the sending station. (International Maritime 
+Dictionary, 2nd Ed.)
+
+References:
+
+INT 1:   IM 30; IS 1;
+
+M-4:  485.1; 487.3;
+
+Remarks:
+
+The object `radar station' is used to encode the technical equipment 
+itself independent of the building or structure where it is 
+installed.  This building or structure, e.g. mast, tower, building, 
+radar dome is a different object.
+
+Distinction: radar line; radar range; radar transponder beacon;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radar transponder beacon    
+
+Acronym: RTPBCN  Code: 103
+Radar transponder beacon     RTPBCN 103
+
+Set Attribute_A: CATRTB; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ RADWAL; SECTR1; SECTR2; SIGGRP; SIGSEQ; STATUS; VALMXR;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A transponder beacon transmitting a coded signal on radar frequency, 
+permitting an interrogating craft to determine the bearing and range 
+of the transponder. Also called racon. (IHO Dictionary, S-32, 5th 
+Edition, 4137)
+
+References:
+
+INT 1:  IS 2-3;
+
+M-4:  486.1-3;
+
+Remarks:
+
+The object class `radar transponder beacon' is only used to encode the 
+technical equipment independent of the structure on which it is 
+located (e.g. a beacon, light-vessel or tower).
+
+Distinction: radar line; radar range; radar station;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radio calling_in point      
+
+Acronym: RDOCAL  Code: 104
+Radio calling_in point       RDOCAL 104
+
+Set Attribute_A: COMCHA; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ ORIENT; PEREND; PERSTA; STATUS; TRAFIC;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Also called radio reporting points, they have been established in 
+certain busy waterways and port approaches to assist traffic control. 
+On passing these points or crossing a defined line vessels are 
+required to report on VHF to a Traffic Control Centre. (adapted from 
+IHO Chart Specifications, M-4)
+
+References:
+
+INT 1:  IM 40;
+
+M-4:  488;
+
+Remarks:
+
+The attribute `orientation' (ORIENT) encodes the orientation of the 
+traffic flow at that point.
+
+Distinction: radio station; pilot boarding place;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Radio station     
+
+Acronym: RDOSTA  Code: 105
+Radio station      RDOSTA 105
+
+Set Attribute_A: CALSGN; CATROS; COMCHA; DATEND; DATSTA; 
+ ESTRNG; NOBJNM; OBJNAM; ORIENT; PEREND; PERSTA; SIGFRQ; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A place equipped to transmit radio waves. Such a station may be either 
+stationary or mobile, and may also be provided with a radio receiver. 
+In British terminology, also called w/t station. (IHO Dictionary, 
+S-32, 5th Edition, 4191)
+
+References:
+
+INT 1:   IS 10-16;
+
+M-4:  480.1; 481.1-3; 482; 483; 484;
+
+Remarks:
+
+The transmission of a radio station may serve to provide mariners with 
+a line of position. (IHO Chart Specifications, M-4)
+
+The object "radio station" is used to encode the point of transmission 
+of the signal.
+
+Distinction: radio calling in point; radar station;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Railway      
+
+Acronym: RAILWY  Code: 106
+Railway       RAILWY 106
+
+Set Attribute_A:      CONDTN; HEIGHT; NOBJNM; OBJNAM; STATUS; 
+ VERACC;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A rail or set of parallel rails on which a train or tram runs. 
+(Digital Geographic Information Working Group, Oct.87)
+
+References:
+
+INT 1:   ID 13;
+
+M-4:  328.4; 362.1_2;
+
+Remarks:
+
+Distinction: road; tunnel;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Ramp         
+
+Acronym: RMPARE
+Ramp          RMPARE
+INT 1:  IF 23;
+
+M-4:  not specified
+
+
+Set Attribute_A: CONDTN; HORCLR; HORLEN; HORWID; NATCON; 
+ NOBJNM; OBJNAM; STATUS; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Line; Area;
+
+
+Definition:
+-----------
+
+A sloping structure that can either be used as a landing place at 
+variable water levels, for small vessels, landing ships, or a ferry 
+boat, or  for  hauling  a  cradle  carrying  a vessel. (IHO 
+Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+Distinction:  slipway;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A ramp should be encoded as a shoreline construction 
+(SLCONS) with a category of shoreline construction (CATSLC) value 12.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Rapids       
+
+Acronym: RAPIDS  Code: 107
+Rapids        RAPIDS 107
+
+Set Attribute_A:      NOBJNM; OBJNAM; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Portions of a stream with accelerated current where it descends 
+rapidly but without a break in the slope of the bed sufficient to form 
+a waterfall. Usually used in the plural. (IHO Dictionary, S-32, 5th 
+Edition, 4228)
+
+References:
+
+INT 1:  IC 22;
+
+M-4:  353.5;
+
+Remarks:
+
+Distinction:  current - non-gravitational; tidal stream - 
+ harmonic prediction; tidal stream - non-harmonic prediction; 
+ tidal stream panel data; tidal stream - time series; water 
+ turbulence; waterfall;
+  GEO OBJECT CLASSES
+
+
+
+Object Class: Recommended route centerline     
+
+Acronym: RCRTCL  Code: 108
+Recommended route centerline      RCRTCL 108
+
+Set Attribute_A: CATTRK; DATEND; DATSTA; DRVAL1; DRVAL2; 
+ NOBJNM; OBJNAM; ORIENT; PEREND; PERSTA; QUASOU; SOUACC; 
+ STATUS; TECSOU; TRAFIC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A recommended route is a route of undefined width, for the convenience 
+of ships in transit, which is often marked by centerline buoys. (IHO 
+Dictionary, S-32, 5th Edition, 4448)
+
+The recommended route centerline indicates the `centerline' of a 
+recommended route.
+
+References:
+
+INT 1:  IM 28.1;
+
+M-4:  435.4;
+
+Remarks:
+
+A recommended route describes the regulation of navigation for 
+non_hydrographic reasons such as the prevention of collision or the 
+avoidance of pollution risks. It is generally laid down by a national 
+or international authority other than the hydrographic authority. (IHO 
+Chart Specifications, M-4)
+
+Distinction:  recommended traffic lane part; recommended track;
+  GEO OBJECT CLASSES
+
+
+
+Object Class: Recommended track      
+
+Acronym: RECTRC  Code: 109
+Recommended track       RECTRC 109
+
+Set Attribute_A: CATTRK; DATEND; DATSTA; DRVAL1; DRVAL2; 
+ NOBJNM; OBJNAM; ORIENT; PEREND; PERSTA; QUASOU; SOUACC; 
+ STATUS; TECSOU; TRAFIC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A track recommended to all or only certain vessels. (IHO Dictionary, 
+S-32, 5th Edition, 5576)
+
+References:
+
+INT 1:  IM 3-4, 5.1, 5.2, 6;
+
+M-4:  432.1; 434;
+
+Remarks:
+
+Recommended tracks include all channels recommended for hydrographic 
+reasons to lead safely between shoal depths. The use of such tracks is 
+generally left to the discretion of the mariner and will depend on the 
+vessel's draught, the state of the tide, adequacy of navigational aids 
+and so on. (IHO Chart Specifications, M-4)
+
+The recommended track is that portion of a  `navigation line' that a 
+ship should use for navigation. (see below)
+
+  RECTRC
+<­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­>
+    Navaid    Navaid 
+X--------------X---------­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
+
+NAVLNE
+<­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­>
+
+In the case of a two-way recommended track, only one value of 
+orientation is encoded (in the attribute ORIENT); the other value can 
+be deduced (i.e. the value in ORIENT + 180 degrees) .
+
+Distinction:  navigation line; recommended route centerline; 
+ recommended traffic lane part;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Recommended traffic lane part    
+
+Acronym: RCTLPT  Code: 110
+Recommended traffic lane part     RCTLPT 110
+
+Set Attribute_A:      DATEND; DATSTA; ORIENT; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An optional part of an IMO_adopted routing measure.... Several 
+Hydrographic Offices, in consultation with their Ministries of 
+Transport, have added recommended directions in areas such as the 
+outer approaches to major ports in order to show the best routes for 
+crossing traffic or to minimize head_on encounters.(...) (IHO Chart 
+Specifications, M-4)
+
+References:
+
+INT 1:  IM 26.1-2;
+
+M-4:  435.5;
+
+Remarks:
+
+The object `recommended traffic lane part' indicates the recommended 
+traffic flow e.g.
+_  between two TSS
+_  in the entrance areas of a TSS
+_  beside a deep water route.
+
+The complete recommended traffic lane consists of one or more parts 
+depending on the various shapes of the recommended traffic lane.
+
+The orientation of the recommended traffic lane part is defined by the 
+middle_line of the recommended traffic lane part relating to the 
+general direction of the recommended traffic lane part.
+
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Rescue station         
+
+Acronym: RSCSTA  Code: 111
+Rescue station          RSCSTA 111
+
+Set Attribute_A:      CATRSC; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A place at which life saving equipment is held. (IHO Chart 
+Specifications, M-4)
+
+References:
+
+INT 1:  IT 12-14; IQ 124;
+
+M-4:   493; 493.1-2;
+
+Remarks:
+
+This object encodes the service available at this location.  The 
+structure housing the service should be coded separately.
+
+Distinction:  beacon special purpose/general; building single; 
+ coastguard station;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Restricted area        
+
+Acronym: RESARE  Code: 112
+Restricted area         RESARE 112
+
+Set Attribute_A: CATREA; DATEND; DATSTA; NOBJNM; OBJNAM; 
+ PEREND; PERSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A specified area designated by an appropriate authority within which 
+navigation is restricted in accordance with certain specified 
+conditions. (adapted from IHO Dictionary, S-32, 5th Edition, 4366)
+
+References:
+
+INT 1:  IL 3; IN 2.1-2, 20-22, 25-26, 31, 34;
+
+M-4: 431.4; 439.2-4; 441.8; 445.2; 448.1-2; 449.5;
+
+Remarks:
+
+Distinction:  anchorage area; cable area; caution area; dumping 
+ ground; depth area; fairway; dredged area; deep water route; 
+ military practice area; pipeline area; swept area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Retro-reflector         
+
+Acronym: RETRFL  Code: 113
+Retro-reflector          RETRFL 113
+
+Set Attribute_A: COLOUR; COLPAT; HEIGHT; MARSYS; STATUS; 
+ VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A means of distinguishing unlighted marks at night. Retro-reflective 
+material is secured to the mark in a particular pattern to reflect 
+back light. (Adapted from the UKHO NP735, 5th Edition). 
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+The body carrying the retro-reflector is a separate object.
+
+Distinction:  beacon, cardinal; beacon, isolated danger; 
+ beacon, lateral; beacon, safe water; beacon special 
+ purpose/general; buoy, cardinal; buoy, installation; buoy, 
+ isolated danger; buoy, lateral; buoy, safe water; buoy, 
+ special purpose/general; radar reflector;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: River        
+
+Acronym: RIVERS  Code: 114
+River         RIVERS 114
+
+Set Attribute_A:      NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A relatively large natural stream of water. (IHO Dictionary, S-32, 5th 
+Edition, 4405)
+
+References:
+
+INT 1:  IC 20, 21;
+
+M-4:  353.1_4;
+
+Remarks:
+
+The object `river' describes the area of the river, the object `river 
+bank' its banks.
+
+Distinction:  canal; lake; river bank; sea area/named water 
+ area; tideway;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: River bank        
+
+Acronym: RIVBNK  Code: 115
+River bank         RIVBNK 115
+
+Set Attribute_A:      NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The limit line between the water area of a river and the area of land.
+
+References:
+
+INT 1:  IC 20, 21;
+
+M-4:  353.1-4;
+
+Remarks:
+
+Distinction:  canal bank; coastline; lake shore; river; 
+ shoreline construction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Road         
+
+Acronym: ROADWY  Code: 116
+Road          ROADWY 
+
+Set Attribute_A:      CATROD; CONDTN; NATCON; NOBJNM; OBJNAM; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A road is an open way for the passage of vehicles. (United States 
+Geological Survey, Jan.89)
+
+References:
+
+INT 1:  ID 10-12;
+
+M-4:  365.1-3;
+
+Remarks:
+
+Distinction: causeway; railway; square;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Road crossing         
+
+Acronym: RODCRS
+Road crossing          RODCRS
+INT 1:  ID 10-12;
+
+M-4:  465.1-3;
+
+
+Set Attribute_A:      CATROD; CONDTN; NATCON; NOBJNM; OBJNAM; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+The area where two roads are crossing.
+
+Remark:
+
+Distinction:  road part; square; built-up area;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A road crossing should be encoded as a road (ROADWY) 
+with a category of road (CATROD) value 7.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Road part         
+
+Acronym: ROADPT
+Road part          ROADPT
+
+Set Attribute_A:      CATROD; CONDTN; NATCON; NOBJNM; OBJNAM; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A road is an open way for passage of vehicles. (United States 
+Geological Survey, Jan.89)
+
+References:
+
+INT 1:  ID 10-12;
+
+M-4:  365.1-3;
+
+Remarks:
+
+The symbolization for paper chart presentation in small scales is 
+related to the category of road (attribute `CATROD').
+
+Distinction:  square; built_up area; road crossing;
+
+This object class is obsolete. It is only shown here for reasons of 
+backward compatibility. Roads should be encoded using the object class 
+road (ROADWY).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Runway       
+
+Acronym: RUNWAY  Code: 117
+Runway        RUNWAY 117
+
+Set Attribute_A:      CATRUN; CONDTN; CONVIS; NATCON; NOBJNM; 
+ OBJNAM; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A defined rectangular area, on a land aerodrome, prepared for the 
+landing and take-off run of aircraft along its length. (IHO 
+Dictionary, S-32, 5th Edition, 4465)
+
+A site on which helicopters may land and take off. (IHO Dictionary, 
+S-32, 5th Edition, 2232)
+
+References:
+
+INT 1:  ID 17;
+
+M-4:  366;
+
+Remarks:
+
+Distinction: airport area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Salt pan          
+
+Acronym: SLTPAN
+Salt pan           SLTPAN
+INT 1:  IC 24;
+
+M-4:  353.7;
+
+
+Set Attribute_A:      NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Area;
+
+
+Definition:
+-----------
+
+A flat area of the natural surface covered with salt deposits which 
+result from the evaporation of sea water. (Digital Geographic 
+Information Working Group, Oct 1987)
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A salt pan should be encoded as a land region (LNDRGN) 
+with a category of land region (CATLND) value 15.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Sand waves        
+
+Acronym: SNDWAV  Code: 118
+Sand waves         SNDWAV 118
+
+Set Attribute_A: VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A large mobile wave-like sediment feature in shallow water and 
+composed of sand. The wavelength may reach 100 metres, the amplitude 
+may be up to 20 metres. 
+
+References:
+
+INT 1:  IJ 14;
+
+M-4:  428.1;
+
+Remarks:
+
+Distinction: seabed area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Sea area/named water area   
+
+Acronym: SEAARE  Code: 119
+Sea area/named water area    SEAARE 119
+
+Set Attribute_A:      CATSEA; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A geographically defined part of the sea or other navigable waters. It 
+may be specified within its limits by its proper name.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Each sea area is defined independent of any other. Smaller sea areas 
+may be located within larger sea areas.
+
+Distinction:  depth area; seabed area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Sea_plane landing area      
+
+Acronym: SPLARE  Code: 120
+Sea_plane landing area       SPLARE 120
+
+Set Attribute_A:      NOBJNM; OBJNAM; PEREND; PERSTA; RESTRN; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A designated portion of water for the landing and take-off of 
+sea_planes.
+
+References:
+
+INT 1:  IN 13;
+
+M-4:  449.6;
+
+Remarks:
+
+Distinction: airport area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Seabed area       
+
+Acronym: SBDARE  Code: 121
+Seabed area        SBDARE 121
+
+Set Attribute_A:      COLOUR; NATQUA; NATSUR; NOBJNM; OBJNAM; 
+ WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of the sea where the nature of bottom is homogeneous.
+
+The nature of bottom includes the material of which it is composed and 
+its physical characteristics. Also called character (or 
+characteristics) of the bottom, or quality of the bottom. (IHO 
+Dictionary, S-32, 5th Edition, 515).
+
+References:
+
+INT 1:  IJ 1-11, 30-39;
+
+M-4:  425.5-6; 426; 427;
+
+Remarks:
+
+Generally, it is not possible to define a seabed area by its real 
+extent. For that reason, the characteristics of the seabed area may be 
+represented at one single position.
+
+Distinction:  sand wave; sea area/named water area; weed/kelp;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Shoreline construction      
+
+Acronym: SLCONS  Code: 122
+Shoreline construction       SLCONS 122
+
+Set Attribute_A: CATSLC; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; DATEND; DATSTA; HEIGHT; HORACC; HORCLR; HORLEN; 
+ HORWID; NATCON; NOBJNM; OBJNAM; STATUS; VERACC; VERDAT; 
+ VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A fixed (not afloat) artificial structure between the water and the 
+land, i.e. a man-made coastline.
+
+References:
+
+INT 1:  IF 2, 4, 5, 6, 12-15, 18,23, 33;
+
+M-4:  313.2, 4; 321.1-4; 322.1-2; 324.1;
+
+Remarks:
+
+Distinction:  canal bank; coastline; lake shore; land area; 
+ pontoon; river bank;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Signal station, traffic     
+
+Acronym: SISTAT  Code: 123
+Signal station, traffic      SISTAT 123
+
+Set Attribute_A:      CATSIT; COMCHA; DATEND; DATSTA; NOBJNM; 
+ OBJNAM; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A signal station is a place on shore from which signals are made to 
+ships at sea. (IHO Dictionary, S-32, 5th Edition, 4742)
+
+Traffic signal stations regulate the movement of traffic. (IHO Chart 
+Specifications, M-4)
+
+References:
+
+INT 1:  IT 21-25.2;
+
+M-4:  495.1-5;
+
+Remarks:
+
+This object class is used to describe the function of the signal 
+station rather than the structure on which the station is sited.
+
+Distinction:  signal station, warning;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Signal station, warning     
+
+Acronym: SISTAW  Code: 124
+Signal station, warning      SISTAW 124
+
+Set Attribute_A:      CATSIW; COMCHA; DATEND; DATSTA; NOBJNM; 
+ OBJNAM; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A signal station is a place on shore from which signals are made to 
+ships at sea. (IHO Dictionary, S-32, 5th Edition, 4742)
+
+References:
+
+INT 1:  IT 20, 26, 28-36;
+
+M-4:  490.3; 494.1-2; 496.1-3; 497;
+
+Remarks:
+
+This object class is used to describe the function of the signal 
+station rather than the structure on which the station is sited.
+
+Distinction:  signal station, traffic;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Silo         
+
+Acronym: SILBUI
+Silo          SILBUI
+INT 1 Reference:      IE 33;
+
+Chart Specification:  376.3;
+
+
+Set Attribute_A: BUISHP; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; HEIGHT; NATCON; NOBJNM; OBJNAM; PRODCT; QUAVEM; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; PICREP; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+An enclosed container, used for storing grain or fodder. (Digital 
+Geographic Information Working Group, Oct.87)
+
+Remarks:
+
+Distinction:  tank;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A silo should be encoded as a silo/tank (SILTNK) with a 
+category of silo (CATSIL) value 1.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Silo/tank         
+
+Acronym: SILTNK  Code: 125
+Silo/tank          SILTNK 125
+
+Set Attribute_A: BUISHP; CATSIL; COLOUR; COLPAT; CONDTN; 
+ CONRAD; CONVIS; ELEVAT; HEIGHT; NATCON; NOBJNM; OBJNAM; 
+ PRODCT; STATUS; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An enclosed container, used for storage (Digital Geographic 
+Information Working Group, Oct.87)
+
+References:
+
+INT 1:  IE 2, 32-33;
+
+M-4:  340.2; 376.2-3;
+
+Remarks:
+
+Distinction:  landmark; production/storage area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Slipway      
+
+Acronym: SLIPWY
+Slipway       SLIPWY
+INT 1:  IF 23
+
+M-4:  324,1;
+
+
+Set Attribute_A: CONDTN; HORCLR; HORLEN; HORWID; NATCON; 
+ NOBJNM; OBJNAM; STATUS; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Line; Area;
+
+
+Definition:
+-----------
+
+The prepared, and usually reinforced, inclined surface on which keel- 
+and bilge-blocks are laid for supporting a vessel under construction. 
+(IHO Dictionary, S-32, 4th Edition)
+
+Remarks:
+
+Distinction:  ramp;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A slipway should be encoded as a shoreline construction 
+(SLCONS) with a category of shoreline construction (CATSLC) value 13.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Slope topline     
+
+Acronym: SLOTOP  Code: 126
+Slope topline      SLOTOP 126
+
+Set Attribute_A: CATSLO; COLOUR; CONRAD; CONVIS; ELEVAT; 
+ NATCON; NATQUA; NATSUR; NOBJNM; OBJNAM; VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The upper marking of a slope, e.g. the ridge line or the separation 
+line between two different gradients.
+
+References:
+
+INT 1:  IC 3; ID 14, 15;
+
+M-4:  312.1; 363.2; 364.1;
+
+Remarks:
+
+no remark.
+
+Distinction:  land elevation; sloping ground;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Sloping ground         
+
+Acronym: SLOGRD  Code: 127
+Sloping ground          SLOGRD 127
+
+Set Attribute_A:      CATSLO; COLOUR; CONRAD; CONVIS; NATCON; 
+ NATQUA; NATSUR; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An inclined surface (adapted from IHO Dictionary, S-32, 5th Edition, 
+4776).
+
+References:
+
+INT 1:  IC 3, 4, 8; ID 14, 15; IF 1;
+
+M-4:  312.1; 312.3; 313.1; 313.2;
+
+Remarks:
+
+Distinction:  slope topline;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Small craft facility        
+
+Acronym: SMCFAC  Code: 128
+Small craft facility         SMCFAC 128
+
+Set Attribute_A: CATSCF; NOBJNM; OBJNAM; PEREND; PERSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A place at which a service generally of interest to small craft or 
+pleasure boats is available.
+
+References:
+
+INT 1:  IU 2, 3-4, 6-13, 15-31;
+
+M-4:  not specified;
+
+Remarks:
+
+This object class encodes the service available at this location. The 
+structure housing the service should be encoded separately.
+
+Distinction:  building, single; harbour facility; shoreline 
+ construction.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Sounding          
+
+Acronym: SOUNDG  Code: 129
+Sounding           SOUNDG 129
+
+Set Attribute_A:      EXPSOU; NOBJNM; OBJNAM; QUASOU; SOUACC; 
+ STATUS; TECSOU; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A measured water depth or spot which has been reduced to a vertical 
+datum (may be a drying height).
+
+References:
+
+INT 1:  II 10-15;
+
+M-4:  403.1; 410; 412-412.4; 413.1; 417.3;
+
+Remarks:
+
+The value of the sounding is encoded in the 3-D Coordinate field of 
+the Spatial Record Structure (see S-57 Part 3).
+
+Drying heights (drying soundings) are indicated by a negative value.
+
+Distinction:  depth area; wreck; underwater/awash rock; 
+ obstruction;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Spoil ground      
+
+Acronym: SPOGRD
+Spoil ground       SPOGRD
+INT 1:  IN 62.1-2;
+
+M-4:  446.1-2;
+
+
+Set Attribute_A:      NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:  Point; Area;
+
+
+Definition:
+-----------
+
+A sea area where dredged material is deposited. (IHO Dictionary, S-32, 
+4th Edition)
+
+Remarks:
+
+The significance of spoil grounds to the mariner is that very large 
+quantities of material may be dumped, decreasing the depth of water 
+available. (IHO Chart Specifications, M-4)
+
+
+Distinction:  dumping ground; dredged area;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A spoil ground should be encoded as a dumping ground 
+(DMPGRD) with a category of dumping ground (CATDPG) value 5.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Spring       
+
+Acronym: SPRING  Code: 130
+Spring        SPRING 130
+
+Set Attribute_A:      NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A natural issue of water or other substances from the earth. One on 
+the bottom of the sea is called a submarine spring. (IHO Dictionary, 
+S-32, 5th Edition, 4939)
+
+References:
+
+INT 1:  IJ 15;
+
+M-4:  428.3;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Square       
+
+Acronym: SQUARE  Code: 131
+Square        SQUARE 131
+
+Set Attribute_A:      CONDTN; NATCON; NOBJNM; OBJNAM; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An open area within a built_up area surrounded by roads.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction:  road; built_up area; building, single;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Straight territorial sea baseline     
+
+Acronym: STSLNE  Code: 132
+Straight territorial sea baseline      STSLNE 132
+
+Set Attribute_A:      NATION;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A baseline is the line from which the outer limits of the territorial 
+sea and certain other outer limits are measured. (IHO Dictionary, 
+S-32, 5th Edition, 390)
+
+Straight baselines are a system of straight lines joining specified or 
+discrete points on the low-water line, usually known as straight 
+baseline turning points. (IHO Dictionary, S-32, 5th Edition, 393)
+
+References:
+
+INT 1:  IN 42;
+
+M-4:  440.4;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Submarine transit lane      
+
+Acronym: SUBTLN  Code: 133
+Submarine transit lane       SUBTLN 133
+
+Set Attribute_A:      NOBJNM; OBJNAM; RESTRN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area where submarines may navigate under water or at the surface.
+
+References:
+
+INT 1:  IN 33;
+
+M-4:  441.5;
+
+Remarks:
+
+Distinction: military practice area;
+ GEO OBJECT CLASSES
+
+
+
+Object Class:  Swept Area  
+
+Acronym: SWPARE Code: 134
+Swept Area  SWPARE 134
+
+ Set Attribute_A: DRVAL1; QUASOU; SOUACC; TECSOU; VERDAT;
+
+Set Attribute_B:  INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:  RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area that has been determined to be clear of navigational dangers 
+to a specified depth (adapted from IHO Dictionary, S-32, 5th Edition, 
+5248).
+
+References:
+
+INT 1: II 24;
+
+M-4: 415.1; 415.2;
+
+Remarks:
+
+Distinction:  depth area; dredged area; unsurveyed area;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Tank         
+
+Acronym: TNKCON
+Tank          TNKCON
+
+Set Attribute_A: COLOUR; COLPAT; CONDTN; CONRAD; CONVIS; 
+ HEIGHT; NATCON; NOBJNM; OBJNAM; PRODCT; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+A fixed structure for storing liquids or gases. (Derived from IHO 
+Dictionary, S-32, 4th Edition)
+
+References:
+
+INT 1:  IE 32;
+
+M-4:  376.1_2;
+
+Remarks:
+
+Distinction:  silo;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A tank should be encoded as a silo/tank (SILTNK) with a 
+category of silo (CATSIL) value 2.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Telepheric        
+
+Acronym: TELPHC
+Telepheric         TELPHC
+INT 1:  ID 25;
+
+M-4:  382.3;
+
+
+Set Attribute_A: CONDTN; CONRAD; CONVIS; DATEND; DATSTA; 
+ LIFCAP; NOBJNM; OBJNAM; QUAVEM; STATUS; VERCLR;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Line;
+
+
+Definition:
+-----------
+
+A construction of cables strung between elevated supports on which 
+carrier units are suspended.
+
+Remarks:
+
+The elevated supports are separate objects.
+
+Distinction:  cable, overhead;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A telepheric should be encoded as a conveyor (CONVYR) 
+with a category of conveyor (CATCON) value 1.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Territorial sea area        
+
+Acronym: TESARE  Code: 135
+Territorial sea area         TESARE 135
+
+Set Attribute_A:      NATION; RESTRN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The territorial sea is a belt of water of a defined breadth but not 
+exceeding 12 nautical miles measured seaward from the territorial sea 
+baseline. (IHO Dictionary, S-32, 5th Edition, 5360)
+
+References:
+
+INT 1:  IN 43;
+
+M-4:  440.5;
+
+Remarks:
+
+Distinction: administrative area; contiguous zone; continental 
+ shelf area; exclusive economic zone; fishery zone; restricted 
+ area;
+
+
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tidal stream - flood/ebb   
+
+Acronym: TS_FEB  Code: 160
+
+
+Set Attribute_A: CAT_TS; CURVEL; DATEND; DATSTA; NOBJNM; OBJNAM;
+ORIENT; PEREND; PERSTA;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC; 
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A tidal stream (or tidal current) is a horizontal movement of water 
+associated with the rise and fall of the tide caused by tide-producing 
+forces. (Adapted from IHO Dictionary, S-32, 5th Edition)
+
+Approximate tidal stream rates may be given as discrete rate values 
+for flood and ebb flow during springs.
+
+References:
+
+INT 1:  IH 40-41;
+
+M-4:  407.4;
+
+Remarks:
+
+Distinction: tidal stream - harmonic prediction; tidal stream - 
+ non harmonic prediction; tidal stream panel data; tidal stream 
+ - time series;
+
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tidal stream - harmonic prediction   
+
+Acronym: TS_PRH  Code: 136
+Tidal stream - harmonic prediction    TS_PRH 136
+
+Set Attribute_A: NOBJNM; OBJNAM; T_MTOD; T_VAHC; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A tidal stream (or tidal current) is an alternating horizontal 
+movement of water associated with the rise and fall of the tide caused 
+by tide-producing forces. (IHO Dictionary, S-32, 5th Edition, 1169)
+
+Predicted tidal stream rates may be calculated using parameters 
+(harmonic constituents) and an appropriate harmonic calculation 
+algorithm.
+
+References:
+
+INT 1:  IH 40-41;
+
+M-4:  407.4; 408.2;
+
+Remarks:
+
+The object `tidal stream - harmonic prediction' encodes parameters for 
+use when predicting tidal streams by harmonic methods.
+
+The supplier of any parameters must be consulted on how to use data 
+provided using this object class, and which calculation algorithms to 
+use with the data.
+
+Distinction: current - non-gravitational; tidal stream - 
+ non-harmonic prediction; tidal stream panel data; tidal stream 
+ - time series;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tidal stream - non-harmonic prediction   
+
+Acronym: TS_PNH  Code: 137
+Tidal stream - non-harmonic prediction    TS_PNH 137
+
+Set Attribute_A: NOBJNM; OBJNAM; T_THDF; T_MTOD; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A tidal stream (or tidal current) is an alternating horizontal 
+movement of water associated with the rise and fall of the tide caused 
+by tide-producing forces. (IHO Dictionary, S-32, 5th Edition, 1169)
+
+Predicted tidal stream rates may be calculated using time and rate 
+differences with respect to a reference station (and associated tidal 
+stream predictions).
+
+References:
+
+INT 1:  IH 40-41;
+
+M-4:  407.4; 408.2;
+
+Remarks:
+
+The object `tidal stream - non-harmonic prediction' encodes 
+information for use when predicting times and rates for tidal streams 
+by non-harmonic methods.
+
+The supplier of any parameters must be consulted on how to use this 
+data, and which calculation algorithms to use with the data.
+
+Distinction: current - non-gravitational; tidal stream - 
+ harmonic prediction; tidal stream panel data; tidal stream - 
+ time series;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tidal stream panel data      
+
+Acronym: TS_PAD  Code: 138
+Tidal stream panel data       TS_PAD 138
+
+Set Attribute_A: NOBJNM; OBJNAM; TS_TSP;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A tidal stream (or tidal current) is an alternating horizontal 
+movement of water associated with the rise and fall of the tide caused 
+by tide-producing forces. (IHO Dictionary, S-32, 5th Edition, 1169)
+
+Approximate tidal stream rates may be given as discrete rate values at 
+a specified interval before or after a high water.
+
+References:
+
+INT 1:  IH 40-41;
+
+M-4:  407.4; 408.2;
+
+Remarks:
+
+The object `tidal stream panel data' encodes data for use in a tidal 
+panel.
+
+Distinction: current - non-gravitational; tidal stream - 
+ harmonic prediction; tidal stream - non-harmonic prediction; 
+ tidal stream - time series;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tidal stream - time series      
+
+Acronym: TS_TIS  Code: 139
+Tidal stream - time series       TS_TIS 139
+
+Set Attribute_A: NOBJNM; OBJNAM; TIMEND; TIMSTA; T_TINT; 
+ TS_TSV; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A tidal stream (or tidal current) is an alternating horizontal 
+movement of water associated with the rise and fall of the tide caused 
+by tide-producing forces. (IHO Dictionary, S-32, 5th Edition, 1169)
+
+Tidal stream rates over time may be approximated by a series of rate 
+values given at regular time intervals, starting from a specified 
+moment in time.
+
+References:
+
+INT 1:  IH 40-41;
+
+M-4:  407.4; 408.2;
+
+Remarks:
+
+The object `tidal stream - time series' encodes rates of tidal stream 
+at equal time intervals.
+
+Distinction: current - non-gravitational;tidal stream - 
+ harmonic prediction; tidal stream - non-harmonic prediction; 
+ tidal stream panel data;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tide - harmonic prediction   
+
+Acronym: T_HMON  Code: 140
+Tide - harmonic prediction     T_HMON 140
+
+Set Attribute_A: NOBJNM; OBJNAM; T_ACWL; T_MTOD; T_VAHC; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Tide - the periodic rise and fall of the surface of the sea, due 
+principally to the gravitational interaction between moon, sun and 
+earth. (adopted from IHO Dictionary, S-32, 5th Edition, 5429)
+
+Predicted tidal heights may be calculated using parameters (harmonic 
+constituents) and an appropriate harmonic calculation algorithm.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+The object `tide - harmonic predictions' encodes parameters for use 
+when predicting tidal heights by harmonic methods.
+
+The supplier of any parameters must be consulted on how to use this 
+data, and which calculation algorithms to use with the data.
+
+Distinction: tide - non-harmonic prediction; tide - time series;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tide - non-harmonic prediction    
+
+Acronym: T_NHMN  Code: 141
+Tide - non-harmonic prediction     T_NHMN 141
+
+Set Attribute_A: NOBJNM; OBJNAM; T_ACWL; T_MTOD; T_THDF; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Tide - the periodic rise and fall of the surface of the sea, due 
+principally to the gravitational interaction between moon, sun and 
+earth. (adopted from IHO Dictionary, S-32, 5th Edition, 5429)
+
+Predicted tidal heights may be calculated using time and height 
+differences with respect to a reference port (and associated tidal 
+predictions).
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+The object `tide - non-harmonic prediction' encodes information for 
+use when predicting times and heights for high and low waters by 
+non-harmonic methods.
+
+The supplier of any parameters must be consulted on how to use data 
+provided using this object class, and which calculation algorithms to 
+use with the data.
+
+Distinction: tide - harmonic prediction; tide - time series;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tide - time series       
+
+Acronym: T_TIMS  Code: 142
+Tide - time series        T_TIMS 142
+
+Set Attribute_A: NOBJNM; OBJNAM; T_ACWL; T_HWLW; T_TINT; 
+ T_TSVL; TIMEND; TIMSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Tide - the periodic rise and fall of the surface of the sea, due 
+principally to the gravitational interaction between moon, sun and 
+earth. (adopted from IHO Dictionary, S-32, 5th Edition, 5429)
+
+Tidal heights over time may be approximated by a series of height 
+values given at regular time intervals, starting from a specified 
+moment in time.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+The object `tide - time series' encodes tidal heights at equal time 
+intervals and times and heights of high waters and low waters.
+
+Distinction: tide - harmonic prediction; tide - non-harmonic 
+ prediction;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tideway      
+
+Acronym: TIDEWY  Code: 143
+Tideway       TIDEWY 143
+
+Set Attribute_A:      NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A natural water course in intertidal areas where water flows during 
+the ebb or flow.
+
+A channel through which a tidal current runs. (IHO Dictionary, S-32, 
+5th Edition, 5502)
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction:  canal; river; sea area/named water area
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Topmark          
+
+Acronym: TOPMAR  Code: 144
+Topmark           TOPMAR 144
+
+Set Attribute_A: COLOUR; COLPAT; HEIGHT; MARSYS; STATUS; 
+ TOPSHP; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A characteristic shape secured at the top of a buoy or beacon to aid 
+in its identification. (IHO Dictionary, S-32, 5th Edition, 5548)
+
+References:
+
+INT 1:  IQ 9;
+
+M-4:  463.1;
+
+Remarks:
+
+The body carrying the topmark is a separate object.
+
+Distinction:  beacon, cardinal; beacon, isolated danger; 
+ beacon, lateral; beacon, safe water; beacon special 
+ purpose/general; buoy, cardinal; buoy, installation; buoy, 
+ isolated danger; buoy, lateral; buoy, safe water; buoy, 
+ special purpose/general; daymark;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Tower        
+
+Acronym: TOWERS
+Tower         TOWERS
+
+
+Set Attribute_A: CATTOW; COLOUR; COLPAT; CONDTN; CONRAD; 
+ CONVIS; HEIGHT; NATCON; NOBJNM; OBJNAM; QUAVEM; STATUS; 
+ VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A relatively tall structure which may be used for observation, 
+support, storage or communication etc.. (Digital Geographic 
+Information Working Group, Oct.87)
+
+References:
+
+INT 1:   IE 20-21, 29, 30.2;
+
+M-4:  374,2-3; 375,2-3; 487;
+
+Remarks:
+
+The object `tower' is independent of any equipment carried upon it. 
+This is specified by other objects. e.g. radar station, light.
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A tower should be encoded as a landmark (LNDMRK) with 
+category of landmark (CATLMK) value 17.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Traffic separation line     
+
+Acronym: TSELNE  Code: 145
+Traffic separation line      TSELNE 145
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A traffic separation scheme is a scheme which aims to reduce the risk 
+of collision in congested and/or converging areas by separating 
+traffic moving in opposite, or nearly opposite, directions. (IHO 
+Dictionary, S-32, 5th Edition, 5585)
+
+A traffic separation line is a line separating traffic lanes in which 
+ships are travelling in opposite or nearly opposite directions; or 
+separating traffic lanes designated for particular classes of ships 
+proceeding in the same direction (IMO Ships Routeing, 6th Edition).
+
+References:
+
+INT 1:  IM 12;
+
+M-4:  435.1;
+
+Remarks:
+
+Distinction:  traffic separation scheme boundary; traffic 
+ separation scheme crossing; traffic separation scheme lane 
+ part; traffic separation scheme roundabout; traffic separation 
+ zone;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Traffic separation scheme boundary    
+
+Acronym: TSSBND  Code: 146
+Traffic separation scheme boundary     TSSBND 146
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A traffic separation scheme is a scheme which aims to reduce the risk 
+of collision in congested and/or converging areas by separating 
+traffic moving in opposite, or nearly opposite, directions. (IHO 
+Dictionary, S-32, 5th Edition, 5585)
+
+The boundary of a traffic separation scheme is the outer limit of a 
+traffic lane part or a traffic separation scheme roundabout.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  436;
+
+Remarks:
+
+Distinction: traffic separation line; traffic separation scheme 
+ crossing; traffic separation scheme lane part; traffic 
+ separation scheme roundabout; traffic separation zone;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Traffic separation scheme crossing    
+
+Acronym: TSSCRS  Code: 147
+Traffic separation scheme crossing     TSSCRS 147
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A traffic separation scheme is a scheme which aims to reduce the risk 
+of collision in congested and/or converging areas by separating 
+traffic moving in opposite, or nearly opposite, directions. (IHO 
+Dictionary, S-32, 5th Edition, 5585)
+
+A traffic separation scheme crossing is a defined area where traffic 
+lanes cross.
+
+References:
+
+INT 1:  IM 23;
+
+M-4:  435.1;
+
+Remarks:
+
+Distinction:  traffic separation line; traffic separation 
+ scheme boundary; traffic separation scheme lane part; traffic 
+ separation scheme roundabout; traffic separation zone;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Traffic separation scheme lane part   
+
+Acronym: TSSLPT  Code: 148
+Traffic separation scheme lane part    TSSLPT 148
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; ORIENT; RESTRN; 
+ STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A traffic separation scheme is a scheme which aims to reduce the risk 
+of collision in congested and/or converging areas by separating 
+traffic moving in opposite, or nearly opposite, directions. (IHO 
+Dictionary, S-32, 5th Edition, 5585)
+
+A traffic lane is an area within defined limits in which one-way 
+traffic flow is established (IMO Ships Routeing, 6th Edition).
+
+A traffic separation scheme lane part is an area of a traffic lane in 
+which the direction of flow of traffic is uniform.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+The complete traffic lane may consist of one or more lane parts 
+depending  on the shape of the lane.
+
+Distinction: recommended traffic lane part; traffic separation 
+ line; traffic separation scheme boundary; traffic separation 
+ scheme crossing; traffic separation scheme roundabout; traffic 
+ separation zone;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Traffic separation scheme roundabout  
+
+Acronym: TSSRON  Code: 149
+Traffic separation scheme roundabout   TSSRON 149
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; RESTRN; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A traffic separation scheme is a scheme which aims to reduce the risk 
+of collision in congested and/or converging areas by separating 
+traffic moving in opposite, or nearly opposite, directions. (IHO 
+Dictionary, S-32, 5th Edition, 5585)
+
+A roundabout is a traffic separation scheme in which traffic moves in 
+a counter-clockwise direction around a specified point or zone. (IHO 
+Dictionary, S-32, 5th Edition, 4448)
+
+References:
+
+INT 1:  IM 21;
+
+M-4:  435.1;
+
+Remarks:
+
+Distinction:  traffic separation line; traffic separation 
+ scheme boundary; traffic separation scheme crossing; traffic 
+ separation scheme lane part; traffic separation zone;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Traffic separation zone     
+
+Acronym: TSEZNE  Code: 150
+Traffic separation zone      TSEZNE 150
+
+Set Attribute_A:      CATTSS; DATEND; DATSTA; STATUS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A traffic separation scheme is a scheme which aims to reduce the risk 
+of collision in congested and/or converging areas by separating 
+traffic moving in opposite, or nearly opposite, directions. (IHO 
+Dictionary, S-32, 5th Edition, 5585)
+
+A traffic separation zone is a zone separating the lanes in which 
+ships are proceeding in opposite or nearly opposite directions; or 
+separating traffic lanes designated for particular classes of ships 
+proceeding in the same direction (IMO Ships Routeing, 6th Edition).
+
+References:
+
+INT 1:  IM 13, 20.1;
+
+M-4:  435.1;
+
+Remarks:
+
+Distinction:  traffic separation line; traffic separation 
+ scheme boundary; traffic separation scheme crossing; traffic 
+ separation scheme lane part; traffic separation scheme 
+ roundabout;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Tree         
+
+Acronym: TREPNT
+Tree          TREPNT
+INT 1:  IC 31-31.8;
+
+M-4:  354.2;
+
+
+Set Attribute_A: CATTRE; CONVIS; HEIGHT; NOBJNM; OBJNAM; 
+ QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+A woody perennial plant having a self supporting main stem or trunk 
+and a definite crown. (United States Geological Survey, Jan.89)
+
+Remarks:
+
+Distinction:  vegetation;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A tree should be encoded as vegetation (VEGATN) with an 
+appropriate category of vegetation (CATVEG) value.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Tunnel       
+
+Acronym: TUNNEL  Code: 151
+Tunnel        TUNNEL 151
+
+Set Attribute_A:      BURDEP; CONDTN; HORACC; HORCLR; NOBJNM; 
+ OBJNAM; STATUS; VERACC; VERCLR;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A passage that is open to the atmosphere at both ends, buried under 
+the sea bed or laid over the sea floor or bored under the ground or 
+through mountains. 
+
+References:
+
+INT 1:  ID 16;
+
+M-4:  363.1;
+
+Remarks:
+
+Distinction: railway; road;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Tunnel entrance        
+
+Acronym: TNLENT
+Tunnel entrance         TNLENT
+INT 1:  ID 16;
+
+M-4:  363.1;
+
+
+Set Attribute_A:      HORCLR; NOBJNM; OBJNAM; VERCLR;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Line;
+
+
+Definition:
+-----------
+
+An opening that affords entry to an underground or underwater passage. 
+(United States Geological Survey, Jan.89)
+
+Remarks:
+
+No remarks
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A tunnel should be encoded using the object class 
+tunnel (TUNNEL).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Two_way route part     
+
+Acronym: TWRTPT  Code: 152
+Two_way route part      TWRTPT 152
+
+Set Attribute_A: CATTRK; DATEND; DATSTA; DRVAL1; DRVAL2; 
+ ORIENT; QUASOU; SOUACC; STATUS; TECSOU; TRAFIC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A two-way route is a route within defined limits inside which two-way 
+traffic is established, aimed at providing safe passage of ships 
+through waters where navigation is difficult or dangerous. (IHO 
+Dictionary, S-32, 5th Edition, 5712)
+
+A two-way route part is an area of a two-way route within which 
+traffic flow is generally along one bearing (and possibly its 
+reciprocal).
+
+References:
+
+INT 1:  IM 28.2;
+
+M-4:  435.6;
+
+Remarks:
+
+The complete two_way route consists of one or more parts depending on 
+the shape of the two_way route.
+
+The orientation of the two_way route part is defined by its centerline 
+and is related to the general direction of the two_way route.
+
+Distinction: deep water route part; recommended traffic lane 
+ part; traffic separation scheme lane part;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Underwater/awash rock       
+
+Acronym: UWTROC  Code: 153
+Underwater/awash rock        UWTROC 153
+
+Set Attribute_A: EXPSOU; NATQUA; NATSUR; NOBJNM; OBJNAM; 
+ QUASOU; SOUACC; STATUS; TECSOU; VALSOU; VERDAT; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A concreted mass of stony material or coral which dries, is awash or 
+is below the water surface.
+
+References:
+
+INT 1:  IK 12, 13, 14-16;
+
+M-4:  421.3_5;
+
+Remarks:
+
+Distinction:  obstruction; sounding; wreck;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Unsurveyed area       
+
+Acronym: UNSARE  Code: 154
+Unsurveyed area        UNSARE 154
+
+Set Attribute_A: 
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area for which no bathymetric survey information is available.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  417.8;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Vegetation        
+
+Acronym: VEGATN  Code: 155
+Vegetation         VEGATN 155
+
+Set Attribute_A:      CATVEG; CONVIS; ELEVAT; HEIGHT, NOBJNM; 
+ OBJNAM; VERACC; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Collections of, or individual plants.
+
+References:
+
+INT 1:  IC 14, 30, 31.1-8, 32;
+
+M-4:  312.4; 354.1-2,4;
+
+Remarks:
+
+Distinction:  seabed area; weed/kelp;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Vegetation area        
+
+Acronym: VEGARE
+Vegetation area         VEGARE
+
+Set Attribute_A:      CATVEG; CONVIS; HEIGHT, NOBJNM; OBJNAM; 
+ VERACC; VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A land area or an intertidal area, covered by any kind of plants.
+
+References:
+
+INT 1:  IC 14, 30, 32;
+
+M-4:  312.4; 354;
+
+Remarks:
+
+Distinction:  coastline; seabed area; tree; weed/kelp;
+
+This object class is obsolete. It is only included here for reasons of 
+backward compatibility. Vegetation areas should be encoded as 
+vegetation (VEGATN).
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Water turbulence       
+
+Acronym: WATTUR  Code: 156
+Water turbulence        WATTUR 156
+
+Set Attribute_A:      CATWAT; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The disturbance of water caused by the interaction of any combination 
+of waves, currents, tidal streams, wind, shoal patches and 
+obstructions.
+
+References:
+
+INT 1:  IH 44, 45; IK 17;
+
+M-4:  423.1; 423.2; 423.3;
+
+Remarks:
+
+No remarks.
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Waterfall         
+
+Acronym: WATFAL  Code: 157
+Waterfall          WATFAL 157
+
+Set Attribute_A:      CONVIS; NOBJNM; OBJNAM; VERACC; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A sudden descent of water over a step in the bed of a river. In place 
+names commonly shortened to fall or falls, e.g. Niagara Falls. 
+
+References:
+
+INT 1:  IC 22;
+
+M-4:  353.5;
+
+Remarks:
+
+Distinction:  rapids;
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Weed/Kelp         
+
+Acronym: WEDKLP  Code: 158
+Weed/Kelp          WEDKLP 158
+
+Set Attribute_A:      CATWED; NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Seaweed is the general name for marine plants of the Algae class which 
+grow in long narrow ribbons. (International Maritime Dictionary, 2nd 
+Ed.)
+
+Kelp is one of an order (laminariales) of usually large, blade-shaped 
+or vine-like brown algae. (IHO Dictionary, S-32, 5th Edition, 2611)
+
+References:
+
+INT 1:  IJ 13.1, 13.2;
+
+M-4:  428.2;
+
+Remarks:
+
+Kelp is often an indication of the presence of submerged rocks. (IHO 
+Chart Specifications M-4)
+
+Distinction: seabed area; vegetation;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Weir         
+
+Acronym: WIRLNE 
+Weir          WIRLNE
+
+Set Attribute_A: CONDTN; DATEND; DATSTA; NATCON; NOBJNM; 
+ OBJNAM; QUAVEM; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A dam erected across a river to raise the level of the water. The word 
+is now restricted to smaller works, the larger are called dams. (IHO 
+Dictionary, S-32, 5th Edition, 5967)
+
+References:
+
+INT 1:  IF 44;
+
+M-4:  364.2;
+
+Remarks:
+
+Distinction:  causeway; dam;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A weir should be encoded as a dam (DAMCON) with a 
+category of dam (CATDAM) value 1.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Windmill          
+
+Acronym: WNDMIL
+Windmill           WNDMIL
+INT 1:  IE 25.1, 25.2;
+
+M-4:  374.5;
+
+
+Set Attribute_A: COLOUR; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NATCON; NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point; Area;
+
+
+Definition:
+-----------
+
+A  mill that runs on the energy generated by a wheel of adjustable 
+blades or flats rotated by the wind.   (United States Geological 
+Survey, Jan.89)
+
+Remarks:
+
+Distinction:  windmotor;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A windmill should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 18.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Windmotor         
+
+Acronym: WIMCON
+Windmotor          WIMCON
+INT 1:  IE 26;
+
+M-4:  374.6;
+
+
+Set Attribute_A: COLOUR; CONDTN; CONRAD; CONVIS; HEIGHT; 
+ NOBJNM; OBJNAM; QUAVEM; VERDAT; VERLEN;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Point;
+
+
+Definition:
+-----------
+
+A modern structure for use of wind power. (IHO Chart Specifications, 
+M-4)
+
+
+Remarks:
+
+Distinction:  windmill;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A windmotor should be encoded as a landmark (LNDMRK) 
+with a category of landmark (CATLMK) value 19.
+
+ DELETED - DO NOT USE
+ GEO OBJECT CLASSES
+
+
+
+Object Class: Wreck        
+
+Acronym: WRECKS  Code: 159
+Wreck         WRECKS 159
+
+Set Attribute_A: CATWRK; CONRAD; CONVIS; EXPSOU; HEIGHT; 
+ NOBJNM; OBJNAM; QUASOU; SOUACC; STATUS; TECSOU; VALSOU; 
+ VERACC; VERDAT; VERLEN; WATLEV;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+The ruined remains of a stranded or sunken vessel which has been 
+rendered useless. (IHO Dictionary, S-32, 5th Edition, 6027)
+
+References:
+
+INT 1:  IK 20-30;
+
+M-4:  422-422.8;
+
+Remarks:
+
+Distinction:  depth area; hulk; obstruction; sounding; 
+ underwater/awash rock;
+ GEO OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Zero metre _ contour        
+
+Acronym: ZEMCNT
+Zero metre _ contour         ZEMCNT
+INT 1:  II 30;
+
+M-4:  404.2; 410-411;
+
+
+Set Attribute_A:      VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; SCAMAX; SCAMIN;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Geometric Primitive:   Line;
+
+
+Definition:
+-----------
+
+The limit line between an area permanently covered by water and an 
+intertidal area.
+
+Remarks:
+
+Distinction:  coastline; depth contour;
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. A zero metre contour should be encoded as a depth 
+contour (DEPCNT) with a value of depth contour (VALDCO) of zero.
+
+ DELETED - DO NOT USE
+ Page intentionally left blank
+
+
+ 1.3 Meta Object Classes
+ META OBJECT CLASSES
+
+
+
+Object Class:  Accuracy of data        
+
+Acronym: M_ACCY  Code: 300
+Accuracy of data         M_ACCY 300
+
+Set Attribute_A: HORACC; POSACC; SOUACC; VERACC;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which the best estimate of the overall accuracy of the 
+data is uniform. The overall accuracy takes into account for example 
+the source accuracy, chart scale, digitising accuracy etc.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction: quality of data; survey reliability;
+ META OBJECT CLASSES
+
+
+
+Object Class:  Compilation scale of data    
+
+Acronym: M_CSCL  Code: 301
+Compilation scale of data     M_CSCL 301
+
+Set Attribute_A: CSCALE;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which the data was originally compiled at a uniform 
+scale. For example, it may define the scale of the paper chart from 
+which the data was digitised.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+No remarks.
+ META OBJECT CLASSES
+
+
+
+Object Class:  Coverage       
+
+Acronym: M_COVR  Code: 302
+Coverage        M_COVR 
+
+Set Attribute_A: CATCOV;
+
+Set Attribute_B:      INFORM; NINFOM; 
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+Definition:
+-----------
+
+A geographical area that describes the coverage and extent of spatial 
+objects.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+This object class is intended to support an indication of coverage.
+ META OBJECT CLASSES
+
+
+
+Object Class:  Horizontal datum of data     
+
+Acronym: M_HDAT  Code: 303
+Horizontal datum of data      M_HDAT 303
+
+Set Attribute_A: HORDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of uniform horizontal datum.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction: horizontal datum shift parameters;
+ META OBJECT CLASSES
+
+
+
+Object Class:  Horizontal datum shift parameters     
+
+Acronym: M_HOPA  Code: 304
+Horizontal datum shift parameters      M_HOPA 
+
+Set Attribute_A: HORDAT; SHIPAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMAX; SCAMIN; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which a uniform shift exists between a specific 
+geodetic datum and the datum of the data within this area.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction: horizontal datum of data;
+ META OBJECT CLASSES
+
+
+
+Object Class:  Nautical publication information  
+
+Acronym: M_NPUB  Code: 305
+Nautical publication information   M_NPUB 305
+
+Set Attribute_A: 
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; PUBREF; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Used to relate additional nautical information or publications to the 
+data.
+
+References:
+
+INT 1:  not specified
+
+M-4:  not specified
+
+Remarks:
+
+For example, geographic areas may be defined that relate to sections 
+in Sailing Directions (Coast Pilots).
+ META OBJECT CLASSES
+
+
+
+Object Class:  Navigational system of marks    
+
+Acronym: M_NSYS  Code: 306
+Navigational system of marks     M_NSYS 306
+
+Set Attribute_A: MARSYS; ORIENT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; SCAMIN; SCAMAX; 
+ TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which a specific system of navigational marks applies 
+and/or a common direction of buoyage.
+
+References:
+
+INT 1:  IQ 130-130.6;
+
+M-4:  461.1-4;
+
+Remarks:
+
+No remarks.
+ META OBJECT CLASSES
+
+
+
+Object Class:  Production information       
+
+Acronym: M_PROD  Code: 307
+Production information        M_PROD 307
+
+Set Attribute_A: AGENCY; CPDATE; NATION; NMDATE; PRCTRY;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which uniform data production parameters apply.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+No remarks.
+ META OBJECT CLASSES
+
+
+
+Object Class: Quality of data
+
+Acronym: M_QUAL Code: 308
+Quality of data M_QUAL 308
+
+Set Attribute_A: CATQUA; CATZOC; DRVAL1; DRVAL2; POSACC; 
+ SOUACC; SUREND; SURSTA; TECSOU; VERDAT;
+
+Set Attribute_B: INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C: RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which a uniform assessment of the quality of the data 
+exists.
+
+References:
+
+INT 1: not specified;
+
+M-4: not specified;
+
+Remarks:
+
+Distinction: accuracy of data; survey reliability;
+ META OBJECT CLASSES
+
+
+
+Object Class:  Sounding datum     
+
+Acronym: M_SDAT  Code: 309
+Sounding datum      M_SDAT 309
+
+Set Attribute_A: VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of uniform sounding datum.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction: vertical datum;
+ META OBJECT CLASSES
+
+
+
+Object Class:  Survey reliability      
+
+Acronym: M_SREL  Code: 310
+Survey reliability       M_SREL 310
+
+Set Attribute_A: QUAPOS; QUASOU; SCVAL1; SCVAL2; SDISMN; 
+ SDISMX; SURATH; SUREND; SURSTA; SURTYP; TECSOU;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area within which a uniform assessment of the reliability of source 
+survey information exists.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  170.2; 178;
+
+Remarks:
+
+Distinction: accuracy of data; quality of data;
+ META OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class:  Survey source      
+
+Acronym: M_SSOR 
+Survey source       M_SSOR
+
+Set Attribute_A: DATEND; DATSTA; SCVAL1; SCVAL2; SURATH; 
+ SUREND; SURSTA; SURTYP; TECSOU;
+
+Set Attribute_B:      INFORM; NINFOM;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+This object class defines an area within which the details of the 
+source survey(s) used for chart compilation are uniform.
+
+References:
+
+INT 1:  Not specified
+
+M-4:  170; 171; 174; 175; 176; 177; 178;
+
+Remarks:
+
+The source data provides a guide to the degree of confidence a mariner 
+should have in the adequacy and accuracy of charted depths and 
+positions.
+
+This object is obsolete. It is only shown here for reasons of backward 
+compatibility. Survey source should be encoded under survey 
+reliability (M_SREL).
+
+ DELETED - DO NOT USE
+ META OBJECT CLASSES
+
+
+
+Object Class:  Units of measurement of data      
+
+Acronym: M_UNIT  Code: 311
+Units of measurement of data       M_UNIT 311
+
+Set Attribute_A: DUNITS; HUNITS; PUNITS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of uniform units of depth and/or height measurement.
+ 
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+No remarks;
+ META OBJECT CLASSES
+
+
+
+Object Class:  Vertical datum of data       
+
+Acronym: M_VDAT  Code: 312
+Vertical datum of data        M_VDAT 312
+
+Set Attribute_A: VERDAT;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area of uniform vertical datum.
+
+References:
+
+INT 1:  not specified;
+
+M-4:  not specified;
+
+Remarks:
+
+Distinction: sounding datum;
+ Page intentionally left blank
+
+ 1.4 Collection Object Classes
+ COLLECTION OBJECT CLASSES
+
+
+
+Object Class: Aggregation
+
+Acronym: C_AGGR  Code: 400
+Aggregation C_AGGR 400
+
+ Set Attribute_A: NOBJNM; OBJNAM; 
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Used to identify an aggregation of two or more objects.  This 
+aggregation may be named.
+
+Remarks:
+
+An aggregation could be used to combine objects that are related in 
+some way (is-a-part-of, is-a-component-of) into a higher level object.
+
+For example: an aggregation relationship may be used to form a traffic 
+separation scheme from traffic separation lane parts, boundaries, etc.
+
+Distinction : association; stacked on/stacked under;
+ COLLECTION OBJECT CLASSES
+
+
+
+Object Class: Association
+
+Acronym: C_ASSO  Code: 401
+Association C_ASSO 401
+
+ Set Attribute_A: NOBJNM; OBJNAM;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Used to identify an association between two or more objects.  The 
+association may be named.
+
+Remarks:
+
+For example: an association relationship may be used to indicate that 
+a buoy marks a wreck.
+
+Distinction : aggregation; stacked on/stacked under;
+ COLLECTION OBJECT CLASSES
+
+
+
+Object Class: Stacked on/stacked under
+
+Acronym: C_STAC  Code: 402
+Stacked on/stacked under C_STAC 402
+
+Set Attribute_A:
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+Used to identify the order of stacking of objects (eg. bridge on top 
+ of road). 
+
+Remarks:
+
+The order in which objects are stacked is indicated in the Feature 
+Record to Feature Object Pointer field (FFPT, refer to S-57 Part 3).
+
+Distinction: aggregation; association;
+
+ 1.5 Cartographic Object Classes
+ CARTOGRAPHIC OBJECT CLASSES
+
+
+
+Object Class: Cartographic area
+
+Acronym: $AREAS  Code: 500
+Cartographic area $AREAS 500
+
+ Set Attribute_A: COLOUR; ORIENT; $SCODE; $TINTS;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area in which a certain cartographic symbolization is required.
+
+Remarks:
+
+No remarks.
+ CARTOGRAPHIC OBJECT CLASSES
+
+
+
+Object Class: Cartographic line
+
+Acronym: $LINES  Code: 501
+Cartographic line $LINES 501
+
+ Set Attribute_A: $SCODE;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A line with a certain cartographic symbolization.
+
+Remarks:
+
+No remarks.
+ CARTOGRAPHIC OBJECT CLASSES
+
+
+
+Object Class: Cartographic symbol
+
+Acronym: $CSYMB  Code: 502
+Cartographic symbol $CSYMB 502
+
+ Set Attribute_A: ORIENT; $SCALE; $SCODE;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A point with a certain cartographic symbolization.
+
+Remarks:
+
+No remarks.
+ CARTOGRAPHIC OBJECT CLASSES
+
+ DELETED - DO NOT USE
+
+Object Class: Closing line
+
+Acronym: $CLOLN
+Closing line $CLOLN
+
+ Set Attribute_A: 
+
+Set Attribute_B:      SCAMAX; SCAMIN;
+
+Set Attribute_C:      
+
+
+Definition:
+-----------
+
+Remarks:
+
+This object is obsolete.  It is only shown here for reasons of 
+backward compatibility.  Closing lines should be encoded using the 
+Masking indicator subfield (MASK) of the Feature Record to Spatial 
+Record Pointer field (FSPT, refer to S-57 Part 3).
+
+ DELETED - DO NOT USE
+ CARTOGRAPHIC OBJECT CLASSES
+
+
+
+Object Class: Compass
+
+Acronym: $COMPS  Code: 503
+Compass $COMPS 503
+
+ Set Attribute_A: $CSIZE; RYRMGV; VALACM; VALMAG;
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A circle graduated in degrees clockwise form 0 (north) to 360 used to 
+facilitate measurements of direction. May be oriented to true or 
+magnetic north. (adapted from IHO Dictionary, S-32, 5th Edition, 942)
+
+Remarks:
+
+This object is used to transfer the parameters required to represent 
+the magnetic and/or true compass cartographically.
+ CARTOGRAPHIC OBJECT CLASSES
+
+DELETED - DO NOT USE
+
+Object Class: Shallow water blue
+
+Acronym: $SHABL
+Shallow water blue $SHABL
+
+ Set Attribute_A: $TINTS
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+An area that is to be cartographically represented by a `shallow water 
+blue' tint.
+
+Remarks:
+
+To be used to define depth areas which need to be coloured blue (since 
+the limits of the shallow water blue areas on paper charts are not 
+universally standardised). Corresponding cartographic objects are not 
+required for land area (buff tint) and intertidal area (green tint), 
+since these can be deduced from existing real world objects.
+
+This object class is obsolete. It is only included here for reasons of 
+backward compatibility. Shallow water blue areas should be encoded 
+using the object class cartographic area ($AREAS).
+
+DELETED - DO NOT USE
+ CARTOGRAPHIC OBJECT CLASSES
+
+
+
+Object Class: Text
+
+Acronym: $TEXTS  Code: 504
+Text $TEXTS 504
+
+ Set Attribute_A: $CHARS; COLOUR; $JUSTH; $JUSTV; $NTXST; 
+ $SPACE; $TXSTR; 
+
+Set Attribute_B:      INFORM; NINFOM; NTXTDS; PICREP; SCAMAX; 
+ SCAMIN; TXTDSC;
+
+Set Attribute_C:      RECDAT; RECIND; SORDAT; SORIND;
+
+
+Definition:
+-----------
+
+A text string that is to be represented using a certain cartographic 
+symbolization.
+
+Remarks:
+
+May be used for all text strings which are required to be represented 
+in graphical form with particular positioning and display 
+characteristics.
+
+Rotation and curving of text can be handled by the use of 2 or more 
+x,y coordinate pairs in the associated spatial object.

Added: packages/openev/branches/upstream/current/contrib/S52/ex0_xwin.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/ex0_xwin.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/ex0_xwin.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,227 @@
+/***********************************************************************/
+/* This example code demonstrates an OpenGL application program that   */
+/*   displays a blue cube and a red pyramid in a simple X window.      */
+/*                                                                     */
+/*                                                                     */
+/* The features of this example include:                               */
+/*   - Opens an X window                                               */
+/*   - Creates an OpenGL viewport                                      */
+/*   - Renders a simple static world                                   */
+/*                                                                     */
+/* Considerations:                                                     */
+/*   - A lot of code is necessary to open the GLX window (X w/ OpenGL) */
+/*   - The main loop would typically watch for many more X Events      */
+/*                                                                     */
+/* This application uses the files:                                    */
+/*   - shapes.c  (Bill Sherman's simple OpenGL shapes)                 */
+/*                                                                     */
+/* Copyright 1998, University of Illinois Board of Trustees            */
+/***********************************************************************/
+#include <stdio.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include "S52GL.h"
+
+#include <stdlib.h>       // exit()
+
+
+void	draw_world();
+extern void setupData();
+static int      snglBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 4, None};
+static int      dblBuf[] = {GLX_RGBA,
+                            GLX_DEPTH_SIZE, 4,
+                            GLX_DOUBLEBUFFER,
+                            GLX_RED_SIZE,3,
+                            GLX_GREEN_SIZE,3,
+                            GLX_BLUE_SIZE,3,
+                            GLX_STENCIL_SIZE,1,
+                            None};
+
+Display        *dpy;
+Window          win;
+GLboolean       doubleBuffer = GL_TRUE;
+
+
+int main(int argc, char* argv[])
+{
+    XVisualInfo    *vi;
+    Colormap        cmap;
+    XSetWindowAttributes swa;
+    GLXContext      cx;
+    XEvent          event;
+    int             dummy;
+
+    /********************************/
+    /*** Initialize GLX routines  ***/
+    /********************************/
+    /*** open a connection to the X server ***/
+    dpy = XOpenDisplay(NULL);
+    if (dpy == NULL) {
+        perror("could not open display");
+        exit(0);
+    }
+
+    /*** make sure OpenGL's GLX extension supported ***/
+    if (!glXQueryExtension(dpy, &dummy, &dummy)) {
+        perror("X server has no OpenGL GLX extension");
+        exit(0);
+    }
+
+    /*** find an appropriate visual ***/
+    /* find an OpenGL-capable RGB visual with depth buffer */
+    vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
+    if (vi == NULL) {
+        vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
+        if (vi == NULL) {
+            perror("no RGB visual with depth buffer");
+            exit(0);
+        }
+        doubleBuffer = GL_FALSE;
+    }
+    if (vi->class != TrueColor) {
+        perror("TrueColor visual required for this program");
+        exit(0);
+    }
+
+    /*** create an OpenGL rendering context  ***/
+    /* create an OpenGL rendering context */
+    cx = glXCreateContext(dpy, vi, /* no sharing of display lists */ None,
+                          /* direct rendering if possible */ GL_TRUE);
+    if (cx == NULL) {
+        perror("could not create rendering context");
+        exit(0);
+    }
+
+    /*** create an X window with the selected visual ***/
+    /* create an X colormap since probably not using default visual */
+    cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
+    swa.colormap = cmap;
+    swa.border_pixel = 0;
+    swa.event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask;
+    win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, 493, 493, 0, vi->depth,
+                        InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
+    XSetStandardProperties(dpy, win, "glxsimple", "glxsimple", None, argv, argc, NULL);
+
+    /*** bind the rendering context to the window ***/
+    glXMakeCurrent(dpy, win, cx);
+
+    /*** request the X window to be displayed on the screen ***/
+    XMapWindow(dpy, win);
+
+    /*** configure the OpenGL context for rendering ***/
+    //z	glEnable(GL_DEPTH_TEST);/* enable depth buffering */
+    glDepthFunc(GL_LESS);	/* pedantic, GL_LESS is the default */
+    glClearDepth(1.0);	/* pedantic, 1.0 is the default */
+    /* frame buffer clears should be to black */
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    /* set up projection transform */
+    //glMatrixMode(GL_PROJECTION);
+    //glLoadIdentity();
+    //glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);
+    /* establish initial viewport */
+    glViewport(0, 0, 493, 493);	/* pedantic, full window size is default viewport */
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    //      glOrtho(0,100, 100,0, -1,1);
+    int width = 493;
+    int height = 493;
+    glOrtho(-width/2.0, width/2.0, -height/2.0, height/2.0, -1000.0f, 1000.0f);
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glDisable(GL_NORMALIZE);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_DITHER);
+    glShadeModel(GL_FLAT);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glRotated(0.0, 0.0, 0.0, 1.0);
+    //    glScale(1500, 1500,1.0);
+    glScaled(3944, 3944,1.0);
+
+    glTranslated(-10.4375, -55.44625, 0.0);
+
+    S52_GL_initGL(dpy, 0);
+    if (!S52_GL_validCtx()){
+      printf("not a proper context. Exiting ...");
+      exit(1);
+    }
+    
+    setupData();
+    fprintf(stderr, "Now starting ... \n");
+    /***************************/
+    /*** do world simulation ***/
+    /***************************/
+    while (1) {
+        do {
+            XNextEvent(dpy, &event);
+            switch (event.type) {
+                case ConfigureNotify:
+                    ;//	glViewport(0, 0, event.xconfigure.width, event.xconfigure.height);
+            }
+        } while (XPending(dpy));
+
+        /*** Handle projection ***/
+        //		glMatrixMode(GL_MODELVIEW);
+        //	glLoadIdentity();
+        //	glTranslatef(0.0, -2.0, -3.0);
+
+        /*** Render the world ***/
+        //		draw_world();
+        glClearColor(0,0,0,1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        S52_GL_draw(0);
+        /*** Swap buffers (or flush GL cue) ***/
+        if (doubleBuffer)
+            glXSwapBuffers(dpy, win);
+        else
+            glFlush();
+        glXMakeCurrent(dpy, win, cx);
+    }
+
+    return 1;
+}
+
+
+
+/* ----------------8<-----------------8<-----------------8<----------------*/
+/* In a non-example applicatation, the following would be a separate file. */
+
+/**************************/
+/**************************/
+/** The Graphics section **/
+/**************************/
+/**************************/
+
+void draw_cube();
+void draw_pyramid();
+
+
+/*********************/
+/* draw_world(): ... */
+/*********************/
+void draw_world(void)
+{
+    glShadeModel(GL_SMOOTH);
+
+    /**************************************/
+    /* clear the screen -- very important */
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    glClearDepth(1.0);
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+    /************************/
+    /* draw all the objects */
+
+    /* a blue cube */
+    glColor3ub(100, 100, 255);
+    glTranslatef(-2.0, 1.0, -6.0);
+    //	draw_cube();
+
+    /* a red pyramid */
+    glColor3ub(255, 100, 100);
+    glTranslatef(4.0, 0.0, 0.0);
+    //	draw_pyramid();
+}

Added: packages/openev/branches/upstream/current/contrib/S52/gvS57layer.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/gvS57layer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/gvS57layer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,221 @@
+// gvS57layer.c: S57 layer classe for OpenEV
+//
+// Project:  OpENCview/OpenEV
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+#include "gvS57layer.h"
+
+#include "S52GL.h"          // S52_GL_objectSetup()
+#include "S52PL.h"          // S52_PL_lookup(), S52_LUP, S52_LUPtnm
+#include "S57src.h"         // S57_loadObject()
+#include "S52utils.h"       // PRINTF()
+
+#include <stdlib.h>
+#include <stdio.h>          // printf()
+#include <string.h>         // strncmp()
+
+#include <gtk/gtksignal.h>
+//#include <gtkgl/gdkgl.h>    // GdkGLContext
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include <gmodule.h>        // g_module_init()
+
+#include <glib.h>           // GQuark, datalist, ...
+
+static GModule *self = NULL;  // handel to libS52.so (this)
+
+static void _motion_handle_hint(GtkWidget *view, GdkEventMotion *event)
+{
+    if (event->type==GDK_BUTTON_RELEASE && event->state & GDK_SHIFT_MASK) {
+        S57_geo *geoData = S52_GL_doPick(event->x, event->y);
+        S57_unlinkObj(geoData);
+
+        //gtk_signal_emit_by_name(GTK_OBJECT(view), "gldraw");
+    }
+}
+
+static void gv_S57_layer_setup(GvLayer *rlayer, GvViewArea *view)
+// load S57 layer's objects
+{
+    static int first = 1;
+    //static int debug = 0;
+    GdkVisual    *_visual;
+    GdkGLContext *_glcontext = NULL;
+
+    int   i       = 0;
+    gint  nShapes = 0;
+    char *name    = NULL;
+
+    if (NULL == rlayer) {
+        PRINTF("ERROR no layer\n");
+        return;
+    }
+
+    // at this point we can connect the signal from class view
+    if (first) {
+        ////////////////////////////////////////////////////////////////
+        //
+        // set visual & get GL info
+        //
+        {
+            int attrlist[] = {
+                GDK_GL_RGBA,
+                GDK_GL_DOUBLEBUFFER,
+                //GDK_GL_DEPTH_SIZE,   4,
+                GDK_GL_RED_SIZE,     3,
+                GDK_GL_GREEN_SIZE,   3,
+                GDK_GL_BLUE_SIZE,    3,
+                GDK_GL_STENCIL_SIZE, 1,
+                GDK_GL_NONE
+            };
+
+            printf("gv_S57_layer_setup initialising \n");
+            if (!S52_GL_validCtx()) {
+                // check/setup GL for S52 rendering
+
+                //g_return_if_fail(GTK_WIDGET_REALIZED(GTK_WIDGET(view)));
+
+                //gtk_gl_area_make_current(GTK_GL_AREA(view));
+                _visual = gdk_gl_choose_visual(attrlist);
+                if (_visual == NULL) {
+                    PRINTF("gdk_gl_choose_visual.. failed \n");
+                    exit(0);
+                    //return 0;
+                }
+
+                //old_glcontext = *glcontext;
+                //glcontext = gdk_gl_context_share_new(visual, NULL , TRUE);
+                _glcontext = gdk_gl_context_share_new(_visual, NULL , TRUE);
+                if (_glcontext == NULL || !S52_GL_validCtx()) {
+                    PRINTF("gdk_gl_context_share_new.. failed \n");
+                    exit(0);
+                    //return 0;
+                }
+
+            }
+            S52_GL_init(GDK_DISPLAY(),0);
+        }
+
+        gtk_signal_connect_object(GTK_OBJECT(view), "gldraw",
+                                  GTK_SIGNAL_FUNC(S52_GL_draw),
+                                  GTK_OBJECT(view));
+
+        gtk_signal_connect_object(GTK_OBJECT(view), "button-release-event",
+                                  GTK_SIGNAL_FUNC(_motion_handle_hint),
+                                  GTK_OBJECT(view));
+
+        first = 0;
+
+        /*
+        {
+            // set
+            S57_geo *geoData = S57_loadObject("NORTHAR1", NULL);
+            S52_obj *obj     = S52_PL_getObj(geoData);
+            S52_GL_objectSetup(obj);
+        }
+        */
+    }
+
+    nShapes = gv_shapes_num_shapes(GV_SHAPES_LAYER(rlayer)->data);
+    name    = (char *)gv_data_get_name(GV_DATA(rlayer));
+    printf("LAYER NAME: %s (%d)\n", name, nShapes);
+
+    for (i=0; i<nShapes; ++i) {
+        GvShape *shape = gv_shapes_get_shape(GV_SHAPES_LAYER(rlayer)->data, i);
+
+        S52_loadObject(name, (srcData*)shape);
+    }
+
+    return;
+}
+
+
+//-----------------------------------------------
+//
+// INIT SECTION
+//
+//-----------------------------------------------
+
+static void gv_S57_layer_init(GvS57Layer *rlayer)
+{
+    // layer's pre-init stuff goes here
+    //PRINTF("connecting 'setup' signal\n");
+
+    gtk_signal_connect_object(GTK_OBJECT(layer), "setup",
+                              GTK_SIGNAL_FUNC(gv_S57_layer_setup),
+                              GTK_OBJECT(layer));
+}
+
+#if 0
+static void gv_S57_layer_finalize     (GvS57Layer *rlayer, GvViewArea *view)
+{
+    return;
+}
+#endif
+
+
+//--------------------------------------------------
+//
+// PLUD-IN SECTION
+//
+//--------------------------------------------------
+
+
+void          _layer_init(GvShapesLayer *layer)
+{
+    return gv_S57_layer_init(layer);
+}
+
+const gchar * _ogr_driver_name()
+{
+    // FIXME: get this programaticaly
+    //OGRSFDriverH  hDriver =  ... load_S57_driver_ ...
+    //return OGR_Dr_GetName(hDriver);
+
+    // pulled from: gdal/ogr/ogrsf_frmts/s57/ogrs57driver.cpp:69
+    // in  'const char *OGRS57Driver::GetName()'
+    return "S57";
+}
+
+const gchar *g_module_check_init(GModule *module)
+{
+    PRINTF("loading libS52.so ...\n");
+
+    if (NULL == self) {
+        self = module;
+        g_print("self name = %s\n", g_basename(g_module_name(self)));
+        // will print "self name = libS52.so"
+    }
+    return NULL;
+}
+
+void         g_module_unload()
+{
+    PRINTF("unloading libS52.so ...\n");
+
+    S52_PL_done();
+    S52_GL_done();
+
+    return;
+}

Added: packages/openev/branches/upstream/current/contrib/S52/gvS57layer.h
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/gvS57layer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/gvS57layer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,65 @@
+// gvS57layer.h: S57 layer classe header for OpenEV
+//
+// Project:  OpENCview/OpenEV
+
+/* 
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef _GVS57LAYER_H_
+#define _GVS57LAYER_H_
+
+#include "gvshapeslayer.h"  // GvShapes
+#include <gmodule.h>        // GModule
+
+
+#define GV_TYPE_S57_LAYER          (gv_S57_layer_get_type ())
+#define GV_S57_LAYER(obj)          (GTK_CHECK_CAST ((obj), GV_TYPE_S57_LAYER, GvS57Layer))
+#define GV_S57_LAYER_CLASS(klass)  (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_S57_LAYER, GvS57LayerClass))
+#define GV_IS_S57_LAYER(obj)       (GTK_CHECK_TYPE ((obj),GV_TYPE_S57_LAYER))
+#define GV_IS_S57_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), \
+                                                           GV_TYPE_S57_LAYER))
+
+typedef struct _GvS57Layer       GvS57Layer;
+typedef struct _GvS57LayerClass  GvS57LayerClass;
+
+
+struct _GvS57Layer
+{
+    GvShapesLayer layer;
+};
+
+struct _GvS57LayerClass
+{
+    GvShapesLayerClass parent_class;
+};
+
+GtkType     gv_S57_layer_get_type(void);
+GtkObject*  gv_S57_layer_new(GvShapes *shapes);
+
+void        gv_S57_layer_init(GvShapesLayer *layer);
+
+// used in plug-in mode --put here for 'completess'
+// since it's part of the interface
+void         _layer_init(GvShapesLayer *layer);
+const gchar *_ogr_driver_name();
+const gchar *g_module_check_init(GModule *module);
+void         g_module_unload();
+
+#endif /* __GVS57LAYER_H__ */
+

Added: packages/openev/branches/upstream/current/contrib/S52/ogrS57layers.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/ogrS57layers.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/ogrS57layers.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,118 @@
+// ogrS57layers.c: load feature from OGR directly (used by ex0)
+//
+// Project:  OpENCview
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+#include "S52utils.h"   // PRINTF()
+
+#include "ogr_api.h"    // OGROpen(), etc.
+
+#include "S57src.h"     // S57_loadObject()
+#include "S52PL.h"      // S52_PL_getLUP()
+#include "S52GL.h"      // S52_GL_objectSetup()
+
+
+static int _loadVector(const char *filename, int iLayer)
+{
+
+    OGRDataSourceH hDS;
+    OGRSFDriverH hDriver;
+    hDS = OGROpen( filename, FALSE, &hDriver );
+    int cntFeature = 0;
+
+    if (!hDS){
+      printf("Could not load file !! \n");
+      return 0;
+    }
+    
+    for (iLayer=0; iLayer<OGR_DS_GetLayerCount(hDS); iLayer++) {
+        OGRFeatureH     hFeature = NULL;
+        OGRLayerH       hLayer   = OGR_DS_GetLayer(hDS, iLayer);
+        OGRFeatureDefnH defn     = OGR_L_GetLayerDefn(hLayer);
+        const char     *name     = OGR_FD_GetName(defn);
+
+        while ( (hFeature = OGR_L_GetNextFeature(hLayer)) != NULL ) {
+            S57_geo *geoData = S57_loadObject(hFeature);
+            //          OGR_F_Destroy(hFeature); I see dumps here :-(
+
+            if (!geoData){
+                printf("missed a feature !!!\n");
+                continue;
+            }
+            cntFeature++;
+
+
+            // FIXME: not multi-thread because of LUP->cmdList not locked
+            S52_LUP *LUP     = S52_PL_getLUP(name, geoData);
+
+            if (LUP == NULL)
+                PRINTF("NO look-up for Object: %s\n", name);
+            else
+                S52_GL_objectSetup(LUP, geoData, 0);
+
+        }
+    }
+
+    OGR_DS_Destroy (hDS);
+
+    return cntFeature;
+}
+
+
+void setupData(int loadData)
+{
+  {   // setup env. var for OGR/S57
+    char *env = g_getenv("OGR_S57_OPTIONS");
+
+    
+    if (!env ||
+        NULL == strstr(env, "UPDATES:ON")        ||
+        NULL == strstr(env, "LNAM_REFS:ON")      ||
+        NULL == strstr(env, "SPLIT_MULTIPOINT:ON"))
+    {
+        //	env = g_string_new(env);
+#ifdef SOLARIS
+      putenv("OGR_S57_OPTIONS=LNAM_REFS:ON,UPDATES:ON,SPLIT_MULTIPOINT:ON");
+#else
+      setenv("OGR_S57_OPTIONS", "LNAM_REFS:ON,UPDATES:ON,SPLIT_MULTIPOINT:ON", 1);
+#endif
+    }
+
+    OGRRegisterAll();
+
+    if (!loadData)
+      return;
+    
+    {
+        valueBuf chartPath = {'\0'};
+        //char chartPath[200];
+        if (0 == S52_getConfig(CONF_CHART, &chartPath)) {
+            printf("  .. exiting !\n");
+            exit(0);
+        } else {
+            _loadVector(chartPath, 0);
+            //_loadVector(VecViewFront, chartPath, 0);
+        }
+    }
+
+  }
+}

Added: packages/openev/branches/upstream/current/contrib/S52/s52test.c
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/s52test.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/s52test.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,481 @@
+// s52test.c: test driver for libS52.a, libgv.a, libGDAL.so --no python
+//            Derived from Frank Warmerdan openev/testmain.c rev 1.28
+//
+// Project:  OpENCview/OpenEV
+
+/*
+    This file is part of the OpENCview project, a viewer of ENC
+    Copyright (C) 2000-2004  Sylvain Duclos sduclos at users.sourceforgue.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+//#include "gvS57layer.h"         // gv_S57_layer_new()
+#include "S52utils.h"           // PRINTF()
+#include "S57data.h"            // geocoord
+
+#include <gtk/gtk.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtkgl/gtkglarea.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#include <glib.h>               // GString
+
+#include "gvviewarea.h"         // GV_VIEW_AREA()
+#include "gvtoolbox.h"          // GvToolBox
+#include "gvviewlink.h"         // GvViewLink
+#include "gvundo.h"             // gv_undo_register_data()
+#include "gvselecttool.h"       // gv_selection_tool_new()
+#include "gvzoompantool.h"      // gv_zoompan_tool_new()
+#include "gvpointtool.h"        // gv_point_tool_new()
+#include "gvlinetool.h"         // gv_line_tool_new()
+#include "gvareatool.h"         // gv_area_tool_new()
+#include "gvnodetool.h"         // gv_node_tool_new()
+#include "gvroitool.h"          // gv_roi_tool_new()
+#include "ogr_api.h"            // OGROpen(), etc.
+
+//#include <math.h>               // HUGE_VAL
+
+
+
+static GvToolbox  *toolbox      = NULL;
+static GvViewLink *link         = NULL;
+
+static GtkWidget  *VecViewBack  = NULL;
+static GtkWidget  *VecViewFront = NULL;
+static GtkWidget  *VecView      = NULL;
+static GtkWidget  *glarea       = NULL;
+
+static const char *_filename    = NULL;
+
+// FOXME: get this programmaticaly
+#define DRVNAME "S57"
+
+static void _key_press_cb( GtkObject * object, GdkEventKey * event )
+
+{
+    GvViewArea     *view = GV_VIEW_AREA(object);
+
+    if( event->keyval == 't' ) {
+        GTimeVal      cur_time;
+        //double    start_time = g_get_current_time_as_double();
+        double    start_time = 0.0;
+        double    end_time, spf;
+        int       i, frame_count = 20;
+
+        g_get_current_time( &cur_time );
+        start_time = cur_time.tv_sec + cur_time.tv_usec / 1000000.0;
+
+        for( i = 0; i < frame_count; i++ )
+            gv_view_area_expose(GTK_WIDGET(view), NULL);
+
+        g_get_current_time( &cur_time );
+        end_time = cur_time.tv_sec + cur_time.tv_usec / 1000000.0;
+        //end_time =  g_get_current_time_as_double();
+
+        spf = (end_time - start_time) / frame_count;
+        printf( "Speed is %.2ffps\n", 1.0 / spf );
+    }
+}
+
+
+static void _toolbar_callback(GtkWidget *widget, gpointer data)
+{
+    gv_toolbox_activate_tool(toolbox, (gchar*)data);
+}
+
+
+static void _link_callback(GtkWidget *widget, gpointer data)
+{
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+        gv_view_link_enable(link);
+    else
+        gv_view_link_disable(link);
+}
+
+
+static void _create_toolbar()
+{
+    GtkWidget *win;
+    GtkWidget *toolbar;
+    GtkWidget *but;
+
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    toolbar = gtk_toolbar_new(GTK_ORIENTATION_VERTICAL, GTK_TOOLBAR_TEXT);
+    gtk_container_add(GTK_CONTAINER(win), toolbar);
+
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   NULL,
+                                   "Zoom",
+                                   "Zoom tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "zoompan");
+
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   but,
+                                   "Select",
+                                   "Selection tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "select");
+
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   but,
+                                   "Draw Points",
+                                   "Point drawing tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "point");
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   but,
+                                   "Draw Line",
+                                   "Line drawing tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "line");
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   but,
+                                   "Draw Area",
+                                   "Area drawing tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "area");
+
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   but,
+                                   "Edit Node",
+                                   "Node edit tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "node");
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+                                   but,
+                                   "Draw ROI",
+                                   "ROI drawing tool",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_toolbar_callback),
+                                   (void *) "roi");
+    but =
+        gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+                                   GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
+                                   NULL,
+                                   "Link",
+                                   "Link views together",
+                                   NULL,
+                                   NULL,
+                                   GTK_SIGNAL_FUNC(_link_callback),
+                                   NULL);
+
+    gtk_signal_connect(GTK_OBJECT(win), "delete-event",
+                       GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
+
+    gtk_widget_show(toolbar);
+    gtk_widget_show(win);
+}
+
+static void _gv_loadLayer_cb(OGRLayerH ogrlayer, void *drvName)
+// callback to load the layer into GV
+{
+    GtkObject *gvlayer    = NULL;
+    GvShapes  *shape_data = GV_SHAPES(gv_shapes_from_ogr_layer(ogrlayer));
+        
+    gv_data_set_property(GV_DATA(shape_data), "_filename",        _filename);
+    gv_data_set_property(GV_DATA(shape_data), "_ogr_driver_name", DRVNAME);
+
+    gv_undo_register_data(GV_DATA(shape_data));
+
+    gvlayer = gv_shapes_layer_new(shape_data);
+
+    gv_view_area_add_layer(view, gvlayer);
+    gv_view_area_set_active_layer(view, gvlayer);
+
+    return;
+}
+
+static void _loadCell(const char *filename)
+{
+    GvViewArea *view = GV_VIEW_AREA(VecView);
+
+    S52_loadCell(filename, _gv_loadLayer_cb);
+
+    return;
+}
+
+static gint _init(GtkWidget *widget)
+{
+  // OpenGL functions can be called only if make_current returns true
+  if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) {
+      glViewport(0,0, widget->allocation.width, widget->allocation.height);
+      glMatrixMode(GL_PROJECTION);
+      glLoadIdentity();
+      glOrtho(0,100, 100,0, -1,1);
+      glMatrixMode(GL_MODELVIEW);
+      glLoadIdentity();
+  }
+
+  return TRUE;
+}
+
+
+static gint _draw(GtkWidget *widget, GdkEventExpose *event)
+// When widget is exposed it's contents are redrawn.
+{
+  // Draw only last expose.
+  if (event->count > 0)
+      return TRUE;
+
+  // OpenGL functions can be called only if make_current returns true
+  if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) {
+
+      // Draw simple triangle
+      glClearColor(0,0,0,1);
+      glClear(GL_COLOR_BUFFER_BIT);
+      glColor3f(1,1,1);
+      glBegin(GL_TRIANGLES);
+      glVertex2f(10,10);
+      glVertex2f(10,90);
+      glVertex2f(90,90);
+      glEnd();
+
+      // Swap backbuffer to front
+      gtk_gl_area_swapbuffers(GTK_GL_AREA(widget));
+
+  }
+
+  return TRUE;
+}
+
+static gint _reshape(GtkWidget *widget, GdkEventConfigure *event)
+// When glarea widget size changes, viewport size is set to match the new size
+{
+  // OpenGL functions can be called only if make_current returns true
+  if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) {
+      glViewport(0,0, widget->allocation.width, widget->allocation.height);
+  }
+
+  return TRUE;
+}
+
+static gint _glareaNew(GtkWidget *window)
+{
+    int attrlist[] = {
+        GDK_GL_RGBA,
+        GDK_GL_DOUBLEBUFFER,
+        GDK_GL_DEPTH_SIZE,   4,
+        GDK_GL_RED_SIZE,     3,
+        GDK_GL_GREEN_SIZE,   3,
+        GDK_GL_BLUE_SIZE,    3,
+        GDK_GL_STENCIL_SIZE, 1,
+        GDK_GL_NONE
+    };
+
+    // Create new OpenGL widget.
+    glarea = GTK_WIDGET(gtk_gl_area_new(attrlist));
+    // Events for widget must be set before X Window is created
+    gtk_widget_set_events(GTK_WIDGET(glarea),
+                          GDK_EXPOSURE_MASK|
+                          GDK_BUTTON_PRESS_MASK);
+
+    // Connect signal handlers
+    // Redraw image when exposed.
+    gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
+                       GTK_SIGNAL_FUNC(_draw), NULL);
+    // When window is resized viewport needs to be resized also.
+    gtk_signal_connect(GTK_OBJECT(glarea), "configure_event",
+                       GTK_SIGNAL_FUNC(_reshape), NULL);
+    // Do initialization when widget has been realized.
+    gtk_signal_connect(GTK_OBJECT(glarea), "realize",
+                       GTK_SIGNAL_FUNC(_init), NULL);
+
+    // set minimum size
+    gtk_widget_set_usize(GTK_WIDGET(glarea), 100,100);
+
+    // put glarea into window and show it all
+    gtk_container_add(GTK_CONTAINER(window),GTK_WIDGET(glarea));
+    gtk_widget_show(GTK_WIDGET(glarea));
+
+    return 1;
+}
+
+int main(int argc, char **argv)
+{
+    GtkWidget *win;
+    GtkWidget *swin;
+    GString   *genv = NULL;
+
+    //g_thread_init(NULL);
+    //gdk_threads_init();
+
+    gtk_init(&argc, &argv);
+    //printf("after gtk_init\n");
+
+    VecView = gv_view_area_new();
+    VecViewBack = VecView;
+    //VecViewBack  = gv_view_area_new();
+    //VecViewFront = gv_view_area_new();
+
+    if (NULL == VecViewBack){
+        printf("main.c:ERROR: VecView == NULL!!  no OpenGL .. exit!\n");
+        exit(0);
+    }
+
+    {   //unsigned int value = HUGE_VAL;
+        //int nan   = NaN;                             printf
+        //PRINTF("int infinity value = -2147483648 =  HUGE_VAL = %i\n", value);
+        //PRINTF("int infinity size = 8 = sizeof(HUGE_VAL) = %i\n", sizeof(value));
+        //PRINTF("int          : sizeof(int)    = %i\n", sizeof(int));
+        //PRINTF("float        : sizeof(float)  = %i\n", sizeof(float));
+        //PRINTF("double       : sizeof(double) = %i\n", sizeof(double));
+        //PRINTF("unsigned long: sizeof(unsigned long)= %i\n",sizeof(unsigned long));
+        //exit(0);
+    }
+
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    //glareaNew(win);
+
+    toolbox = GV_TOOLBOX(gv_toolbox_new());
+    gv_toolbox_add_tool(toolbox, "select",  gv_selection_tool_new());
+    gv_toolbox_add_tool(toolbox, "zoompan", gv_zoompan_tool_new());
+    gv_toolbox_add_tool(toolbox, "point",   gv_point_tool_new());
+    gv_toolbox_add_tool(toolbox, "line",    gv_line_tool_new());
+    gv_toolbox_add_tool(toolbox, "area",    gv_area_tool_new());
+    gv_toolbox_add_tool(toolbox, "node",    gv_node_tool_new());
+    gv_toolbox_add_tool(toolbox, "roi",     gv_roi_tool_new());
+
+    link = GV_VIEW_LINK(gv_view_link_new());
+
+
+    {   // setup env. var for OGR/S57
+        gchar *env = g_getenv("OGR_S57_OPTIONS");
+
+
+        if (env  == NULL                             ||
+            NULL == strstr(env, "UPDATES:ON")        ||
+            NULL == strstr(env, "LNAM_REFS:ON")      ||
+            NULL == strstr(env, "SPLIT_MULTIPOINT:ON"))
+        {
+            genv = g_string_new(env);
+            setenv("OGR_S57_OPTIONS", "LNAM_REFS:ON,UPDATES:ON,SPLIT_MULTIPOINT:ON", 1);
+        }
+
+        //PRINTF("CURRENT SETUP:\n");
+        //PRINTF("   OGR_S57_OPTIONS = %s \n", g_getenv("OGR_S57_OPTIONS"));
+        //PRINTF("   S57_CSV         = %s \n", g_getenv("S57_CSV"));
+        //PRINTF("   OGRMakefile CFG = %s \n", g_getenv("CFG"));
+        //PRINTF("   gvgeocoord size = %i \n", sizeof(geocoord));
+        //PRINTF("   libgv  HAVE_OGR = %s \n", (gv_have_ogr_support())? "Yes":"No");
+        //PRINTF("   libgv  HAVE_S52 = %s \n", (gv_have_S52_support())? "Yes":"No");
+
+
+        g_assert(gv_have_ogr_support());
+        //g_assert(gv_have_S52_support());
+        //g_on_error_query (NULL);
+    }
+
+    gtk_window_set_default_size( GTK_WINDOW(win), 512, 512 );
+//    gtk_window_set_default_size( GTK_WINDOW(win), 200, 200 );
+
+    // 2D
+    gv_view_area_set_mode(GV_VIEW_AREA(VecViewBack ), 0);
+    //gv_view_area_set_mode(GV_VIEW_AREA(VecViewFront), 0);
+
+    gtk_drawing_area_size(GTK_DRAWING_AREA(VecViewBack ), 500, 500);
+    //gtk_drawing_area_size(GTK_DRAWING_AREA(VecViewFront), 200, 200);
+//    gtk_drawing_area_size(GTK_DRAWING_AREA(VecView), 200, 200);
+
+    swin = gtk_scrolled_window_new(NULL, NULL);
+
+    gtk_container_add(GTK_CONTAINER(win), swin);
+    gtk_container_add(GTK_CONTAINER(swin), VecViewBack);
+
+
+    //gv_view_area_add_layer(GV_VIEW_AREA(view), gv_shapes_layer_new(shapes));
+
+    gtk_signal_connect_object(GTK_OBJECT(VecViewBack), "key-press-event",
+                              GTK_SIGNAL_FUNC(_key_press_cb),
+                              GTK_OBJECT(VecViewBack) );
+
+    //gtk_signal_connect_object(GTK_OBJECT(VecViewFront), "key-press-event",
+    //                          GTK_SIGNAL_FUNC(_key_press_cb),
+    //                          GTK_OBJECT(VecViewFront) );
+
+    gtk_widget_show(VecViewBack );
+    //gtk_widget_show(VecViewFront);
+    gtk_widget_show(swin);
+    gtk_widget_show(win);
+    gtk_widget_grab_focus(VecViewBack);
+
+    gtk_signal_connect(GTK_OBJECT(win), "delete-event",
+                       GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
+
+    gtk_quit_add_destroy(1, GTK_OBJECT(win));
+
+    gv_tool_activate(GV_TOOL(toolbox), GV_VIEW_AREA(VecViewBack));
+    gv_view_link_register_view(link, GV_VIEW_AREA(VecViewBack));
+    //gv_view_link_register_view(link, GV_VIEW_AREA(VecViewFront));
+    gv_toolbox_activate_tool( toolbox, "zoompan" );
+
+
+    _create_toolbar();
+
+    {
+        valueBuf chartPath = {'\0'};
+        if (0 == S52_getConfig(CONF_CHART, &chartPath)) {
+            printf("  .. exiting !\n");
+            exit(0);
+        } else {
+            _loadCell(chartPath);
+        }
+    }
+
+    gdk_threads_enter();
+    gtk_main();
+    gdk_threads_leave();
+
+    // put back env. var. as it was
+    if (NULL != genv){
+        setenv("OGR_S57_OPTIONS", genv->str, 1);
+        g_string_free(genv, TRUE);
+    }
+
+    // FIXME: check this --for mem leak
+    //g_mem_profile ()
+
+    return 0;
+}

Added: packages/openev/branches/upstream/current/contrib/S52/s52test.conf
===================================================================
--- packages/openev/branches/upstream/current/contrib/S52/s52test.conf	                        (rev 0)
+++ packages/openev/branches/upstream/current/contrib/S52/s52test.conf	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,40 @@
+# s52test.conf: basic configuration file for testing
+# 
+# line with '#' are ignore 
+#
+# entry are made of LABEL/VALUE  strings pair (space delimited)
+#
+# Replace <your_full_path_to_ENC> to suit your setup for label CHART.
+# Label PLIB, and OGR_S57_OPTIONS have hardcoded default value.
+
+
+### HARDCODED ####
+# lib path & name
+#PLIB 		<some_path>
+
+### DEFAULT CHART TO LOAD ###
+#
+#CHART <your_full_path_to_ENC>
+CHART /home/user/dev/gis/data/S57/a/CA49995A.000
+#CHART /home/user/dev/gis/data/S57/b/CA49995B.000
+#CHART /home/user/dev/gis/data/S57/c/CA49995C.000
+#CHART /home/user/dev/gis/data/S57/d/CA49995D.000
+#CHART /home/user/dev/gis/data/S57/i/CA39995I.000
+
+# chart catalog path 
+#CATALOG	<your_full_path_to_file>
+#CATALOG    /home/user/gis/data/S57/a/CATALOG.030
+
+
+### ENVIRONNEMENT VARIAVLE OVERRIDE ###
+#
+# path to IHOOCDD.XXX or s57objectclasses.csv/s57attributes.csv
+#S57_CSV
+
+# OGR behaviour
+#OGR_S57_OPTIONS=LNAM_REFS,UPDATES,SPLIT_MULTIPOINT,ADD_SOUNDG_DEPTH
+#FIXME: define behaviour options (!?)
+
+
+
+

Added: packages/openev/branches/upstream/current/crs.c
===================================================================
--- packages/openev/branches/upstream/current/crs.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/crs.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,485 @@
+/***************************************************************************/
+/***************************************************************************/
+/*
+    CRS.C - Center for Remote Sensing rectification routines
+
+    Written By: Brian J. Buckley
+
+            At: The Center for Remote Sensing
+                Michigan State University
+                302 Berkey Hall
+                East Lansing, MI  48824
+                (517)353-7195
+
+    Written: 12/19/91
+
+    Last Update: 12/26/91 Brian J. Buckley
+    Last Update:  1/24/92 Brian J. Buckley
+      Added printout of trnfile. Triggered by BDEBUG.
+    Last Update:  1/27/92 Brian J. Buckley
+      Fixed bug so that only the active control points were used.
+*/
+/***************************************************************************/
+/***************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "crs.h"
+
+typedef double DOUBLE;
+
+/* STRUCTURE FOR USE INTERNALLY WITH THESE FUNCTIONS.  THESE FUNCTIONS EXPECT
+   SQUARE MATRICES SO ONLY ONE VARIABLE IS GIVEN (N) FOR THE MATRIX SIZE */
+
+struct MATRIX
+{
+    int     n;     /* SIZE OF THIS MATRIX (N x N) */
+    DOUBLE *v;
+};
+
+/* CALCULATE OFFSET INTO ARRAY BASED ON R/C */
+
+#define M(row,col) m->v[(((row)-1)*(m->n))+(col)-1]
+
+/***************************************************************************/
+/*
+*/
+/***************************************************************************/
+
+#define MSUCCESS     1 /* SUCCESS */
+#define MNPTERR      0 /* NOT ENOUGH POINTS */
+#define MUNSOLVABLE -1 /* NOT SOLVABLE */
+#define MMEMERR     -2 /* NOT ENOUGH MEMORY */
+#define MPARMERR    -3 /* PARAMETER ERROR */
+#define MINTERR     -4 /* INTERNAL ERROR */
+
+/***************************************************************************/
+/*
+    FUNCTION PROTOTYPES FOR STATIC (INTERNAL) FUNCTIONS
+*/
+/***************************************************************************/
+
+static int calccoef(struct Control_Points *,double *,double *,int);
+static int calcls(struct Control_Points *,struct MATRIX *,
+                  DOUBLE *,DOUBLE *,double *,double *);
+static int exactdet(struct Control_Points *,struct MATRIX *,
+                    DOUBLE *,DOUBLE *,double *,double *);
+static int solvemat(struct MATRIX *,DOUBLE *,DOUBLE *,double *,double *);
+static DOUBLE term(int,double,double);
+
+/***************************************************************************/
+/*
+    TRANSFORM A SINGLE COORDINATE PAIR.
+*/
+/***************************************************************************/
+
+int 
+CRS_georef (
+    double e1,  /* EASTINGS TO BE TRANSFORMED */
+    double n1,  /* NORTHINGS TO BE TRANSFORMED */
+    double *e,  /* EASTINGS TO BE TRANSFORMED */
+    double *n,  /* NORTHINGS TO BE TRANSFORMED */
+    double E[], /* EASTING COEFFICIENTS */
+    double N[], /* NORTHING COEFFICIENTS */
+    int order  /* ORDER OF TRANSFORMATION TO BE PERFORMED, MUST MATCH THE
+               ORDER USED TO CALCULATE THE COEFFICIENTS */
+)
+  {
+  DOUBLE e3, e2n, en2, n3, e2, en, n2;
+
+  switch(order)
+    {
+    case 1:
+
+      *e = E[0] + E[1] * e1 + E[2] * n1;
+      *n = N[0] + N[1] * e1 + N[2] * n1;
+      break;
+
+    case 2:
+
+      e2 = e1 * e1;
+      n2 = n1 * n1;
+      en = e1 * n1;
+
+      *e = E[0]      + E[1] * e1 + E[2] * n1 +
+           E[3] * e2 + E[4] * en + E[5] * n2;
+      *n = N[0]      + N[1] * e1 + N[2] * n1 +
+           N[3] * e2 + N[4] * en + N[5] * n2;
+      break;
+
+    case 3:
+
+      e2  = e1 * e1;
+      en  = e1 * n1;
+      n2  = n1 * n1;
+      e3  = e1 * e2;
+      e2n = e2 * n1;
+      en2 = e1 * n2;
+      n3  = n1 * n2;
+
+      *e = E[0]      +
+           E[1] * e1 + E[2] * n1  +
+           E[3] * e2 + E[4] * en  + E[5] * n2  +
+           E[6] * e3 + E[7] * e2n + E[8] * en2 + E[9] * n3;
+      *n = N[0]      +
+           N[1] * e1 + N[2] * n1  +
+           N[3] * e2 + N[4] * en  + N[5] * n2  +
+           N[6] * e3 + N[7] * e2n + N[8] * en2 + N[9] * n3;
+      break;
+
+    default:
+
+      return(MPARMERR);
+      break;
+    }
+
+  return(MSUCCESS);
+  }
+
+/***************************************************************************/
+/*
+    COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS
+*/
+/***************************************************************************/
+
+int 
+CRS_compute_georef_equations (struct Control_Points *cp, double E12[], double N12[], double E21[], double N21[], int order)
+  {
+  double *tempptr;
+  int status;
+
+  if(order < 1 || order > MAXORDER)
+    return(MPARMERR);
+
+  /* CALCULATE THE FORWARD TRANSFORMATION COEFFICIENTS */
+
+  status = calccoef(cp,E12,N12,order);
+  if(status != MSUCCESS)
+    return(status);
+
+  /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS */
+
+  tempptr = cp->e1;
+  cp->e1 = cp->e2;
+  cp->e2 = tempptr;
+  tempptr = cp->n1;
+  cp->n1 = cp->n2;
+  cp->n2 = tempptr;
+
+  /* CALCULATE THE BACKWARD TRANSFORMATION COEFFICIENTS */
+
+  status = calccoef(cp,E21,N21,order);
+
+  /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS BACK */
+
+  tempptr = cp->e1;
+  cp->e1 = cp->e2;
+  cp->e2 = tempptr;
+  tempptr = cp->n1;
+  cp->n1 = cp->n2;
+  cp->n2 = tempptr;
+
+  return(status);
+  }
+
+/***************************************************************************/
+/*
+    COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS
+*/
+/***************************************************************************/
+
+static int 
+calccoef (struct Control_Points *cp, double E[], double N[], int order)
+  {
+  struct MATRIX m;
+  DOUBLE *a;
+  DOUBLE *b;
+  int numactive;   /* NUMBER OF ACTIVE CONTROL POINTS */
+  int status, i;
+
+  /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */
+
+  for(i = numactive = 0 ; i < cp->count ; i++)
+    {
+    if(cp->status[i] > 0)
+      numactive++;
+    }
+
+  /* CALCULATE THE MINIMUM NUMBER OF CONTROL POINTS NEEDED TO DETERMINE
+     A TRANSFORMATION OF THIS ORDER */
+
+  m.n = ((order + 1) * (order + 2)) / 2;
+
+  if(numactive < m.n)
+    return(MNPTERR);
+
+  /* INITIALIZE MATRIX */
+
+  m.v = (DOUBLE *)G_calloc(m.n*m.n,sizeof(DOUBLE));
+  if(m.v == NULL)
+    {
+    return(MMEMERR);
+    }
+  a = (DOUBLE *)G_calloc(m.n,sizeof(DOUBLE));
+  if(a == NULL)
+    {
+    G_free((char *)m.v);
+    return(MMEMERR);
+    }
+  b = (DOUBLE *)G_calloc(m.n,sizeof(DOUBLE));
+  if(b == NULL)
+    {
+    G_free((char *)m.v);
+    G_free((char *)a);
+    return(MMEMERR);
+    }
+
+  if(numactive == m.n)
+    status = exactdet(cp,&m,a,b,E,N);
+  else
+    status = calcls(cp,&m,a,b,E,N);
+
+  G_free((char *)m.v);
+  G_free((char *)a);
+  G_free((char *)b);
+
+  return(status);
+  }
+
+/***************************************************************************/
+/*
+    CALCULATE THE TRANSFORMATION COEFFICIENTS WITH EXACTLY THE MINIMUM
+    NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION.
+*/
+/***************************************************************************/
+
+static int exactdet (
+    struct Control_Points *cp,
+    struct MATRIX *m,
+    DOUBLE a[],
+    DOUBLE b[],
+    double E[],     /* EASTING COEFFICIENTS */
+    double N[]     /* NORTHING COEFFICIENTS */
+)
+  {
+  int pntnow, currow, j;
+
+  currow = 1;
+  for(pntnow = 0 ; pntnow < cp->count ; pntnow++)
+    {
+    if(cp->status[pntnow] > 0)
+      {
+      /* POPULATE MATRIX M */
+
+      for(j = 1 ; j <= m->n ; j++)
+        {
+        M(currow,j) = term(j,cp->e1[pntnow],cp->n1[pntnow]);
+        }
+
+      /* POPULATE MATRIX A AND B */
+
+      a[currow-1] = cp->e2[pntnow];
+      b[currow-1] = cp->n2[pntnow];
+
+      currow++;
+      }
+    }
+
+  if(currow - 1 != m->n)
+    return(MINTERR);
+
+  return(solvemat(m,a,b,E,N));
+  }
+
+/***************************************************************************/
+/*
+    CALCULATE THE TRANSFORMATION COEFFICIENTS WITH MORE THAN THE MINIMUM
+    NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION.  THIS
+    ROUTINE USES THE LEAST SQUARES METHOD TO COMPUTE THE COEFFICIENTS.
+*/
+/***************************************************************************/
+
+static int calcls (
+    struct Control_Points *cp,
+    struct MATRIX *m,
+    DOUBLE a[],
+    DOUBLE b[],
+    double E[],     /* EASTING COEFFICIENTS */
+    double N[]     /* NORTHING COEFFICIENTS */
+)
+  {
+  int i, j, n, numactive = 0;
+
+  /* INITIALIZE THE UPPER HALF OF THE MATRIX AND THE TWO COLUMN VECTORS */
+
+  for(i = 1 ; i <= m->n ; i++)
+    {
+    for(j = i ; j <= m->n ; j++)
+      M(i,j) = 0.0;
+    a[i-1] = b[i-1] = 0.0;
+    }
+
+  /* SUM THE UPPER HALF OF THE MATRIX AND THE COLUMN VECTORS ACCORDING TO
+     THE LEAST SQUARES METHOD OF SOLVING OVER DETERMINED SYSTEMS */
+
+  for(n = 0 ; n < cp->count ; n++)
+    {
+    if(cp->status[n] > 0)
+      {
+      numactive++;
+      for(i = 1 ; i <= m->n ; i++)
+        {
+        for(j = i ; j <= m->n ; j++)
+          M(i,j) += term(i,cp->e1[n],cp->n1[n]) * term(j,cp->e1[n],cp->n1[n]);
+
+        a[i-1] += cp->e2[n] * term(i,cp->e1[n],cp->n1[n]);
+        b[i-1] += cp->n2[n] * term(i,cp->e1[n],cp->n1[n]);
+        }
+      }
+    }
+
+  if(numactive <= m->n)
+    return(MINTERR);
+
+  /* TRANSPOSE VALUES IN UPPER HALF OF M TO OTHER HALF */
+
+  for(i = 2 ; i <= m->n ; i++)
+    {
+    for(j = 1 ; j < i ; j++)
+      M(i,j) = M(j,i);
+    }
+
+  return(solvemat(m,a,b,E,N));
+  }
+
+/***************************************************************************/
+/*
+    CALCULATE THE X/Y TERM BASED ON THE TERM NUMBER
+
+ORDER\TERM   1    2    3    4    5    6    7    8    9   10
+  1        e0n0 e1n0 e0n1
+  2        e0n0 e1n0 e0n1 e2n0 e1n1 e0n2
+  3        e0n0 e1n0 e0n1 e2n0 e1n1 e0n2 e3n0 e2n1 e1n2 e0n3
+*/
+/***************************************************************************/
+
+static DOUBLE term (int term, double e, double n)
+  {
+  switch(term)
+    {
+    case  1: return((DOUBLE)1.0);
+    case  2: return((DOUBLE)e);
+    case  3: return((DOUBLE)n);
+    case  4: return((DOUBLE)(e*e));
+    case  5: return((DOUBLE)(e*n));
+    case  6: return((DOUBLE)(n*n));
+    case  7: return((DOUBLE)(e*e*e));
+    case  8: return((DOUBLE)(e*e*n));
+    case  9: return((DOUBLE)(e*n*n));
+    case 10: return((DOUBLE)(n*n*n));
+    }
+  return((DOUBLE)0.0);
+  }
+
+/***************************************************************************/
+/*
+    SOLVE FOR THE 'E' AND 'N' COEFFICIENTS BY USING A SOMEWHAT MODIFIED
+    GAUSSIAN ELIMINATION METHOD.
+
+    | M11 M12 ... M1n | | E0   |   | a0   |
+    | M21 M22 ... M2n | | E1   | = | a1   |
+    |  .   .   .   .  | | .    |   | .    |
+    | Mn1 Mn2 ... Mnn | | En-1 |   | an-1 |
+
+    and
+
+    | M11 M12 ... M1n | | N0   |   | b0   |
+    | M21 M22 ... M2n | | N1   | = | b1   |
+    |  .   .   .   .  | | .    |   | .    |
+    | Mn1 Mn2 ... Mnn | | Nn-1 |   | bn-1 |
+*/
+/***************************************************************************/
+
+static int solvemat (struct MATRIX *m,
+  DOUBLE a[], DOUBLE b[], double E[], double N[])
+{
+  int i, j, i2, j2, imark;
+  DOUBLE factor, temp;
+  DOUBLE pivot;  /* ACTUAL VALUE OF THE LARGEST PIVOT CANDIDATE */
+
+  for(i = 1 ; i <= m->n ; i++)
+    {
+    j = i;
+
+    /* find row with largest magnitude value for pivot value */
+
+    pivot = M(i,j);
+    imark = i;
+    for(i2 = i + 1 ; i2 <= m->n ; i2++)
+      {
+      temp = fabs(M(i2,j));
+      if(temp > fabs(pivot))
+        {
+        pivot = M(i2,j);
+        imark = i2;
+        }
+      }
+
+    /* if the pivot is very small then the points are nearly co-linear */
+    /* co-linear points result in an undefined matrix, and nearly */
+    /* co-linear points results in a solution with rounding error */
+
+    if(pivot == 0.0)
+      return(MUNSOLVABLE);
+
+    /* if row with highest pivot is not the current row, switch them */
+ 
+    if(imark != i)
+      {
+      for(j2 = 1 ; j2 <= m->n ; j2++)
+        {
+        temp = M(imark,j2);
+        M(imark,j2) = M(i,j2);
+        M(i,j2) = temp;
+        }
+
+      temp = a[imark-1];
+      a[imark-1] = a[i-1];
+      a[i-1] = temp;
+
+      temp = b[imark-1];
+      b[imark-1] = b[i-1];
+      b[i-1] = temp;
+      }
+
+    /* compute zeros above and below the pivot, and compute
+       values for the rest of the row as well */
+
+    for(i2 = 1 ; i2 <= m->n ; i2++)
+      {
+      if(i2 != i)
+        {
+        factor = M(i2,j) / pivot;
+        for(j2 = j ; j2 <= m->n ; j2++)
+          M(i2,j2) -= factor * M(i,j2);
+        a[i2-1] -= factor * a[i-1];
+        b[i2-1] -= factor * b[i-1];
+        }
+      }
+    }
+
+  /* SINCE ALL OTHER VALUES IN THE MATRIX ARE ZERO NOW, CALCULATE THE
+     COEFFICIENTS BY DIVIDING THE COLUMN VECTORS BY THE DIAGONAL VALUES. */
+
+  for(i = 1 ; i <= m->n ; i++)
+    {
+    E[i-1] = a[i-1] / M(i,i);
+    N[i-1] = b[i-1] / M(i,i);
+    }
+
+  return(MSUCCESS);
+  }

Added: packages/openev/branches/upstream/current/crs.h
===================================================================
--- packages/openev/branches/upstream/current/crs.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/crs.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/***************************************************************************/
+/*
+    CRS.H - Center for Remote Sensing rectification routines
+
+    Written By: Brian J. Buckley
+
+            At: The Center for Remote Sensing
+                Michigan State University
+                302 Berkey Hall
+                East Lansing, MI  48824
+                (517)353-7195
+
+    Written: 12/19/91
+
+    Last Update: 12/26/91 Brian J. Buckley
+*/
+/***************************************************************************/
+/***************************************************************************/
+
+#define MAXORDER 3
+
+#define G_calloc calloc
+#define G_free free
+
+struct Control_Points
+{
+    int  count;
+    double *e1;
+    double *n1;
+    double *e2;
+    double *n2;
+    int *status;
+};
+
+/* crs.c */
+int CRS_georef(double, double, double *, double *, double [], double [], int);
+int CRS_compute_georef_equations(struct Control_Points *,
+    double [], double [], double [], double [], int);
+

Added: packages/openev/branches/upstream/current/dbfopen.c
===================================================================
--- packages/openev/branches/upstream/current/dbfopen.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/dbfopen.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1448 @@
+/******************************************************************************
+ * $Id: dbfopen.c,v 1.44 2002/05/07 13:46:11 warmerda Exp $
+ *
+ * Project:  Shapelib
+ * Purpose:  Implementation of .dbf access API documented in dbf_api.html.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * This software is available under the following "MIT Style" license,
+ * or at the option of the licensee under the LGPL (see LICENSE.LGPL).  This
+ * option is discussed in more detail in shapelib.html.
+ *
+ * --
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: dbfopen.c,v $
+ * Revision 1.44  2002/05/07 13:46:11  warmerda
+ * Added DBFWriteAttributeDirectly().
+ *
+ * Revision 1.43  2002/02/13 19:39:21  warmerda
+ * Fix casting issues in DBFCloneEmpty().
+ *
+ * Revision 1.42  2002/01/15 14:36:07  warmerda
+ * updated email address
+ *
+ * Revision 1.41  2002/01/15 14:31:49  warmerda
+ * compute rather than copying nHeaderLength in DBFCloneEmpty()
+ *
+ * Revision 1.40  2002/01/09 04:32:35  warmerda
+ * fixed to read correct amount of header
+ *
+ * Revision 1.39  2001/12/11 22:41:03  warmerda
+ * improve io related error checking when reading header
+ *
+ * Revision 1.38  2001/11/28 16:07:31  warmerda
+ * Cleanup to avoid compiler warnings as suggested by Richard Hash.
+ *
+ * Revision 1.37  2001/07/04 05:18:09  warmerda
+ * do last fix properly
+ *
+ * Revision 1.36  2001/07/04 05:16:09  warmerda
+ * fixed fieldname comparison in DBFGetFieldIndex
+ *
+ * Revision 1.35  2001/06/22 02:10:06  warmerda
+ * fixed NULL shape support with help from Jim Matthews
+ *
+ * Revision 1.33  2001/05/31 19:20:13  warmerda
+ * added DBFGetFieldIndex()
+ *
+ * Revision 1.32  2001/05/31 18:15:40  warmerda
+ * Added support for NULL fields in DBF files
+ *
+ * Revision 1.31  2001/05/23 13:36:52  warmerda
+ * added use of SHPAPI_CALL
+ *
+ * Revision 1.30  2000/12/05 14:43:38  warmerda
+ * DBReadAttribute() white space trimming bug fix
+ *
+ * Revision 1.29  2000/10/05 14:36:44  warmerda
+ * fix bug with writing very wide numeric fields
+ *
+ * Revision 1.28  2000/09/25 14:18:07  warmerda
+ * Added some casts of strlen() return result to fix warnings on some
+ * systems, as submitted by Daniel.
+ *
+ * Revision 1.27  2000/09/25 14:15:51  warmerda
+ * added DBFGetNativeFieldType()
+ *
+ * Revision 1.26  2000/07/07 13:39:45  warmerda
+ * removed unused variables, and added system include files
+ *
+ * Revision 1.25  2000/05/29 18:19:13  warmerda
+ * avoid use of uchar, and adding casting fix
+ *
+ * Revision 1.24  2000/05/23 13:38:27  warmerda
+ * Added error checks on return results of fread() and fseek().
+ *
+ * Revision 1.23  2000/05/23 13:25:49  warmerda
+ * Avoid crashing if field or record are out of range in dbfread*attribute().
+ *
+ * Revision 1.22  1999/12/15 13:47:24  warmerda
+ * Added stdlib.h to ensure that atof() is prototyped.
+ *
+ * Revision 1.21  1999/12/13 17:25:46  warmerda
+ * Added support for upper case .DBF extention.
+ *
+ * Revision 1.20  1999/11/30 16:32:11  warmerda
+ * Use atof() instead of sscanf().
+ *
+ * Revision 1.19  1999/11/05 14:12:04  warmerda
+ * updated license terms
+ *
+ * Revision 1.18  1999/07/27 00:53:28  warmerda
+ * ensure that whole old field value clear on write of string
+ *
+ * Revision 1.1  1999/07/05 18:58:07  warmerda
+ * New
+ *
+ * Revision 1.17  1999/06/11 19:14:12  warmerda
+ * Fixed some memory leaks.
+ *
+ * Revision 1.16  1999/06/11 19:04:11  warmerda
+ * Remoted some unused variables.
+ *
+ * Revision 1.15  1999/05/11 03:19:28  warmerda
+ * added new Tuple api, and improved extension handling - add from candrsn
+ *
+ * Revision 1.14  1999/05/04 15:01:48  warmerda
+ * Added 'F' support.
+ *
+ * Revision 1.13  1999/03/23 17:38:59  warmerda
+ * DBFAddField() now actually does return the new field number, or -1 if
+ * it fails.
+ *
+ * Revision 1.12  1999/03/06 02:54:46  warmerda
+ * Added logic to convert shapefile name to dbf filename in DBFOpen()
+ * for convenience.
+ *
+ * Revision 1.11  1998/12/31 15:30:34  warmerda
+ * Improved the interchangability of numeric and string attributes.  Add
+ * white space trimming option for attributes.
+ *
+ * Revision 1.10  1998/12/03 16:36:44  warmerda
+ * Use r+b instead of rb+ for binary access.
+ *
+ * Revision 1.9  1998/12/03 15:34:23  warmerda
+ * Updated copyright message.
+ *
+ * Revision 1.8  1997/12/04 15:40:15  warmerda
+ * Added newline character after field definitions.
+ *
+ * Revision 1.7  1997/03/06 14:02:10  warmerda
+ * Ensure bUpdated is initialized.
+ *
+ * Revision 1.6  1996/02/12 04:54:41  warmerda
+ * Ensure that DBFWriteAttribute() returns TRUE if it succeeds.
+ *
+ * Revision 1.5  1995/10/21  03:15:12  warmerda
+ * Changed to use binary file access, and ensure that the
+ * field name field is zero filled, and limited to 10 chars.
+ *
+ * Revision 1.4  1995/08/24  18:10:42  warmerda
+ * Added use of SfRealloc() to avoid pre-ANSI realloc() functions such
+ * as on the Sun.
+ *
+ * Revision 1.3  1995/08/04  03:15:16  warmerda
+ * Fixed up header.
+ *
+ * Revision 1.2  1995/08/04  03:14:43  warmerda
+ * Added header.
+ */
+
+static char rcsid[] = 
+  "$Id: dbfopen.c,v 1.44 2002/05/07 13:46:11 warmerda Exp $";
+
+#include "shapefil.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifndef FALSE
+#  define FALSE     0
+#  define TRUE      1
+#endif
+
+static int  nStringFieldLen = 0;
+static char * pszStringField = NULL;
+
+/************************************************************************/
+/*                             SfRealloc()                              */
+/*                                                                      */
+/*      A realloc cover function that will access a NULL pointer as     */
+/*      a valid input.                                                  */
+/************************************************************************/
+
+static void * SfRealloc( void * pMem, int nNewSize )
+
+{
+    if( pMem == NULL )
+        return( (void *) malloc(nNewSize) );
+    else
+        return( (void *) realloc(pMem,nNewSize) );
+}
+
+/************************************************************************/
+/*                           DBFWriteHeader()                           */
+/*                                                                      */
+/*      This is called to write out the file header, and field          */
+/*      descriptions before writing any actual data records.  This      */
+/*      also computes all the DBFDataSet field offset/size/decimals     */
+/*      and so forth values.                                            */
+/************************************************************************/
+
+static void DBFWriteHeader(DBFHandle psDBF)
+
+{
+    unsigned char   abyHeader[XBASE_FLDHDR_SZ];
+    int     i;
+
+    if( !psDBF->bNoHeader )
+        return;
+
+    psDBF->bNoHeader = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*  Initialize the file header information.             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < XBASE_FLDHDR_SZ; i++ )
+        abyHeader[i] = 0;
+
+    abyHeader[0] = 0x03;        /* memo field? - just copying   */
+
+    /* date updated on close, record count preset at zero */
+
+    abyHeader[8] = psDBF->nHeaderLength % 256;
+    abyHeader[9] = psDBF->nHeaderLength / 256;
+    
+    abyHeader[10] = psDBF->nRecordLength % 256;
+    abyHeader[11] = psDBF->nRecordLength / 256;
+
+/* -------------------------------------------------------------------- */
+/*      Write the initial 32 byte file header, and all the field        */
+/*      descriptions.                                           */
+/* -------------------------------------------------------------------- */
+    fseek( psDBF->fp, 0, 0 );
+    fwrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
+    fwrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, psDBF->fp );
+
+/* -------------------------------------------------------------------- */
+/*      Write out the newline character if there is room for it.        */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 )
+    {
+        char    cNewline;
+
+        cNewline = 0x0d;
+        fwrite( &cNewline, 1, 1, psDBF->fp );
+    }
+}
+
+/************************************************************************/
+/*                           DBFFlushRecord()                           */
+/*                                                                      */
+/*      Write out the current record if there is one.                   */
+/************************************************************************/
+
+static void DBFFlushRecord( DBFHandle psDBF )
+
+{
+    int     nRecordOffset;
+
+    if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
+    {
+    psDBF->bCurrentRecordModified = FALSE;
+
+    nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord 
+                                                 + psDBF->nHeaderLength;
+
+    fseek( psDBF->fp, nRecordOffset, 0 );
+    fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+    }
+}
+
+/************************************************************************/
+/*                              DBFOpen()                               */
+/*                                                                      */
+/*      Open a .dbf file.                                               */
+/************************************************************************/
+   
+DBFHandle SHPAPI_CALL
+DBFOpen( const char * pszFilename, const char * pszAccess )
+
+{
+    DBFHandle       psDBF;
+    unsigned char       *pabyBuf;
+    int         nFields, nHeadLen, nRecLen, iField, i;
+    char        *pszBasename, *pszFullname;
+
+/* -------------------------------------------------------------------- */
+/*      We only allow the access strings "rb" and "r+".                  */
+/* -------------------------------------------------------------------- */
+    if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 
+        && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0
+        && strcmp(pszAccess,"r+b") != 0 )
+        return( NULL );
+
+    if( strcmp(pszAccess,"r") == 0 )
+        pszAccess = "rb";
+ 
+    if( strcmp(pszAccess,"r+") == 0 )
+        pszAccess = "rb+";
+
+/* -------------------------------------------------------------------- */
+/*  Compute the base (layer) name.  If there is any extension   */
+/*  on the passed in filename we will strip it off.         */
+/* -------------------------------------------------------------------- */
+    pszBasename = (char *) malloc(strlen(pszFilename)+5);
+    strcpy( pszBasename, pszFilename );
+    for( i = strlen(pszBasename)-1; 
+     i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+           && pszBasename[i] != '\\';
+     i-- ) {}
+
+    if( pszBasename[i] == '.' )
+        pszBasename[i] = '\0';
+
+    pszFullname = (char *) malloc(strlen(pszBasename) + 5);
+    sprintf( pszFullname, "%s.dbf", pszBasename );
+        
+    psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
+    psDBF->fp = fopen( pszFullname, pszAccess );
+
+    if( psDBF->fp == NULL )
+    {
+        sprintf( pszFullname, "%s.DBF", pszBasename );
+        psDBF->fp = fopen(pszFullname, pszAccess );
+    }
+    
+    free( pszBasename );
+    free( pszFullname );
+    
+    if( psDBF->fp == NULL )
+    {
+        free( psDBF );
+        return( NULL );
+    }
+
+    psDBF->bNoHeader = FALSE;
+    psDBF->nCurrentRecord = -1;
+    psDBF->bCurrentRecordModified = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*  Read Table Header info                                              */
+/* -------------------------------------------------------------------- */
+    pabyBuf = (unsigned char *) malloc(500);
+    if( fread( pabyBuf, 32, 1, psDBF->fp ) != 1 )
+    {
+        fclose( psDBF->fp );
+        free( pabyBuf );
+        free( psDBF );
+        return NULL;
+    }
+
+    psDBF->nRecords = 
+     pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256;
+
+    psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256;
+    psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256;
+    
+    psDBF->nFields = nFields = (nHeadLen - 32) / 32;
+
+    psDBF->pszCurrentRecord = (char *) malloc(nRecLen);
+
+/* -------------------------------------------------------------------- */
+/*  Read in Field Definitions                                           */
+/* -------------------------------------------------------------------- */
+    
+    pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen);
+    psDBF->pszHeader = (char *) pabyBuf;
+
+    fseek( psDBF->fp, 32, 0 );
+    if( fread( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 )
+    {
+        fclose( psDBF->fp );
+        free( pabyBuf );
+        free( psDBF );
+        return NULL;
+    }
+
+    psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields);
+    psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields);
+    psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields);
+    psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields);
+
+    for( iField = 0; iField < nFields; iField++ )
+    {
+    unsigned char       *pabyFInfo;
+
+    pabyFInfo = pabyBuf+iField*32;
+
+    if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' )
+    {
+        psDBF->panFieldSize[iField] = pabyFInfo[16];
+        psDBF->panFieldDecimals[iField] = pabyFInfo[17];
+    }
+    else
+    {
+        psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256;
+        psDBF->panFieldDecimals[iField] = 0;
+    }
+
+    psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
+    if( iField == 0 )
+        psDBF->panFieldOffset[iField] = 1;
+    else
+        psDBF->panFieldOffset[iField] = 
+          psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1];
+    }
+
+    return( psDBF );
+}
+
+/************************************************************************/
+/*                              DBFClose()                              */
+/************************************************************************/
+
+void SHPAPI_CALL
+DBFClose(DBFHandle psDBF)
+{
+/* -------------------------------------------------------------------- */
+/*      Write out header if not already written.                        */
+/* -------------------------------------------------------------------- */
+    if( psDBF->bNoHeader )
+        DBFWriteHeader( psDBF );
+
+    DBFFlushRecord( psDBF );
+
+/* -------------------------------------------------------------------- */
+/*      Update last access date, and number of records if we have   */
+/*  write access.                                   */
+/* -------------------------------------------------------------------- */
+    if( psDBF->bUpdated )
+    {
+    unsigned char       abyFileHeader[32];
+
+    fseek( psDBF->fp, 0, 0 );
+    fread( abyFileHeader, 32, 1, psDBF->fp );
+
+    abyFileHeader[1] = 95;          /* YY */
+    abyFileHeader[2] = 7;           /* MM */
+    abyFileHeader[3] = 26;          /* DD */
+
+    abyFileHeader[4] = psDBF->nRecords % 256;
+    abyFileHeader[5] = (psDBF->nRecords/256) % 256;
+    abyFileHeader[6] = (psDBF->nRecords/(256*256)) % 256;
+    abyFileHeader[7] = (psDBF->nRecords/(256*256*256)) % 256;
+
+    fseek( psDBF->fp, 0, 0 );
+    fwrite( abyFileHeader, 32, 1, psDBF->fp );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Close, and free resources.                                      */
+/* -------------------------------------------------------------------- */
+    fclose( psDBF->fp );
+
+    if( psDBF->panFieldOffset != NULL )
+    {
+        free( psDBF->panFieldOffset );
+        free( psDBF->panFieldSize );
+        free( psDBF->panFieldDecimals );
+        free( psDBF->pachFieldType );
+    }
+
+    free( psDBF->pszHeader );
+    free( psDBF->pszCurrentRecord );
+
+    free( psDBF );
+
+    if( pszStringField != NULL )
+    {
+        free( pszStringField );
+        pszStringField = NULL;
+        nStringFieldLen = 0;
+    }
+}
+
+/************************************************************************/
+/*                             DBFCreate()                              */
+/*                                                                      */
+/*      Create a new .dbf file.                                         */
+/************************************************************************/
+
+DBFHandle SHPAPI_CALL
+DBFCreate( const char * pszFilename )
+
+{
+    DBFHandle   psDBF;
+    FILE    *fp;
+    char    *pszFullname, *pszBasename;
+    int     i;
+
+/* -------------------------------------------------------------------- */
+/*  Compute the base (layer) name.  If there is any extension   */
+/*  on the passed in filename we will strip it off.         */
+/* -------------------------------------------------------------------- */
+    pszBasename = (char *) malloc(strlen(pszFilename)+5);
+    strcpy( pszBasename, pszFilename );
+    for( i = strlen(pszBasename)-1; 
+     i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+           && pszBasename[i] != '\\';
+     i-- ) {}
+
+    if( pszBasename[i] == '.' )
+        pszBasename[i] = '\0';
+
+    pszFullname = (char *) malloc(strlen(pszBasename) + 5);
+    sprintf( pszFullname, "%s.dbf", pszBasename );
+    free( pszBasename );
+
+/* -------------------------------------------------------------------- */
+/*      Create the file.                                                */
+/* -------------------------------------------------------------------- */
+    fp = fopen( pszFullname, "wb" );
+    if( fp == NULL )
+        return( NULL );
+
+    fputc( 0, fp );
+    fclose( fp );
+
+    fp = fopen( pszFullname, "rb+" );
+    if( fp == NULL )
+        return( NULL );
+
+    free( pszFullname );
+
+/* -------------------------------------------------------------------- */
+/*  Create the info structure.                  */
+/* -------------------------------------------------------------------- */
+    psDBF = (DBFHandle) malloc(sizeof(DBFInfo));
+
+    psDBF->fp = fp;
+    psDBF->nRecords = 0;
+    psDBF->nFields = 0;
+    psDBF->nRecordLength = 1;
+    psDBF->nHeaderLength = 33;
+    
+    psDBF->panFieldOffset = NULL;
+    psDBF->panFieldSize = NULL;
+    psDBF->panFieldDecimals = NULL;
+    psDBF->pachFieldType = NULL;
+    psDBF->pszHeader = NULL;
+
+    psDBF->nCurrentRecord = -1;
+    psDBF->bCurrentRecordModified = FALSE;
+    psDBF->pszCurrentRecord = NULL;
+
+    psDBF->bNoHeader = TRUE;
+
+    return( psDBF );
+}
+
+/************************************************************************/
+/*                            DBFAddField()                             */
+/*                                                                      */
+/*      Add a field to a newly created .dbf file before any records     */
+/*      are written.                                                    */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFAddField(DBFHandle psDBF, const char * pszFieldName, 
+            DBFFieldType eType, int nWidth, int nDecimals )
+
+{
+    char    *pszFInfo;
+    int     i;
+
+/* -------------------------------------------------------------------- */
+/*      Do some checking to ensure we can add records to this file.     */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nRecords > 0 )
+        return( -1 );
+
+    if( !psDBF->bNoHeader )
+        return( -1 );
+
+    if( eType != FTDouble && nDecimals != 0 )
+        return( -1 );
+
+/* -------------------------------------------------------------------- */
+/*      SfRealloc all the arrays larger to hold the additional field      */
+/*      information.                                                    */
+/* -------------------------------------------------------------------- */
+    psDBF->nFields++;
+
+    psDBF->panFieldOffset = (int *) 
+      SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
+
+    psDBF->panFieldSize = (int *) 
+      SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
+
+    psDBF->panFieldDecimals = (int *) 
+      SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
+
+    psDBF->pachFieldType = (char *) 
+      SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
+
+/* -------------------------------------------------------------------- */
+/*      Assign the new field information fields.                        */
+/* -------------------------------------------------------------------- */
+    psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
+    psDBF->nRecordLength += nWidth;
+    psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
+    psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
+
+    if( eType == FTString )
+        psDBF->pachFieldType[psDBF->nFields-1] = 'C';
+    else
+        psDBF->pachFieldType[psDBF->nFields-1] = 'N';
+
+/* -------------------------------------------------------------------- */
+/*      Extend the required header information.                         */
+/* -------------------------------------------------------------------- */
+    psDBF->nHeaderLength += 32;
+    psDBF->bUpdated = FALSE;
+
+    psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
+
+    pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1);
+
+    for( i = 0; i < 32; i++ )
+        pszFInfo[i] = '\0';
+
+    if( (int) strlen(pszFieldName) < 10 )
+        strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
+    else
+        strncpy( pszFInfo, pszFieldName, 10);
+
+    pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
+
+    if( eType == FTString )
+    {
+        pszFInfo[16] = nWidth % 256;
+        pszFInfo[17] = nWidth / 256;
+    }
+    else
+    {
+        pszFInfo[16] = nWidth;
+        pszFInfo[17] = nDecimals;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Make the current record buffer appropriately larger.            */
+/* -------------------------------------------------------------------- */
+    psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
+                           psDBF->nRecordLength);
+
+    return( psDBF->nFields-1 );
+}
+
+/************************************************************************/
+/*                          DBFReadAttribute()                          */
+/*                                                                      */
+/*      Read one of the attribute fields of a record.                   */
+/************************************************************************/
+
+static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
+                              char chReqType )
+
+{
+    int         nRecordOffset;
+    unsigned char   *pabyRec;
+    void    *pReturnField = NULL;
+
+    static double dDoubleField;
+
+/* -------------------------------------------------------------------- */
+/*      Verify selection.                                               */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity >= psDBF->nRecords )
+        return( NULL );
+
+    if( iField < 0 || iField >= psDBF->nFields )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*  Have we read the record?                    */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+    DBFFlushRecord( psDBF );
+
+    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+    if( fseek( psDBF->fp, nRecordOffset, 0 ) != 0 )
+        {
+            fprintf( stderr, "fseek(%d) failed on DBF file.\n",
+                     nRecordOffset );
+            return NULL;
+        }
+
+    if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 
+                   1, psDBF->fp ) != 1 )
+        {
+            fprintf( stderr, "fread(%d) failed on DBF file.\n",
+                     psDBF->nRecordLength );
+            return NULL;
+        }
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
+
+/* -------------------------------------------------------------------- */
+/*  Ensure our field buffer is large enough to hold this buffer.    */
+/* -------------------------------------------------------------------- */
+    if( psDBF->panFieldSize[iField]+1 > nStringFieldLen )
+    {
+    nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10;
+    pszStringField = (char *) SfRealloc(pszStringField,nStringFieldLen);
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Extract the requested field.                    */
+/* -------------------------------------------------------------------- */
+    strncpy( pszStringField, 
+         ((const char *) pabyRec) + psDBF->panFieldOffset[iField],
+         psDBF->panFieldSize[iField] );
+    pszStringField[psDBF->panFieldSize[iField]] = '\0';
+
+    pReturnField = pszStringField;
+
+/* -------------------------------------------------------------------- */
+/*      Decode the field.                                               */
+/* -------------------------------------------------------------------- */
+    if( chReqType == 'N' )
+    {
+        dDoubleField = atof(pszStringField);
+
+    pReturnField = &dDoubleField;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Should we trim white space off the string attribute value?      */
+/* -------------------------------------------------------------------- */
+#ifdef TRIM_DBF_WHITESPACE
+    else
+    {
+        char    *pchSrc, *pchDst;
+
+        pchDst = pchSrc = pszStringField;
+        while( *pchSrc == ' ' )
+            pchSrc++;
+
+        while( *pchSrc != '\0' )
+            *(pchDst++) = *(pchSrc++);
+        *pchDst = '\0';
+
+        while( pchDst != pszStringField && *(--pchDst) == ' ' )
+            *pchDst = '\0';
+    }
+#endif
+    
+    return( pReturnField );
+}
+
+/************************************************************************/
+/*                        DBFReadIntAttribute()                         */
+/*                                                                      */
+/*      Read an integer attribute.                                      */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    double  *pdValue;
+
+    pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
+
+    if( pdValue == NULL )
+        return 0;
+    else
+        return( (int) *pdValue );
+}
+
+/************************************************************************/
+/*                        DBFReadDoubleAttribute()                      */
+/*                                                                      */
+/*      Read a double attribute.                                        */
+/************************************************************************/
+
+double SHPAPI_CALL
+DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    double  *pdValue;
+
+    pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
+
+    if( pdValue == NULL )
+        return 0.0;
+    else
+        return( *pdValue );
+}
+
+/************************************************************************/
+/*                        DBFReadStringAttribute()                      */
+/*                                                                      */
+/*      Read a string attribute.                                        */
+/************************************************************************/
+
+const char SHPAPI_CALL1(*)
+DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) );
+}
+
+/************************************************************************/
+/*                         DBFIsAttributeNULL()                         */
+/*                                                                      */
+/*      Return TRUE if value for field is NULL.                         */
+/*                                                                      */
+/*      Contributed by Jim Matthews.                                    */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    const char  *pszValue;
+    int i;
+
+    pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
+
+    if( pszValue == NULL )
+        return TRUE;
+
+    switch(psDBF->pachFieldType[iField])
+    {
+      case 'N':
+      case 'F':
+        /*
+        ** We accept all asterisks or all blanks as NULL 
+        ** though according to the spec I think it should be all 
+        ** asterisks. 
+        */
+        if( pszValue[0] == '*' )
+            return TRUE;
+
+        for( i = 0; pszValue[i] != '\0'; i++ )
+        {
+            if( pszValue[i] != ' ' )
+                return FALSE;
+        }
+        return TRUE;
+
+      case 'D':
+        /* NULL date fields have value "00000000" */
+        return strncmp(pszValue,"00000000",8) == 0;
+
+      case 'L':
+        /* NULL boolean fields have value "?" */ 
+        return pszValue[0] == '?';
+
+      default:
+        /* empty string fields are considered NULL */
+        return strlen(pszValue) == 0;
+    }
+}
+
+/************************************************************************/
+/*                          DBFGetFieldCount()                          */
+/*                                                                      */
+/*      Return the number of fields in this table.                      */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFGetFieldCount( DBFHandle psDBF )
+
+{
+    return( psDBF->nFields );
+}
+
+/************************************************************************/
+/*                         DBFGetRecordCount()                          */
+/*                                                                      */
+/*      Return the number of records in this table.                     */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFGetRecordCount( DBFHandle psDBF )
+
+{
+    return( psDBF->nRecords );
+}
+
+/************************************************************************/
+/*                          DBFGetFieldInfo()                           */
+/*                                                                      */
+/*      Return any requested information about the field.               */
+/************************************************************************/
+
+DBFFieldType SHPAPI_CALL
+DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
+                 int * pnWidth, int * pnDecimals )
+
+{
+    if( iField < 0 || iField >= psDBF->nFields )
+        return( FTInvalid );
+
+    if( pnWidth != NULL )
+        *pnWidth = psDBF->panFieldSize[iField];
+
+    if( pnDecimals != NULL )
+        *pnDecimals = psDBF->panFieldDecimals[iField];
+
+    if( pszFieldName != NULL )
+    {
+    int i;
+
+    strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 );
+    pszFieldName[11] = '\0';
+    for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- )
+        pszFieldName[i] = '\0';
+    }
+
+    if( psDBF->pachFieldType[iField] == 'N' 
+        || psDBF->pachFieldType[iField] == 'F'
+        || psDBF->pachFieldType[iField] == 'D' )
+    {
+    if( psDBF->panFieldDecimals[iField] > 0 )
+        return( FTDouble );
+    else
+        return( FTInteger );
+    }
+    else
+    {
+    return( FTString );
+    }
+}
+
+/************************************************************************/
+/*                         DBFWriteAttribute()                          */
+/*                                  */
+/*  Write an attribute record to the file.              */
+/************************************************************************/
+
+static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
+                 void * pValue )
+
+{
+    int         nRecordOffset, i, j;
+    unsigned char   *pabyRec;
+    char    szSField[400], szFormat[20];
+
+/* -------------------------------------------------------------------- */
+/*  Is this a valid record?                     */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity > psDBF->nRecords )
+        return( FALSE );
+
+    if( psDBF->bNoHeader )
+        DBFWriteHeader(psDBF);
+
+/* -------------------------------------------------------------------- */
+/*      Is this a brand new record?                                     */
+/* -------------------------------------------------------------------- */
+    if( hEntity == psDBF->nRecords )
+    {
+    DBFFlushRecord( psDBF );
+
+    psDBF->nRecords++;
+    for( i = 0; i < psDBF->nRecordLength; i++ )
+        psDBF->pszCurrentRecord[i] = ' ';
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Is this an existing record, but different than the last one     */
+/*      we accessed?                                                    */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+    DBFFlushRecord( psDBF );
+
+    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+    fseek( psDBF->fp, nRecordOffset, 0 );
+    fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
+
+    psDBF->bCurrentRecordModified = TRUE;
+    psDBF->bUpdated = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Translate NULL value to valid DBF file representation.          */
+/*                                                                      */
+/*      Contributed by Jim Matthews.                                    */
+/* -------------------------------------------------------------------- */
+    if( pValue == NULL )
+    {
+        switch(psDBF->pachFieldType[iField])
+        {
+          case 'N':
+          case 'F':
+        /* NULL numeric fields have value "****************" */
+            memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', 
+                    psDBF->panFieldSize[iField] );
+            break;
+
+          case 'D':
+        /* NULL date fields have value "00000000" */
+            memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', 
+                    psDBF->panFieldSize[iField] );
+            break;
+
+          case 'L':
+        /* NULL boolean fields have value "?" */ 
+            memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', 
+                    psDBF->panFieldSize[iField] );
+            break;
+
+          default:
+            /* empty string fields are considered NULL */
+            memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '\0', 
+                    psDBF->panFieldSize[iField] );
+            break;
+        }
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Assign all the record fields.                                   */
+/* -------------------------------------------------------------------- */
+    switch( psDBF->pachFieldType[iField] )
+    {
+      case 'D':
+      case 'N':
+      case 'F':
+    if( psDBF->panFieldDecimals[iField] == 0 )
+    {
+            int     nWidth = psDBF->panFieldSize[iField];
+
+            if( sizeof(szSField)-2 < nWidth )
+                nWidth = sizeof(szSField)-2;
+
+        sprintf( szFormat, "%%%dd", nWidth );
+        sprintf(szSField, szFormat, (int) *((double *) pValue) );
+        if( (int)strlen(szSField) > psDBF->panFieldSize[iField] )
+            szSField[psDBF->panFieldSize[iField]] = '\0';
+
+        strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
+            szSField, strlen(szSField) );
+    }
+    else
+    {
+            int     nWidth = psDBF->panFieldSize[iField];
+
+            if( sizeof(szSField)-2 < nWidth )
+                nWidth = sizeof(szSField)-2;
+
+        sprintf( szFormat, "%%%d.%df", 
+                     nWidth, psDBF->panFieldDecimals[iField] );
+        sprintf(szSField, szFormat, *((double *) pValue) );
+        if( (int) strlen(szSField) > psDBF->panFieldSize[iField] )
+            szSField[psDBF->panFieldSize[iField]] = '\0';
+        strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
+            szSField, strlen(szSField) );
+    }
+    break;
+
+      default:
+    if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] )
+        j = psDBF->panFieldSize[iField];
+    else
+        {
+            memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
+                    psDBF->panFieldSize[iField] );
+        j = strlen((char *) pValue);
+        }
+
+    strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
+        (char *) pValue, j );
+    break;
+    }
+
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                     DBFWriteAttributeDirectly()                      */
+/*                                                                      */
+/*      Write an attribute record to the file, but without any          */
+/*      reformatting based on type.  The provided buffer is written     */
+/*      as is to the field position in the record.                      */
+/************************************************************************/
+
+int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
+                              void * pValue )
+
+{
+    int         nRecordOffset, i, j;
+    unsigned char   *pabyRec;
+
+/* -------------------------------------------------------------------- */
+/*  Is this a valid record?                     */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity > psDBF->nRecords )
+        return( FALSE );
+
+    if( psDBF->bNoHeader )
+        DBFWriteHeader(psDBF);
+
+/* -------------------------------------------------------------------- */
+/*      Is this a brand new record?                                     */
+/* -------------------------------------------------------------------- */
+    if( hEntity == psDBF->nRecords )
+    {
+    DBFFlushRecord( psDBF );
+
+    psDBF->nRecords++;
+    for( i = 0; i < psDBF->nRecordLength; i++ )
+        psDBF->pszCurrentRecord[i] = ' ';
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Is this an existing record, but different than the last one     */
+/*      we accessed?                                                    */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+    DBFFlushRecord( psDBF );
+
+    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+    fseek( psDBF->fp, nRecordOffset, 0 );
+    fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
+
+/* -------------------------------------------------------------------- */
+/*      Assign all the record fields.                                   */
+/* -------------------------------------------------------------------- */
+    if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] )
+        j = psDBF->panFieldSize[iField];
+    else
+    {
+        memset( pabyRec+psDBF->panFieldOffset[iField], ' ',
+                psDBF->panFieldSize[iField] );
+        j = strlen((char *) pValue);
+    }
+
+    strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
+            (char *) pValue, j );
+
+    psDBF->bCurrentRecordModified = TRUE;
+    psDBF->bUpdated = TRUE;
+
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                      DBFWriteDoubleAttribute()                       */
+/*                                                                      */
+/*      Write a double attribute.                                       */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
+                         double dValue )
+
+{
+    return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
+}
+
+/************************************************************************/
+/*                      DBFWriteIntegerAttribute()                      */
+/*                                                                      */
+/*      Write a integer attribute.                                      */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField,
+                          int nValue )
+
+{
+    double  dValue = nValue;
+
+    return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
+}
+
+/************************************************************************/
+/*                      DBFWriteStringAttribute()                       */
+/*                                                                      */
+/*      Write a string attribute.                                       */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
+                         const char * pszValue )
+
+{
+    return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) );
+}
+
+/************************************************************************/
+/*                      DBFWriteNULLAttribute()                         */
+/*                                                                      */
+/*      Write a string attribute.                                       */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) );
+}
+
+/************************************************************************/
+/*                         DBFWriteTuple()                              */
+/*                                  */
+/*  Write an attribute record to the file.              */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
+
+{
+    int         nRecordOffset, i;
+    unsigned char   *pabyRec;
+
+/* -------------------------------------------------------------------- */
+/*  Is this a valid record?                     */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity > psDBF->nRecords )
+        return( FALSE );
+
+    if( psDBF->bNoHeader )
+        DBFWriteHeader(psDBF);
+
+/* -------------------------------------------------------------------- */
+/*      Is this a brand new record?                                     */
+/* -------------------------------------------------------------------- */
+    if( hEntity == psDBF->nRecords )
+    {
+    DBFFlushRecord( psDBF );
+
+    psDBF->nRecords++;
+    for( i = 0; i < psDBF->nRecordLength; i++ )
+        psDBF->pszCurrentRecord[i] = ' ';
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Is this an existing record, but different than the last one     */
+/*      we accessed?                                                    */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+    DBFFlushRecord( psDBF );
+
+    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+    fseek( psDBF->fp, nRecordOffset, 0 );
+    fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
+
+    memcpy ( pabyRec, pRawTuple,  psDBF->nRecordLength );
+
+    psDBF->bCurrentRecordModified = TRUE;
+    psDBF->bUpdated = TRUE;
+
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                          DBFReadTuple()                              */
+/*                                                                      */
+/*      Read one of the attribute fields of a record.                   */
+/************************************************************************/
+
+const char SHPAPI_CALL1(*)
+DBFReadTuple(DBFHandle psDBF, int hEntity )
+
+{
+    int         nRecordOffset;
+    unsigned char   *pabyRec;
+    static char *pReturnTuple = NULL;
+
+    static int  nTupleLen = 0;
+
+/* -------------------------------------------------------------------- */
+/*  Have we read the record?                    */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity >= psDBF->nRecords )
+        return( NULL );
+
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+    DBFFlushRecord( psDBF );
+
+    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+    fseek( psDBF->fp, nRecordOffset, 0 );
+    fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+    psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
+
+    if ( nTupleLen < psDBF->nRecordLength) {
+      nTupleLen = psDBF->nRecordLength;
+      pReturnTuple = (char *) SfRealloc(pReturnTuple, psDBF->nRecordLength);
+    }
+    
+    memcpy ( pReturnTuple, pabyRec, psDBF->nRecordLength );
+        
+    return( pReturnTuple );
+}
+
+/************************************************************************/
+/*                          DBFCloneEmpty()                              */
+/*                                                                      */
+/*      Read one of the attribute fields of a record.                   */
+/************************************************************************/
+
+DBFHandle SHPAPI_CALL
+DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) 
+{
+    DBFHandle   newDBF;
+
+   newDBF = DBFCreate ( pszFilename );
+   if ( newDBF == NULL ) return ( NULL ); 
+   
+   newDBF->pszHeader = (char *) malloc ( 32 * psDBF->nFields );
+   memcpy ( newDBF->pszHeader, psDBF->pszHeader, 32 * psDBF->nFields );
+   
+   newDBF->nFields = psDBF->nFields;
+   newDBF->nRecordLength = psDBF->nRecordLength;
+   newDBF->nHeaderLength = 32 * (psDBF->nFields+1);
+    
+   newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); 
+   memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
+   newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields );
+   memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
+   newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields );
+   memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
+   newDBF->pachFieldType = (char *) malloc ( sizeof(int) * psDBF->nFields );
+   memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(int) * psDBF->nFields );
+
+   newDBF->bNoHeader = TRUE;
+   newDBF->bUpdated = TRUE;
+   
+   DBFWriteHeader ( newDBF );
+   DBFClose ( newDBF );
+   
+   newDBF = DBFOpen ( pszFilename, "rb+" );
+
+   return ( newDBF );
+}
+
+/************************************************************************/
+/*                       DBFGetNativeFieldType()                        */
+/*                                                                      */
+/*      Return the DBase field type for the specified field.            */
+/*                                                                      */
+/*      Value can be one of: 'C' (String), 'D' (Date), 'F' (Float),     */
+/*                           'N' (Numeric, with or without decimal),    */
+/*                           'L' (Logical),                             */
+/*                           'M' (Memo: 10 digits .DBT block ptr)       */
+/************************************************************************/
+
+char SHPAPI_CALL
+DBFGetNativeFieldType( DBFHandle psDBF, int iField )
+
+{
+    if( iField >=0 && iField < psDBF->nFields )
+        return psDBF->pachFieldType[iField];
+
+    return  ' ';
+}
+
+/************************************************************************/
+/*                            str_to_upper()                            */
+/************************************************************************/
+
+static void str_to_upper (char *string)
+{
+    int len;
+    short i = -1;
+
+    len = strlen (string);
+
+    while (++i < len)
+        if (isalpha(string[i]) && islower(string[i]))
+            string[i] = toupper ((int)string[i]);
+}
+
+/************************************************************************/
+/*                          DBFGetFieldIndex()                          */
+/*                                                                      */
+/*      Get the index number for a field in a .dbf file.                */
+/*                                                                      */
+/*      Contributed by Jim Matthews.                                    */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
+
+{
+    char          name[12], name1[12], name2[12];
+    int           i;
+
+    strncpy(name1, pszFieldName,11);
+    str_to_upper(name1);
+
+    for( i = 0; i < DBFGetFieldCount(psDBF); i++ )
+    {
+        DBFGetFieldInfo( psDBF, i, name, NULL, NULL );
+        strncpy(name2,name,11);
+        str_to_upper(name2);
+
+        if(!strncmp(name1,name2,10))
+            return(i);
+    }
+    return(-1);
+}

Added: packages/openev/branches/upstream/current/delivery/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/delivery/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+/INSTALL.TXT/1.3/Fri Aug 25 19:50:28 2000//
+/install/1.9/Mon Jun 25 19:25:06 2001//
+/license.txt/1.1/Tue Aug 22 18:02:54 2000//
+/mkdist/1.9/Thu Sep 12 13:08:14 2002//
+/mksrcdist.sh/1.1/Thu Jan 24 15:15:54 2002//
+/package/1.4/Mon Jun 25 19:25:06 2001//
+/setup_openev/1.6/Thu Feb  1 03:27:23 2001//
+D

Added: packages/openev/branches/upstream/current/delivery/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/delivery/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/delivery

Added: packages/openev/branches/upstream/current/delivery/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/delivery/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/delivery/INSTALL.TXT
===================================================================
--- packages/openev/branches/upstream/current/delivery/INSTALL.TXT	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/INSTALL.TXT	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,9 @@
+OpenEV UNIX Installation
+
+To Install:
+   - go into the unix directory on the CD by typing "cd unix" from the CD root
+   - execute "./install [linux|solaris|irix|nt] [location]" ensuring you have write permission
+     to the location you specify
+
+To Run:
+- execute "[location]/bin/openev"

Added: packages/openev/branches/upstream/current/delivery/install
===================================================================
--- packages/openev/branches/upstream/current/delivery/install	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/install	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+#
+# echo with no linefeed at end of line (system dependent)
+# 
+echo_no_lf()
+{
+    case `uname -a` in
+        Linux*) echo -n "${1}";;
+        *)      echo "${1}\c";;
+    esac
+}   
+
+#
+# Check the command line parameters
+#
+if [ -z "${2}" ];
+then
+    echo "usage: $0 [linux|solaris|irix|nt] [location]"
+    exit 1
+fi
+
+#
+# Check parameter 1: platform
+#
+case "${1}" in
+    linux)   ARCHPATH=Linux;;
+    solaris) ARCHPATH=SunOS;;
+    irix)    ARCHPATH=IRIX64;;
+    nt)      ARCHPATH=nt;;
+    *)
+        echo "Platform must be one of linux, solaris, irix, or nt."
+        exit 1;;
+esac
+
+#
+# Check parameter 1: insar directory
+#
+if [ "${1}" = "nt" ]; then
+    case  "${2}" in
+       [A-Za-z][:=]/*);;
+       *) echo "Input parameter must be a full path."; exit 1;;
+    esac
+else
+    case  "${2}" in
+       /*);;
+       *) echo "Input parameter must be a full path."; exit 1;;
+    esac
+fi
+INSTALLDIR=${2}
+
+
+#
+# Check OpenEV directory doesn't already exist
+#
+if [ -d ${2} ]; then
+    echo_no_lf "OpenEV already exists in the directory ${2}, do you want to overwrite it [Y/n]?"
+    read OVERWRITE
+    if [ "${OVERWRITE}" = "n" -o "${OVERWRITE}" = "N" ]; then
+        echo "Installation aborted.  No action taken."
+        exit 1
+    fi
+fi
+
+# Try to make the directory
+mkdir -p ${INSTALLDIR}
+
+#
+# Check for Write permission in directory
+#
+if ! [ -w ${2} ]; then
+    echo "Installation failed.  You need to have write permission in ${2}."
+    exit 1
+fi
+
+# --------------------- Real Work Below --------------------
+echo "--Starting OpenEV Installation--"
+
+# Copy OS specific files
+echo "Copying platform dependent file - OpenEV, Python, GTK, Mesa etc."
+
+if [ "${ARCHPATH}" = "Linux" ]; then
+  cp -rf Linux/* ${INSTALLDIR}
+fi
+
+if [ "${ARCHPATH}" = "IRIX64" ]; then
+  cp -rf IRIX64/* ${INSTALLDIR}
+fi
+
+if [ "${ARCHPATH}" = "SunOS" ]; then
+  cp -rf SunOS/* ${INSTALLDIR}
+fi
+
+# Copy OS independent files
+echo "Copying platform independent file"
+cp -rf common/* ${INSTALLDIR}
+
+#setup a link to openev in bin directory
+#ln -s ${INSTALLDIR}/pymod/openev.py ${INSTALLDIR}/bin/openev
+
+# Byte compile Python and OpenEV directories recursively
+echo "Compiling Python Code"
+`${INSTALLDIR}/bin/python -O ${INSTALLDIR}/lib/python2.1/compileall.py ${INSTALLDIR} > /dev/null`
+
+# Create run scripts, to set environment variables and execute openev
+chmod -R u+rw ${INSTALLDIR}
+echo '#!/bin/sh' > ${INSTALLDIR}/first_lines
+echo "OPENEVHOME=$2" >> ${INSTALLDIR}/first_lines
+cat ${INSTALLDIR}/first_lines setup_openev > ${INSTALLDIR}/bin/openev
+rm -f ${INSTALLDIR}/first_lines
+
+chmod a+rx ${INSTALLDIR}/bin/openev
+
+# Done
+echo "Done.  OpenEV Installed Successfully in ${2}."
+echo "Run ${2}/bin/openev to start OpenEV."


Property changes on: packages/openev/branches/upstream/current/delivery/install
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/delivery/license.txt
===================================================================
--- packages/openev/branches/upstream/current/delivery/license.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/license.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+

Added: packages/openev/branches/upstream/current/delivery/mkdist
===================================================================
--- packages/openev/branches/upstream/current/delivery/mkdist	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/mkdist	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+INSTALLDIR=../delivery
+OSTYPE=`uname`
+GDAL=../../gdal
+
+
+# ---- Work - Platform Independent --- 
+
+# Create all directories
+echo "Creating delivery Directory"
+mkdir -p ${INSTALLDIR}/${OSTYPE}/bin
+mkdir -p ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+mkdir -p ${INSTALLDIR}/${OSTYPE}/pymod
+mkdir -p ${INSTALLDIR}/${OSTYPE}/tools
+# include python headers for building tools
+mkdir -p ${INSTALLDIR}/${OSTYPE}/include
+mkdir -p ${INSTALLDIR}/common
+
+# Copy OpenEV specific things
+echo "Copying OpenEV"
+cp ../gvtest ${INSTALLDIR}/${OSTYPE}/bin
+cp -r ../html ${INSTALLDIR}/common
+cp -r ../pics ${INSTALLDIR}/common
+cp -r ../ramps ${INSTALLDIR}/common
+
+# Copy OpenEV pymod directory
+cp ../pymod/*.so ../pymod/*.py ${INSTALLDIR}/${OSTYPE}/pymod
+
+# Don't copy tools- not stable yet.  Directory is expected though
+# cp ../tools/*.py ${INSTALLDIR}/${OSTYPE}/tools
+
+# Copy libraries other than Python, NumPy and GTK
+echo "Copying GDAL"
+cp ${GDAL}/libgdal.1.1.so ${INSTALLDIR}/${OSTYPE}/lib
+cp ${GDAL}/apps/gdaladdo ${INSTALLDIR}/${OSTYPE}/bin
+cp ${GDAL}/apps/gdalinfo ${INSTALLDIR}/${OSTYPE}/bin
+cp ${GDAL}/apps/gdal_translate ${INSTALLDIR}/${OSTYPE}/bin
+cp ${GDAL}/pymod/*.py ${GDAL}/pymod/*.so ${INSTALLDIR}/${OSTYPE}/pymod
+
+# ---- Work - Platform Independent --- 
+
+if [ "${OSTYPE}" = "Linux" ]; then
+  PYTHON=/data/local_installations/lib/python2.1
+  PYTHONBIN=/data/local_installations/bin/python
+  GTKLIB=/data/local_installations/lib
+  PYTHONINC=/data/local_installations/include/python2.1
+  GNUPLOT_BIN=/home/gwalter/bin
+
+  # Copy Python, NumPy and PyGTK
+  echo "Copying Python and Site-Packages"
+  cp -r ${PYTHON} ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${PYTHONBIN} ${INSTALLDIR}/${OSTYPE}/bin
+  cp -r ${PYTHONINC} ${INSTALLDIR}/${OSTYPE}/include
+
+  echo "Copying GTK"
+  cp ${GTKLIB}/libgtkgl.so.5.0.0 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgtk-1.2.so.0.9.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgdk-1.2.so.0.9.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgmodule-1.2.so.0.0.10 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgthread-1.2.so.0.0.10 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libglib-1.2.so.0.0.10 ${INSTALLDIR}/${OSTYPE}/lib
+
+  # GL Stuff
+  echo "Copying Mesa GL libraries and other supporting libraries"
+  # cp /usr/lib/libGLcore.so.1 ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+  cp /data/local_installations/lib/libGL.so.1.2.030402 ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+  cp /data/local_installations/lib/libGLU.so.1.1.030402 ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+
+  # Gnuplot stuff
+  cp ${GNUPLOT_BIN}/gnuplot ${INSTALLDIR}/${OSTYPE}/bin
+  cp ${GNUPLOT_BIN}/gnuplot_x11 ${INSTALLDIR}/${OSTYPE}/bin
+
+  # Other libs
+  cp /usr/lib/libjpeg.so.62 ${INSTALLDIR}/${OSTYPE}/lib
+  cp /usr/lib/libpng.so.2 ${INSTALLDIR}/${OSTYPE}/lib
+  cp /lib/libpthread.so.0 ${INSTALLDIR}/${OSTYPE}/lib
+  cp /usr/lib/libvga.so.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp /data/local_installations/lib/libproj.so.0.2.0 ${INSTALLDIR}/${OSTYPE}/lib
+fi
+
+
+if [ "${OSTYPE}" = "IRIX64" ]; then
+  PYTHON=/esa2/temp_gwalter/local_installations/lib/python2.1
+  PYTHONBIN=/esa2/temp_gwalter/local_installations/bin/python
+  GTKLIB=/esa2/temp_gwalter/local_installations/lib
+  PYTHONINC=/esa2/temp_gwalter/local_installations/include/python2.1
+  GNUPLOT_BIN=/usr/local/bin
+
+  # Copy Python, NumPy and PyGTK
+  echo "Copying Python and Site-Packages"
+  cp -r ${PYTHON} ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${PYTHONBIN} ${INSTALLDIR}/${OSTYPE}/bin
+  cp -r ${PYTHONINC} ${INSTALLDIR}/${OSTYPE}/include
+
+  # Copy GTK libraries
+  echo "Copying GTK"
+  cp ${GTKLIB}/libgtkgl.so.6.0 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgtk-1.2.so.1.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgdk-1.2.so.1.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgmodule-1.2.so.1.10 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgthread-1.2.so.1.10 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libglib-1.2.so.1.10 ${INSTALLDIR}/${OSTYPE}/lib
+
+  # GL Stuff
+  echo "Copying Mesa GL libraries and other supporting libraries"
+  cp /esa2/temp_gwalter/local_installations/lib/libGL.so ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+  cp /esa2/temp_gwalter/local_installations/lib/libGLU.so ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+
+  # Gnuplot stuff
+  cp ${GNUPLOT_BIN}/gnuplot ${INSTALLDIR}/${OSTYPE}/bin
+  cp ${GNUPLOT_BIN}/gnuplot_x11 ${INSTALLDIR}/${OSTYPE}/bin
+
+  # Other libs
+  cp /usr/lib/libjpeg.so ${INSTALLDIR}/${OSTYPE}/lib
+  cp /usr/lib/libpng.so ${INSTALLDIR}/${OSTYPE}/lib
+  cp /usr/lib/libpthread.so ${INSTALLDIR}/${OSTYPE}/lib
+  cp /esa2/temp_gwalter/local_installations/lib/libproj.so.1.0 ${INSTALLDIR}/${OSTYPE}/lib
+fi
+
+if [ "${OSTYPE}" = "SunOS" ]; then
+  PYTHON=/stripe2/temp_gwalter/local_installations/lib/python2.1
+  PYTHONBIN=/stripe2/temp_gwalter/local_installations/bin/python
+  GTKLIB=/stripe2/temp_gwalter/local_installations/lib
+  PYTHONINC=/stripe2/temp_gwalter/local_installations/include/python2.1
+  GNUPLOT_BIN=/opt/sfw/bin
+
+  # Copy Python, NumPy and PyGTK
+  echo "Copying Python and Site-Packages"
+  cp -r ${PYTHON} ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${PYTHONBIN} ${INSTALLDIR}/${OSTYPE}/bin
+  cp -r ${PYTHONINC} ${INSTALLDIR}/${OSTYPE}/include
+
+  # Copy GTK libraries
+  echo "Copying GTK"
+  cp ${GTKLIB}/libgtkgl.so.5.0.0 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgtk-1.2.so.0.9.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgdk-1.2.so.0.9.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgmodule-1.2.so.0.0.10 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libgthread-1.2.so.0.0.10 ${INSTALLDIR}/${OSTYPE}/lib
+  cp ${GTKLIB}/libglib-1.2.so.0.0.10 ${INSTALLDIR}/${OSTYPE}/lib
+
+  # GL Stuff
+  echo "Copying Mesa GL libraries and other supporting libraries"
+  cp /stripe2/temp_gwalter/local_installations/lib/libGL.so.1.2.030402 ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+  cp /stripe2/temp_gwalter/local_installations/lib/libGLU.so.1.1.030402 ${INSTALLDIR}/${OSTYPE}/lib/Mesa
+
+  # Gnuplot stuff
+  cp ${GNUPLOT_BIN}/gnuplot ${INSTALLDIR}/${OSTYPE}/bin
+  cp ${GNUPLOT_BIN}/gnuplot_x11 ${INSTALLDIR}/${OSTYPE}/bin
+  cp ${GNUPLOT_BIN}/../lib/libpng.so.2 ${INSTALLDIR}/${OSTYPE}/lib
+
+  # Other libs
+  #cp /usr/lib/libjpeg.so ${INSTALLDIR}/${OSTYPE}/lib
+  #cp /usr/lib/libpng.so ${INSTALLDIR}/${OSTYPE}/lib
+  cp /usr/lib/libpthread.so.1 ${INSTALLDIR}/${OSTYPE}/lib
+  cp /stripe2/temp_gwalter/local_installations/lib/libproj.so.0.2.0 ${INSTALLDIR}/${OSTYPE}/lib
+  # libstdc++.so is linked to in gdal/openev. Later should
+  # try to get rid of this dependency...
+  cp /opt/sfw/lib/libstdc++.so.2.10.0 ${INSTALLDIR}/${OSTYPE}/lib
+
+fi
+
+
+# Remove Unnecessary Python stuff to save space
+rm -r ${INSTALLDIR}/${OSTYPE}/lib/python2.1/test
+rm -r ${INSTALLDIR}/${OSTYPE}/lib/python2.1/*.pyc
+rm -r ${INSTALLDIR}/${OSTYPE}/lib/python2.1/*.pyo
+rm -r ${INSTALLDIR}/${OSTYPE}/lib/python2.1/site-packages/*.pyc
+rm -r ${INSTALLDIR}/${OSTYPE}/lib/python2.1/site-packages/*.pyo
+
+echo "Done"


Property changes on: packages/openev/branches/upstream/current/delivery/mkdist
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/delivery/mksrcdist.sh
===================================================================
--- packages/openev/branches/upstream/current/delivery/mksrcdist.sh	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/mksrcdist.sh	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+if [ $# -lt 1 ] ; then
+  echo "Usage: mksrcdist version"
+  echo
+  echo "Example: mksrcdist.sh 1.1.4"
+  exit
+fi
+
+SRC_VERSION=$1
+COMPRESSED_VERSION=`echo $SRC_VERSION | tr -d .`
+
+rm -rf dist_wrk  
+mkdir dist_wrk
+cd dist_wrk
+
+export CVSROOT=:pserver:anonymous at cvs.openev.sf.net:/cvsroot/openev
+
+echo "Please type [ENTER] if prompted for a password."
+cvs login 
+
+cvs checkout openev
+
+if [ \! -d openev ] ; then
+  echo "cvs checkout reported an error ... abandoning mksrcdist"
+  cd ..
+  rm -rf dist_wrk
+  exit
+fi
+
+find openev -name CVS -exec rm -rf {} \;
+
+mv openev openev-${SRC_VERSION}
+
+rm -f ../openev-${SRC_VERSION}.tar.gz ../openev${COMPRESSED_VERSION}.zip
+
+tar cf ../openev-${SRC_VERSION}.tar openev-${SRC_VERSION}
+gzip -9 ../openev-${SRC_VERSION}.tar
+zip -r ../openev${COMPRESSED_VERSION}.zip openev-${SRC_VERSION}
+
+cd ..
+rm -rf dist_wrk


Property changes on: packages/openev/branches/upstream/current/delivery/mksrcdist.sh
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/delivery/package
===================================================================
--- packages/openev/branches/upstream/current/delivery/package	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/package	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#
+# Check parameter 1: platform
+#
+case "${1}" in
+    linux)   ARCHPATH=openev/Linux;;
+    solaris) ARCHPATH=openev/SunOS;;
+    irix)    ARCHPATH=openev/IRIX64;;
+    all)     ARCHPATH=openev/Linux openev/IRIX64 openev/SunOS;;
+    *)
+        echo "Platform must be one of linux, solaris, irix."
+        exit 1;;
+esac
+
+
+# Create a tar file, from delivery directory
+
+cd ..
+mv delivery openev
+tar -cvf openev.tar openev/INSTALL.TXT openev/license.txt openev/install openev/setup_openev openev/common ${ARCHPATH}
+mv openev delivery 
+cd delivery


Property changes on: packages/openev/branches/upstream/current/delivery/package
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/delivery/setup_openev
===================================================================
--- packages/openev/branches/upstream/current/delivery/setup_openev	                        (rev 0)
+++ packages/openev/branches/upstream/current/delivery/setup_openev	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,22 @@
+
+# Setup Environment Variables for OpenEV
+
+PYTHONHOME=${OPENEVHOME}
+PYTHONPATH=${OPENEVHOME}/pymod:${PYTHONPATH}
+LD_LIBRARY_PATH=${OPENEVHOME}/lib:${LD_LIBRARY_PATH}
+PATH=${OPENEVHOME}/bin:${PATH}
+
+# check for accelerated hardware flag
+if [ "${1}" = "-h" ]; then
+    shift
+    echo "Setup for user installed hardware acceleration"
+else
+    echo "Default software rendering mode (use -h if accelerated video card installed)."
+    LD_LIBRARY_PATH=${OPENEVHOME}/lib/Mesa:${LD_LIBRARY_PATH}
+fi
+
+export PYTHONHOME PYTHONPATH LD_LIBRARY_PATH PATH
+
+
+# Run OpenEV
+${OPENEVHOME}/pymod/openev.py "$@"


Property changes on: packages/openev/branches/upstream/current/delivery/setup_openev
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/doc/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/doc/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,16 @@
+/Makefile/1.5/Fri Jul 14 16:05:48 2000//
+/asilogo.eps/1.1/Tue Jun 20 14:33:16 2000//
+/future.tex/1.1/Tue Jun 20 15:34:18 2000//
+/gvclass.fig/1.1/Tue Jun 20 14:33:16 2000//
+/gvdata.fig/1.1/Tue Jun 20 14:33:16 2000//
+/l2h-init.perl/1.1/Fri Jul 14 16:04:28 2000//
+/layerdlg.eps/1.1/Tue Jun 20 14:33:16 2000//
+/lodgen.fig/1.1/Tue Jun 20 14:33:16 2000//
+/openev.tex/1.1/Tue Jun 20 14:33:16 2000//
+/openev_bigpicture.fig/1.1/Tue Jun 20 14:33:16 2000//
+/openevlogo.eps/1.1/Tue Jun 20 14:33:16 2000//
+/openevreport.cls/1.1/Tue Jun 20 14:33:16 2000//
+/openevreport.perl/1.1/Fri Jul 14 15:50:33 2000//
+/phaseclip.fig/1.1/Tue Jun 20 14:33:16 2000//
+/toolbar.eps/1.1/Tue Jun 20 14:33:16 2000//
+D

Added: packages/openev/branches/upstream/current/doc/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/doc/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/doc

Added: packages/openev/branches/upstream/current/doc/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/doc/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/doc/Makefile
===================================================================
--- packages/openev/branches/upstream/current/doc/Makefile	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/Makefile	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,42 @@
+DOC = openev
+FUTURE = future
+L2H_OPT = -show_section_numbers -no_navigation
+L2H_INC = l2h-init.perl
+SPLIT = 0
+L2H_FLAGS = $(L2H_OPT) -split $(SPLIT) -init_file $(L2H_INC)
+
+FIGS = openev_bigpicture.eps gvclass.eps gvdata.eps lodgen.eps phaseclip.eps
+
+default: all
+
+%.eps : %.fig
+	fig2dev -L ps $< $@
+
+$(FUTURE).dvi : $(FUTURE).tex
+	latex $*
+#	- at grep 'Rerun to get cross-references right' $*.log && latex $*
+
+$(DOC).dvi : $(DOC).tex $(FIGS)
+	latex $*
+	- at grep 'Rerun to get cross-references right' $*.log && latex $*
+
+$(DOC)/%.html : %.tex $(FIGS) $(L2H_INC)
+	latex2html $(L2H_FLAGS) $*
+
+all: $(DOC).dvi $(FUTURE).dvi
+
+print: $(DOC).dvi
+	dvips $*
+
+html: $(DOC)/$(DOC).html
+
+html-force: $(FIGS)
+	latex2html $(L2H_FLAGS)
+
+html-clean:
+	rm -rf $(DOC)
+
+clean:
+	rm -f $(DOC).aux $(DOC).dvi $(DOC).log $(DOC).toc $(FIGS)
+	rm -f $(FUTURE).aux $(FUTURE).dvi $(FUTURE).log
+	rm -rf $(DOC) $(FUTURE)

Added: packages/openev/branches/upstream/current/doc/asilogo.eps
===================================================================
--- packages/openev/branches/upstream/current/doc/asilogo.eps	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/asilogo.eps	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,344 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: (ImageMagick)
+%%Title: (asilogo.eps)
+%%CreationDate: (Fri Feb 25 14:25:33 2000)
+%%BoundingBox: 0 0 251 46
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 1
+%%Pages: 0
+%%EndComments
+
+%%BeginDefaults
+%%PageOrientation: Portrait
+%%EndDefaults
+
+%%BeginProlog
+%
+% Display a color image.  The image is displayed in color on
+% Postscript viewers or printers that support color, otherwise
+% it is displayed as grayscale.
+%
+/buffer 512 string def
+/byte 1 string def
+/color_packet 3 string def
+/pixels 768 string def
+
+/DirectClassPacket
+{
+  %
+  % Get a DirectClass packet.
+  %
+  % Parameters:
+  %   red.
+  %   green.
+  %   blue.
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile color_packet readhexstring pop pop
+  compression 0 gt
+  {
+    /number_pixels 3 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add 3 mul def
+  } ifelse
+  0 3 number_pixels 1 sub
+  {
+    pixels exch color_packet putinterval
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/DirectClassImage
+{
+  %
+  % Display a DirectClass image.
+  %
+  systemdict /colorimage known
+  {
+    columns rows 8
+    [
+      columns 0 0
+      rows neg 0 rows
+    ]
+    { DirectClassPacket } false 3 colorimage
+  }
+  {
+    %
+    % No colorimage operator;  convert to grayscale.
+    %
+    columns rows 8
+    [
+      columns 0 0
+      rows neg 0 rows
+    ]
+    { GrayDirectClassPacket } image
+  } ifelse
+} bind def
+
+/GrayDirectClassPacket
+{
+  %
+  % Get a DirectClass packet;  convert to grayscale.
+  %
+  % Parameters:
+  %   red
+  %   green
+  %   blue
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile color_packet readhexstring pop pop
+  color_packet 0 get 0.299 mul
+  color_packet 1 get 0.587 mul add
+  color_packet 2 get 0.114 mul add
+  cvi
+  /gray_packet exch def
+  compression 0 gt
+  {
+    /number_pixels 1 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add def
+  } ifelse
+  0 1 number_pixels 1 sub
+  {
+    pixels exch gray_packet put
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/GrayPseudoClassPacket
+{
+  %
+  % Get a PseudoClass packet;  convert to grayscale.
+  %
+  % Parameters:
+  %   index: index into the colormap.
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile byte readhexstring pop 0 get
+  /offset exch 3 mul def
+  /color_packet colormap offset 3 getinterval def
+  color_packet 0 get 0.299 mul
+  color_packet 1 get 0.587 mul add
+  color_packet 2 get 0.114 mul add
+  cvi
+  /gray_packet exch def
+  compression 0 gt
+  {
+    /number_pixels 1 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add def
+  } ifelse
+  0 1 number_pixels 1 sub
+  {
+    pixels exch gray_packet put
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/PseudoClassPacket
+{
+  %
+  % Get a PseudoClass packet.
+  %
+  % Parameters:
+  %   index: index into the colormap.
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile byte readhexstring pop 0 get
+  /offset exch 3 mul def
+  /color_packet colormap offset 3 getinterval def
+  compression 0 gt
+  {
+    /number_pixels 3 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add 3 mul def
+  } ifelse
+  0 3 number_pixels 1 sub
+  {
+    pixels exch color_packet putinterval
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/PseudoClassImage
+{
+  %
+  % Display a PseudoClass image.
+  %
+  % Parameters:
+  %   class: 0-PseudoClass or 1-Grayscale.
+  %
+  currentfile buffer readline pop
+  token pop /class exch def pop
+  class 0 gt
+  {
+    currentfile buffer readline pop
+    token pop /depth exch def pop
+    /grays columns 8 add depth sub depth mul 8 idiv string def
+    columns rows depth
+    [
+      columns 0 0
+      rows neg 0 rows
+    ]
+    { currentfile grays readhexstring pop } image
+  }
+  {
+    %
+    % Parameters:
+    %   colors: number of colors in the colormap.
+    %   colormap: red, green, blue color packets.
+    %
+    currentfile buffer readline pop
+    token pop /colors exch def pop
+    /colors colors 3 mul def
+    /colormap colors string def
+    currentfile colormap readhexstring pop pop
+    systemdict /colorimage known
+    {
+      columns rows 8
+      [
+        columns 0 0
+        rows neg 0 rows
+      ]
+      { PseudoClassPacket } false 3 colorimage
+    }
+    {
+      %
+      % No colorimage operator;  convert to grayscale.
+      %
+      columns rows 8
+      [
+        columns 0 0
+        rows neg 0 rows
+      ]
+      { GrayPseudoClassPacket } image
+    } ifelse
+  } ifelse
+} bind def
+
+/DisplayImage
+{
+  %
+  % Display a DirectClass or PseudoClass image.
+  %
+  % Parameters:
+  %   x & y translation.
+  %   x & y scale.
+  %   label pointsize.
+  %   image label.
+  %   image columns & rows.
+  %   class: 0-DirectClass or 1-PseudoClass.
+  %   compression: 0-RunlengthEncodedCompression or 1-NoCompression.
+  %   hex color packets.
+  %
+  gsave
+  currentfile buffer readline pop
+  token pop /x exch def
+  token pop /y exch def pop
+  x y translate
+  currentfile buffer readline pop
+  token pop /x exch def
+  token pop /y exch def pop
+  currentfile buffer readline pop
+  token pop /pointsize exch def pop
+  /Helvetica findfont pointsize scalefont setfont
+  x y scale
+  currentfile buffer readline pop
+  token pop /columns exch def
+  token pop /rows exch def pop
+  currentfile buffer readline pop
+  token pop /class exch def pop
+  currentfile buffer readline pop
+  token pop /compression exch def pop
+  class 0 gt { PseudoClassImage } { DirectClassImage } ifelse
+  grestore
+} bind def
+%%EndProlog
+%%Page:  1 1
+%%PageBoundingBox: 0 0 251 46
+userdict begin
+%%BeginData:
+DisplayImage
+0 0
+251.000000 46.000000
+12
+251 46
+1
+0
+0
+26
+000000
+040404
+0c0c0c
+111111
+161616
+1c1c1c
+222222
+292929
+393939
+4d4d4d
+666666
+777777
+808080
+868686
+969696
+999999
+a0a0a4
+b2b2b2
+c0c0c0
+cbcbcb
+d7d7d7
+dddddd
+e3e3e3
+f1f1f1
+f8f8f8
+ffffff
+19880c0619f10c0819ef0c0a19ed11000c0119050c0319ec0c01190618000c0219f60c03
+19f60c0219f60c0219f70c02195f12000006110019130000191400181909000719240000
+19120c0219030000190f00071908001819090006190d1200000d190f0b00000119130018
+190900071923000219100c0219040002190d00071908001819090006190c0011190d0003
+1912001819090007192300020a00190e0c0219050004190b00071908001819090006190b
+0012190c00040e00191100181909000719220004190d0c02190600061909000719080018
+19090006190a0012190c0006191100181909000719210006190b0d000c01190700081907
+00071908001819090006190a0006190600031200190b1700000719100018190900071920
+000819090c021908000a190500071908001819090006190a000519180009190f00181909
+0007191f0c000008020019070c021909000c190300071908001819090006190a00051917
+000b190e001819090007191e0c00000a19060c01190b000e190100071908001819090006
+190a00071914000c1917000719110007191c0c01000c19040c01190c0018191100071911
+0006190a000a19101400000d1916000719110007191b0c01000e190112000c01190d0018
+1911000719110006190b000c190d000f1915000719110007191a0c011800000f0c01190f
+00181911000719110006190c0c00000d1909000719000008191400071911000719181300
+0c000d0019000007190000070c00191000181911000719110006190f000c090019060008
+19010007191400071911000719170c011000190000070100190100071910001819110007
+1911000619110300000a170019040f00000719030007191300071911000719160c011901
+000819020c000007190f0006190000101911000719110006191500081904000719040008
+191200071911000719150c0119011600000719040008190e00061902000e191100071911
+0006191700061903000719010c0219010008191100071911001319080c01190200071901
+0c0219000c000007190e00061904000c1911000719110006191800051902000819000f00
+0c03190100071911000719110013190612000c01190200070e0019000c0419010007190d
+00061906000a1911000719110006190c0700190a000519010a00000719010c040d001901
+00071910000719110013190514000c011902000819010c04190115000007190c00061908
+00081911000719110006190c000508001902050000061901000719020c04130019010100
+0007190f00071911001319040c0219020b00000719020c0419020008190b0006190a0006
+1911000719110006190b00121900000719040c0319030008190e00071911001319030c02
+1903000719030c0419030007190b0006190c00041911000719110006190b001104000008
+19050c01190500071700190d00071911001319020e000c011903000719060c0019060007
+190a0006190e00021911000719110006190a001219000007190f0007190d000719110013
+19010c0219030008190f000719090006191000001911000719110006190c0600000d195f
+18000c01180019f60c0219f60c0219f60c0219f60c0219f70c0219f60c0219f60d000c02
+19f60c0319090c02120019e80c0319050e000c030d0019e90c0e19eb0c0c19ed0c0919f0
+0c060d0019f30c010d00199f
+%%EndData
+end
+%%PageTrailer
+%%Trailer
+%%BoundingBox: 0 0 251 46
+%%EOF

Added: packages/openev/branches/upstream/current/doc/future.tex
===================================================================
--- packages/openev/branches/upstream/current/doc/future.tex	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/future.tex	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,48 @@
+\documentclass{article}
+
+\begin{document}
+
+\title{Improvements to OpenEV}
+\date{2000-06-20}
+\author{OpenEV Project}
+\maketitle
+
+\section{Future Directions}
+
+As OpenEV matures, new tools and layer classes, and common UI elements
+will be added to enhance its capabilities.  The object oriented
+structure ensures that this growth is possible.
+
+The follow sections list ideas that could be added to OpenEV.
+
+\subsection{3D}
+
+One particular area for improvement in OpenEV is in its handling of 3D imagery.
+
+\begin{itemize}
+\item continuous LOD approach to rendering the 3D mesh is required to allow for
+interactive frame rates when dealing with large elevation datasets.
+\item intellegent tesselation of mesh
+\item 3D datasets, such as scene graph elements (e.g. city buildings) could be added.
+\item LUT for mesh would allow elevation data to be visualized without a drape
+\item lighting could be added for more realistic view, this would also facilitate viewing
+elevation data without a drape.
+\end{itemize}
+
+
+\subsection{R\&D Tools}
+
+From its conception OpenEV has been designed to be a R\&D tool.  
+
+\begin{itemize}
+\item Another important direction is coupling the display capabilities with
+a Python shell window, to create an interactive numerical analysis and
+image manipulation environment in the vein of Research Systems's IDL,
+or The Mathwork's MATLAB.  This will eventually become an integrated
+environment where the user is able to combine plug-in processing and
+analysis functions, plotting capabilities, image display, point and
+vector marking, and a powerful scripting language.  The goal is to
+build a platform for image processing R\&D.
+\end{itemize}
+
+\end{document}
\ No newline at end of file

Added: packages/openev/branches/upstream/current/doc/gvclass.fig
===================================================================
--- packages/openev/branches/upstream/current/doc/gvclass.fig	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/gvclass.fig	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,101 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 4200 600 5400 600 5400 900 4200 900 4200 600
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 4200 1500 5400 1500 5400 1800 4200 1800 4200 1500
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 4200 2100 5400 2100 5400 2400 4200 2400 4200 2100
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 1200 1200 9000 1200
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 600 1500 1800 1500 1800 1800 600 1800 600 1500
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 600 2100 1800 2100 1800 2400 600 2400 600 2100
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 1200 1200 1200 1500
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 1200 1800 1200 2100
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 2400 3000 3600 3000 3600 3300 2400 3300 2400 3000
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 900 3900 2700 3900 2700 4200 900 4200 900 3900
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 4800 900 4800 1500
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 4800 1800 4800 2100
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 4800 2400 4800 2700
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 3000 2700 8100 2700
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 3000 2700 3000 3000
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 3000 3300 3000 3600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 1800 3600 4200 3600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 1800 3600 1800 3900
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 3300 3900 5100 3900 5100 4200 3300 4200 3300 3900
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 4200 3600 4200 3900
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 7500 3000 8700 3000 8700 3300 7500 3300 7500 3000
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 8100 2700 8100 3000
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 8400 3900 9600 3900 9600 4200 8400 4200 8400 3900
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9000 1200 9000 3900
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 3900 4800 5700 4800 5700 5100 3900 5100 3900 4800
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 6000 4800 7200 4800 7200 5100 6000 5100 6000 4800
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 7500 4800 8700 4800 8700 5100 7500 5100 7500 4800
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 9000 4800 10200 4800 10200 5100 9000 5100 9000 4800
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9000 4200 9000 4500
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9600 4500 9600 4800
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 8100 4500 8100 4800
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 6600 4500 6600 4800
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 4800 4500 4800 4800
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 4800 4500 9600 4500
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 1800 4200 1800 4650
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 4950 3000 6450 3000 6450 3300 4950 3300 4950 3000
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5700 2700 5700 3000
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 900 4650 2700 4650 2700 4950 900 4950 900 4650
+4 1 0 100 0 20 12 0.0000 4 180 825 4800 825 GtkObject\001
+4 1 0 100 0 20 12 0.0000 4 135 660 4800 1725 GtkData\001
+4 1 0 100 0 20 12 0.0000 4 135 630 4800 2325 GvData\001
+4 1 0 100 0 20 12 0.0000 4 135 915 1200 1725 GtkGLArea\001
+4 1 0 100 0 20 12 0.0000 4 135 1065 1200 2325 GvViewArea\001
+4 1 0 100 0 20 12 0.0000 4 180 720 3000 3225 GvLayer\001
+4 1 0 100 0 20 12 0.0000 4 180 1350 1800 4125 GvShapesLayer\001
+4 1 0 100 0 20 12 0.0000 4 180 1245 4200 4125 GvRasterLayer\001
+4 1 0 100 0 20 12 0.0000 4 135 765 8100 3225 GvRaster\001
+4 1 0 100 0 20 12 0.0000 4 135 600 9000 4125 GvTool\001
+4 1 0 100 0 20 12 0.0000 4 135 1380 4800 5025 GvSelectionTool\001
+4 1 0 100 0 20 12 0.0000 4 135 960 6600 5025 GvLineTool\001
+4 1 0 100 0 20 12 0.0000 4 135 1050 8100 5025 GvNodeTool\001
+4 1 0 100 0 20 12 0.0000 4 135 900 9600 5025 GvToolbox\001
+4 0 0 100 0 0 12 0.0000 4 180 1185 1200 4875 GvPqueryLayer\001
+4 0 0 100 0 0 12 0.0000 4 180 780 5250 3225 GvShapes\001

Added: packages/openev/branches/upstream/current/doc/gvdata.fig
===================================================================
--- packages/openev/branches/upstream/current/doc/gvdata.fig	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/gvdata.fig	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,40 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 4275 900 5325 1950
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 4800 1425 456 456 4800 1425 5250 1500
+4 1 0 100 0 20 12 0.0000 4 135 510 4800 1500 Points\001
+4 1 0 100 0 20 12 0.0000 4 135 390 4800 1275 UTM\001
+4 1 0 100 0 20 12 0.0000 4 180 480 4800 1725 Layer\001
+-6
+6 1050 1650 1950 2550
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 1500 2100 437 437 1500 2100 1875 2325
+4 1 0 100 0 20 12 0.0000 4 135 375 1500 2025 LCC\001
+4 1 0 100 0 20 12 0.0000 4 135 510 1500 2250 Points\001
+-6
+6 2475 900 3525 1950
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 3000 1425 456 456 3000 1425 3450 1500
+4 1 0 100 0 20 12 0.0000 4 135 390 3000 1350 UTM\001
+4 1 0 100 0 20 12 0.0000 4 135 510 3000 1575 Points\001
+-6
+6 4350 2325 5250 3225
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 4800 2775 450 450 4800 2775 5250 2775
+4 1 0 100 0 20 12 0.0000 4 135 375 4800 2625 LCC\001
+4 1 0 100 0 20 12 0.0000 4 135 510 4800 2850 Points\001
+4 1 0 100 0 20 12 0.0000 4 180 480 4800 3075 Layer\001
+-6
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 1875 1875 2550 1575
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3450 1425 4350 1425
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 1950 2250 4350 2700

Added: packages/openev/branches/upstream/current/doc/l2h-init.perl
===================================================================
--- packages/openev/branches/upstream/current/doc/l2h-init.perl	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/l2h-init.perl	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+# latex2html init file for openev
+
+$BODYTEXT = "bgcolor=white text=black";
+$TITLE = "OpenEV Library Design";

Added: packages/openev/branches/upstream/current/doc/layerdlg.eps
===================================================================
--- packages/openev/branches/upstream/current/doc/layerdlg.eps	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/layerdlg.eps	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,924 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: (ImageMagick)
+%%Title: (layerdlg.eps)
+%%CreationDate: (Fri Feb 25 14:26:49 2000)
+%%BoundingBox: 0 0 208 223
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 1
+%%Pages: 0
+%%EndComments
+
+%%BeginDefaults
+%%PageOrientation: Portrait
+%%EndDefaults
+
+%%BeginProlog
+%
+% Display a color image.  The image is displayed in color on
+% Postscript viewers or printers that support color, otherwise
+% it is displayed as grayscale.
+%
+/buffer 512 string def
+/byte 1 string def
+/color_packet 3 string def
+/pixels 768 string def
+
+/DirectClassPacket
+{
+  %
+  % Get a DirectClass packet.
+  %
+  % Parameters:
+  %   red.
+  %   green.
+  %   blue.
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile color_packet readhexstring pop pop
+  compression 0 gt
+  {
+    /number_pixels 3 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add 3 mul def
+  } ifelse
+  0 3 number_pixels 1 sub
+  {
+    pixels exch color_packet putinterval
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/DirectClassImage
+{
+  %
+  % Display a DirectClass image.
+  %
+  systemdict /colorimage known
+  {
+    columns rows 8
+    [
+      columns 0 0
+      rows neg 0 rows
+    ]
+    { DirectClassPacket } false 3 colorimage
+  }
+  {
+    %
+    % No colorimage operator;  convert to grayscale.
+    %
+    columns rows 8
+    [
+      columns 0 0
+      rows neg 0 rows
+    ]
+    { GrayDirectClassPacket } image
+  } ifelse
+} bind def
+
+/GrayDirectClassPacket
+{
+  %
+  % Get a DirectClass packet;  convert to grayscale.
+  %
+  % Parameters:
+  %   red
+  %   green
+  %   blue
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile color_packet readhexstring pop pop
+  color_packet 0 get 0.299 mul
+  color_packet 1 get 0.587 mul add
+  color_packet 2 get 0.114 mul add
+  cvi
+  /gray_packet exch def
+  compression 0 gt
+  {
+    /number_pixels 1 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add def
+  } ifelse
+  0 1 number_pixels 1 sub
+  {
+    pixels exch gray_packet put
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/GrayPseudoClassPacket
+{
+  %
+  % Get a PseudoClass packet;  convert to grayscale.
+  %
+  % Parameters:
+  %   index: index into the colormap.
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile byte readhexstring pop 0 get
+  /offset exch 3 mul def
+  /color_packet colormap offset 3 getinterval def
+  color_packet 0 get 0.299 mul
+  color_packet 1 get 0.587 mul add
+  color_packet 2 get 0.114 mul add
+  cvi
+  /gray_packet exch def
+  compression 0 gt
+  {
+    /number_pixels 1 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add def
+  } ifelse
+  0 1 number_pixels 1 sub
+  {
+    pixels exch gray_packet put
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/PseudoClassPacket
+{
+  %
+  % Get a PseudoClass packet.
+  %
+  % Parameters:
+  %   index: index into the colormap.
+  %   length: number of pixels minus one of this color (optional).
+  %
+  currentfile byte readhexstring pop 0 get
+  /offset exch 3 mul def
+  /color_packet colormap offset 3 getinterval def
+  compression 0 gt
+  {
+    /number_pixels 3 def
+  }
+  {
+    currentfile byte readhexstring pop 0 get
+    /number_pixels exch 1 add 3 mul def
+  } ifelse
+  0 3 number_pixels 1 sub
+  {
+    pixels exch color_packet putinterval
+  } for
+  pixels 0 number_pixels getinterval
+} bind def
+
+/PseudoClassImage
+{
+  %
+  % Display a PseudoClass image.
+  %
+  % Parameters:
+  %   class: 0-PseudoClass or 1-Grayscale.
+  %
+  currentfile buffer readline pop
+  token pop /class exch def pop
+  class 0 gt
+  {
+    currentfile buffer readline pop
+    token pop /depth exch def pop
+    /grays columns 8 add depth sub depth mul 8 idiv string def
+    columns rows depth
+    [
+      columns 0 0
+      rows neg 0 rows
+    ]
+    { currentfile grays readhexstring pop } image
+  }
+  {
+    %
+    % Parameters:
+    %   colors: number of colors in the colormap.
+    %   colormap: red, green, blue color packets.
+    %
+    currentfile buffer readline pop
+    token pop /colors exch def pop
+    /colors colors 3 mul def
+    /colormap colors string def
+    currentfile colormap readhexstring pop pop
+    systemdict /colorimage known
+    {
+      columns rows 8
+      [
+        columns 0 0
+        rows neg 0 rows
+      ]
+      { PseudoClassPacket } false 3 colorimage
+    }
+    {
+      %
+      % No colorimage operator;  convert to grayscale.
+      %
+      columns rows 8
+      [
+        columns 0 0
+        rows neg 0 rows
+      ]
+      { GrayPseudoClassPacket } image
+    } ifelse
+  } ifelse
+} bind def
+
+/DisplayImage
+{
+  %
+  % Display a DirectClass or PseudoClass image.
+  %
+  % Parameters:
+  %   x & y translation.
+  %   x & y scale.
+  %   label pointsize.
+  %   image label.
+  %   image columns & rows.
+  %   class: 0-DirectClass or 1-PseudoClass.
+  %   compression: 0-RunlengthEncodedCompression or 1-NoCompression.
+  %   hex color packets.
+  %
+  gsave
+  currentfile buffer readline pop
+  token pop /x exch def
+  token pop /y exch def pop
+  x y translate
+  currentfile buffer readline pop
+  token pop /x exch def
+  token pop /y exch def pop
+  currentfile buffer readline pop
+  token pop /pointsize exch def pop
+  /Helvetica findfont pointsize scalefont setfont
+  x y scale
+  currentfile buffer readline pop
+  token pop /columns exch def
+  token pop /rows exch def pop
+  currentfile buffer readline pop
+  token pop /class exch def pop
+  currentfile buffer readline pop
+  token pop /compression exch def pop
+  class 0 gt { PseudoClassImage } { DirectClassImage } ifelse
+  grestore
+} bind def
+%%EndProlog
+%%Page:  1 1
+%%PageBoundingBox: 0 0 208 223
+userdict begin
+%%BeginData:
+DisplayImage
+0 0
+208.000000 223.000000
+12
+208 223
+1
+0
+0
+174
+b5b6b5
+a5aaa5
+292829
+9c9e94
+949694
+94968c
+949a94
+94928c
+9c9a94
+9c9e9c
+9ca29c
+a5a29c
+a5a69c
+6b6963
+080808
+9c9a9c
+949294
+8c8e8c
+6b6d6b
+393839
+000000
+8c8a8c
+9c9694
+8c928c
+948e8c
+313031
+adaaa5
+b5b2b5
+8c8a84
+7b797b
+5a595a
+424542
+424142
+424139
+394139
+393c39
+525152
+6b716b
+a5a2a5
+5a5d5a
+4a4d4a
+636563
+737173
+7b7973
+737573
+7b7d7b
+737973
+636163
+5a615a
+848684
+7b7573
+63615a
+635d5a
+636963
+293029
+7b7d73
+8c8684
+524d4a
+313029
+848284
+84827b
+313431
+adaead
+73716b
+4a494a
+6b696b
+292c29
+847d73
+4a4942
+312c29
+6b6d63
+5a5952
+847d7b
+736d6b
+4a4542
+52514a
+080c08
+a5a6a5
+292421
+524d42
+84867b
+101010
+635d52
+393431
+212421
+6b6563
+292821
+63655a
+424539
+393831
+adaaad
+5a5552
+6b655a
+dedbde
+b5b2ad
+ffffff
+e7e3e7
+adaeb5
+dedfde
+adb2ad
+d6d7d6
+313831
+d6d3d6
+cecfce
+c6c7c6
+c6c3c6
+bdbebd
+84868c
+adaea5
+8c868c
+181818
+e7e7e7
+efebef
+7b8e9c
+cecbce
+bdbabd
+00ff00
+cecfc6
+b5b6ad
+004d00
+7b827b
+efefef
+848a84
+ffff00
+8c8e84
+7b8284
+84756b
+7b7563
+7b716b
+8c796b
+de9a10
+efbe00
+efb600
+7b717b
+ef9e08
+f7c700
+ffcb08
+f7f7f7
+7b756b
+ffb608
+ffcf00
+f7f3f7
+94aebd
+ffebd6
+f7fbf7
+effbff
+c6c7ce
+cecbd6
+c6c3ce
+313429
+c6c7bd
+bdbeb5
+c6c3bd
+fffbff
+f7f3ef
+fffbf7
+d6d7de
+cecfd6
+7b7d84
+8c8e94
+42454a
+4a454a
+d6d3ce
+b5bab5
+bdc3bd
+bdbab5
+c6cbc6
+4a5152
+bdc7d6
+738e94
+f7fbff
+9cb2ce
+f7f7ff
+525952
+00cd01000200000103000401050104000501040106000402070005000401060004070600
+040006000400060008040600030109000a000b010a0009000a0009010a00090103000903
+0300080003020900030408000301090003000901030209000b010a000900030108010300
+09000a020901030009010a000b010a0209000a0009010a010b010a060b01090003010905
+030006000400060008020306090208000302080006010801060104000600040005000402
+07000501040206000300090203010903030009030301090103030900030109020b000a00
+0c000b000d000e0000000b000400080104020803040c0801040f080004000f0008030400
+08000406080104000800041810000701100104001004070010000701101c070010050700
+10000704110e07041001070010020408080104000f0008000f0a08061000120013001400
+00000700110315001102150011091000110007011001040008020f0008000f0016000407
+1001040b1600040810020700110417000701110517000701100107001000170018011001
+04011000040a0800041910020700100504001001040c1600040710080407100007041800
+1101070111021000120019001400000004001500110318000f001a001b00000007011c00
+10020700100304041d001e001f002000210120022100200f210020022100200f21002003
+210020002101200021022007210420002200210020012100200321002001210023242400
+1200150010030700110318000f001a001b00000007011c0007001100070111020f001a00
+1b00000007011c00070010050f001a001b00000007011500040108011700250019001400
+00000a0010031500260000001a00160011001c021d001002070010010400100327002300
+280029002a002b022c102a042c032b002c0d2b042c062b001d022d031d092d0b1d012d02
+1d002d001d122b002c022e002b002e002c002a002f0030002d000702110207001c002600
+00001a00160011001c022b0011031c00260000001a00160011001c022b00110110011c00
+260000001a00160011001c022b0011010700040012001900140000000900100231000f00
+0000180032060d00110018000700100007031000270023002f002b011d032d011d072d09
+1d002d0d1d022e002c022e011d002e001d112b012c072b002c0d2b032c072b001d042b02
+1d092a003100080004031c000f000000070032060d00100006001c000f0010002c003302
+340232000d0004011c000f00000007003203330132000d00070010000400350036001400
+00001a00040218000000100037070d002c000700100004041d0023002f001d0314011d0a
+2d011d0e2b011d0a2b001d012b011d022b002c0c2b001d042d001d002d0b1d022e012c01
+2e011d002b001d042d011d022d001d002d031d0b2d001100040307000000100037070d00
+2b001000070000001300190638000d002b00100007000000100019011500370039001901
+07000d001d001000040035003a00140000000c0009010f000b0026002b082c0027001000
+04051e0024001d002c022b00140100002c0214022c012b0014012e0114012b0114022c00
+2b002e0014012b0014001d0114022d201d002d021d002d001d032b001d022b001d072b00
+1d002b011d032c062e002b001d042d081d013b00150004030b0026002b082c0027000700
+0b0026003a0731002c00270007000b0026002b003a023c003a013d0018002c0027001001
+0d003a00140000000a0009023e0038003f09400010000905400041002d04140100002d03
+000014012d001d00140100001d00140100001401000014011d0114030000140100022d0c
+3b0231013b003c003b003c042d061d0a2d1a3c002d003c033b013c022d013c002d003c01
+3b0010000f033e0038003f09400018003e0038004200150007001500310038002b004200
+43003f00440011003e0038003f0034004203450007002b003f004400150004000d003a00
+140000000c0007001101000032003f08460020001c000703170123002a002c002b031401
+00002b001d01140300001d0114011d001400000114010000140100001d00140100021402
+1d002b012c002e012c022e012b002e032b002c152b002e002b011d002b001d022d001d00
+2d0b3b053c003b003c002d063c002d0b100004021000000032003f084600210031000000
+3200420007003f03470042004800460021003b00000032003f013400420145001c003f01
+460021003b0008000d003600140000000b0004023e00490934004a001c00110520002a00
+2c022e01140100002b001d0014010000140100002c0114010000140000002c0014040000
+2e00140100002b011d0014022c062a062c072a052c072b001d002b012c002b011d042b01
+2e012c032e002b002e012b032c042e002c001d022d011d002d001d002d0a04000f020400
+3e0049093400440011003e00490042001c004903470042002b0034004a001c003e004901
+4b0042043800490034004a001c000700250013004c0000004d0004020f000d0033043403
+21004b001500080009010b00260130002d003b003103140100003b011401000014010000
+3b0214013c0000002d00140100032d00140100002d03140100002d0c1d032d0a1d0e2b00
+2e002c082a002c002a002c002a002c042e002c002b011d052d001d002d0b310009000401
+080004000f000d0033004e053700340021004b001c000f000d004e002b0039014f012100
+4e00490021004b001c000f000d004a004e01420050004e02480021004b001c0010002500
+13005100000009000f01040015002b0052074400530029003100070004042b0029002c00
+1d0314042d0014011d0014011d012d00140100001d0214032b001d00140100002c011402
+00012b002c052a022c002a042c002a072c032b011d042d181d012d181100260008000f00
+040111002b005200540548004400530055001c0011002b0054073400530055001c001100
+2b004f0054004e003c005201540056003f00530055001500100012001300140000004d00
+0f02040038004b0621003a0044002b000700040641001d002e002c012b0100042c000001
+1d0000011d00140100011d0300032e0100012c002e002c0000021d022d021d062b012c01
+1d012b001d062d011d042b052c072b012c052a002c0d1d022d011d082d011d0031002600
+090004021000170038004b003f0555003a0044002b001100100038005704330121003a00
+440032001500110038004b0046002c004b0233011f004a002c001c00040025003d001400
+000001000f02040015002d0047004b014f00580059003a0044002c001100040008000404
+100004002d001d032d061d05140100001d052d021d022d063c003b013c003b012d1a1d04
+2b011d012c031d032d021d082b011d092d021d09310026005a0004041000310037004700
+4b014f00580059003a001f003f0031001100170031002b005b004b014f00580059003a00
+1f0049003c001c0148002b005b004b014f00580059003a001f0049003c0015000f003500
+3600140000000c0009010f01070031003c0049005c003d005900210047002c001c001005
+170010021700110015001d002c072b022c0300011d002b001d052d0b3b0e2d003b022d01
+3b002d183c003b003c003b012d1f15000f005a0109000f0009030f00100038003c003f00
+55003d0059001f0027002b00110008000f01100031003c003f0055003d00590021004700
+32001c00040211003c002d00490057003d0059002100470032001c00100004000f000d00
+3a001400000001000f0109010f00040007001c003c031c001000040008000f0008000400
+080004030800040109005a001b0000771b005a00260008000f0109050800100015003b00
+3c021c0004000f0009020f00160010001500310311000f00090026000b0009010f000400
+1100310315000400090211002c003d0014000000090004010f0009010f0216020f130908
+0f1904010f02040210030701100504021000041208000f0208010f000801040310000400
+100a11010700170111000700170011010700100017001002040010000400100004031001
+040010020402100004060f0004000f0016020f0009010f0704060f0004012c003d001400
+00000c000f015dc704002b003d00140000005e0009000f005dc704003f00190014000000
+1b0009015dc715002a001900140000001a0004015dc7080012001900140000001a001001
+5d255f9b600014005d0310002c003d00140000000c0004015d255f00609a610014005d03
+04002a003d00140000005a000f015d255f00609a610014005d030f002c003d0014000000
+1a00170011005d255f00629a610014005d0310002b003d001400000001000f015d255f00
+5d0514005d0414015d1214005d78610014005d0304003f003d0014000000630004015d05
+14005d0414015d175f005d0514005d0414005d1214015d78610014005d0304002d001300
+140000001a00040010005d0514005d0414005d185f006406140064021400640014006401
+140164011400640114006401140064051400646a1902640a610014005d0304002d006500
+140000001b0010015d0614005d0214005d0014005d0114015d0114005d0114005d011400
+5d0114005d075f0066061400660214006600140066001400660114006600140066011400
+6601140066051400666a1902660a610014005d0311002d003d0014000000010010015d06
+14005d0214005d0014005d0014005d0114005d0014005d0114005d0114005d0a5f006606
+140066021400660014006600140366011400660014006600140066061400666819066608
+610014005d0310002e003d00140000003e0010015d0614005d0214005d0014005d001403
+5d0114005d0014005d0014005d0b5f006707140067001400670114006700140067041400
+670014006700140067061400676919046709610014005d0310002c003d00140000000100
+15015d0714005d0014005d0114005d0014005d0414005d0014005d0014005d0b5f006807
+140068001400680114006800140068011400680214006800140068071400686a1902680a
+610014005d0315002c003d00140000001b0010015d0714005d0014005d0114005d001400
+5d0114005d0214005d0014005d0c5f006808140068021400680114016803140068001400
+68071400686b1900680b610014005d0310002d003d00140000005e003b015d0814005d02
+14005d0114015d0314005d0014005d0314005d075f00699a610014005d0315002e003d00
+140000003e002c015d255f006a9a610014005d032d013d00140000003e0031015d255f00
+6a9a610014005d031d003f001900140000001b0031006b005d255f00009a610014005d03
+2d003c0013001400000131015d256000619b14005d0331002c003d00140000001b003101
+5d25199c14005d033b011300140000006c0015015dc731002b003d00140000005e003101
+5dc731003c001300140000003e0011015dc731002d003d00140000005a0031003b005dc7
+6b002d001300140000013b015d0214ad280029005d02620e5d023b002e003d0014000000
+1b0031006d005d026e003d0028ab0f0000005d026200190c62005d0231001d0065001400
+0000630048015d023d0029005dab6f005f005d02620019006f0270016f0070026f007000
+190062005d0238002b006500140000001b0015015d023d0029005dab6f005f005d026200
+19007000620060005d02640067005d006701190062005d0215002d003d00140000013800
+31005d023d0029005d015fa75d016f005f005d0262001900700060005d0060005d006200
+64005d0066006701190062005d0231002d00650014000001310038005d023d0029005d01
+71a75d016f005f005d026200190060015d0064005d016700660072006701190062005d02
+3b002d006500140000003e0015001c005d023d0029005d0171a75d016f005f005d026200
+19006f00620164005d0042001900660067007201190062005d022d016500140000001b00
+3b015d023d0029005d0171221417716c5d016f005f005d02620019006f005d0062006600
+130019003d017202190062005d0231001d003d00140000001b00310038005d023d002900
+5d0171221417716c5d016f005f005d026200190060006201670066003d00190067006900
+6801190062005d023b002d006500140000001b003b003c005d023d0029005d0171221417
+716c5d016f005f005d0262001900700067016400660042003d0068007200680069001900
+62005d023c002d005900140000012d015d023d0029005d0171221417716c5d016f005f00
+5d02620019006f0066006400720167007201690072006a00190062005d0231002e003d00
+1400000131015d023d0029005d0171221417716c5d016f005f005d02620019006f006701
+64006701680072006a0073006900190062005d023b002d005900140000012d015d023d00
+29005d017122141274001403716c5d016f005f005d026200190070006600670068007200
+68016903190062005d023c0031002300140000001b0031015d023d0029005d0171221406
+7402140774011403716c5d016f005f005d026200190004006c0075006300760063000600
+0c00760075007600190062005d0231002d006500140000011c0031005d023d0029005d01
+7122140374027702740214047400770074001402716c5d016f005f005d02620019003d00
+19013d0219003d0142002300190062005d0231012300140000012d015d023d0029005d01
+7122140174017708740114017400770174001402716c5d016f005f005d02620019007000
+6f0170006f03600070006900190062005d023c001c002300140000013c0078005d023d00
+29005d01710a1406711014017400770a740014017400770274001401716c5d016f005f00
+5d026200190066007202680372013e00190062005d023101130014000001170007005d02
+3d0029005d017108140a710e140174007709740014037401770074001401716c5d016f00
+5f005d0262001900790070006f0070006f00700179006f017200190062005d022d003c00
+13001400000111015d023d0029005d017106140e710c1401740077027400770674001404
+74021400716c5d016f005f005d02620019007000600062005d0064006600720068006900
+73003e00190062005d0231001d0059001400000131003b005d023d0029005d0171051410
+710b14007400770274001400740177057400140574001400710c1402715c5d016f005f00
+5d02620019007000600062005d006400660072006800690073003e00190062005d023b00
+310013001400000131007a005d023d0029005d01710414035f0114035f0014015f011403
+710a140074007702740014027401770474001406710b140071021400715b5d016f005f00
+5d02620019007000600062005d006400660072006800690073003e00190062005d023100
+15002300140000017a001c005d023d0029005d01710314035f0114085f02140271091400
+74007701740014047400770574001405710b140071051402710114027100140171021401
+710414027101140071001400710114017101140271021401712e5d016f005f005d026200
+19006f00600062005d00640066007200690173003e00190062005d0215002d0013001400
+000115015d023d0029005d01710314015f0314085f041400710914007400770174001405
+7400770574001404710c1402710114007102140071001400710114007101140071001400
+7101140071061400710014017101140071011400710314007100140071011400712d5d01
+6f005f005d02620019007000600062005d006400660072006800690073003e0019006200
+5d02310113001400000131015d023d0029005d01710314015f0314085f03140171091400
+74007701740014057400770674001403710f140071001400710214007100140071011400
+71011400710014037104140271001400710214037101140271011401712e5d016f005f00
+5d02620019007000600062005d006400660072006800690073003e00190062005d023100
+2d0059001400000131015d023d0029005d01710414025f0214065f031401710a14007400
+7701740014037401770774011402710b1400710214007100140071021400710014007101
+140071011400710014007106140071011400710014007102140071031400710114007103
+1400712d5d016f005f005d02620019007000600062005d00640066007200680069007300
+3e00190062005d027a001c0023001400000111015d023d0029005d01710514025f011406
+5f011402710b1400740077017404770674021404710b1400710214007100140071021400
+710014007101140071011400710014007101140071031400710114007100140071021400
+7101140071001400710114007100140071011400712d5d016f005f005d02620019007000
+600062005d006400660072006800690073003e00190062005d0231001100230014000001
+15015d023d0029005d01710614035f0014045f001403710c14007400770a74021407710c
+140271021402710114007101140071011400710114017105140171001401710314017102
+14017100140071001401712e5d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d0210003100130014000001310015005d023d002900
+5d017108140a710e1400740077087401140a716c5d016f005f005d026200190070006000
+62005d006400660072006800690073003e00190062005d02110031001300140000011500
+11005d023d0029005d01710a140671101400740077087400140b716c5d016f005f005d02
+620019007000600062005d006400660072006800690073003e00190062005d0207001100
+23001400000115015d023d0029005d0171221401740077087400140a716c5d016f005f00
+5d02620019007000600062005d006400660072006800690073003e00190062005d021500
+070020001400000131015d023d0029005d0171221401740077087400140a716c5d016f00
+5f005d02620019007000600062005d006400660072006800690073003e00190062005d02
+10001100230014000001110015005d023d0029005d0171221402740277057400140a716c
+5d016f005f005d02620019007000600062005d006400660072006800690073003e001900
+62005d021500310013001400000110015d023d0029005d01712214057404770174001409
+716c5d016f005f005d02620019007000600062005d006400660072006800690073003e00
+190062005d021100310023001400000115015d023d0029005d017122140a74021409716c
+5d016f005f005d02620019007000600062005d006400660072006800690073003e001900
+62005d0217001500230014000001040010005d023d0029005d0171221417716c5d016f00
+5f005d02620019007000600062005d006400660072006800690073003e00190062005d02
+1000110023001400000111015d023d0029005d0171221417716c5d016f005f005d026200
+19007000600062005d006400660072006800690073003e00190062005d02100017002300
+1400000110015d023d0029005d0171221417716c5d016f005f005d026200190070006000
+62005d006400660072006800690073003e00190062005d02110123001400000104015d02
+3d0029005d0171221417716c5d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d021000110023001400000110015d023d0029005d01
+71221417716c5d016f005f005d02620019007000600062005d0064006600720068006900
+73003e00190062005d02110123001400000110015d023d0029005d0171a75d016f005f00
+5d02620019007000600062005d006400660072006800690073003e00190062005d020700
+1100230014000001070010005d023d0029005d0171a75d016f005f005d02620019007000
+600062005d006400660072006800690073003e00190062005d0211001500230014000001
+1c015d023d0029005d015fa75d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d020700110023001400000115015d023d0029005d01
+5fa75d016f005f005d02620019007000600062005d006400660072006800690073003e00
+190062005d02150011002300140000011c015d023d0029005d015fa75d016f005f005d02
+620019007000600062005d006400660072006800690073003e00190062005d0211000400
+20001400000111015d023d0029005d015f2214175f6c5d016f005f005d02620019007000
+600062005d006400660072006800690073003e00190062005d0211000700200014000001
+15015d023d0029005d015f22140a7b00140b5f6c5d016f005f005d026200190070006000
+62005d006400660072006800690073003e00190062005d021c0010002000140000011c01
+5d023d0029005d015f2214097b00140c5f6c5d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d021c0011002300140000011c015d02
+3d0029005d015f227b0014077b00140d5f6c5d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d023100110023001400000131015d02
+3d0029005d015f227b0014067b00140e5f6c5d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d02110121001400000131003c005d02
+3d0029005d015f227b0014057b00140f5f6c5d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d02500015002300140000017a015d02
+3d0029005d015f227b0014057b00140f5f6c5d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d02310007002100140000013b003c00
+5d023d0029005d015f227b0014057b00140f5f6c5d016f005f005d026200190070006000
+62005d006400660072006800690073003e00190062005d0231001c002300140000013101
+5d023d0029005d015f2214007b0014047b0014027b00140b5f6c5d016f005f005d026200
+19007000600062005d006400660072006800690073003e00190062005d021c007c002300
+1400000115007a005d023d0029005d015f0a14065f1014007b0014047b0014037b011409
+5f6c5d016f005f005d02620019007000600062005d006400660072006800690073003e00
+190062005d02310007002100140000012d015d023d0029005d015f08140a5f0e14007b00
+14047b0014057b0114075f6c5d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d023c007c002300140000013c015d023d0029005d01
+5f06140e5f0c14007b0014047b0014077b0114055f6c5d016f005f005d02620019007000
+600062005d006400660072006800690073003e00190062005d0231000700200014000001
+31015d023d0029005d015f0514105f0b14007b0014047b0014087b0014055f0c14025f17
+14005f0014005f415d016f005f005d02620019007000600062005d006400660072006800
+690073003e00190062005d0231001100230014000001170007005d023d0029005d015f04
+14035f0114035f0014015f0114035f0a14007b0014047b0014097b0014045f0b14005f02
+14005f1614005f435d016f005f005d02620019007000600062005d006400660072006800
+690073003e00190062005d022d001c0023001400000111015d023d0029005d015f031403
+5f0114085f0214025f0914017b0014037b0014097b0014045f0b14005f0514025f011402
+5f0014015f0214015f0414005f0014005f0014005f0014015f0214015f0214015f325d01
+6f005f005d02620019007000600062005d006400660072006800690073003e0019006200
+5d023100100021001400000131007a005d023d0029005d015f0314015f0314085f041400
+5f0914027b0014037b0014087b0014045f0c14025f0114005f0214005f0014005f011400
+5f0114005f0014005f0114005f0314005f0014005f0014015f0114005f0014005f011400
+5f0014005f0114005f315d016f005f005d02620019007000600062005d00640066007200
+6800690073003e00190062005d02310007002100140000017a001c005d023d0029005d01
+5f0314015f0314085f0314015f0914037b0114017b0014097b0014035f0f14005f001400
+5f0214005f0014005f0114005f0114005f0014035f0314005f0014005f0014005f021400
+5f0014035f0114015f325d016f005f005d02620019007000600062007d007e007f008000
+1b00690073003e00190062005d021500100020001400000115015d023d0029005d015f04
+14025f0214065f0314015f0a14057b0014017b0014087b0014035f0b14005f0214005f00
+14005f0214005f0014005f0114005f0114005f0014005f0614005f0014005f0014005f02
+14005f0014005f0614005f315d016f005f005d0262001900700060006200810082008300
+84007900690073003e00190062005d023100070021001400000131015d023d0029005d01
+5f0514025f0114065f0114025f0b14067b0014017b0014087b0014025f0b14005f021400
+5f0014005f0214005f0014005f0114005f0114005f0014005f0114005f0314005f001400
+5f0014005f0214005f0014005f0114005f0014005f0114005f315d016f005f005d026200
+190070006000620085008600870088008900690073003e00190062005d02310008001f00
+1400000131015d023d0029005d015f0614035f0014045f0014035f0c14057b0014027b00
+14097b0014015f0c14025f0214025f0114005f0114005f0114005f0114015f0414005f00
+14005f0014005f0214005f0114015f0214015f325d016f005f005d026200190070006000
+62008a008b008c018d00690073003e00190062005d027a00070020001400000111015d02
+3d0029005d015f08140a5f0e14057b0014037b0014097b0014005f6c5d016f005f005d02
+620019007000600062008e008f00900091008d00690073003e00190062005d0231001000
+20001400000131015d023d0029005d015f0a14065f1014047b0014057b00140a5f6c5d01
+6f005f005d02620019007000600062005d006400660072006800690073003e0019006200
+5d023100070020001400000115015d023d0029005d015f2214037b0014067b00140a5f6c
+5d016f005f005d02620019007000600062005d006400660072006800690073003e001900
+62005d0210000f001f0014000001150011005d023d0029005d015f2214027b0014087b00
+14095f6c5d016f005f005d02620019007000600062005d00640066007200680069007300
+3e00190062005d02070004001f001400000115015d023d0029005d015f2214027b001408
+7b0014095f6c5d016f005f005d02620019007000600062005d0064006600720068006900
+73003e00190062005d02150008001f001400000131015d023d0029005d015f2214017b00
+140a7b0014085f6c5d016f005f005d02620019007000600062005d006400660072006800
+690073003e00190062005d02100009001f0014000001110015005d023d0029005d015f22
+14017b00140a7b0014085f6c5d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d021500040020001400000110015d023d0029005d01
+5f2214017b00140b7b0014075f6c5d016f005f005d02620019007000600062005d006400
+660072006800690073003e00190062005d02110009001f001400000115015d023d002900
+5d015f2214007b0014155f6c5d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d021700040020001400000110015d023d0029005d01
+5f2214007b0014155f6c5d016f005f005d02620019007000600062005d00640066007200
+6800690073003e00190062005d0211001000200014000001040010005d023d0029005d01
+5f2214175f6c5d016f005f005d02620019007000600062005d0064006600720068006900
+73003e00190062005d02100009001f001400000110015d023d0029005d015f2214175f6c
+5d016f005f005d02620019007000600062005d006400660072006800690073003e001900
+62005d021100100020001400000104015d023d0029005d015fa75d016f005f005d026200
+19007000600062005d006400660072006800690073003e00190062005d02100009001f00
+1400000110015d023d0029005d015fa75d016f005f005d02620019007000600062005d00
+6400660072006800690073003e00190062005d0211000f001f001400000110015d023d00
+29005d015fa75d016f005f005d02620019007000600062005d0064006600720068006900
+73003e00190062005d02070009001f001400000110015d023d0029005d015fa75d016f00
+5f005d02620019007000600062005d006400660072006800690073003e00190062005d02
+110009001f00140000011c015d023d0029005d015fa75d016f005f005d02620019007000
+600062005d006400660072006800690073003e00190062005d02070009001f0014000001
+11015d023d0029005d015fa75d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d02110009001f001400000111015d023d0029005d01
+5fa75d016f005f005d02620019007000600062005d006400660072006800690073003e00
+190062005d02150009001f00140000011c015d023d0029005d015fa75d016f005f005d02
+620019007000600062005d006400660072006800690073003e00190062005d0207000f00
+1f001400000107015d023d0029005d015fa75d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d021100090028004c00000111001500
+5d023d0029005d015fa75d016f005f005d02620019007000600062005d00640066007200
+6800690073003e00190062005d020700090028004c00000107015d023d0029005d015fa7
+5d016f005f005d02620019007000600062005d006400660072006800690073003e001900
+62005d021c00040028005100000111015d023d0029005d015fa75d016f005f005d026200
+19007000600062005d006400660072006800690073003e00190062005d02110009002800
+4c0000011c015d023d0029005d015fa75d016f005f005d02620019007000600062005d00
+6400660072006800690073003e00190062005d021c00090028004c00000107015d023d00
+29005d015fa75d016f005f005d02620019007000600062005d0064006600720068006900
+73003e00190062005d020700090040000e00000107015d023d0029005d015fa75d016f00
+5f005d02620019007000600062005d006400660072006800690073003e00190062005d02
+0700090028004c00000104015d023d0029005d015fa75d016f005f005d02620019007000
+600062005d006400660072006800690073003e00190062005d021100090028000e000001
+07015d023d0029005d015fa75d016f005f005d02620019007000600062005d0064006600
+72006800690073003e00190062005d021000090028004c00000111015d023d0029005d01
+5fa75d016f005f005d02620019007000600062005d006400660072006800690073003e00
+190062005d020400090028004c00000107015d023d0029005d015fa75d016f005f005d02
+620019007000600062005d006400660072006800690073003e00190062005d0207000900
+28004c00000107015d023d0029005d015fa75d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d020700090028004c00000110015d02
+3d0029005d015fa75d016f005f005d02620019007000600062005d006400660072006800
+690073003e00190062005d0204000f0028005100000107015d023d0029005d015fa75d01
+6f005f005d02620019007000600062005d006400660072006800690073003e0019006200
+5d020700090028004c00000104015d023d0029005d015fa75d016f005f005d0262001900
+7000600062005d006400660072006800690073003e00190062005d020400260028000e00
+000109015d023d0029005d015fa75d016f005f005d02620019007000600062005d006400
+660072006800690073003e00190062005d020800260028004c00000104015d023d002900
+5d015fa75d016f005f005d02620019007000600062005d00640066007200680069007300
+3e00190062005d021600090028004c00000110015d023d0029005d015fa75d016f005f00
+5d02620019007000600062005d006400660072006800690073003e00190062005d021000
+090028005100000110015d023d0029005d015fa75d016f005f005d026200190070006000
+62005d006400660072006800690073003e00190062005d0204000f002800510000011101
+5d023d0029005d015fa75d016f005f005d02620019007000600062005d00640066007200
+6800690073003e00190062005d020f00260028000e00000109015d023d0029005d015fa7
+5d016f005f005d02620019007000600062005d006400660072006800690073003e001900
+62005d021000090028004c000001040010005d023d0029005d015fa75d016f005f005d02
+620019007000600062005d006400660072006800690073003e00190062005d0204000900
+28004c00000104015d023d0029005d015fa75d016f005f005d0262001900700060006200
+5d006400660072006800690073003e00190062005d020800090028004c0000010f000800
+5d023d0029005d015fa75d016f005f005d026200190064001b005a003e025a003e011b00
+5a00190062005d020800260028004c00000108000f005d023d0029005d015fa75d016f00
+5f005d0262001900700067006800920072029300940068006100190062005d0204000900
+28004c000001040010005d023d0029005d015fa75d016f005f005d026200190072005a00
+1b013e001b015a0000001b005a00190062005d020400090028000e00000111015d023d00
+29005d015fa75d016f005f005d0262001900650095003d003a0195003d0042003a001900
+3d00190062005d021000090028000e00000110015d023d0029005d015fa75d016f005f00
+5d0262001900050a190062005d0204000900280051000001070010005d023d0029005d01
+5fa75d016f005f005d02620019000500960076006300760003000c009700980097006300
+190062005d0210000f0028005100000104015d023d0029005d015fa75d016f005f005d02
+62001900990079008d019a009b00790089008d0079008900190062005d0211000f004000
+4c00000104015d023d0029005d015fa75d016f005f005d026200190070006f0060005d02
+640066015d007200190062005d0211000f0040004c00000111015d023d0029005d015fa7
+5d016f005f005d02620019008d0062005d006201640066009c009d007200660019006200
+5d021100040040004c00000115015d023d0029005d015fa75d016f005f005d0262001900
+700060005d016200640066037200190062005d021100040040000e00000111015d023d00
+29005d015fa75d016f005f005d0262001900600166005d0066003d004200660068006700
+7200190062005d021c003b0023004c00000131015d023d0029005d015fa75d016f005f00
+5d026200190062015d0167003d01660072016800190062005d0231009e00200051000001
+31015d023d0029005d015fa75d016f005f005d02620019006401660119003d0119007200
+6a007200190062005d027a00310020004c00000111015d023d0029005d015fa75d016f00
+5f005d02620019005d00640066021901680072016a00190062005d023100150020004c00
+000131015d023d0029005d015fa75d016f005f005d026200190067005d00670072006600
+7200670068006902190062005d0231006b001f004c00000115015d023d0029005dab6f00
+5f005d0262001900670066006701720168016a0069006a00190062005d0210009f001f00
+0e000001310015005d023d0029005dab6f005f005d026200190067037201680069006800
+6a007300190062005d0211001500a00051000001150011005d022d001b006fab5f015d02
+6200190c62005d02070015001f004c00000131015d020f0067005fad5d02620e5d021001
+1f004c000001110015005dc71500100040004c00000110015dc711000400a1000e000001
+15015dc7170015001f005100000110015d0262af5d1411014a004c000001040010005d02
+620019ad62005d14100011004a005100000111015d02620019006f0570006f0305006500
+72007000640070866f0070027900660070003d00050008008d02700160005d0167006600
+7200190062005d14100015001f004c00000110015d02620019006f00600162015d006600
+64006600a201960095005a0067001b00608a700072006f00190005004d008d0060006201
+5d016400660064006701190062005d14110015001f004c00000110015d02620019006f00
+62015d036601670176003d005a0068005a00628a6f0072006f0019000500a3008d006000
+5d0062005d0164006600670066006700190062005d14110010004a004c00000110015d02
+62001900700062005d006200640166016701720063003a005a0092003e005d427d007e00
+7f0080001b005d427000720070003d000500a4008d00620060005d016400660272011900
+62005d14070015001f005100000110015d02620019006f00620164011900660167007201
+76003a005a0072003e0064428100820083008400790064426f0068006f003d0005007600
+8d00620064015d0064001900660067017200190062005d14110015001f00510000011c01
+5d02620019006f005d0164003d0019003d01670072006800030095005a0072003e006642
+850086008700880089006642700068006f003d000500a5008d0064005d0119023d007201
+6800190062005d14070010001f000e00000111015d02620019007000640166003d001902
+7200680069000c003d005a0072005a0072428a008b008c018d007242700068006f001900
+050063008d00640066013d0019003d00190072006801190062005d14110015001f004c00
+000111015d02620019006f006400660167003d00670068026900970042005a0093003e00
+68428e008f00900091008d00683e69006802790068006f003d00050006008d0066006400
+660067011900720068016900190062005d14150011001f004c0000011c015d0262001900
+7000660167026802690198003a005a0094003e00698a6f00720060003d0005004d008d00
+6600670172006700680072006902190062005d14070015001f005100000107015d026200
+19006f0066016700720067007200680069016a00970019005a0068001b00738a6f007200
+70004200050076008d00660167007201680269006a00190062005d1407006d001f005100
+0001110015005d02620019006f0067027201680069016a0163003d005a0061005a003e8a
+72003e00690023000500a6008d006700720067007201680069026a00190062005d140700
+3b0020005100000107015d02620019ad62005d141c00310020004c0000001b0011015d02
+62af5d1411003b002000510000011c015dc71c00310020004c00000107015dc707003b00
+20004c00000107015dc707007a0020004c00000104015dc7110078001f00510000011101
+5d031b2b5d021b2b5d021b315d021b2c5d03110031001f005100000107015d031b00a72a
+79005d011b00a72a79005d011b00a73079005d011b00a72b79005d021000310020004c00
+000107015d031b00a700a827a900a700aa005d011b00a700a827a900a700aa005d011b00
+a700a82da900a700aa005d011b00a700a828a900a700aa005d020700310021004c000001
+07015d031b00a700a800ab26a900a700aa005d011b00a700a800ab26a900a700aa005d01
+1b00a700a800ab2ca900a700aa005d011b00a700a800ab27a900a700aa005d0207003c00
+1f005100000110015d031b00a700a800ab111405ab0ea900a700aa005d011b00a700a800
+ab26a900a700aa005d011b00a700a800ab2ca900a700aa005d011b00a700a800ab27a900
+a700aa005d020400500020004c00000107015d031b00a700a800ab1014015f0314002d00
+ab0da900a70089005d011b00a700a800ab26a900a70089005d011b00a700a800ab2ca900
+a70089005d011b00a700a800ab0e14002d00ab0614002d00ab0da900a70089005d020700
+11001f000e00000104015d031b00a700a800ab0f14005f0014005f0314002d00ab0da900
+a7009a005d011b00a700a800ab26a900a7009a005d011b00a700a800ab2ca900a7009a00
+5d011b00a700a800ab0d14022d00ab0414022d00ab0ca900a7009a005d0204007a001f00
+51000000760009015d031b00a700a800ab0e14005f0114005f0314002d00ab0da900a700
+8d005d011b00a700a800ab26a900a7008d005d011b00a700a800ab2ca900a7008d005d01
+1b00a700a800ab0e14022d00ab0214022d00ab0da900a7008d005d020800310020004c00
+000104015d031b00a700a800ab0d14045f0314002d00ab0da900a7008d005d011b00a700
+a800ab111401ab12a900a7008d005d011b00a700a800ab0e140dab0fa900a7008d005d01
+1b00a700a800ab0f14022d00ab0014022d00ab0ea900a7008d005d02080031001f005100
+0000760004015d031b00a700a800ab0d14005f0714002d00ab0da900a7008d005d011b00
+a700a800ab1014032d00ab10a900a7008d005d011b00a700a800ab0f140b2d00ab0fa900
+a7008d005d011b00a700a800ab1014052d00ab0fa900a7008d005d0216001c001f004c00
+000110015d031b00a700a800ab0d14005f0714002d00ab0da900a7008d005d011b00a700
+a800ab0f14052d00ab0fa900a7008d005d011b00a700a800ab1014092d00ab10a900a700
+8d005d011b00a700a800ab1114032d00ab10a900a7008d005d020400500020004c000001
+11015d031b00a700a800ab0d14005f0714002d00ab0da900a7008d005d011b00a700a800
+ab0e14072d00ab0ea900a7008d005d011b00a700a800ab1114072d00ab11a900a7008d00
+5d011b00a700a800ab1114032d00ab10a900a7008d005d020f003c0022004c0000010901
+5d031b00a700a800ab0d14005f0714002d00ab0da900a7008d005d011b00a700a800ab0d
+14092d00ab0da900a7008d005d011b00a700a800ab1214052d00ab12a900a7008d005d01
+1b00a700a800ab1014052d00ab0fa900a7008d005d0210002e002300510000000c000400
+10005d031b00a700a800ab0d14005f0714002d00ab0da900a7008d005d011b00a700a800
+ab0c140b2d00ab0ca900a7008d005d011b00a700a800ab1314032d00ab13a900a7008d00
+5d011b00a700a800ab0f14022d0114022d00ab0ea900a7008d005d0204002e0023004c00
+00000a0004015d031b00a700a800ab0d14005f0714002d00ab0da900a7008d005d011b00
+a700a800ab0d2d0bab0ca900a7008d005d011b00a700a800ab1414012d00ab14a900a700
+8d005d011b00a700a800ab0e14022d00ab0214022d00ab0da900a7008d005d020f002e00
+23004c00000001000f015d031b00a700a800ab0d14005f0714002d00ab0da900a7008d00
+5d011b00a700a800ab26a900a7008d005d011b00a700a800ab152d00ab15a900a7008d00
+5d011b00a700a800ab0d14022d00ab0414022d00ab0ca900a7008d005d020f002c002300
+51000000090004015d031b00a700a800ab0d14092d00ab0da900a7008d005d011b00a700
+a800ab26a900a7008d005d011b00a700a800ab2ca900a7008d005d011b00a700a800ab0e
+14002d00ab0614002d00ab0da900a7008d005d0204002c0023004c0000001a0009015d03
+1b00a700a800ab0e2d09ab0da900a70089005d011b00a700a800ab26a900a70089005d01
+1b00a700a800ab2ca900a70089005d011b00a700a800ab27a900a70089005d0204002c00
+13004c0000000100100004005d031b00a700a800ab26a900a70089005d011b00a700a800
+ab26a900a70089005d011b00a700a800ab2ca900a70089005d011b00a700a800ab27a900
+a70089005d0204002a0013004c0000000100080004005d031b00a700a800ab26a900a700
+8d005d011b00a700a800ab26a900a7008d005d011b00a700a800ab2ca900a7008d005d01
+1b00a700a800ab27a900a7008d005d020f002c0023004c0000000900040008005d031b00
+a700a928a7008d005d011b00a700a928a7008d005d011b00a700a92ea7008d005d011b00
+a700a929a7008d005d020800250023005100000004000f015d031b00a72a70005d011b00
+a72a70005d011b00a73070005d011b00a72b70005d020f00120013005100000006000901
+5d047900ac008902790189048d04ac0189167000aa005d027900ac008902790189048d04
+ac0189167000aa005d027900ac008902790289058d04ac0289197000aa005d027900ac00
+8902790189048d04ac0289167000aa005d02080025001300510000003e000b015dc70900
+3f0013004c00000004025dc70900410013004c00000007000f015dc708002c0013004c00
+00000900070011020702110007011800111a070118001004070110050700180107001015
+0700100a040408010f0209190f0009000f0604000802040210010703110f150011001503
+1c0015061105070010010700100304001002040010020800290013005100000011002900
+2f0129002f0033002f00290033062f003302300027111e0647031e062717300233012f01
+29002f0229052f0033052f0033002f0033042f0129042f0033012f0029002f0133013000
+27013000270930013307300027083000270530023302271c42004c000a00ad003d001901
+3d0019003d0265003d6365003d0059013d00650059011300590365013d0165003d0e5901
+650059013d1d19003d0119003d0119073d0019013d075600420051004c00540051054c0a
+51044c1051024c0d51024c0651004c0151054c0851014c0151044c1d0e014c000e1d4c01
+0e014c010e004c000e0c4c030e1514004c02
+%%EndData
+end
+%%PageTrailer
+%%Trailer
+%%BoundingBox: 0 0 208 223
+%%EOF

Added: packages/openev/branches/upstream/current/doc/lodgen.fig
===================================================================
--- packages/openev/branches/upstream/current/doc/lodgen.fig	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/lodgen.fig	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,117 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+5 1 0 1 0 7 100 0 -1 0.000 0 1 0 0 8850.000 5287.500 8400 6600 8850 6675 9300 6600
+6 1200 2700 2100 3600
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 1200 2700 2100 2700 2100 3600 1200 3600 1200 2700
+4 1 0 100 0 20 10 0.0000 4 120 420 1650 3075 LOD0\001
+4 1 0 100 0 20 10 0.0000 4 120 390 1650 3270 cache\001
+-6
+6 2400 4200 3300 5100
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 2850 4650 424 424 2850 4650 3150 4950
+4 1 0 100 0 20 10 0.0000 4 120 540 2850 4845 average\001
+4 1 0 100 0 20 10 0.0000 4 150 450 2850 4650 4 pixel\001
+-6
+6 4800 4200 5700 5100
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 5250 4650 424 424 5250 4650 5550 4950
+4 1 0 100 0 20 10 0.0000 4 120 540 5250 4845 average\001
+4 1 0 100 0 20 10 0.0000 4 150 450 5250 4650 4 pixel\001
+-6
+6 7200 4200 8100 5100
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 7650 4650 424 424 7650 4650 7950 4950
+4 1 0 100 0 20 10 0.0000 4 120 540 7650 4845 average\001
+4 1 0 100 0 20 10 0.0000 4 150 450 7650 4650 4 pixel\001
+-6
+6 9600 4200 10500 5100
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 10050 4650 424 424 10050 4650 10350 4950
+4 1 0 100 0 20 10 0.0000 4 120 540 10050 4845 average\001
+4 1 0 100 0 20 10 0.0000 4 150 450 10050 4650 4 pixel\001
+-6
+1 3 0 1 0 0 100 0 20 0.000 1 0.0000 1650 4650 40 40 1650 4650 1690 4650
+1 3 0 1 0 0 100 0 20 0.000 1 0.0000 4050 4650 40 40 4050 4650 4090 4650
+1 3 0 1 0 0 100 0 20 0.000 1 0.0000 6450 4650 40 40 6450 4650 6490 4650
+1 3 0 1 0 0 100 0 20 0.000 1 0.0000 8850 4650 40 40 8850 4650 8890 4650
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 8850 5700 450 75 8850 5700 9300 5775
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 300 4650 2325 4650
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 3375 4650 4725 4650
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 5775 4650 7125 4650
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 8175 4650 9525 4650
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 10575 4650 11250 4650 11250 3675
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 1650 4650 1650 3675
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4050 4650 4050 3675
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6450 4650 6450 3675
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 8850 4650 8850 3675
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 3600 2700 4500 2700 4500 3600 3600 3600 3600 2700
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 6000 2700 6900 2700 6900 3600 6000 3600 6000 2700
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 8400 2700 9300 2700 9300 3600 8400 3600 8400 2700
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 10800 2700 11700 2700 11700 3600 10800 3600 10800 2700
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 1650 2625 1650 1725
+2 4 0 1 0 7 100 0 -1 0.000 0 0 15 0 0 5
+	 11700 1200 1200 1200 1200 1650 11700 1650 11700 1200
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4050 2625 4050 1725
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6450 2625 6450 1725
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 8850 2625 8850 1725
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 11250 2625 11250 1725
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 11775 1425 12900 1425
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 8400 5700 8400 6600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9300 5700 9300 6600
+2 1 1 1 0 7 100 0 -1 4.000 0 0 -1 1 1 2
+	0 0 1.00 60.00 120.00
+	0 0 1.00 60.00 120.00
+	 8850 4725 8850 5550
+4 1 0 100 0 20 10 0.0000 4 120 420 4050 3075 LOD1\001
+4 1 0 100 0 20 10 0.0000 4 120 390 4050 3270 cache\001
+4 1 0 100 0 20 10 0.0000 4 120 420 6450 3075 LOD2\001
+4 1 0 100 0 20 10 0.0000 4 120 390 6450 3270 cache\001
+4 1 0 100 0 20 10 0.0000 4 120 420 8850 3075 LOD3\001
+4 1 0 100 0 20 10 0.0000 4 120 390 8850 3270 cache\001
+4 1 0 100 0 20 10 0.0000 4 120 420 11250 3075 LOD4\001
+4 1 0 100 0 20 10 0.0000 4 120 390 11250 3270 cache\001
+4 1 0 100 0 20 12 0.0000 4 135 975 6450 1500 LOD Select\001
+4 1 0 100 0 20 10 0.0000 4 150 450 12300 1350 output\001
+4 1 0 100 0 20 10 0.0000 4 150 345 750 4575 input\001
+4 1 0 100 0 20 10 0.0000 4 120 420 8850 6150 LOD3\001
+4 1 0 100 0 20 10 0.0000 4 120 195 8850 6345 file\001

Added: packages/openev/branches/upstream/current/doc/openev.tex
===================================================================
--- packages/openev/branches/upstream/current/doc/openev.tex	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/openev.tex	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1039 @@
+\documentclass{openevreport}
+
+\renewcommand{\bibname}{References}
+\foottext{Version 1.1, 2000-06-19 \newline\scriptsize
+Use and duplication of this document or any information
+contained herein is subject to the notice on the front of this
+document.}
+
+\begin{document}
+
+\begin{titlepage}
+\pagestyle{empty}
+\centering
+\vspace*{0.75in}
+
+\includegraphics[width=2.5in]{openevlogo.eps} \\
+\vspace{0.2in}
+{\bf\Large
+Library Design\\
+}
+\vspace{0.5in}
+{\large
+Revision 1.1
+\\
+June 19, 2000 \\
+\vspace{0.2in}
+\copyright Atlantis Scientific Inc.
+}
+
+GeoInnovations 1999 \\
+
+\vspace{0.5in}
+\includegraphics[width=2.5in]{asilogo.eps} \\
+Scientific Inc. \\
+20 Colonade Road, Suite 110 \\
+Nepean, Ontario  K2E 7M6 \\
+email:  openev at atlsci.com \\
+\vspace{0.7in}
+
+\framebox{
+\begin{minipage}[t]{6in}
+Copyright \copyright 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ 
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+ 
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+ 
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+\end{minipage}
+}
+
+\end{titlepage}
+
+\pagenumbering{roman}
+
+\setlength{\parskip}{1.0ex}
+\tableofcontents
+\setlength{\parskip}{3.0ex}
+\clearpage
+\pagenumbering{arabic}
+\setcounter{page}{1}
+
+\chapter{Introduction}
+
+This document describes the ``OpenEV'' library, which is a tool for
+interactive visualization of raster and vector data, with a particular
+emphasis on remotely sensed data and geographic datasets.
+
+In summary, OpenEV is targeted at the following feature set:
+\begin{itemize}
+\item Run on popular platforms.
+\item Handle 2D/3D raster and vector data.
+\item Gracefully handle very large (gigabyte) raster datasets. 
+\item Display real and complex raster data.
+\item Handle multi-channel data.
+\item Represent multi-channel data in a number of ways (RGB, HSV).
+\item Provide multiple views of a single dataset with different
+interpretations.
+\item Display multiple datasets within a single view.
+\item Understand and interpret georeferencing information, and provide 
+on-the-fly reprojection of datasets as necessary.
+\item Provide printed output.
+\item Provide view manipulation function (pan, zoom, rotate) at
+interactive frame rates.
+\item Be able to play a role in a larger tool development or image
+analysis environment.
+\item Take advantage of commodity hardware for 2D/3D acceleration, if
+present.
+\end{itemize}
+
+The discussion begins with a description of how OpenEV fits in a
+broader environment, including third party
+libraries it relies on.  The OpenEV architecture and class hierarchy is
+then summarized.  This is followed by a more detailed description of
+the OpenEV undo system, data model and layer drawing model.
+Interactive systems, such as OpenEV tools and common user interface
+elements are described.
+
+A familiarity with the concepts of computer graphics, and with OpenGL
+in particular, is assumed.  The reader is referred to \cite{woo:opengl}
+for a general background of this subject.
+
+\chapter{The Broad Environment}
+
+The OpenEV library provides both C and Python language bindings.  While
+the OpenEV C level library can be used as part of other specialized
+applications, it's main function is to participate in a broader
+environment of ``pluggable'' models.  Python provides the glue to tie
+these modules together.
+
+\begin{figure}
+\centering
+\includegraphics[width=6in]{openev_bigpicture.eps}
+\caption{The Broad Environment}
+\label{fig:bigpicture}
+\end{figure}
+
+Figure \ref{fig:bigpicture} shows the OpenEV C library and PyOpenEV
+Python bindings in the broader context.  The other pieces in this
+context are as follows:
+\begin{description}
+\item[OpenGL] The portable graphics library.
+\item[Gtk+] The portable user interface components library.
+\item[PyGtk] Python bindings for Gtk+.
+\item[Basic Viewer UI] Simple dialogs that are common to most OpenEV
+based applications.
+\item[GDAL I/O Lib] The geospatial raster data I/O formats library.
+\item[PyGDAL I/O] Python bindings for the GDAL I/O Lib.
+\item[Proj.4] The C level cartographic projections library.
+\item[NumPy] The Numerical Python library, which provides Python
+access to array objects.
+\item[Numerical Analysis] Standard numerical analysis libraries.
+\end{description}
+
+\chapter{Architecture}
+
+OpenEV makes use of the GtkObjects hierarchy which provides a degree of
+object-orientation to the C language.  The OpenEV classes are exposed
+to Python using a C to Python wrapper layer in a manner similar to the
+Gtk Python bindings (PyGtk)\cite{gtk.org}.  Figure \ref{fig:gvclass}
+shows the highlights of the OpenEV class structure.  The list of
+classes in the figure is representative, not exhaustive.
+
+\begin{figure}
+\centering
+\includegraphics[width=6in]{gvclass.eps}
+\caption{OpenEV Class Hierarchy}
+\label{fig:gvclass}
+\end{figure}
+
+\section{GvViewArea}
+
+The GvViewArea is the principle drawing area widget exposed by the
+OpenEV library.  This widget is typically embedded in a top level
+window.  Any number of view areas may be active concurrently.  Each
+view area normally has its own OpenGL context, and a set of GvLayers.
+When the view area receives an expose event, it activates its context 
+and commands each layer to draw in turn.  The set of layers are
+assigned a changeable drawing order, so that one layer may partially
+obscure another (i.e. it is drawn ``on top'').  The view also
+maintains an ``active'' layer, which is the layer currently under edit 
+by a tool.
+
+The view area has a current transformation state (camera position and
+orientation), which it sets before drawing.  In 2D mode the transformation
+state describes a top down orthographic view of the data, while in 3D
+the transformation state describes a perspective view with arbitrary
+orientation. The 3D view mode can be used to view either 3D or 2D data.
+Keyboard and mouse events are
+trapped by the view area which allow the user to modify the
+transformation state (e.g. pan, zoom, rotate).
+
+Because each view area is provided with its own set of layers, two views
+can display a different ``interpretation'' of the same dataset.  For
+instance, one view might display the magnitude of a complex
+interferogram, and another the phase of the same image.  It is also
+possible that multiple views may share the same set of layers (and
+the same OpenGL context).  These views always provide the same
+interpretation of the data, but may be displaying different parts of
+it.
+
+A GvViewArea publishes the following signals (events):
+\begin{itemize}
+\item {\bf active-changed}: The ``active layer'' for editing and 
+interpretation purposes has changed.  This signal is also emitted when 
+new layers are added or removed, even if the active layer does not change.
+
+\item {\bf view-state-changed}: This signal is emitted after the view state 
+changes.
+This includes flipping, zooming and roaming.  It does not include mouse
+position changes, or changes to display characteristics of layers in the
+view. 
+
+\item {\bf gldraw}: Generated after layer drawing to give other application
+components (such as the GvTool) the opportunity to overlay additional 
+drawing on the view for each refresh.
+
+\end{itemize}
+
+\section{GvData}
+
+GvData is an abstract class that provides some essential event
+handling and generic property 
+mechanisms to data containers and layers.  GvData instances
+are connected in a hierarchical parent-child relationship.  These
+relationships are used to propagate data change events through the
+OpenEV system.  Two signals, ``changing'' and ``changed'', are exposed
+by GvData to facilitate this propagation.  Rules for state changes are 
+as follows:
+
+\begin{enumerate}
+\item If a GvData instance is about to change state, it first sends a
+``changing'' event to its parent.  The event propagates up the
+ancestor tree to the top node (parentless) GvData which emits a
+``changing'' signal.  Only the top node emits this signal.
+\item Once the state change is affected, the GvData instance sends a
+``changed'' event to its parent.  If the parent has a
+``child-changed'' handler, it is called.  The event propagates up the
+ancestor tree to the top node GvData which emits a ``changed''
+signal.
+\item Each child of the top node GvData traps the ``changed'' signal
+and re-emits the signal.  If the child has a ``changed'' handler, it
+is called.  The event propagates down the ancestor tree, fanning out
+across all descendants.
+\end{enumerate}
+
+A GvData subclass can use the ``changed'' and ``child-changed'' event
+handlers to adjust their state to reflect the change that has
+occurred.  Normally, the ``child-changed'' handler would pull state
+information from the child, and the ``changed'' handler would pull
+state information from the parent.
+
+\begin{figure}
+\centering
+\includegraphics{gvdata.eps}
+\caption{GvData Hierarchy Example}
+\label{fig:gvdata}
+\end{figure}
+
+To illustrate change event handling, consider the example GvData
+hierarchy in figure \ref{fig:gvdata}.  Here we have two layers showing 
+different representations of the same set of points, one in UTM (Universal Transverse Mercator)
+projection and one in LCC projection.  The original dataset is in an
+LCC projection.  An intermediate data set (UTM points) is created to
+cache the points after transformation to UTM.
+
+Now consider the effect of a tool applied to the UTM points
+representation, which moves a point in UTM space.  Prior to
+affecting the translation, the UTM points object sends a ``changing''
+event to the LCC (Lambert Conformed Conic) point object, which emits a ``changing'' signal.
+This signal is trapped by the undo mechanism (see Chapter 4).  After the
+change, a ``changed'' event is sent to the LCC points object.  The
+``child-changed'' handler of this object pulls the new location of the 
+translated point from the UTM points object.  The LCC points object
+repositions the point in LCC space and emits a ``changed'' signal.
+Both layers eventually trap this signal and force a redraw to display
+the change.
+
+The GvData class also provides a mechanism to get and set state
+``mementos'', which are used to facilitate undo and redo capabilities
+(see below).
+
+\section{GvLayer}
+
+GvLayer is an abstract class whose subclasses know how to draw a
+representation of a dataset using OpenGL commands.  Most layers
+provide an adjustable internal state which effects the representation
+(e.g. colour of the lines in a line layer).  The GvLayer class
+provides facilities to get the extents (bounding region) of the layer
+and change the visibility of the layer.
+
+The GvLayer publishes the following signals:
+\begin{itemize}
+\item {\bf setup}: Generated when a layer is attached to a GvViewArea, 
+and normally trapped by the specific layer class (ie. GvRasterLayer) to 
+perform view specific setup operations.  Note that technically a GvLayer 
+can be attached to more than one GvViewArea though this is not being
+utilized, and may be specifically forbidden in the future. 
+
+\item {\bf teardown}: Generated when a layer is removed from a GvViewArea,
+and normally trapped by the specific layer class to perform disconnect
+operations. 
+
+\item {\bf draw}: Generated by the GvViewArea on the layer to trigger
+it to draw itself.
+
+\item {\bf get-extents}: Used to fetch the layer extents.
+
+\item {\bf display-change}: Notification that some aspect of the display
+characteristics has changed, and that a redraw will be necessary.  Normally
+trapped by GvViewArea to trigger a redraw.  Note that it is intended to 
+be distinct from a data-change signal which indicates that the underlying
+raster or vector data (normally from a parent GvData) has changed. 
+
+\end{itemize}
+
+\section{GvShapeLayer}
+
+The GvShapeLayer class provides a layer of abstraction to datasets
+containing shapes (vector data).  This abstraction is used by certain
+tools which can operate on any kind of shape (such as the node editing 
+tool).
+
+The GvShapeLayer maintains a list of ``selected'' shapes and permits
+translate and delete operations on the currently selected shapes.
+Facilities for node manipulation (move, insert, delete) are provided.
+Functions to ``pick'' a shape or a node (determine which shape/node
+the mouse cursor is over) are provided.
+
+The GvShapeLayer publishes the following signals, many of which are
+just intended to be caught by the derived layer to trigger an operation:
+
+\begin{itemize}
+
+\item {\bf draw-selected}: Cause the layer to draw the selected shape(s) in
+a special manner indicating their selection.  
+
+\item {\bf delete-selected}: Cause the layer to delete the currently selected
+shape(s). 
+
+\item {\bf translate-selected}: Cause the layer to 
+translate the selected objects by the specified amount. 
+
+\item {\bf pick-shape}: Cause the layer to draw all shapes in picking mode
+(with their identifier as the GL name. 
+
+\item {\bf pick-node}: Cause the layer to draw all nodes of the selected
+shapes in picking mode.
+
+\item {\bf get-node}: Cause the layer to return the position of the indicated
+node. 
+
+\item {\bf move-node}: Cause the layer to move the indicated node by the 
+indicated amount.
+
+\item {\bf insert-node}: Cause the layer to insert a node at the indication
+position.
+
+\item {\bf delete-node}: Cause the layer to delete the indicated node.
+
+\item {\bf node-motion}: Indicate that a node has moved. 
+
+\item {\bf selection-changed}: Indicate that the selection list has 
+changed.  Used by other application components wanting to act on, or report
+information about the current selection.
+
+\end{itemize}
+
+\section{Data Containers}
+
+The classes GvShapes, and GvRaster (see \ref{fig:gvclass}) are examples of OpenEV
+data containers.  These classes provide access to the data they
+contain, and may expose methods for modifying the data.  Normally a
+GvLayer subclass points to a corresponding data container
+(e.g. GvRasterLayer points to a GvRaster instance).
+
+\section{GvTool}
+
+The GvTool class is an abstract base class for OpenEV tools.  Tools are
+used to interact with the user for the purpose of querying or
+manipulating datasets.  When a tool is ``activated'' on a particular
+view area, it captures certain mouse and keyboard events that occur in
+the view window to provide this interaction.  If a tool needs to draw
+in the view window, it traps the ``draw'' signal from a layer or from
+the view to trigger drawing.
+
+\chapter{Undo/Redo System}
+
+OpenEV provides a flexible system for recording user interactions with
+datasets in order to provide undoable and redoable operations.  For
+instance, if a user deletes a shape from a shape layer, an undo
+function is available to restore the shape.  If an undo is performed,
+a redo function is available to delete the shape again.
+
+The undo system (called GvUndo) maintains a stack of so-called data
+state ``mementos'', which contain enough information to reverse an
+operation on a dataset (a GvData object).  Each GvData object wishing
+to employ the GvUndo must be registered with the system.  GvUndo
+connects to the objects ``changing'' signal (thus only root level
+GvData objects should be registered).  When this signal is received,
+GvUndo asks the GvData for a memento describing the change about to
+take place (essentially a snapshot of the current state), and pushes
+the memento onto the undo stack.
+
+Mementos are opaque to GvUndo.  Only the originating GvData object
+can interpret them.  If an undo request is issued, a memento is popped
+off the stack and the memento is handed back to the GvData object to be
+used.  The originating GvData object is also responsible for deleting
+the memento when the undo stack is cleared.
+
+In some cases a tool may perform a function which is not undoable.
+The undo system may be temporarily ``closed'' for this purpose.  When
+closed, GvUndo will ignore ``changing'' signals.  Undoing may also be
+temporarily disabled by a tool in the middle of a complex operation.
+When disabled, GvUndo will ignore events that would pop mementos off the 
+stack.
+
+A separate redo stack is maintained by GvUndo.  Before a memento is
+popped off the undo stack, a flag is set which temporarily puts GvUndo 
+in ``redo mode''.  When the GvData object makes a change as the result
+of the undo, it emits a ``changing'' signal which is trapped by
+GvUndo.  A new memento is created, and since the redo mode flag is
+set, it is pushed onto the redo stack.  If the user requests a redo
+operation, the memento at the top of the redo stack is used.  Finally,
+once a new undoable operation is pushed onto the undo stack, the redo
+stack is cleared.
+
+\chapter{Data Model}
+
+OpenEV is able to display a fairly diverse array of geographic
+datasets.   The primitives in OpenEV are: 
+point, polyline, area, and raster.  Other primitives may be added in the
+future.
+
+Geographic primitives have no knowledge of the coordinate system,
+projection or geographic datum to which their data is referenced.
+Such information is instead associated with the GvData container class in
+the from of an OpenGIS ``Well Known Text'' coordinate system descriptions. 
+
+The model used in OpenEV for vector data is based on the OGDI data model
+\cite{mor:ogdi}, and the OpenGIS ``Simple Features'' specification. 
+The current data model is explicitly three dimensional.
+
+\section{Shapes}
+
+Point, polylines are areas are all managed internally as GvShape objects, 
+and held in a GvShapes container class.  
+
+All shapes have a properties list
+(GvProperties) which can be used to hold per-shape attributes useful for GIS
+applications. Drawing
+override information or other application or user specific data.  There is
+no concept of a fixed container wide schema (set of attributes) applied for
+all shapes ... each shape can have an independent list of properties. 
+
+\begin{itemize}
+
+\item {\bf Points} - A point GvShape has one 3D vertex.
+
+\item {\bf Polylines} - A polyline GvShape 
+contains a list of points forming one contiguous line. 
+
+\item {\bf Areas} - A GvShape area which consists of one outer ring, and zero
+or more inner rings, each consisting of 3 or more points which implicitly
+forms a ring (the last point does not need to match the first ... it is 
+assumed the ring should be closed).  The inner rings represent ``holes''
+in the area.  Inner rings are constrained to be within the outer ring, 
+non-intersecting with other rings, and to not be nested. 
+
+\end{itemize}
+
+The GvShape object provides a unified interface to the points and rings
+in a shape for points, line and areas.  Thus a point GvShape can be 
+accessed as if it were an area with one ring containing only one point.  
+The only way to determine the underlying type is to explicitly query it. 
+
+Note that currently GvShape's are not GtkObjects, in order to keep them
+as lightweight as possible.  The following definitions are used internally
+for shape objects.  The flags field is used to keep track of some display
+related information, and the GvAreaShape also contains a tesselated
+``OpenGL ready'' form of the area for rapid display.
+
+\begin{verbatim}
+#define GVSHAPE_POINT      1
+#define GVSHAPE_LINE       2
+#define GVSHAPE_AREA       3
+
+typedef struct
+{
+    gint      type;
+    guint     flags;
+    GvProperties properties;
+} GvShape;
+
+typedef struct
+{
+    gint      type;
+    guint     flags;
+    GvProperties properties;
+    float     x;
+    float     y;
+    float     z;
+} GvPointShape;
+
+typedef struct
+{
+    gint      type;
+    guint     flags;
+    GvProperties properties;
+    int       num_nodes;
+    float     *xyz_nodes;
+} GvLineShape;
+
+typedef struct
+{
+    gint      type;
+    guint     flags;
+    GvProperties properties;
+    int       num_rings;
+    int       *num_ring_nodes;
+    float     **xyz_ring_nodes;
+
+    /* tesselation information */
+    gint      fill_objects; /* -1 is untesselated, -2 is `do not tesselate' */
+    GArray    *mode_offset;
+    GArray    *fill;
+} GvAreaShape;
+\end{verbatim}
+
+\section{Raster}
+
+The GvRaster class provides access to image data in tiles.  Because
+image data size is potentially very large, GvRaster does not store the
+entire image in memory.  Instead, it connects to a data input
+abstraction object provided by the GDAL Data I/O Library.  This abstraction
+object may be connected to a memory buffer (in the case of an image in
+virtual memory) or to an image file, socket, etc.  Since reading data
+from this object is potentially slow, GvRaster maintains a local cache
+of the image tiles.  The maximum cache size is a parameter of
+GvRaster.  When the cache size limit is reached, tiles in the cache
+are discarded on a least recently used (LRU) basis.  The exact
+strategy for determining which tiles to discard will be tuned during
+the testing phase of the project.
+
+GvRaster is also responsible for level of detail (LOD) reduction of
+image tiles, also known as \emph{pyramiding}.  The LOD reduction
+process is performed ``on-the-fly'' as each tile is loaded.  LOD0 is
+the full resolution data, with nominal tile dimensions (adjustable) of
+$256\times 256$ pixels (maximum 512KB/tile).  LOD1 through LODN are
+generated by recursively applying a $2\times 2$ box filter (4 pixel
+average) and decimating by 2 in rows and columns.  For example,
+nominal tile dimensions for LOD1 through LOD3 are $128\times 128$
+(128KB), $64\times 64$ (32kB), and $32\times 32$ (8kB),
+respectively.  The process is stopped if the LOD being generated
+already exists in the cache.  Figure \ref{fig:lodgen} illustrates the
+LOD reduction process.  The number of levels generated will be tuned during the
+testing phase of the project.
+
+Since the data contained in a GvRaster can take on a number of
+different types (integer or floating point, real or complex, different 
+bit depth), and since either averaging, or decimation may be desired, there are
+a number of reduction kernels required.  GvRaster
+switches to the right kernel at run time.  Currently, an averaging algorithm
+is employed in computing LOD1 through LODN
+
+In order to accelerate initial overview display, GDAL provides for access
+to pre-built levels of detail in source data files when available. 
+A pyramid level can be attached to one or more LOD stages (see LOD3
+in the figure).  If the LOD3 file existed when the pipeline was constructed,
+it would be used to fill in the LOD3 and LOD4 caches without needing
+to read the full resolution image.  Such persistent caches are only
+attached to lower LODs, where the total data size is relatively small
+(a few megabytes).  Addition of a LOD file to the raster cache is the
+responsibility of the application, and can be either automatic, or
+triggered by user request.
+
+\begin{figure}
+\centering
+\includegraphics[width=6in]{lodgen.eps}
+\caption{LOD Reduction and Cache}
+\label{fig:lodgen}
+\end{figure}
+
+The total available cache size is split evenly across each LOD, except
+the lowest LOD cache which should handle every tile in the image.
+Each LOD cache has a separate LRU list.  Requests for a tile/LOD
+combination can be made in either blocking or non-blocking mode.  In
+blocking mode, the tile/LOD will be loaded if it is not in the cache
+before the request completes.  In non-blocking mode, the request always
+returns immediately to the calling function with one of the following
+status codes:
+\begin{description}
+\item[hit] The tile/LOD was found in the cache.  The tile is
+returned.
+\item[suboptimal] The tile/LOD was not found, but a lower LOD is
+available.  The lower LOD tile is returned.
+\item[miss] No tile was found to match the request.
+\end{description}
+The GvRasterLayer class uses the non-blocking mode to implement
+asynchronous tile loading.
+
+\section{Mesh}
+
+A GvMesh is a collection of points mapping a regular grid to
+arbitrary points in 2D or 3D space.  It is used to guide a spatial warp
+for raster data.  For instance, a slant-range SAR image may be
+projected ``on-the-fly'' to UTM space by precomputing the UTM
+coordinates of a sparse mesh of points, spaced regularly in image
+(slant-range) space.  The mesh is used to generate triangle strips
+when texture mapping the image into place in UTM space.
+
+Currently GvMesh objects are only used as a component of a GvRasterLayer. 
+When a new GvRasterLayer is created (related to a particular GvRaster), a
+corresponding GvMesh is created.  The output coordinates of the mesh are
+view georeferenced coordinates.  
+
+Initially the GvMesh is setup in identity form, mapping coordinates on the
+GvRaster to the pixel/line locations.  Subsequently this may be transformed
+into georeferenced coordinates using an affine geotransform, or using a
+polynomial warp based on GCPs.  If the GvViewArea is utilizing a different
+coordinate system (projection) than the GvRaster, then an additional pass
+is made to reproject the mesh coordinates into the desired projection.
+
+In the 2D case, the mesh is a regular grid of vertex points which lie 
+in a plane.  By associating a third dimension to each vertex a 3D surface is 
+created.  The raster data is then ``projected'' onto the mesh surface using 
+the same warping technique as was used to accomplish the 2D projection.  The 
+resulting mesh provides a correct warping of the raster data in all three 
+dimensions.
+
+Information for the third dimension is derived
+from a GvRaster layer which is coregistered with the reference space.
+Nominally such a layer contains elevation data, so the result is
+a tessellated, multi-resolution terrain model.  The resulting mesh
+can be used for 3D visualization of terrain data in
+conjunction with a coregistered ``drape'' image.
+
+\chapter{Layer Drawing}
+
+Static content in the view window (i.e. not tool related as in Section
+\ref{sec:tools}) is drawn by layer objects.  Normally layers draw a
+representation of a data container, but are not limited to this
+purpose.  Other layer functions include such things as drawing grid
+lines, or displaying user provided annotation, such as text or
+legends.
+
+\section{Raster}
+
+Rasters are drawn using OpenGL's texture mapping capabilities.
+While not strictly required for 2D image drawing, texture mapping
+allows OpenEV to take advantage of the automatic warping, and 
+interpolation features 
+of OpenGL in order to provide continuous zoom levels, image rotation
+and reprojection, and extensions to 3D (e.g. draping over height
+fields).
+
+Each GvRasterLayer connects to one or more GvRaster objects and a
+GvMesh object.  The GvMesh is used to map image coordinates to the
+view space (see below).  The GvRaster objects provide image data which 
+is used to generate textures one tile at a time.
+
+The GvRasterLayer maintains an internal cache of which textures are
+available.  When a draw event is received, it computes which textures
+are required to draw the scene (a tile/LOD combination) and compares
+this to what is available in the cache.  If the appropriate texture is
+not available, it attempts to find a lower resolution texture that
+covers the tile.  If no other texture is found, the tile is not
+drawn.
+
+Texture generation is driven asynchronously from the draw event
+(e.g. by an input idle event).  A texture miss during a draw will
+schedule an asynchronous update.  The update begins by generating a
+list of textures required for the current view and comparing this
+against the tile cache.  Of the missing tile/LOD combinations, the
+``best'' one to generate is chosen according to the following order of 
+preference:
+\begin{enumerate}
+\item The tile/LOD is in the GvRaster cache (highest preference).
+\item A lower LOD of the same tile is in the cache.
+\item The tile/LOD must be loaded to the cache (lowest preference).
+\end{enumerate}
+Non-blocking GvRaster tile loading is used to query the raw cache
+state.  Once a few tiles/LODs have been generated, a view refresh is
+forced.  This will display the new tiles and reschedule an asynchronous 
+update if required.
+
+The texture generation method depends on the data type of the GvRaster 
+layer.  The layer switches to the appropriate method at run time.
+
+\subsection{Real Data}
+
+User adjustable minimum and maximum values are used to scale and clamp
+each input value to an integer in the range 0-255.  In the case of
+floating point data, values are first scaled and clamped from 0-1 and
+then passed through a fast float to int conversion function.  In
+either case, the scaled values are used as unsigned bytes to create a
+\texttt{GL\_LUMINANCE} texture.
+
+Real data textures can be rendered in a variety of ways to achieve
+various effects.  For example:
+\begin{itemize}
+\item The texture can be modulated with an arbitrary fragment
+colour to map value to shades of that colour (e.g. gray scale or
+aqua-marine scale, etc.).
+\item An alpha value can be set for the fragment colour.  The texture
+can then be filter (alpha) blended with the background to achieve a
+constant transparency for the image.
+\item A 256 colour look up table (LUT) can be applied as the texture
+is loaded.  The colours in the table can be used directly, or can be
+modulated by the fragment colour/alpha.
+\end{itemize}
+
+\subsection{Complex Data}
+
+To translate complex (I\&Q) values to an RGB tuple, a two dimensional
+look up table (LUT) is used.  A user adjustable maximum magnitude
+parameter is used to determine a scaling factor:
+\begin{quote}\begin{verbatim}
+scale = 0.5 / max_mag;
+\end{verbatim}\end{quote}
+The scaled sample cannot be clamped independently in I and Q, since
+this would modify the sample phase.  A \emph{phase preserving}
+clamping is performed to clip the sample to the LUT boundary (see
+figure \ref{fig:phaseclip}):
+\begin{quote}\begin{verbatim}
+I *= scale;
+Q *= scale;
+if (fabs(I) > 0.5)
+{
+    Q *= 0.5 / fabs(I);
+    I = 0.5 * sign(I);
+}
+if (fabs(Q) > 0.5)
+{
+    I *= 0.5 / fabs(Q);
+    Q = 0.5 * sign(Q);
+}
+I += 0.5;
+Q += 0.5;
+\end{verbatim}\end{quote}
+The resulting I and Q are in the range $[0,1]$, with phase preserved.
+
+\begin{figure}
+\centering
+\includegraphics[scale=0.9]{phaseclip.eps}
+\caption{Phase Preserving Clamping}
+\label{fig:phaseclip}
+\end{figure}
+
+After scaling, I and Q are converted to integers in the range
+$[0,255]$ using a fast float to integer conversion.  They are then
+applied as two indices to a 2D LUT. This can either be a $256\times
+256$ LUT, or a $64\times 64$ LUT with bilinear interpolation over the
+2 lowest order bits.
+
+The LUT is generated to map I\&Q into RGB by way of an HSV transform.
+At startup, the LUT maps phase angle into hue and complex magnitude
+into value (brightness).  This mapping is adjustable by the user.  For
+instance, phase can be mapped to value, with hue and saturation set to
+zero, to produce a gray scale phase image.
+
+For each entry in the table, a floating point I and Q index is
+generated in the range $[-1,1]$.  A phase angle from $[0,1]$ is
+calculated using (atan2(Q,I) + PI) / (2*PI).  A magnitude is
+calculated using sqrt(I*I + Q*Q) with values clamped to $[0,1]$.
+Floating point RGB is computed using an HSV to RGB transform.  These
+are converted to integer $[0,255]$ and placed in the LUT.
+
+\section{GvShapesLayer}
+
+The GvShape objects are drawn using GL primitives suitable to the type
+of geometry.  Various style parameters (colour, width, etc) are controlled
+by layer wide defaults stored as properties on the GvShapesLayer, and can
+be override on a per-shape basis by properties of the shapes. 
+
+\subsection{Points}
+
+GvPointShapes are drawn using a small cross symbol.  The point cross size, 
+and color are properties that can be specified at the layer, or shape level.
+Note that per-shape drawing overrides can incur substantial performance
+overhead.
+
+Different symbols are drawn for points marked as ``selected''.  The
+default symbol is a cross with a square around it.
+
+\subsection{Polylines}
+
+Polylines are drawn using a \texttt{GL\_LINE\_STRIP} in a user
+selectable colour and line width.  Selected lines have small squares drawn at 
+each vertex (node) in the same colour (using \texttt{GL\_POINTS}).
+
+\subsection{Areas}
+
+Since OpenGL cannot draw non-convex polygons, or polygons with holes,
+each GvAreaShape must be ``tessellated'' prior to drawing.  That is, a set
+of triangles must be constructed that, when drawn together, make up
+the region described by the GvArea.  The OpenGL GLU library
+tessellation routines are used for this purpose.  When a new GvAreaShape is
+created, it is first checked for proper ring winding (clockwise or
+counterclockwise ordering of nodes), and then tessellated.  The
+resulting triangles are stored with the GvAreaShape, in the fill\_objects, 
+mode\_offset, and fill fields.  Any modifications to
+the area result in a re-tessellation.  An error encountered during
+tessellation indicates that GvArea is invalid (e.g. crossing or
+self-intersecting rings) and it is drawn without fill.
+
+Areas are drawn using the filled triangles for the interior and a
+\texttt{GL\_LINE\_LOOP} for each ring (the region boundaries).
+Selected areas have an additional \texttt{GL\_POINTS} drawn at each
+vertex.  Edges, and fill can be different colors.  In most cases
+the interior fill have a non-opaque alpha value
+applied, so that it does not completely obscure the layers below it.
+
+\chapter{Tools}
+\label{sec:tools}
+
+Tools provide interactive dataset querying and editing capabilities to
+OpenEV.  OpenEV's object oriented architecture makes it reasonably easy
+to add additional tools to OpenEV to support specific application
+requirements.  This section describes some common tools.
+
+\section{Toolbox}
+
+The GvToolbox class provides a means of multiplexing several tools
+across multiple view areas.  The toolbox maintains a list of
+registered view areas and tools.  A particular tool can be
+``activated'' by name.  The toolbox traps pointer enter and pointer
+leave notification messages from the view areas.  When the pointer
+enters a view window, the current tool is activated on that view.  When 
+it leaves, the tool is deactivated.  The tool therefore needs only to
+know about one view at a time.
+
+The toolbox is normally connected to a toolbar widget containing icons 
+for all available tools.
+
+\section{Selection Tool}
+
+The selection tool allows the user to change the current ``selected''
+shape (point, line, area).  Multiple shapes can be selected by
+dragging out a region (rubber-banding), or shift-clicking.  Only
+shapes within the same layer can be selected this way.
+
+Once selected, two operations can be performed on the shapes: delete
+and move (translate).  The user deletes the selected shapes by
+pressing the delete key.  Shapes are moved by clicking and dragging
+the mouse over one of the selected shapes.
+
+\section{Line Tool}
+
+The line tool is used to draw new lines or append to an existing
+line.  The user clicks to start a new line, moves the mouse to the
+next node in the line, and clicks again to insert a node.  The view
+state (pan, zoom, etc.) may be changed between clicks.  After drawing
+the final node, the user right clicks to finish the line.  While
+drawing, the delete key can be pressed to remove the last node drawn.
+
+Existing lines can be extended by clicking on one of the end nodes of
+a line, and proceeding as above.
+
+\section{Area Tool}
+
+The area tool is used to draw new areas, or to insert holes in an
+existing area.  To start a new area, the user proceeds in the same
+manner as drawing a line.  Once the right mouse button is pressed, the 
+area tool closes the loop (connects the last node to the first) and
+fills in the area.
+
+To insert a hole in an existing area, the user clicks somewhere in the 
+area's interior to insert the first node, and proceeds as above.  The
+area's interior fill is disabled during hole drawing to aid in viewing
+layers under the area layer.
+
+\section{Node Tool}
+
+The node tool is used to add, delete or move nodes in an existing
+shape (line or area).  The user first clicks on a shape to select it.
+To delete a node, the user clicks on the node to highlight it (denoted
+by an oversized vertex marker), and presses the delete button.  Nodes
+are moved by clicking and dragging them.  New nodes are inserted by
+clicking on a line segment between two nodes.  
+
+When moving a node belonging to an area, the area's interior fill is
+disabled to aid in viewing layers under the area layer.
+
+\section{Point Query Tool}
+
+The point query tool is used to identify the view space coordinates of
+locations in the view, to query the value of a raster layer, and to
+measure distances between locations in a view.  Optionally, raster
+layer ``row and column'' coordinates can be displayed.  When first
+invoked, the point query tool creates a temporary point query layer
+and attaches it to the view area.  Subsequent invocations will use the
+same layer.
+
+The user clicks to insert a new query point in the layer.  The point
+is marked with an identifying symbol, accompanied by a small amount of 
+text which gives the coordinates of the point.  If a raster layer is
+currently selected in the view area, the value of the raster at that
+point is also displayed.  The point can be moved by clicking and
+dragging.  The coordinates and raster value are updated as the point
+is moved.
+
+The user can Ctrl-click over a query point to begin dragging out a
+query second point.  A line is drawn between the two points.  Text
+near the center of the line gives the line length in view
+coordinates.
+
+Query points are removed by clicking to select them, and pressing
+delete.  It is also possible to clear all query points (through a
+separate menu function).
+
+\section{Other Tools}
+
+Briefly, other tools planned for future incorporation into OpenEV are:
+\begin{itemize}
+\item A generic control point marking tool, which can be extended for
+application specific uses.
+\item An general region-of-interest (ROI) marking tool, which can be
+used for sub-image extraction, for example.
+\item A grid overlay tool.
+\end{itemize}
+
+\chapter{Common UI Elements}
+
+The OpenEV facilities can be used in a variety of applications.  Most of
+these share some functionality, and therefore can benefit from a
+library of common user interface elements.  This section describes
+sample elements, written largely in Python, which interact with OpenEV.
+
+\section{Icon Bar}
+The Icon Bar (see \ref{fig:iconbar} for prototype) provides a top level 
+user interface.  It provides access to many of the commonly used features
+OpenEV provides through menus and icons.  From the File menu the user can
+create new views, open files (raster or vector) into the current view, print
+or close down the application.  The Edit menu provides access to the toolbox
+(for vector type operation described above), layer control (see \ref{sec:layer-control}) and
+preferences.  The Help menu launches the online help (see \ref{sec:online-help}).  The row
+of icons provide quick access the following (from left
+to right):  open new file, print view, linear scaling of view, equalization
+enhancement of view, fit all layer in view, zoom to 1:1, zoom in by factor of two,
+zoom out by factor of two, launch online help. 
+
+\begin{figure}
+\centering
+\includegraphics{toolbar.eps}
+\caption{Basic Icon Bar}
+\label{fig:iconbar}
+\end{figure}
+
+\section{Layer Control}
+\label{sec:layer-control}
+
+The layer control dialog provides the user with a list of layers
+associated with a view area, and provides some controls to manipulate
+the layers, and the ability to launch layer-specific properties panels.  
+Layer manipulation functions accessible via the layer control dialog, or
+layer-specific properties panels include:
+
+\begin{itemize}
+\item Change the active (selected) layer.
+\item Raise or lower a layer.
+\item Delete a layer.
+\item Change the name of a layer.
+\item Change the visibility of a layer.
+\item Change the opacity of a layer.
+\item Change the default color of a layer.
+\item Change the raster scaling parameters of a layer.
+\item Change the raster interpolation schema for a layer.
+\item Change the raster bands used for a layer.
+\end{itemize}
+Layers are identified by name, and by a thumbnail image.  Figure
+\ref{fig:layerdlg} shows a conceptual layer dialog.
+
+\begin{figure}
+\centering
+\includegraphics{layerdlg.eps}
+\caption{Conceptual Layer Dialog}
+\label{fig:layerdlg}
+\end{figure}
+
+\section{Toolbar}
+
+The toolbar dialog is a simple button box widget connected to a OpenEV
+toolbox tool.  The user changes the currently active tool with this
+dialog.
+
+\section{On-Line Help}
+\label{sec:online-help}
+
+A general on-line help system, based on HTML formatted help pages, is
+available for OpenEV application development.  Providing access to help
+is the responsibility of the application, but is normally provided in
+a drop down menu.  Help pages are viewed through a user configurable
+external web browser.
+
+Applications use the python {\tt gvhtml.LaunchHTML()} to display help or
+other web pages.  
+
+\section{Printing}
+
+In order to enable printing, the GvViewArea has the ability to render
+its contents at an arbitrary resolution, providing the resulting raster 
+RGB data to application callbacks.  Particular mechanisms are built on
+this to write the result to PostScript, or any GDAL supported raster format.
+
+The rendering mechanism is invoked by the 
+{\tt gv\_view\_area\_render\_to\_func() }
+function, which takes a view to render, a resolution and a callback to 
+provide the data to. The rendering is accomplished by altering the view port
+and rendering into the backbuffer of the GvViewArea.  This is fetched
+by using the glReadPixels() call to fetch the result back from the
+back buffer.  
+
+One downside to this approach is that on eight or sixteen bit
+display systems, the resulting rendering is normally done at the
+color resolution of the visual.  Also, it appears some GL implementations
+(such as the 1999 Linux NVidia drivers) don't support glReadPixels(). 
+However, the greatest limitation with this architecture is that text, and
+vector graphics are rendered at the same resolution as the raster data
+rather than being transported in descriptive form to be rendered by the
+printer at it's best resolution.  
+
+For higher resolution renderings the region to render is split into smaller 
+tiles matching the size of the viewport, and the result reassembled for
+output.  
+
+Currently output drivers exist for PostScript 
+({\tt gv\_view\_area\_print\_postscript\_to\_file()}) and GDAL supported raster 
+formats such as TIFF and PNG 
+({\tt gv\_view\_area\_print\_to\_file()}).  In time it is expected to implement
+support for a few other common outputs such as HPGL and writing directly to
+MS Windows printer drivers. 
+
+
+\begin{thebibliography}{9}
+
+\bibitem{woo:opengl} M.Woo, J.Neider, T.Davis, D.Schreiner, OpenGL
+Architecture Review Board, \emph{OpenGL Programming Guide: The
+Official Guide to Learning OpenGL, Version 1.2}, 3rd ed.,
+Addison-Wesley, 1999.
+
+\bibitem{gtk.org} The GIMP Toolkit homepage.  http://www.gtk.org
+
+\bibitem{mor:ogdi} P.Morin, \emph{OGDI: Programmer Reference},
+ver. 3.0, OGDI Research Institute, OGDI-RI-98001, May 1998.\\
+http://132.156.30.81/iii/docs/programmers\_guide/ogdi.pdf
+
+\end{thebibliography}
+
+\end{document}

Added: packages/openev/branches/upstream/current/doc/openev_bigpicture.fig
===================================================================
--- packages/openev/branches/upstream/current/doc/openev_bigpicture.fig	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/openev_bigpicture.fig	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,108 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 2639 3923 3764 4627
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 3201 4275 562 352 3201 4275 3764 4627
+4 1 0 100 0 20 11 0.0000 4 120 660 3201 4275 Numerical\001
+4 1 0 100 0 20 11 0.0000 4 150 555 3201 4486 Analysis\001
+-6
+6 9600 4838 10725 5259
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 10162 5048 562 211 10162 5048 10725 5259
+4 1 0 100 0 20 11 0.0000 4 150 585 10162 5119 OpenGL\001
+-6
+6 6365 3713 7350 4275
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 6858 3994 492 281 6858 3994 7350 4275
+4 1 0 100 0 20 11 0.0000 4 120 690 6858 3994 GDAL I/O\001
+4 1 0 100 0 20 11 0.0000 4 120 210 6858 4205 Lib\001
+-6
+6 4819 1041 5944 1463
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 5381 1252 562 211 5381 1252 5944 1463
+4 1 0 100 0 20 11 0.0000 4 150 765 5381 1322 Application\001
+-6
+6 9881 3713 10725 4134
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 10303 3923 422 211 10303 3923 10725 4134
+4 1 0 100 0 20 11 0.0000 4 120 345 10303 3994 Gtk+\001
+-6
+6 2850 2728 3694 3150
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 3272 2939 422 211 3272 2939 3694 3150
+4 1 0 100 0 20 11 0.0000 4 150 495 3272 3009 NumPy\001
+-6
+6 9881 2025 10725 2447
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 10303 2236 422 211 10303 2236 10725 2447
+4 1 0 100 0 20 11 0.0000 4 150 435 10303 2306 PyGtk\001
+-6
+6 7350 2025 8756 2447
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 8053 2236 703 211 8053 2236 8756 2447
+4 0 0 100 0 20 9 0.0000 4 105 855 7561 2306 Basic Viewer UI\001
+-6
+6 4748 4205 5733 4627
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 5275 4416 457 211 5275 4416 5733 4627
+4 1 0 100 0 20 12 0.0000 4 135 645 5275 4486 PROJ.4\001
+-6
+6 5451 2517 6717 2939
+1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 6050 2728 598 211 6050 2728 6647 2939
+4 0 0 50 0 0 11 0.0000 4 135 825 5592 2798 PyGDAL I/O\001
+-6
+6 7875 4275 9075 4800
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 8464 4553 562 211 8464 4553 9027 4763
+4 1 0 100 0 20 11 0.0000 4 150 585 8475 4627 OpenEV\001
+-6
+6 7875 2700 9075 3225
+1 1 0 1 0 7 100 0 -1 0.000 1 0.0000 8476 2941 562 211 8476 2941 9038 3152
+4 1 0 100 0 20 11 0.0000 4 150 810 8475 3009 Py OpenEV\001
+-6
+2 1 2 1 0 7 100 0 -1 3.000 0 0 -1 0 0 2
+	 600 3450 11700 3450
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 5944 1322 9951 2095
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 5873 1392 7631 2025
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 8123 2447 8334 2728
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 10303 2447 10303 3713
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 8475 3150 8475 4275
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 9037 4627 9600 4908
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 9037 4486 9881 4064
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 7983 4416 7280 4134
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 6155 2939 6858 3713
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 5733 1463 6084 2517
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 3272 3150 3272 3923
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 5030 1463 3553 2728
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 4819 4416 3764 4345
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 6365 4134 5733 4416
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 56.25 112.50
+	 7912 4556 5733 4486
+4 0 0 100 0 22 11 0.0000 4 150 525 600 3361 Python\001
+4 0 0 100 0 22 11 0.0000 4 120 120 600 3642 C\001

Added: packages/openev/branches/upstream/current/doc/openevlogo.eps
===================================================================
--- packages/openev/branches/upstream/current/doc/openevlogo.eps	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/openevlogo.eps	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1756 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: GIMP PostScript file plugin V 1.06 by Peter Kirchgessner
+%%Title: /home/srawlin/openev/logo.eps
+%%CreationDate: Mon Jun 19 06:20:23 2000
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%Pages: 1
+%%BoundingBox: 14 14 828 225
+%%EndComments
+%%BeginProlog
+% Use own dictionary to avoid conflicts
+5 dict begin
+%%EndProlog
+%%Page: 1 1
+% Translate for offset
+14.173228 14.173228 translate
+% Translate to begin of first scanline
+0.000000 210.399131 translate
+813.543307 -210.399131 scale
+% Variable to keep one line of raster data
+/scanline 290 3 mul string def
+% Image geometry
+290 75 8
+% Transformation matrix
+[ 290 0 0 75 0 0 ]
+{ currentfile scanline readhexstring pop } false 3
+colorimage
+fefff8fefff8fefffafefffbfcfffdfcfffffcfffffcfffffcfffffcfffffefffffefffffeffff
+fefffffffffdfefefcf8faf9fafcfbfcfcfafefefcfffffdfffffdfffefbfffefbfffefbfefaf7
+faf6f3faf6f3fbf7f4fbf7f4fcf8f5fffbfafff8fbfff8fdfff9fdfdf7fbfaf6f7f8f4f5f4f2f3
+f3f1f2fffefffffefffffcfcfffbfbfff8f9f8eff0fcf2f3fffefbfdfcf7fbfef5fcfef9fdfffa
+fbfffefcfffffcfffffcfffffcfffffcfffffffffffffffffffffdfffefafffef9fffdfafffeff
+fffdfffffdfffffdfffffefffffefffffffffffffffffffffffffffefffffefffffcfffffcffff
+fcfffffefffffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdff
+fffdfffffdfffffdfffffdfffffdfffffeffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffefffffdfffffdfffffdfffffdfffffdff
+fffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdff
+fffdfffffdfffffefffffefffffefffffefffffefffffefffffffffffffffffffffffffffeffff
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffff
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffff
+fefffffefffffefffffefffffefffffefffffefffffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfefffbfafffaf9fffcfbfffcfd
+f8f7fcf8fbfff0f5fbf1f4f9fefffffffefcfffef9fffcf9fffdf7fffcf9fffdf9fffdfcfefffa
+fbfff9fafff7fafff7fafff7fafff7f9fff6f9fff6f9fff6fafff7fbfff8fbfff8fbfff8fbfff8
+fbfff8fbfff8fbfff9fefffbfcfcfafcfbf9fffffdfffefbfefaf7fefaf7fffefbfffefdfffefd
+fffefffffefffffefffffefffefffffdfefff8fcfffefffffffffff9f7f8fffdfdfffefdfaf6f3
+fffffbfbfdf8fafff9f9fffbfbfffcfdfefffffdfffffafffffbfffbf9fafbfbf9fffffdfffffd
+fdfdfbf7f7f5f8f8f6fefefcfdfdfbfcfcfafbfbf9fcfcfafdfdfbfdfdfbfcfcfaf9fbfafbfcfe
+f9fdffeff3f4f8fcfdf8faf9fdfffefffffdfffffdfffefafffefafefefcfefefcfefefffefeff
+fefdfffefeffffffffffffff
+fefff8fefffafefffafefffbfcfffdfcfffffcfffffcfffffcfffffcfffffefffffefffffeffff
+fffffdfffffdfefefcfffffdfffffdfefefcfffffdfffffdf7f6f4faf9f7fffffdf8f7f5f9f8f6
+fefefcfffffdfffffdfffffdfbfbfbf8f8f8fcfafffcfafffefcfffffefffffffffffffffefeff
+fcfcfef5f5f7fcfcfcfbf9fafbf7f6fffdfdfffdfdfff8f9fdf7f7fdfef8fcfff8fdfffafdfffc
+fcfffffcfffffcfffffcfffffcfffffefffffffffffffffffffffdfffefafffef9fffdfafffeff
+fffdfffffdfffffdfffffefffffefffffffffffffffffffffffffffefffffefffffefffffcffff
+fcfffffefffffffefffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdff
+fffdfffffdfffffdfffffdfffffdfffffeffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffefffffdfffffdfffffdfffffdfffffdff
+fffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdff
+fffdfffffdfffffefffffefffffefffffefffffefffffefffffffffffffffffffffffffffeffff
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffff
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffff
+fefffffefffffefffffefffffefffffefffffefffffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfefffbfafcf4f2fff9fafffbff
+fefffffbfffff7feffeef5fbf4f9fdfbfcfefdfcfafffbf8fffbf8fffbfafdfbfcfcfcfefbfdfc
+fcfefbfcfef9fcfef9fcfef9fcfef9fcfef9fcfef9fcfef9fbfdf8fcfef9fcfef9fcfef9fcfef9
+fcfef9fcfef9fcfefbfbfffcf9fefafbfdfcfdfffcfefefcfcfdf8fcfcfafefefcf9f9f7f9f9f9
+fafafafafafcf9fafcf8f9fdf5f9fcf4f8fbf8fcfdfafefffbfbfbf8f6f7fffdfcfffdfdfdf9f6
+fffffbfbfdf8fafff9f9fffbfbfffcfdfefffffdfffffafffffbfff9f7f8fffffdfefefcfafaf8
+fefefcfefffffefffff6f8f7fbfffefafefff9fdfef9fdfefafefffafefff9fdfef7fdfdfbffff
+f9fffffbfffffbfffff6fafbfafcfbf8f8f8f9f9f7fffefcfffefafffefcfefefefefefefefeff
+fefdfffefeffffffffffffff
+fefffafefffafefffbfefffdfefffffefffffefffffefffffefffffeffffffffffffffffffffff
+fffffdfffefcfffefafffefafdfcf8f8f7f3f7f6f2fdfef9fffffdfefffdf6f8f7fcfffffbfffe
+f1f7f7eff5f5eef6f8f1f9fbf7fffff9fffffafffff9fdfff8fdfff7fefff9fffff7fffff3fbfd
+f1f6f9f3f8fbfcfffffefffffbfbfbfaf8f9fbf7f8fffafcfffefdfffffbfefffbfefffbfefffd
+fefffffefffffefffffefffffefffffefffffffffffffffffffffdfffefafffef9fffefafffeff
+fffdfffffdfffffdfffffefffffefffffefffffefffffffffffffffffffffefffffefffffeffff
+fefffffefffffffffffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffeffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffdff
+fffefffffefffffefffffefffffefffffefffffefffffefffffffffffffffffffffffffffeffff
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffff
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffff
+fefffffefffffefffffefffffefffffefffffefffffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfefffbfcfffcfdfffcfefaf8fd
+f5f8fff1fafff1fbfff5fffff7fffff8fffffafffffbfffffafefffafdfff9fefff7fefff8fcff
+fdfcfffffbfffffbfffffbfffffcfffffcfffffcfffffcfffffbfffffbfffffbfffffbfffffbff
+fffbfffffcfffefdfff9fffff8fffff9fffff9fffffbfffffbfffdfbfffffafffefbfffffbffff
+fbfffffbfffffbfffffbfffffbfffffbfffffbfffffafefffafafafbf9fafffbfafffcfcfffefb
+fffffbfcfef9fafff9fafffcfbfffcfdfefffffdfffffafffffbfffffdfefffffdfdfdfdf7f7f7
+f7f9f8fbfffff7fcffecf4f6f8fffff8fffff5fffff5fffff4fffff4fffff3fffff3ffffe3f1f4
+e9f7f8f4fffff0fafbf0f6f6f9fdfcfbfdfcfffffdfffefcfffefcfffdfefefefefefefefdfffe
+fdfefffdfeffffffffffffff
+fefffbfefffbfefffdfefffdfefffffefffffefffffefffffefffffffffffffffffffefffffffd
+fffffdfffefafffefafdfcf7fffef9fffefafdfef9fefffdfcfffdfbfffff2fafcf2fcfef0fbfd
+effefff2fffff2ffffedffffe9fcffe8fbffecfdffeafbffe9faffecfdfff0fffff2fffff3ffff
+f1fffff0fefff0fbfdeef8faf5fdfffcfffffbfcfef5f5f7fafafafefffdfefffdfefffdfeffff
+fefffffefffffefffffefffffffefffffffffffffffffffffffffdfffffbfffffbfffefafffeff
+fffdfffffdfffffdfffffefffffefffffefffffefffffffffffffffffffffffffffffffffeffff
+fefffffefffffefffffefffffffffffefffffffffffefffffffffffefffffffffffeffffffffff
+fefffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffeffffffffff
+fefffffffffffefffffffffffefffffffffffefffffffffffefffffffffffffffffffefffffdff
+fffefffffefffffefffffefffffefffffefffffefffffefffffffffffffffffffffffffffeffff
+fefffffefffffefffffffffffefffffffffffefffffffffffefffffffffffefffffffffffeffff
+fffffffefffffffffffefffffffffffefffffffffffefffffffffffefffffffffffeffffffffff
+fefffffffffffefffffffffffefffffffffffffffffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfefdfbfcf9f7faf7f6fbf6fbff
+f4feffeefeffebfeffeaffffe9ffffebffffecffffedfeffedfeffebfdffeafeffe7fdffeafcff
+eff6fff3f6fff4f6fff4f7fff5f7fff5f8fff6f8fff6f9fff8fafff6f9fff6f8fff4f7fff5f7ff
+f5f8fff7f9fff4fcffebfdffebffffeeffffebffffedfffff0ffffefffffebfffeebfdfdebfdfd
+eefefeeefefeeefefeefffffeffffff0fefff7fffffbfffffdfefffffffffffbfcfff9f9fffefd
+fdfcf8fcfef9fafff9fafffcfbfffcfdfefffefcfffffafffffafefffbfcf8f7f5fbfbfbfeffff
+fcfffff2fafdf3fefff4ffffefffffecffffe9fdffe6fdffe6fdffe6fdffe6fdffe5fcfff0ffff
+effffff2fffff5fffff5fffffbfffffbfcfefffefffffdfefffdfcfffdfefffdfefdfffefbfffe
+fbfffffbfffffeffffffffff
+fcfffdfcfffdfefffffefffffffffffffffffffefffffefffffffffffefffffefffffefffffefd
+fffdfcfffdfafffcf9fbfaf5fcfbf6fffffbfefffdf7fdfbe7f1f0e7f2f4f0ffffedffffe8ffff
+e6ffffe6ffffdefeffd1f3ffc6e8f4c1e3efc1e1f0c2dfedc3e0eec8e6f1d1eff9daf6ffe1fcff
+e3feffe2fbfff0fffff1fffff0fffff2fdfff2fbfff4fcfffbfffffcfffffefffffefffffeffff
+fffffffffefffefdfffefdfffffdfffffefffffefffffefffffffdfffffbfffffbfffffbfffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffffffffffffffffffff
+fffffffefffffefffffcfffffefffffcfffffefffffcfffffefffffcfffffefffffcfffffeffff
+fcfffffefffffcfffffefffffeffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefffffcfffffefffffcfffffeffff
+fcfffffefffffcfffffefffffcfffffefffffcfffffefffffcfffffefffffefffffffefffffeff
+fffefffffefffffefffffefffffefffffeffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffefffffffffffefffffffffffefffffffffffefffffffffffeffffffff
+fffefffffffffffefffffffffffefffffffffffefffffffffffefffffffffffefffffffffffeff
+fffffffffefffffffffffefffffffffffefffffefffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfefbfcfef7fcffe0eef1c3d6dc
+b4ccd6b6d6e3b9deeeb5dcebb2d9e8bce4f0bde3eebde3eebce2efbbe0f0bae1f2b7e0f4b9dff4
+bddcf1c0dcf1c1dcf1c1ddf2c3def3c3dff4c5e0f5c4e0f5c6e1f6c5e1f6c4dff4c2def3c3def3
+c2def3c4dff4c2e1f3bbe1ecbce4ecbde5efbbe3ebbbe3ebbde5edbee4edbce3eabce1e9bddfe8
+bedfe6c0dee6c0dfe4c2dfe5c4e1e7c9e1e5deecedf9fffffcfffffefefefffcfdfdf9f8fffefd
+fcfbf9fefefcfbfffcfafffcfbfffcfdfefffefcfffff9fffffafefffbfafbfaf8fdfdfdfcffff
+f5fdffddeaf0c7dae1bdd3dec8e2efc4e1efbfdfeebddeedbddeedbadeecb8dceab9dbe5bcdbe0
+bcdadcbad3d7e8fbfff4fffff9fffffafbfdfcfafbfffdfefffdfefffdfefffdfefdfffefbfffe
+f8fffffafffffeffffffffff
+fcfffffcfffffefffffefffffffffffffffffffefffffefffffdfffffdfffffefffffefffffefd
+fffdfcfffcf9fefdf9f6f7f2fefffdfafffbeef7f4f2fefef4fffff0ffffd8f1f8dbf9ffbfe1ed
+99bece759faf5987974776884271854473874b788d4976894974874873844974844c7684527a86
+557b8670959ea1c2cbc5e1ecd2eaf4e4fbfff1fffff2fffff1fcfffafffffbfffffdfefffdfeff
+fefdfffefdfffffdfffffdfffffdfffffdfefffdfefffefffffffdfffffdfffffbfffffdfffffd
+fffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeffffffffffffff
+fffffffffffffefffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcffff
+fcfffffcfffffcfffffcfffffeffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefffffcfffffcfffffcfffffcffff
+fcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffefffffffefffffeff
+fffefffffefffffefffffefffffefffffeffffffffffffffffffffffffffffffffffffffffffff
+fffffffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfef9fdfeecfcfcbad3d76b8c93
+3f6771487886508595467f90468193397484397482397381397283387185367086336f89336f87
+3a70883d71873d71873f738940748a41758b42768c43778d42768c41758b40748a3f73893e7288
+3e72883e72883c73883c75893972853871843974863972863770833871843b73843f7486407384
+42738443728244728145717e456f7d4b6d77b3c6caf4fcfefafefffafafcfffdfefdf9fafffefd
+fdfcfafefefcfdfffcfbfffcfbfffcfdfefffefcfffff9fffff9fdfefaf9fffffdfefffff3f8fb
+eaf7fdd7e9f39fb6c46282915073864d7386487386447184437085406d803d6a7d3c68754b7279
+56797b526f73c9e1e5ecfbfff9fffffbfcfffffcfffffcfffffcfefffdfefffdfefdfffefafffe
+f8fffff8fffffeffffffffff
+fcfffffcfffffefffffefffffffefffffefffffefffffdfffffcfffffcfffffdfffffdfffffefd
+fffdfcfffcfbfdfdfbfafffef8fffff7ffffeaf9fcebffffedffffcbe9f49abecc628c9c558496
+487b9041778f3e78903d7993417d9946829e48819c48829a477f9840788f387185336a7e356a7c
+376a7b477686436e7e39606f476b797c9eaab7d4e2d9f5ffecfffff4fefff8fdfff9fcfffcfdff
+fdfcfffefdfffffdfffffcfffffcfffffdfefffdfefffffdfffffdfffffdfefffdfffffdfffffd
+fffefdfffefdfffefdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffd
+fefffdfefffdfefffdfefffdfefffdffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefffdfefffdfefffdfefffdfefffd
+fefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfefffdfffefffffeff
+fffefffffefffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefefef6fffeedffffcbf3f57aa9af
+477d874e8d9c438b9a2974872d7a8e307d8f2f7c8e307b8e2e7b8d2d79902b7990287893287893
+2f7c96317d95327e96337f9735819936829a37839b38849c37839b37839b36829a358199348098
+327e96307c942f7b9338829f2f78982d7696317c9b317a9b2a73932b7494347d9d2d7494327494
+35769239769237738d346e862f677e3462728ea5abf1fbfcfbfffff6f7f9fffefffdf9fafffefd
+fefdfbfefefcfdfffcfcfffdfbfffcfdfefffefcfffffafefff9fbf7f3f2fffffdfdfefff2fcfe
+f2ffffe9ffffa7c6d85b8196507d944d7d94497d95467c964379913f758d3a7088396c7d2c5861
+396065214247cbe7ebedfffff4fdfff6f7fcfffafffffcfffffcfffffdfefffdfefdfffefafffe
+f7fffef8fffefeffffffffff
+fcfffffcfffffefffffefffffffefffffefffffdfffffdfffffcfffffcfffffdfffffefffffdfe
+fffefcfefcfdfcfdfff6ffffe2f4f8e6fbffedffffcae6f180a1b04e788849788c43778d3f7790
+407c984585a14789a94388a94186a74186a73e80a04485a34889a74687a340829c3e7e97417f96
+45829740788f40748a4272884473873d687b39627462889ba6c3d5edfdfff4fdfff5fcfff8fdff
+fcfcfffdfcfffffdfffffcfffffdfefffdfefffdfcfffffdfffffdfffffdfefffdfefffdfffffd
+fffffdfffefdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdffffffffffff
+fffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefefef5ffffdffdffd0fbff88bac3
+4c87954f94a44391a535879d4fa3bb2d82972d82972e80962d7f952c7d98297d99287d9c287b99
+2f809d31819c32829d34849f3686a13787a23888a33989a43a8aa53a8aa53a8aa53989a43787a2
+34849f32829d307f9d3f8bad2f7da12c7aa03482a83281a827769d29789f3685ac2f7da43680a5
+3d83a74183a53e7f9f3875922f6b873161777a959cf3fefff9fffff7f8fcfffefffefafbfffdfe
+fdfcfafffffdfefffdfcfffdfbfffefefefefdfbfefef9fdfefafbfffdfcfffffdf9fdfff2fdff
+edffffd4f1ff94bacf54829a50839e4d84a24b84a14884a045819d3f7b953b758d386f8333636d
+40696d13363cd6f2f6f0ffffeff8fdf5f5fdfffefffffbfffffcfffffdfefffdfefdfffefafffe
+f7fffef8fffefeffffffffff
+fcfffffcfffffefffffefffffffffffffefffffcfffffbfffff9fdfffdfffbf6fafffbfffffeff
+ffffffffffffedf5f8e6f9ffebffffd2f0fb94b5c4688d9f5882984e7e95447a964c87a55897b6
+4d8fb14286a9387ea2367ea4438bb13d86a93f85a73e84a64086a83a81a1367d9d498ead4b90ad
+3578933979953b78943b748f39718a3d708b41738c41708a416a808299a7c1cfdaf7fffff4fdff
+ecf1f7fefffff1eff4fffdfffffcfefffbfbfffcfbfefdfbfffdfefffffffefffffefffffefefc
+fefefcfffefcfefefcfefefcfefefcfefefcfefefcfffffdfffffdfffffdfffffdfffffdfffffd
+fffefcfdfcfafffefcfffffdfffffdfffffdfffffdfffffdfffffdfffefcfffffdfffffdfffffd
+fffffdfffefcfffffdfffffdfffffdffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffefcfffefcfffefcfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffefcfffefcfffefcfffefcfffefcfefefefdfffe
+fefefefffffffffffffffffffffffffffffffffffffffffffdfdfdfdfdfdfefefeffffffffffff
+fffffffffffdfffffdfffefcfffffdfffffdfefdfbfffefcfffffdfffffdfffffdfffffdfffefc
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfefdfbfdfcfafefdfbfffffdfffefc
+fffefcfffefcfffefcfffefcfffefcfffefcfffefcfffffffefefefefefefffffffffffffdfdfd
+fafafafbfbfbfffffffffffffffffffffffffffffffffffffffffff7ffffedffffcff3ff6d9baa
+578fa04e90a6438da63787a22778952b7c973a8aa52f7c9635829e4994b1327d9c337fa13b84a7
+3c7ea03e7f9f3e7f9f3f80a04081a14182a24283a34283a33f80a04182a24485a54283a33e7f9f
+3b7c9c3d7e9e3e80a033779a468cae11577b377fa5246e953f8ab22e7ca3347fa72d769f3b83ab
+3b7fa63d7da1427f9e2c6583205775416e835a757ef5ffffebf5f7fafefffffffffcfafbfdf9fa
+fffefffffffdfdfffcfbfffefbfffefffffffffefffffbfffffbfcfffdfcfffffff6feffe7f5fe
+ebffffbcdcf16793ac6396b35992b0518ead4382a141809f44819d437f99336d850e465726555f
+042b3084a5ace6ffffe0f1f8f8fffffcfffff8f6fbfffbfffffcfffffdfefffdfefdfffefafffe
+f8fffef8fffefeffffffffff
+fcfffffcfffffefffffffffffffefffffdfffffcfffffbfffff9fdfffbfffdf8fcfffefffbfbfd
+f7f8fcfcfffff5ffffebffffc2e6f68eb3c560899d4b788f4b7d985085a44c87a73f7e9f3c7ca0
+36789b367a9f377ba0367ba23a80a43a80a43a7ea12e72953a7ea14f93b6458aab2d7293296e8f
+3479984082a24384a241809f3c799838739138718f366d8b396781506d7f758994c8d9e3f1feff
+edf4fcf7fafffffefff7f3f4fffdfdfffdfdfffefdfffefcfffdfefcfefdfafefdfafefdfeffff
+fefffdfefffffefffdfffffffefffdfffffffffffdfefefefdfdfbfbfbfbfbfbf9fefcfdfffffd
+fffefffffffdfffefffffefdfffefffffefdfffefffffefdfffefffffefdfefafbfffbfafffcfd
+fffdfcfffefffffefdfffefffffefffffeffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffefffffefdfffefffffefdfffeff
+fffefdfffdfefffcfbfffefffffefdfffefffffefdfffefffffefdfffefffffefffffffffeffff
+fefffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffafafafefefcfffefffefefcfffefffffffdfffefffdfdfbfbf9fafcfcfafefcfdfefefc
+fefcfdfdfdfbfefcfdfefefcfcfafbfcfcfafffdfefffffdfffefffffffdfffefffffffdfffeff
+fffffdfffefffffffdfffefffffffdfffefffffffffffffffcfcfcfcfcfcfefefefefefefcfcfc
+fffffffffffffffffffffffffffffffffffffffffffffffffffffff9feffebfbffd5f0ff65889c
+59879f4e86a15293b1377e9e357e9e4188a82b708f38799731708f1754732c6789246084255d82
+2f61843061822f60812f60812e5f802d5e7f2d5e7f2d5e7f36678837688938698a366788336485
+326384366788386b8a245a761d5570245d7b134e6e4c8bac3373972d6e943b7ca23172983f7da2
+3e789d3f7597417491295b7622506843697c3f585ff3fffff0fbfff8fdfffffefffefcfffffcfd
+fffefffffffdfdfffcfdfffefbfffefffffffffffffffbfffffafefcfafbf5f9faf5feffe7f9ff
+e2fdff8cb2c9608fab538aa9538fb15291b43877983776954a86a2306a8212495e1a4b5c143c46
+2c4d54d3f0f6e0f7fdeaf9fef8ffffe9ecf1fffefffffcfffffcfffffdfefffdfefdfffefbfffe
+f8fffefafffefeffffffffff
+fefffffefffffffffffffffffffefffffefffffcfefffbfdfffafefffafefcfbfffefffff3f7fa
+f2f7fbf9ffffebfcffa6c7d8729cb25b889f608fa95e91ae588dac528aab4581a34887aa347297
+3a789d4685a83e7a9e2460841b5779316d8f2c67893671932d698b235f812d6c8d3f80a04182a2
+397b9b347595397a9a3b7c9c3a799a3b77993e7a9c417c9e477a97395c7047616e6f8592f1ffff
+f1feffedf4fafdfefffefcfdfffefdfffefbfffefbfffffdfffefffefffffcfffffbfffffafeff
+fafefdfafefffafefdfcfdfffafefdfcfdfffcfefdfefffffefffffefffffdfffefefeffffffff
+fffffffffefffffdfffffdfefffbfffffbfcfffafefffcfdfffcfffffefffffcfffffdfefffdff
+fffefffffdfffffefffffbfffefcfdfffeffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffcfdfffbfcfef9fdfefafbfef9fd
+fffbfcfffbfffffdfefef9fdfefafbfffafefffcfdfffcfffffdfefffbfffefcfdfefffffcffff
+fcfffffcfffffefffffefffffefffffefffffefefeffffffffffffffffffffffffffffffffffff
+fefefefffffffefffffffffffcfefdfbfbfdfefffffdfdfff6f8f7fffffffefffffffffffeffff
+fffffffefffffffffffefffffffffffefffffffffffdfffefdfdfffafcfbf9f9fbf5f7f6ffffff
+fefffffffffffefffffffffffefffffffffffefffffefffffefffffefffffefffffdfffefbfdfc
+fefffffefffffefffffefffffefffffefffffefffffefffffefffffafdfff2f8ffd0dcf46d809e
+5f7e9d6189ac5688ab3e749a3d759a487ea037698a4d7a9b4e7797305778385c7e35577c2e5073
+2c4d6e2e4d6c2d4c6b2d4c6b2c4b6a2b4a692a49682a496825446326456426456423426121405f
+2241602746652b4a663354653357653f6476224b5f84b1c84978924c7d9b4477963d708f497a98
+48759248728b496f863154682c4b5d4d69751f363ce9fbfdf7fffff7fcfffaf9fefffdfffffeff
+fdf9fafffffdfdfffcfdfffefbfffefffffffffffffffbfffdfbfefafafcfbffffe6f3fbeeffff
+b4d3e7729cb4578aa9578fb24d8caf4887aa3a76983a759341799226596e0c3b4d1b45530d2e37
+9db6bbeefffff0fffff7ffffedf4fafafbfffffdfffffcfffffdfefffdfefffdfefdfffefbfffe
+fafffffafffffeffffffffff
+fefffffefffffffffffffefffffefffffefffffcfefffcfdfffcfffefcfffcfdfffbfffff4fcff
+f3feffeffcffc4dae581a7bc5988a2598ba66699b65287a643799b457da0407b9d336e903c7497
+3f7597255a7a235474376684325f7c2b58752b58773a69873b6a88356684356a892f6684306987
+3e79973e7b9a407d9c3f7b9d3b77993a76983d799d3f7b9f437897355e72496a79425e6aadc3ce
+f2fffff0f9fee6eaebfffffdfffdfafffcf9fffcf9fffcf9fefcfdfefffffcfffffbfffffbffff
+fbfffffbfffffbfffffbfffffbfffffbfffffcfffffbfffffcfffffefffffefffffefffffdfeff
+fcfdfffcfcfefffefffffefffffefffffefffffefffffefffffefffffefffffdfffffdfffffeff
+fffefffffefffffefffffefffffeffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffefffffefffffefffffefffffeff
+fffefffffefffffefffffefffffefffffefffffefffffefffffefffffefffffffffefffffbfffe
+fbfffefbfffefdfffefcfefdfcfefdfcfefdfdfdfdfefefefffffffefefefefefefdfdfdfefefe
+fefefefdfefffbfffffcfdfffafefffefffffcfffffefffffcfffffefffffafefffafbfff6fafb
+f8f9fdf7fbfcfbfcfffbfffffefffffcfffffdfefffcfffffefffffcfffffefffffcfffffcfdff
+fafefffcfdfffafefffcfdfffafefffcfdfffcfdfffefffffefffffefffffefffffefffffeffff
+fcfefdfbfdfcfefffffefffffefffffefffffefffffefffffdfffefcfdfff9faffaeb3d07986a6
+687fa1799bc14c769c4e7ea43f6f954470935077983c5d7c3e59773e5574263959415172465977
+485d78485e76495f774a60784b61794b61794c627a4c627a50667e51677f51677f4f657d4e647c
+4e647c51677f54697c4b5d676073790e252d728d987797a66186994e778d5c869f46728b537d96
+53789253778d5574883c5768354f5c576f79152c32e4f7fbf5fffff5fcfff7f8fdfefcfffffdff
+fdf9fafffefffefefcfdfffefdfffefefffffffffffefcfffdfbfef8fcfdf8ffffe6f7ffe3faff
+83a6ba6793ae5f94b4629cc14c88ac3a769a4981a2487e9a3060762853642349561a3842485c65
+f4ffffdce9f1f7fffff9ffffe8edf1fefffff7f7f7fffdfefffdfefffdfefefefefefefefdfffe
+fbfffffbfffffeffffffffff
+fefffffefffffffffffffefffffefffffefdfffdfcfffcfdfffefffffffff8fcfff9fffff5ffff
+f1ffffd1e4eb8fabb76e98ae598aa85f92af5d90af3d7292356a8a457a9c497e9e4c7d9e3f6e8c
+2f58761e435e2648613d5b73415d723e596e4c687e48667e40607944667f4f748e446e873d6b85
+4a7c974a7d9a477c9b4279983d76943f7798447c9f467ea1467b9a2b5a6e436a794f717d3c5863
+d7eaf1f3fefffbfffffefefcfffefbfffefafffcf9fefaf7fbf9faf9fbfaf9fdfff9fefff9ffff
+f9fffff9fffff9fffff9fffff9fffff9fffffbfffff5fafef8fdfffcfffffcfffffcfffffbffff
+fcfffffcfffffafdfffbfffffcfffffcfffffcfffffcfffffbfefffafefffbfefffafefffafdff
+fafefffbfefffcfffffcfffffeffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffcfdfffcfffffcfffffcfffffcffff
+f9fdfff9fcfff9fdfffcfffffcfffffcfffffbfffffbfefffafefffafdfff9fdfffcfffffbffff
+fbfffefbfffefbfffefbfffefdfffefdfffefefffffefffffffffffefefefcfcfcfcfcfcfffeff
+fffffffbfffff4f9fcf6f9fefbfffffcfffff4f9fcf4f7fcf8fdfffcfffffafffffafdfffbffff
+fcfffffbfffffcfffffbfffffcfffffbfffffafdfff8fdfffbfefffbfffffcfffffbfffffcffff
+fbfffffcfffffbfffffcfffffbfffffcfffffcfffffbfcfefafcfbf9fbfafbfdfcfefffffeffff
+fefffffcfefdfefffffefffffdfffefefffffefffffefffffdfffef9fcfff9fbff8c93b07584a5
+7890b2789ac04872985f8db14c7a9c4871915d809c3d5971344a6117273e2d384e353d54354054
+293a4a263c49273d4a293f4c2b414e2c424f2e44512e4451334956354b58364c59364c59344a57
+334956324855344650303b3d364240303f42c4d8dfbad5e05e7f904b7083527b9149738c547e97
+557a945a7d935c7b8f425d6e3953625a727c1f363ce5f8fcf3fffff5fcfff8f9fefcfafffffdff
+fffdfefffefffefefefdfffefdfffefefffffffffffefcfffcfcfef7fcffebf6faf1ffffa3bfcd
+7599af5b88a56da2c45e96bb4f89ae3d75984f84a34d7b9332596a28485326414833464cacb5bc
+fbfffff1f4fbfafdfff0f4f7fcfffff8f9fbfefffffefefefefefcfefefefefefefefefefefefe
+fdfefffdfeffffffffffffff
+fefffffffffffffefffffffdfffefdfffefdfffdfcfefdfbfffffffefffff4f9fdf5fffff4ffff
+daeef5aac1c96e8e9b618ea5588ba85c8fae578aa9477a99487b9a5081a14b78973c6583375c77
+2f4d653e596e44596a3c4c5b4652604854623c495939485b3647593b50634861754e6a7f4e7187
+557b924b758e4a78924a7995457895477a994b80a05085a75085a439697f32606f49717d294a53
+95acb2f3fffff9fffff6f6f4fffefbfffefafffefbfffefbfffffdfefffffcfffff9fffff8ffff
+f7fffff8fffff8fffff8fffff8fffff9fffff9fffff9fffffbfffffbfffffbfffffbfefffafdff
+fcfffffbfffff8fffff8fffff9fffff9fffff9fffff9fffff8fffff8fffff9fffff9fffff9ffff
+f9fffff9fffff9fffff8fffffbfffffeffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffefffff6fbfef7fffff9fffff9fffff8ffff
+f7fffff9fffff9fffff9fffff9fffff8fffff7fffff7fffff7fffff8fffff8fffffafffffbffff
+fcfffffcfffffcfffffcfffffefffffefffffefffffefffffffffffffffffdfdfdfdfdfdfffeff
+fffffffcfffffafffffbfffffbfffffbfffffbfffff9fefffbfffffafffffafffffbfffffbffff
+fbfffffbfffff7fcfff3f8fcfbfffffbfffffbfffffbfffffafffffafffffbfffffbfffff9feff
+f9fefff9fefff9fefff9fefff9fefff9fefff9fefffcfffffefffffefffffbfdfcfcfefdfeffff
+fefffffefffffefffffdfffefdfffefdfffefefffffefffffdfffef8fdfff2faff8494ae6a81a0
+83a4c35f8aac49799d5489a94f82a1416f89426b7f4a697b4b6272182837bfcbd9bbc1cfbec6d1
+c2d1d8c1d3d7c1d3d7c1d3d7c2d4d8c2d4d8c3d5d9c3d5d9bcced2bdcfd3bfd1d5c0d2d6bfd1d5
+bcced2b9cbcfb9c8cbdbe5e4b8c2c1dfefefe1f5fcc8e4f05478885982985d89a2497693517e9b
+4f7b96577f995c80983e617534536755717f284148e7fcffeffefff3fcfffafafffcfafffffcff
+fffdfffffefffefefefefefefdfffefefffffefffffefcfffbfcfff4fcffe8f5fbe3f7ff88a5b5
+6d93aa6c99b6669bbd558db2538bb04d83a54b7a963f687c3f5f6c2239411d2f31757f81efeef4
+f1ecf3fffdfff7f5fafafafcfefefef4f6f5fefffdfdfffcfdfffcfefefefefefefefefefefefe
+fffdfffffdffffffffffffff
+fffffdfffffdfffffdfffffbfffefbfffefbfffdfcfefdfbfcfcfefcfffff0f8fbf3ffffeafdff
+b3cad2859da76e8f9e7aa8c0699cb95586a6477898477898507f9d4d79964269863759743b576d
+12273a1f2c3c474f5a7b7e87c2c0c5c4c2c7c7c5d0c1c1cdccd0dccdd5e29ca9b95e6f7f445b6d
+486479507389587e955a849d54809b4a7995477896497a9a4b7c9a497d92356675305c694f747d
+58737aedfffff1fbfafefffdfdfcf8fffef9fffefafffefbfffffdfefffff8fcfff3fbfef4ffff
+f2fffff4fffff4fffff4fffff4fffff5fefff5feffeff8fdf5fcfff9fffff9fffffbfffff9feff
+f8fdfff7fefff6fffff3fffff1fffff0fefff0fefff1fffff3fffff4ffffeefcfff0fefff2ffff
+f3fffff4fffff3fffff2fffff4fefffbfffffefefefefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefbfffff8fffff5fffff5fffff5fffff1ffff
+f1fffff4fffff5fffff2fffff1fffff0feffeffdfff0fefff2fffff4fffff7fffff5fefdf7fdfb
+f8fefcf9fffdfbfffefbfffefcfffffcfffff8faf9fcfefdfffffffffffffefefefbfbfbfdfbfc
+fefefff8fdfff9fffff8fffff0f7fdf1f8fef9fffff9fffff9fffff3fafff4fbfff5fcfff2f9ff
+f1f8fef3fafff9fffff9fffff0f7fdf7fefff9fffff9fffff9fffff5fcfff4fbfff5fcfff8ffff
+f8fffff8fffff8fffff8fffff8fffff8fffffafffff2f6f7fcfefdfefffffefffffbfdfcfcfefd
+fefffffdfffefefffffdfffefdfffefdfffefefffffefffffcfefdf6fdffcfe2f17f9eb3678ca6
+76a5bf568fac4585a142849e44859b5590a233677437626924454a0d262ae2f5f9e7f5f8f7ffff
+f5fffff5fffef5fffef5fffef4fefdf4fefdf4fefdf3fdfcf8fffff8fffff8fffff8fffff8ffff
+f8fffff6fffff4fffeebf9f9f1ffffeeffffe7ffff9abece719bb14a7c975e93b34a82a54d85a8
+487ea24e83a35485a33866802d57704e7386223e49e7fcfff1fffff2fbfff9f9fffefcfffffdff
+fffafefffefffefefefefefefdfffefefffffefffffefcfffbfcfff2fcfeeaf9ffa9bfcc829fb1
+6d93aa79a8c6568bad538bb0548aae5588a74a768f32556839515b2a393e434c4bcbcbcbfffaff
+fdf0f9fffbfffffafffffdfff1eff0fffffffcfefbfbfffcfbfffafdfffcfefefefefefefffdff
+fffcfffffcfffffeffffffff
+fffffffffffdfffffdfffffbfffffbfffefbfffdfcfefdfbf8f9fbfbffffedf8fcf0ffffdef4ff
+90acb8688897799eb16595ac598ca748799746759157859f5e88a14c72893d5d72344f62132635
+0e1c27a2a9b3fefefff4eff5fffcfffffafbfffcfffffdfffffdfff5f5fdf2f9fff7ffffcfdfec
+8399a73f5a6d4f6e835b7f9559819a4f7b964d7a995381a25889a743768b437483305c694d727b
+425d64e3f5f7f2fcfbfefffdfefdf9fffffafffefbfffffdf9f9f9ebeff2dde5e8d4dfe5cee1e8
+cbe2eacde1eacde1eacee1e8cfe0e8d1dfe8d1dfe8c9d6ded6e3ebe9f3fcf5fffff8fffff6ffff
+f2fbffedfaffd6e9f0d2e6efcee2ebc9e0e8c9e0e8cce3ebd0e7efd3eaf2c3dae2cbdfe8d5e9f0
+e1f4fbe9fcffeffffff1fffff4fffff8fdfffbfdfcfcfcfcfcfcfafdfcfafdfcf8fdfcf8fdfcfa
+fcfcfcfbfdfcf9fdfef8fdfff6fefff6fefff6fefff4ffffeafdffe5fcffdff6fed5ecf4cee2e9
+cadee5cce0e7d0e4ebcee2e9cde1e8cce0e7cbdfe6cbe2e8cfe6ecd3eaf0d7ebf2ebfaffedfbfe
+eefcfdf1fcfef3fdfff4fefff6fefff9ffffeff3f4f7f8fafffffffffffffdfdfdfaf9f7f8f7f5
+f8f6f7fcfffffbffffe4ebf1b9c2c9a8b2bbb1bec6b1bfc8a1b2bca8b9c3aabac7aabac7a7b7c4
+aab8c3bac8d3d6e2eeedf7fff2fcfff8fffff7ffffebf5fed4e1eabfccd5b1bdc9abb9c4acbac5
+acbac5aabbc5aabbc5aabbc5aabbc5acbac5acbac3c7d2d8dce6e8f1f9fcf8fdfff7fbfef9fafc
+fcfcfefdfdfdfffffffefefefdfdfdfdfffefcfffffbfffff9fffff2ffffa2becc7298ab6b98af
+6296ac69a3bb4c8ca546879d488799477f8e34646e3c636a325154607878f1fffff3fffdf8ffff
+fbfffffbfffdfcfffffbfffdfcfffffcfffdfcfffdfcfffdfcfffdfbfffcfafffbfafffbfcfffd
+fcfffdfcfffdfbfffff5fffff4ffffe3f8fde3ffffc4e8f8658fa552849d639ab8558dae548caf
+4a82a35085a55788a63c69862f5b7451758b193541e8fbfff3fffff2fbfff6f9fefffefffffdff
+fcf6f8fffefdfffefcfefefcfdfffcfefffffefffffcfdfff8fdfff0fdffd8ecf7819baa68879b
+7da2bc6895b25588a75486a75687a7537f9a567a90324f5f2537413b45477e8281fffffdfffbff
+fff5fcfff7fdfffcfffffdfefffefffffffdfcfefbfdfffcfdfffcfdfffcfefefefefefefffdff
+fffcfffffcfffffeffffffff
+fefffffefffffefffffefefcfffefafffffbfffffbfffffdfcfffff0fafcf0ffffebffff9fbccc
+7fa2b678a0b975a3bb679bb3598fa74072896291a55d8698385c6c425f6d2f475122333b0a171d
+dde4eaeceff4edeef2fffefffaf8fbfffefffefcfdfffdfefffffffefffffcfffff8fdffe3edf6
+d0dee9879aa9617b8c5b7a8f587e954d76925e8dab4677975a8daa4f7f95507e8d28505c53747d
+485f65c3d2d5f5fefdfefffdfcfbf7fffefbfffefcfefefefaffffe8f1f6deebf1879ba47192a3
+7197aa6d92a4698c9f7493a57895a56f8a9b6a81918ea4b26a7d8bc8dae6e9fbfff2ffffeeffff
+d5e6ed899da67a97a77293a67494a97194a86a8ea46d91a76f93a9678ba16c8fa37293a6728fa1
+65818f8097a5b2c8d5d4e8f3f0fffff7ffffe8eeeefefffdf8f7f3fffef8fbf4ecfffff6fffdf8
+fafcf9eff8f7f4fffff1ffffe4fcffd5eef3c0d9de9ebbc388acbc658a9d6c91a37598ab799aab
+6382946e8e9d728f9f7592a06f8f9c7393a27697a67498a8779cac7499a9678b9b708d9b7b97a3
+86a1ac91a9b3b7cbd4d7eaf1e6f5fcf7fffff9ffffecf0f3fefffffffffffaf9f7f2eeebfffefb
+fdf9f8fefcfffdfeffeef5fbd3e0e891a3af708a9781a1b07295a87094aa779bb37395ae6989a2
+708ca4668099849ab296a8bedfeffef4ffffe4f4ffa9bccb8ea3b47c93a5728ba17894aa728ea4
+7593ab7696ad7191a86f8fa67191a87291a6718da2728c9b778d9a8fa1add2e0e9f8fffffdfdff
+fefcfffffdfefffafcfffefffdfbfcfefffff9fffff1fcfeecfbfeedffff92b1c37197ac739cb2
+6f9cb36896ad5383995c8aa15b86995a819231515e4b666f3649508e999df8fffffbfdfcfafcf9
+fefffffcfffffefffffcfffffefffffefffffefffffefffffefffffefffffcfefdfbfdfcfcfefd
+fdfffefdfffefdfffefcfdfffbfffff7ffffecffffc5e0eb7293a25e87996594a85f8fa654869d
+56869d58859c59829845697f3a5a6f5d788b203341f4fffff3fdfff4fbfffefffffefcfffefaf9
+fffefbfffbf7fffffafffffbf9faf5fcfffdfafffff0f8faf2ffffeaffff9fbacd7b9aae7fa2b6
+769cb36993ab628ea755819a658ea44f7487395666324a563947503e454bd6d7dbfffffffffdff
+fffefffffefffffefffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffefffffeffffffffffffff
+fcfffffcfffffefffffdfffcfffefcfffffbfffffbfefffdf5fbfbf7ffffe6f8ffc6e0ed7fa0b3
+85abc26c98b36b9db86b9fb7497d935b8ba15d8b9b6086933c5d663d565d31444a111c20bcc4c6
+f3f7f8fefffffffffff0f0f2f5f5f7fffffffdfdfbfdfef9fbfbf9fbfbf9fefffffcfffff7feff
+eef8ffc2d2df6c839367839861859b527c955584a05386a5578aa758879b557f8f375b694b6973
+50646ba7b5b8fafffffefefcfdfcfafffffbfdfdfdfafefff9fffff0fbffeaf8ff96aeba719aae
+709eb5709db46d97ad6f95aa7497ab7292a76c899b6e899a627c8b5d7381ebffffe2f6ffd4e8f1
+97abb4809ba67a9aaf7196b0759ab5779fb9729bb7719db8729ebb6e97b36f96b3698ea865889e
+59788c5a7586688291859caab6cad3eaf9fef6fffffefffdfaf9f4fffef6fbf4eafffbf1fffff8
+f5faf6effbfbeeffffdefafeb9d7df90b2bb7c9ea77197a26b95ab6593ab6f99b26d95ae6e94ab
+6c90a67497ad7194a87497ab789bae7da2b5779db06790a46491a46b98ad6b98ab688ea164879a
+648596678492748e9b7e949f91a3adc3d2d9f7fffff4fcfef9fafcfbfbfbfaf9f7fffcf9fffdfa
+fffbf8fffbfcfdfefff4fbffdeecf58fa7b37191a080a6bb73a0b76b99b36c99b66e9ab76e97b5
+7699b7607f9c5b7694435b75d4e6fadceffeb4c9da8ba5b6809bae809fb47597b07ca0ba7297b2
+739ab7739cb8729bb7729bb7739db66e96b0698fa66184975d7a8a5f76868b9dabacb4bff4f7ff
+fffdfffffdfffff9fcfffdfffdfbfefefffff9fffff1ffffeeffffdff7ff8aabbc7399ae769fb5
+7099af6992a85c8299678ba163869a5875873d5464596b7725323ac1c9ccfcfdfffffefcfffefc
+fffffffefffffefffffefffffefffffefffffffffffffffffffffffffffffdfdfdfdfdfdfefefe
+fefefefefefefefcfdfffbfcfdfdfffbffffeffdffc7dee675939d6288956994a46492a25b889b
+5d889b5e87996083964c6b7d405b6c637889273746f2fefff5fefff5fcfffefffffefcfdfffcf9
+fffefafcf7f1fefbf4fbfaf5fbfcf7fcfffff9fffff3fdffefffffc2dced8aaabf7a9db385a9bf
+749ab1618aa06790a66b94a8567b8e4b6b7a334d5a42565f212e36969da3fefffffbfbfdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fcfffffcfffffdfffefdfffcfffefcfffefafffffdfefffff3f8fbf5ffffebfdff8fa9b887a8bb
+7ea4bb74a0bb608fab5f91aa5d8da36594a84e77894566753f5b66384c551e2d329faaacf9ffff
+e4e8e9fbfcfef7f7f9fffffffcfcfefffffffffffdfffffbfffffdfbfbf9fbfcfefcfffff9ffff
+f7ffffe3f3ff6c81926e899e7497ad648ca65986a36293b350829d608da2568090436773415d68
+586b72869195fbfffffdfdfbfffffdfffffbfdfdfdf8fcfdf9fffff4fffff1ffffa4becb7399ac
+6b99b0729fb6729bb1678da26e91a57493a86885975f7a8b647e8d384e5cecffffeeffffb7cdd8
+879da894aebb7b9bb06d93aa6f94ae739bb56e97b36c95b16f98b66e97b36990ad658aa47094ac
+7898ad7996a86983925b7280718590bbcacff8fffff8faf9fbfaf6fffef8fef7effcf7f1fffffb
+fbfffff4ffffd9edf4afccd48babb671939d6b8d977298a5709ab06e9cb4648ea7638ba46b91a8
+799db3779ab0779aae7598ac7497ab789cb2799fb4749db374a1b673a0b76996ab7ea4b97b9eb1
+7a9bac7191a06e8a986179855e727d83949cccd9dff8fffff7fbfef9fbfaf8f7f5fffefbf6f1ee
+fffefdfcfafbfafbfff8ffffe9f7ff8ea6b27797a685a9bf7ba5bd7da9c272a0ba739fba749bb8
+7899b86784a2607997354d67d6edffcae1ef92acbb7f9aab7996a884a4b96d91a77095af769eb8
+749eb7729cb5709cb5739fb874a0b96f99b26891a781a4b77693a35b7282687a88747e88e8ebf4
+fffefffef8fcfff9fcfffcfefffafefefffff6fefff7fffff4ffffcfe5f081a2b5779db47ca5bb
+6d96ac698ea85e849b6a8ea466869b4d6a7c465d6d5c6e7a1a272ff5fdfffafcfbfffffdfffffd
+fffffffefffffefffffefffffefffffefffffffffffffffffffffffefefefefefefefefefffffd
+fffffdfefdfbfdfcfafffcfbfdfdfdfbffffeefcffc8dfe77896a0668a986e98a86994a5608da0
+628da0648a9d64879a517082445f70647a882e3e4bedfafff6fffff7fcfffefffffcfbf9fffdfa
+fffefafffbf5fbf8f3fcfbf7fefffdfbfffff8fffff5ffffe2f5fca0bbcc7e9eb37b9eb486aac0
+7298af61879e688ea37197aa5a7d902c4c5b49606e31434d414b54f8fdfffffffffefcfdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fbfffffbfffffdfffefffffffffffdfefffafefefcfbfffef1f9fbf3ffffd4e6f07b95a498b9cc
+799fb678a2bb57859f6591aa618ea56790a4496e802d4a585169731f32394f5c62f5ffffe6ebee
+fbfffffefffff4f4f6fffffff8f8fafffffffffffdfffffbfffffdfffffdfbfcfef9fdfff5fcff
+f1fbffedfbff788e9c6b8699779ab06991aa5f8da76293b152819d6390a55980914668743e5b63
+5b6e75758084fbfffffcfcfafffffbfefdfbfefefef7fbfcf8fffff5ffffedfeffa5bfcc81a7ba
+74a1b67fa9bf7ea7bb7094aa7598ac7b9aae6784965972865a7485657c8ce4faffd4eaf7a0b6c1
+8ba1ac96b0bd8eafc27ca0b8789cb47a9fb9729ab46e97b3749dbb769fbb6a92ac678ca66b8fa7
+6a8a9f718ea06d88995c7685677b867b8a8ff6fefff3f7f6fdfcfafffefafffcf6fbf8f3fafcfb
+f9ffffecfbffb7cdd887a5b080a0ad83a7b580a4b07fa6b5729cb477a3bc6f99b27ea6bf80a4bc
+7a9db365859c7090a57797ac6f8fa46f92a87599af7ba1b882acc27eabc0719bb16d93a8779caf
+7d9eb17493a57c98a677919e647883697c83839098f8fffffafffffcfffff5f5f5fffffdf3efee
+fffefdfdfbfcf4f8fbf5feffe8f6ff8da5b17d9dac84a9bc7aa3b96f9bb4638faa668fab6186a1
+5c7e9a5977936b839f496179b5ccdea8c2d17b96a77f9cac7594a883a6ba6b8fa57197ae739bb4
+6f99b16b95ae6996ad6b97b06c99b06a97ae6a93a96e93a56c8c9b4d67765b6d79636d77c1c5ce
+fffefffffafefffbfdfff9fbfffbfffffefff4f9fdf7fffff4ffffbdd3de799aad7da3ba84adc3
+6d96ac698ea863879f6b8ea463839848657750677553656f354248fbfffffafcfbfefdfbfffffd
+fffffffefffffefffffefffffefffffefffffffffffffffffefefefefefefefefefffffffffffd
+fffffdfefdfbfcfbf9fffefdfefefefcfffff1fcffcce0e97b99a36b8f9d729cac6c97a86691a4
+6790a4678da0688b9e557584466270647a87374550e5eff8f6fffff7fcfffdfefffaf9f7fffefb
+fffdf9fffefafefbf6fffffdfcfffffafffff8ffffeaf7ffc0d2de95b2c481a1b67c9fb57ea1b7
+7296ae698fa4668aa05f829658798a223e4c405663293a42acb5bcfcffffefeff1fffeffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fcfdfffcfdfffefefefffffffffffdfefefcfcfefdf9fffff1fbfdf1feff9badb790aab990afc3
+81a5bb6c94ad5d87a0779fb84e748b6a8da1506f813751604f636e202e37c6d1d5e6eef1fbffff
+fefffffcfefdfeffffeeeff1fdfefff7f9f8f8f8f6fdfef9fffffdfefefcfbfcfefafefff9feff
+f3fefff4ffffa0b3c26e88997090a562889f628ea75a89a55d8ba56691a45d829242647047626b
+5c6f767d888cfbfffffefefcfffffbfbfaf8fffffff9fdfff8fffff4ffffe5f6fea2b9c77a9fb2
+6b95ab719ab07096ab5f8399638398607f93476476688195455f70a2b9c9e0f7ff9fb6c49eb6c2
+88a0ac8aa4b17c9baf6c8fa56a8ca56c90aa658aa56088a2638aa7638aa7678fa9678ca66a8ea6
+6181965b7a8e587384556f7e6d839058656ddae2e5fafefffbfbfbfcf8f5fffefbfefefcf8fcff
+edf6fdcedfe99eb5c384a4b38fb0c189aec0769bab6d93a6638ba46b93ad5c819c5c809a4d6d86
+45637b3c586e5470854e6a7f4c687d4d6c814b6e82496d83557b9060899d5f889c6a90a36f94a7
+7091a46685976e8b9b6b8795576d7a5c707953616ae3eef4f5fdfffcfffff4f4f4fbfaf8fdf9f8
+fbfaf8fffffff3f7faf2fbffdae8f190a6b386a3b385a8bc759eb46d97b0658fa86e96b06e92ac
+6a8aa366829a7b91a95e748b9db7c899b5c37f9aab86a6b57091a4789baf668aa07197ae6790a6
+6690a66892aa6993a96993ab6791a76791a76992a65c8193638392435d6a546873616e777c8187
+fffefffffafefffdfffef8fafffcfffffefff3f8fcf8fffff3ffffaec1cf7797ac84a9c38bb0ca
+7095af6e94ab688ca46a8da35f7e924e697a59707e43555f768389f8fdfffdfffefcfcfafefdfb
+fffffffefffffefffffefffffefffffefffffffffffffffffefefefefefefffefcfffffdfffffd
+fffffdfefdfbfcfbf9fffcfbfcfcfcfbfffff1fcffcee2eb7f9ba67091a0779eaf6f98aa6893a4
+6992a46a8fa16c8d9e5a77874963726379863b4952d8e3e9f5fffff7fcfffbfdfcf7f6f4fffefb
+fefbf6fffef9fefdf9fefffff6fbfef2f9fff6ffffd6e4ef9ab0be8eaabf87aac080a3b97497ad
+6f93a97397ad678a9e507184415e6e3b52601628346c7981f9ffffe6e7e9fffdfefffefdfffeff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fbfcfefcfdfffefefefffffffffffdfdfffefafefdf8fefef6ffffd8e7ee889ca794aebb92b1c3
+7ca0b67297b1678fa86b8da667879c5a768b4c6677465968253640727c85f9fffffafefffeffff
+e9ebeaeef0eff6f7f9f7f8fafefffffcfefdfdfdfbfffffbfffffdfbfbf9f9fafcfafefffbffff
+f8ffffeefcffb9cbd97993a47897ab6d93a86d97af5987a16591aa6992a66185954363704e6972
+596c728e999bfafffefefffdfffffbf6f6f4fefffffcfffff8fffff4ffffdeeff99cb3c18baec1
+7da6ba7da3b8779caf6a8da1698a9d637f944b6679758fa026404fcae1f1bdd4e295acba92a9b7
+8da5b17e98a56e8b9d69889d6e8ea57193ac6d91ab6d92ad6d92ad658da7678ca75c819b698ba4
+7696ab7594a85d7a8c4963745d73805d6a72aeb6b9fcfffff9fafcf6f5f3fffefffefffff8ffff
+d6e3ecadc0ce8ca7ba8faec28eb1c57499ac608699668c9f7b9fb77296ae55779044647b405c72
+4b667b4c657952687d4e64794d667a5671845c788d5d7c9065889b6c91a4678c9f769bae7194a8
+7495a87293a47190a25c7886465d6b596f7a53646cb3c0c8eef7fcf9fefff6f7f9fefefefffeff
+f7f5f6fffffff5fafdf3fcffc5d4db94aab78daab887aabd769cb1648ca55c849d6388a2698ba4
+718da5667f9570849c566b7e758f9e74909e65809166839349687a4a6b7e44677b52778a63879d
+658ba06c92a76f98ac6e97ab6992a66790a66a90a57196a87495a44c66734f636e65727a474e54
+f6f7fbfcfafdfffdfffff9fbfffcfffffefff4f9fff8ffffeaf4fda2b4c27e9db289aec88bb0ca
+6f94ae7399b06d91a76b8ba05e7d915570815f7583364751bac7cfe6eef1fcfffffffffdfefefc
+fdfefffbfffffdfefffdfefffdfefffdfefffefefffefefefffffffefefefffdfefffefcfffffd
+fffffdfefdfbfdfcfafefaf9fafafaf8fdfff0fbffd0e3ea829da87192a1799eae719bab6c95a7
+6e94a76d90a370909f5e7a884c6371647883404d55cdd6dbf6fefff9fffffcfefbf5f6f1fffffa
+fcf9f4fffdfafffffdfefffff6fdfff1fbffe8f6ffc1d3e18ea5b581a0b58aadc383a6bc7093a9
+6c8fa57396aa66879a4e6b7d3a54633f556234454fcbd6dcfafffff4f4f6fffefdfffbf8fffffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fbfbfbfdfdfdfefefefffffffefffffdfffefafefff5fdffeef9fdb2c1c89bafba8ea8b595b2c4
+7c9fb3779bb3698da567869b829db2465c71485b6c4151602e3b44cfd8dfeff4f8fdfefff1f1f1
+fffffffffffff5f6f8f7f8faf7fbfefcfffffefffffefffdfefffdfbfdfafafbfdfafefffaffff
+f4fdffe7f4fdb5c7d3879eae86a3b581a4b87099af6490a96a94ac6d93a66182934967724e6670
+53646b95a0a2f3f9f7fdfffcfffffbf5f5f3fefffffbfffff8fffff1feffd4e5ef93aab885a8bb
+80a6bb789eb36d92a569899e67869a627d925770846882932d4454cbe0f18ea4b292a8b6758b98
+7a909d687f8d567081587386607c916180956282996a8ca56d91ab6287a17195af5e829c5f819a
+6a8a9f7392a66582944d6778586e7c6e7b8471787efbfffffafbfdf8f8fafafafcfafdfff2fbff
+bdcdda9cb3c38ca8be8babc27b9fb5698fa4678ba16f92a667879c5773884e697e465f734c6174
+4f627357687a5162725f7080607181687b8a6b82926983926f8b99718e9e68889766899c638699
+6c8da07596a77d9cae6885934c66735a727e657781717f88f0fbfff6fdfff9fdfffffffffcfafb
+fcfcfcfefffff7fcfff5feffadbec596acb98fabb98bacbd7a9fb27399ae6d93a86d90a67292a7
+7c97ac687f916d7f936174856278866077855f76865f798a546f805b788a6382946b8c9f7090a5
+6c8fa36e91a57396aa7499ac7095a86e92a86f94a7698d9d6d8f9b455f6c3d515c67767d484f55
+f7f8fcfffefffffcfefffafcfffafefffefff6f9fff2f9ffdde6ef98aab887a6bb8db1cb87abc5
+6b8fa9779bb37396ac6d8da26380925670815b717f2c3d47e3f0f8e7eff2fcfffffefffffcfefd
+fbfffffafffffbfefffbfffffbfffffbfffffdfefffdfefffefffffefffffdfdfdfdfdfdfffefc
+fffefcfffefcfefdfbfdfcfafbfbfbfafffff4ffffd5e8ef89a1ab7696a37da1b1769bad7297a9
+7297a97293a47491a1627e8c4e6573637780435056c5cfd0f7fffffcfffffdfffcf6f7f2fffffa
+fcf9f4fffffbfefffffbfffff8fffff0fcffcfdfeea9bccd93acc083a2b787aac07f9fb47191a6
+7292a77291a55d7a8c445f7042586630424c9ba8b0f9ffffe5e6eafffefff7f2effffcf9fffffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fbfbfbfcfcfcfefefefffffffefffffbfffff8fdfff4feffd8e3e99aabb39eb4bf98b2bf7d9aaa
+8fb0c3698ca264849989a4b96b8093495c6d4f5f6e404c58778089f0f5fbfcfffffefefeffffff
+fefefef8f8f8fefffff8f9fbfcfffff2f6f7fdfffefefffdfefffdfefffffefffffcfffffbffff
+f3fcfff5ffffb2c2cf8ba1af839eaf82a2b7658ba07098b16e97ad6e93a65f7e904f6b77475e66
+4f5e65939d9fedf1f0f9fbf8fffffbf5f5f3fefffff9fefff4fdffebf8ffcbdce68ba2b083a4b7
+84a9bc7ba0b36d90a36e8da1708d9f6e879b6f8698637a8a697f8dc2d5e491a4b28093a1748893
+5c707b596f7c596f7d5c73835f798a5c778a5f7b917090a7789ab36f91aa6c8ea77395ae7d9db4
+7291a6708ca16b86995872835b717f76838c3f464cf4f9fdfafefffdfefff3f6fbf0f4fde5f2fb
+a5b6c69ab3c794b2ca84a6bf7094ac7397af769ab06a8b9e6781924b60714c5f6e3e505e2f3f4e
+2a384567717d939da97e8892828f9884909c6e7f89536571536772697f8a758f9c7a9aa97497aa
+7091a46a8b9c7999a87390a04c6876435b676c808b3c4d55f7fffff9fffffbfefffeffffefeff1
+fffffff6fafdf5fdfff5ffff9eafb695abb68da9b78dacbe7ea1b46e93a67095a87494a97693a5
+788fa15267785263735163715c6e7c566876576a79576a79566b7c5d7283667d8f627c8d839eb1
+7994a7708d9f708fa17493a77695a97796aa7a9bae698a996f8f9c4b65723b4f58707f865f686d
+f7f8fcfdfbfefffcfdfffbfcfbf9fefffefff6f9ffedf4fed4dce794a4b38fabc190b2cb83a5c0
+688aa37b9bb47797ae7190a4688597536d7e556b79263741f0fdffeff8fdf6fbfefcfffff9fdfe
+fafffff8fffff8fffffafffffafffffbfffffbfffffdfefffefffffefffffdfdfffcfcfcfefcfd
+fffefcfffefcfffefcfffefcfefefefbfffff7ffffdbecf48fa5b07d9aa883a4b3789cac7599a9
+7599a97494a37793a1657f8c516774657781475256c2cbcafafffefcfffdfffffbf8f7f3fffffa
+fbfaf6fdfdfbf8f9fbf3f8fcf7ffffe9f7ffb4c7d890a6bb95b0c58faec386a6bb7595aa7190a4
+7a99ad728fa1516b7c364d5b3d4f5b303e47e0e9f0fbfffff6f6f8fffdfcfffefbfffefbfffffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefdfbf8f7f5fffffff8f8f8fcfdfffcffffedf5f8f7ffffc3d0d8a2b5bc9db3be9bb5c2839eaf
+7897a97e9fb27897ab7d93a86677894151615a66742f3742b2b6bffbfefffffffffefefefffefc
+fffefcfefefefdfefffdfefffbfffffbfffffcfefdfcfefbfdfffefefffffefffffbfffff7fcff
+f2f9fff5ffffa4b5bf8ba1ae85a1af84a3b76d92a56c92a77399ae698a9d6481914f6976657982
+536269bec8caf9fdfcf9fbf8fbfcf7fcfefbfbfffef9fffff4ffffeffdffb0c0cd96adbb8bacbd
+88adbf7a9db06d8ea17d99ae809bae6980926176875c6f7e8497a5aabcca8698a46577835a6c76
+6f818b60727c5c6e7a5669777086947289996c85997894a97190a56c8ca37090a77191a87696ad
+7291a67995aa748fa25670815164737c8992383f45f0f5f9fcfffffcfffffafffff6fdffbccad7
+99abbf92adc291afc97fa1bc7799b27fa2b87595aa6580935265735d6b763f4c55000710a9b2bb
+ebf2fafbfefffcfffff9fcfffbfffff8fdfff8fffff4ffffbdcad084939a7488916885937596a7
+7796a86d8c9e7494a37e9bab486472455c6a697f8c40525cf0fdfff7fffffbfffffbfcfefbfbfd
+fffffffcfffff1fbfde2f0f39fb0b797adb88aa4b195b2c27293a665869981a2b57895a77792a3
+6378895d6f7d3a48551a28332d3947535f6d6672807e8a988e9bab8b98a8778796465767647788
+6275868fa4b7889fb17f95aa7a93a77b94a87e99ac7393a264828d546f7a4f636c4f5e658a9496
+fefffffdfbfefefafbfffcfdfcfafffbfcfff9fcfff9ffffbac2cd9aaab993aec38cacc382a2bb
+7797ae7795ad7d99af7894a96f8a9b748b9b748795697b87f1fffff6fffff2fbfff9fffff0f8fb
+f1fafff2fbfff4fdfff6fdfff7fefff9fefff8fdfff8fcfff3f7fafcfffffefffff7f9f8fefefe
+f4f4f4fffffffcfcfcfdfcfafefefefcfffff7ffffebf9ff8da3ae839fab86a6b580a1b27596a7
+7a99ab7d9aaa7b95a2677f8b566a756778804c5759c5cecdfafffefafffbfbfcf7fefdf8fffffa
+fcfbf7fffffff6fafdf4fbffeffbffcfe0f0a2b7ca8da6bc93afc58faec389a8bc708fa37f9cae
+698497546e7f465c6a506270172530b6c0c9eff6fcf4f8fbfffefffcf8f7fffefbfffcf9fffffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffffdfcfbf9fffffffafcfbf9fdfefbfffff0f9fee9f6fcb5c3cc9dafb99cb2bf9bb5c284a0ae
+7b98a8809fb17c99ab76899a55657545515f5f69733e454fdee1e8faf9fefffffffffefcfffefc
+fffefcfefefcfefefefdfefffbfffffbfffffdfffefdfffcfdfffefefffffefffffefffff9fdff
+f4fbfff4feffa5b3bc8ea1af8aa4b18ca9bb7596a97297aa7ba0b37190a2627c8d50667461737d
+414e54c3cdcffafefdfdfffcfcfdf8fefffdf9fdfcf9fffff5ffffe7f5fea9b9c697adbb91b0c2
+8aadc07fa0b37695a7809bae7a94a56378895a6d7c495b695e6e7b4e5c672c3a452d3b444a5960
+819097909fa677858e73818a7889936577855b70816e88997792a77f9bb17190a57291a67995ab
+7591a67c97aa7892a35d7486576a7984919a2e373cf9fffff5fafefbfffff2f9ffe7effaaab7c7
+9bb0c399b3ca8fadc77999b27292a97594a9688499586f7f5a6871202930091016dee3e7f9fcff
+f5f6fafdfbfefdfbfefdfbfcf7f7f7f2f2f4f3f7f8fbfffff9ffffd8e2e4a1b0b56e89947b9baa
+7e9ead7393a27595a47c99a949657349606e6c828f3b4d57f5fffff4fdfffafffffdfefffdfbfc
+fffffffbfffff3fdffd1dfe2a3b4bb93a9b495afbc8daaba7897a97493a5809fb17c97a85f7684
+55677551626c1c29328d97a0c2c8d4e9effbfbfffff9fffff7fdffeff7fff5fdffe5effbc3cfdd
+7a87977686967d8ea08c9fb0859aad8095a888a2b37793a1637f8b4f67735c6e78465359a9b1b4
+fcfffffffffffffefffffefffaf9fef9fafff8fbffeff5ffb1b9c699a9b89ab3c78faec385a1b7
+7c98ae7a94ab7a95aa758ea26e88997f94a56e818f758793a9bac4c4d1d9b6c1c7c6d1d5bac5cb
+bec8d1becbd4c1cbd4c3cdd6c4cdd4c3ccd3c2c9cfc1c9ccc5cacebabfc2c5c9ccc0c4c5d0d1d3
+aaabadd8d9dbfdfdfdfdfcfafefefefcfffff7ffffebfaff90a4af859fac8aa7b584a4b37898a7
+7c99a97f9ba97d94a2697f8a5769736a79804d5759c6cccaf8fcfbfefffdfdfef9fdfcf7fdfcf7
+fffffbf6f8f7fbfffff8ffffdeecf9b7cadb97b0c48da9c18dabc38aa9bd708d9f8ba8ba7e99aa
+5e7887495f6d4f616f3947525e6871e3eaf2fcfffffafbfdfaf8f9fcf8f7fffefbfffefbfffffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffefbfffffbfefefcfefffff8fcfdf8fffff4ffffd3e2e9a7b8c299adb89cb2bf99b3c084a0ae
+7c99a9809dad7c97a87889994b5966586470545d6672767ffcfffff7f7f9fffefffffdfcfffdfa
+fffefcfefefcfefefefdfefffbfffffbfffffcfffffbfffefdfffefefffffefffffefffffbffff
+f9fefff1faffa2b1b88fa1ad90a8b492adbe7998aa7699ac81a2b57a95a85d748454677564757f
+323f45d2dadcfbfffefefffdfcfef9fefffdf8fcfdf8fffff7ffffdae8f39fafbc99afbd97b6c8
+87aabd7b9aac7794a6748e9f657c8c5467764a5a6919273265727b8a949dcdd7e0f5feffebf4f9
+f2fbfff8fffff3fcfff8fffff7ffffd4e1ea9babb8738695637a8c7790a47590a57692a77c97ac
+7992a67f98ac7d94a662798b5e717f8d9aa2252e33f9fffff4fcfff6fdfff2fbffcdd7e399a9b8
+a0b5c89eb8cf8dabc37d9bb37e9ab07c97aa6e8897667a856a757b1b2024b3b7baf8f9fbf3f3f5
+fffefffffdfdf2ececfffefbfffefbfffefdfcfcfaf2f4f3fcfffff9ffffc6d4d77a929c83a0ae
+89a6b4809dab7b98a67c99a74c68764f66746f859231434df5fffff2fbfffbfffffefffffdfbfc
+fdfdfdf7fffff1fcfebbcacda4b7bd91a7b29fb9c684a0ae7e9bab7a97a77b96a7768d9b556b78
+4f606a435058464f56f3fafff2f5fcfefefffefdfffdfdfffafafff6f9fffbfffff4fbffe9f1fc
+919ba78793a18f9fae9bacbc8699aa788da0849bad7c96a5607b86485e6965767e3b484ec9d1d4
+fafefff8f8fafffefffffefff9f8fdf7fafff9fdffe1e9f4a8b2be9eaebd9bb1c68da8bd829db2
+7f9aaf8099af7e97ab7e95a77d92a3859ba976899780929e7b8c9695a6b0919fa89aa9b093a1aa
+96a4af97a5b09aa8b39ba9b49ca9b29aa7b09aa4ad99a2a9a8afb799a0a69ba0a6a1a6aaa5a8ad
+787c7fc7cbcefefffffcfcfcfefefefbfffff7ffffecfbff92a6af8aa1af8da9b789a6b67c99a9
+7e99aa809aa77f95a26d818c5a6d746d7c814d5758c8ceccf6faf9fefffdfdfef9fffefaf4f3ef
+fffffdeff0f2fbfffff7ffffc4d4e19bb2c494afc492b0ca8aaac186a3b57f9ba97d97a6768d9b
+647a874254604e5c672e3842c7d0d7f7fcfffafbfffffffffffefff9f5f4fffdfafffefbfffffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffefbfffffbfdfcfafefffff8fcfdf7fffff6ffffc0ced7a3b5c19cb2bf9eb5c398afbd849ead
+7e9aa87f9ba97a94a170829048566167717b38414ababfc5fefffffafafcfdfcfafffdfafffdfa
+fffefcfffefcfefefefdfffefbfffffbfffffcfffffbfffefdfffefdfffefefffffefffffcffff
+faffffebf4f9a1aeb692a3ad93a9b694aebd7898a77596a77f9eb07b95a6586d7e5b6d7b6c7a83
+384349e5edeffcfffffcfefbfdfffafcfffdf6fcfcf7fffff5ffffc9d7e299a9b69bb1bf99b8ca
+83a4b57794a67893a46c8393596f7d5666754b5964606a74edf6fff3faffeef6f9fafffffbffff
+fbffffeaeeefeff3f6f0f4f7f2f7fbf7ffffecf9ffb6c6d38093a2758c9e7b94a87c95a98099ad
+7c93a58198aa7f94a5667b8c6375838f9ea51f2a2cf8fffff9fffff0f7fdf2fbffb4c0cc99abb9
+a8bfd19eb9ce85a1b77e9ab07d98ab6d84945e74815b6c74333c41767b7ef6fafdf9fafcf6f4f5
+f4f0effffdfdfffcf9fffaf7fbf6f3fffefbfffffbf0f2efeff4f0f8fefcf3feff89a0a889a5b1
+8fabb78aa6b4829eac809caa526c7b526977738694283a44f5fffff3fcfffcfffffdfdfdfcf8f7
+fffffdf6fffee9f7f8aebdc0a1b4ba99afbaa2bac67e9aa8829dae7d98a97b95a46a808d647680
+5c6b722d383caeb6b9fcfffffffefffcfafdf1eff4f7f5fafffefffffefffbfcfff0f3f8f6fbff
+afb8c1a4aeb892a0ad8ea0ae8598a78297a88ca3b37d94a2586e7b4c5e6a64727b4b565ce6eef1
+fbfffff6f7f9fdfdfdfffffff8f9fdfcfffff9ffffd6dee9a5b1bda6b6c59eb3c68fa5ba869cb1
+879eb08a9fb2879caf869bac889dae8093a28a9dac7e919f8a9ca8889aa699abb790a2ac94a6b0
+93a5b195a7b597a9b799abb79aaab79ba9b49aa7b09aa4ada3adb6aab3ba868f968f969c838a90
+6a6f73d3d8dcfcfffffdfdfdfefefefafefff8ffffeffcff96a8b28ea4b291abba8ca7b87e9aa8
+809aa9839aa88498a372848e6071787380864d5557ccd0d1fcfefdfffffdfcfbf9fffffbf2f1ed
+fffffdf6f7f9f9ffffe3f0f9afc1cf91abbc97b6cb98b8d188a8bf7693a394aebb738a98637684
+6274804c5a65353f49778089fbfffff8fbffeeedf2fffffffffefff6f5f3fcfbf9fffefcffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffdf8fffffbfcfbf9fefffff8fdfff6ffffeefbffb2c3cba5b9c4a2b8c59eb5c393aab8859fac
+849eab829ca97c94a05a6c7853616a566069374047eef3f7fbfcfefdfdfdfffffdfffdfafffef9
+fffefafffefcfefefefdfffefbfffffbfffffcfffffbfffefbfffefbfffefefffffefffffcffff
+faffffe6edf3a3b0b899aab296acb793adba7a97a57595a47b9baa7d94a6576a7b5f6f7e68757e
+4b545bf3f8fbfcfffffcfefbfdfffcfcfffff6fcfcf5ffffedfaffbbc9d49aaab99db2c39ab7c7
+81a2b37a97a9859fb07085965c6f7d657380525f68879099f9fefff4f7fcfdfefffdfdfdf0f0ee
+f4f4f2f8f7f3fefdfbfefefef4f5f7fbfffff8ffffe3f0f9a7b7c48093a2859aab859cae879eb0
+8095a68398a98194a3687b8a6577838a999e1c2729f6fffff8fffff6ffffdee9efacbac5a1b3c1
+a4bbcd91aac07792a7829db0839aaa6a7d8b62737b606d73283136d2dadcd7dcdffcfffffeffff
+e7e7e7f7f6f4fffefdfffbfaf9f8f6f0f0eef6f8f5fcfffff9fffdf1faf9f5ffff97abb28ba6af
+8faab590aab786a0ad839daa526c795168767285932c3d47f1fbfff7fefffefffffaf8f9f9f4f1
+fffffdf8ffffddebecadbcbf9eb1b7a8bcc59bb3bf7f99a8849ead88a2b18198a6687c8763747c
+69767c1c2628eef3f6e9ebeafefdfbfcf8f5fffcfbfffffdfffefcfafaf8f7f9f8fcfffff9feff
+bac3c8aebbc398a9b399adb895acba88a2b1819baa798f9d5164725969765a6770778087fbffff
+fcfffffefffffbfbfbfefefff8fcfffbfffff8ffffc7d1dba4b2bda8b8c7a7b8ca98aabe8ea0b4
+8ea1b28ea1b2899cad8699a8879aa98093a296a9b8788b9a9db3c08ca2af9bb1be93a9b699afbc
+99acbb9baebd9db0bf9fb2c1a1b2c2a1b3c1a0b0bda1afbaabb8c1b1bbc479838c757e857f888f
+676e74eff6fcf7fbfefefefefefefefafefff8fffff1feff9aabb593a6b496adbb8fa9b8809aa9
+819baa869eaa899da6798c9367787f79878a50585bc9cdcefefffffcfcfafaf9f7fffffdf6f6f4
+fdfffefcffffedf4fac6d4dda2b5c397b2c39bbbd091b3cc7ea1b76e8a988ca2ad8599a453656f
+4e5c6548525b3b444be6ebf1ededf5fffefffffefffffdfff6f4f5faf9f7fffffdfefefcffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefbf6fffdf8fdfcfafefffff9fefff6ffffe0edf5acbdc7a9bdc8a8becb9db4c28da4b288a2af
+8ca6b3859fac7b919e52646e6d7c83424f55727b80fbfffff8fcfdfbfbfbfffffbfffef9fffef9
+fffefafffefafefefefdfffefdfefffbfffffcfffffbfffefbfffefcfffffefffffefffffbffff
+f9feffdde4eaa9b4baa5b3bc9cb0b994acb87d9ba67a9aa77e9bab7c91a4596a7c5f6c7c59666f
+697277f5fafdfcfffffefffdfdfffcfafefdf8fefef5ffffe3f0f8adbec8a2b2c19fb4c598b5c5
+82a1b37e9bab8ba5b46a808e5264725e6c77414b54a8adb3fcfffffffffff1f0eef7f3f0fffcf7
+fffefaeee9e3f4efebfffefbfffefff6f7f9f4f9fdebf4fbcad6e297a7b48fa2b18da2b390a3b4
+8598a78699a88194a26b7d8b6879837c8b90263435f8ffffebf5f6f8ffffc2cdd3b0bec9a7b9c7
+a2b9cb8da6ba7891a58aa4b5869caa71828c727f85606b6f636e72f4fffff9ffffdde2e5f5f9fc
+f9fafcefefeffefefefefefefffffff8faf9f1f5f4fbfffff8ffffebf5f6e9f9f9a3b8bd8fa8af
+93abb597afbb89a1ad809aa75269774e64716e828d41525ceef8fff9fffffefffffbfaf8faf5f2
+fffffbf8ffffcfdfdfadbfc3a1b4bab1c5ce94acb8859caa88a2af8ea5b37d93a0677983505f66
+687377384042eff3f4f9fbf8fdfcf7fcf9f2fffff8fffff8faf9f4f7f8f2f4f6f1fcfffdecf5f4
+b1bcbeb1c0c5a8bbc2a6bcc794afba819da97b97a57389975466746a78854a5760a7b0b7fbffff
+fcfffffafbfdfcfcfcfafbfdfbfffff9ffffedf7ffb5c1cda4b4c1a6b6c5a4b4c499aaba91a0b3
+8e9faf8fa0b08d9eae899aaa8699a88da0af8fa2b1768c9a92a9b78ba2b0809aa988a2b189a0b0
+8ba2b28ca3b38ea3b68fa4b590a5b692a5b492a4b293a3b09ba9b486939c616e77525c65828c95
+6c757cf8fffffbfffffefefefefefef9fdfef8fffff4feff9daeb896a9b79ab0be94abbb839dac
+869dab8ba1ae8fa1ab7f90976e7d827f8a8e5a5f63bfc0c4fffffff9f9f9fbfaf8fffffdfcfcfa
+fbfdfcfcffffdbe4e9b0bec79fb5c29eb9cc96b6cb84a8c0789baf819ba88da1aa6a7c8651626a
+4e5861333c43989da3fcffffedecf2faf9fefffefffffdfffbf9fafcfcfcfefefefdfdfdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffbf7fdfaf5fffffdfafcfbf9fffff5ffffcedce5aabcc6b0c4cfaec4d19cb3c18aa1af8ea5b3
+94abb9859da971879254676e7382893f4c52bbc6c8f9fffff8fcfbfcfcfafffffbfffef9fffef9
+fffefafffefafefefefdfffefdfefffbfffffafffffafffefbfffefcfffffefffffefffff9fdff
+f6fbffcfd6dcaab5bbacbac39eb2bb93abb7839fab84a1af829fad758a9b5c6d7d606e7b515b65
+929ba0f7fcfffcfffffefffdfbfffcf5f9f8fafffff5ffffdae7efa6b6c3adbdcca0b5c698b5c5
+82a1b37c99a987a1b05f72814959665f6c753a434aeceff4f7f8fcfaf8f9fffefbfffefaf7f3ea
+fff9f1fdf6eefff8f2fffef8f8f4f1f4f4f4fcfffff9ffffdfe9f397a5b097a9b796a9b897a8b8
+8b9cac8a9bab8597a570808f6d7e886c7b804d5b5cf3fdfeeef8f9e8f3f7b8c5cbb1bfc8aabcc8
+aabfd097b1c2849eaf8da4b27a8e996d7c83717c7e3f4a4c909ea1f4fffff0fbfff8fffff5fdff
+fafffff3f7fafdfeffeff0f2fbfffffbfffff3fcfbf5fffff5fffff4ffffebfdffafc4c998afb7
+9db4bca1b9c38ba3af7e96a25068744e6471687a8664727bf4fdfffbfffffbfbfdfffefbfff9f6
+fffefbf5fffec4d4d4acbec2acbfc5b2c6cf92aab68aa1af8da7b489a0ae798d9863747c536066
+5761637e8786ebefeefefffbfffff8f8f8eefefef4f9f9effbfbf1fffff6f5f8eff8fff8ebf6f2
+b1bfbfafc1c39db5b994afb687a5af84a4af90adbb687e8c596978707e8b404a54ced5ddf8fdff
+f3f7f8f6f8f7fefffff4f8f9fbfffff8ffffdfecf4aab8c3afbfccaabac99eaebd9aaab995a2b3
+8f9fae91a1b092a4b28ea0ae8799a78ea1b07487966e8394778e9e6f89986984956c87986a8596
+6d87986c86976e84996d84966d82936f82916f82917082906676834c5a654a5863424f58717b84
+8c969feef8fff6fdfffefffffdfdfbfbfcfef8fffff5ffffa1afba9aacba9cb2c098afbf89a0b0
+8ba1af90a6b194a6b083949b6f7e837f8a8e666b6facadb1fffffff8f8f8fffffdfdfcfafefefc
+fbfdfcf4f9fccad5d9a9bac2a4bcc8a1bed08cafc37da3ba7fa2b690a8b48a9da442535b4e5d64
+545d665a6169eff2f9eff0f5fffefff6f4f9f3f1f4fcfafdfffffffefefff4f4f6feffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffcf8fcf9f4fffffdf5f9f8f9fffff4ffffc1cfd8a9bbc7b6c9d7b4cad79fb5c38ba1af92a8b5
+99acba8397a2697b854c5d645e6d723f4d50e4eef0f7fcfff4f8f7fffffdfefdf9fffef9fffef9
+fffefafffefafefefefdfffefbfffffbfffffafefdfafefdfbfffefefffffefffffbfffff8fcff
+f4f9fdc4cbd1a8b3b9aebcc59eb0ba93a9b489a1ad8aa6b287a1ae6f82915f6f7e66727e525c65
+b4bbc1fbfffffcfffffafcf9fbfffcf1f7f5f9fffff6ffffd3e0e8a2b3bdb3c5d3a1b7c59cb7c8
+86a3b57d97a8869dad5d707e54626d78828c535a62fafdfff8f9fbfffffdf8f4f1fbf6f2fef9f3
+fffef8fff9f1fffaf4fffbf5fcf8f5fffffdfcffffeef5fbdce9f1abbcc49badb99aacba9aacba
+8d9fad8d9dac8999a675839074818a646f73778284e9f3f4f8ffffcfdadec1ced4adbbc4aebecb
+9fb2c199aebf8ea4b294aab781939d86959c939ea24d585ad6e3e9f4ffffeaf7fdeffafff8ffff
+f2f9fff2f7fbf8fdfffbfffff5fafef9fffff8ffffeef9fdf0fefff2ffffe0f2f6b9cdd4a0b7bf
+a8bfc7abc2ca8ea4af80949f54687155677166747d818c92f9fffffcfffff6f6f6fffffdfffefb
+fefdfbf3fbfdbfced1aabcc0b6c9cfb0c2cc97abb68da3b096acb98da1ac7e909a6172796a787b
+465051bac3c2f7fbfafcfef9fffff8fafaf0fffff6fdfdf3fdfff4fefff8f9fcf5fbfffbdbe6e2
+afbdbdb6c8caa7bcc1a5bec59fbac38fabb786a0ad5b6d7b5b6976707d863e4750e9f0f6fcffff
+f8f9fbfefffffefffff4f5f7fbfffff9ffffdae4eda8b5bebdcbd6b4c2cfa6b3c3a4b1c19dabb8
+94a2af93a1ae94a5af8d9ea881939d81939d586c75617580657b88526977657c8a506a79566d7d
+566b7c546a7854677852657451637151637152626f52626f495762404e59525f685c697268727b
+c8d3d9e9f2fbf9fffffffffdfdfef9fafcfbfafffff7ffffa2b0b99cacb99fb2c09db3c18ea4b1
+8fa5b095a9b297a8b084969a6f7e817e898b7075789d9ea0fbfbfdf9f9fbfffffff3f3f3fcfefd
+fafeffdfe7eac0cbd1abbcc4afc2d0a4becd8daabc82a2b78facbe879ba66f7e854b5860455056
+475057acb1b7fcfffffefffffffefffffefffffefff7f5f6fdfdfffffffffcfcfcffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffdf6fdfcf7fdfffcfbfffff2fbffecf9ffb8c9d3b2c4d0b4c7d5b5c8d7a2b5c393a5b39babb8
+9fadb886939c6a777f667177636e72656e73edf5f7f9fdfef8faf9fffffdfbfaf6fffffbfffffb
+fffffbfffffbfffffffefffffbfffffafefffffffdf7f7f5fefefcfefffffbfcfefcfffffbffff
+eaf1f7bfc8d1acb6c0b2c0cba4b4c192a4b28fa2b0889baa93a6b4677882626f78727c854a5358
+c6cbcef9fdfefefffffefffdfbfffef8fefcf6fefff6ffffcad9e0abbcc4b7c9d5a6b9c89eb5c7
+879eb08499aa8a9dac5b6b7863717a525c65899297f8fdfff9fdfefbfdfcfcfcfafdfcfafffbfa
+fffafafffbf8fefbf6fdfef8fefffbfbfffff8fffff3ffffd6e8ecafc2c9a2b6bfa9bdc89fb1bd
+92a2af92a0ad96a2b0707988929aa5575e64a0a8aaebf3f6f7ffffd2dde1b6c1c7b3c0c9b4c2cd
+a4b4c19eaebb97a7b48e9eab97a8b28d9ba4909da56b7880bbc8d0c4d1d9bdc7d0c7d1dac5d0d6
+bdc6cdd0d9e0d1dae1d0d9e0d0d9e0d0d9e0d0dbe1d0dbe1d1dce2cfdce2cfdee5bbcfd8c0d6e1
+a1b8c090a4ad9bacb47e8d945d6a706d767b4f575aaaafb2fefffffefffff3f3f5fffffff8f6f9
+feffffeaf1f7c4cfd5b2bfc5b9c8cfb1bfc89daeb896a8b299abb596a7af7081885160656a7879
+323c3be6efecf9fffbfefffbfdfcf8fffffafffefafcfbf7fffffbfefffbfbfdfafafffed6dee0
+bec8cab6c3c9b5c4cba6b4bf9cacb998aab88d9daa6270796b767c636c7370777df7fcfff3f7f8
+fefffffcfefdfffffff9fafcfefffff9fcffd6dde5b8c1cab4bcc7b9c3cfa4b0bea7b3c1a1adb9
+99a6afa1aeb699a8ab8393937e8e8d6b7b7a6d807e71838365777b73868c6a7d846d7f896c7d87
+717e87717e86727c86727c85727c85727c85727c85717b847d868d6f787f707980656e755a636a
+d3dce1f7fefffafffffffffbfcfbf6fbfcf7fcfffff1f9fbafbdc0a0afb6a1b4bba2b6bf97abb4
+8fa3aa97aab09aacb0879797798787808c8a7d83838b8c8efcfdfffefffffafefffcfffff6fbfe
+eff7fad1dae1b6c0c9b4c1cab4c5cf9caebc92a5b495abb98ea1af87949c606970545d6450595e
+62696fecf1f5fcfffffdfefffefffffffffffffefffffffdfffefffffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fdfdf5fbfcf6fbfdfafbffffeffafee7f6fdb8c9d3b3c5d1b7c9d7b5c7d5a3b5c397a7b4a2aeba
+a2acb58790976b747b7a8388535c619da5a8f3f8fbfcfffffafcfbfffffdfffefafffffbfffffb
+fffffbfffffbfffffffefffffdfefffcfefdfffffdfbfaf8fefefcfefffffbfcfefbfffff8fdff
+e2ebf2c1cad3b1bec7b8c4d0a8b6c395a5b496a6b58fa1af96a6b3738089626d73717a814b5356
+e3e7eafcfffffefffffbfdfafefffffafffff6feffeffafec5d4d9adbec6baccd6abbecca0b3c4
+90a2b68e9faf7686955866736b7881465157b7c1c3f7fffff9fffffcfefdfcfefdfefcfdfefcfd
+fffcfdfefdfbfcfbf7fefffbfafffbf4fdfaf7fffff0ffffd0e3e7b0c5caa5b9c0a9bdc6a0b2be
+96a6b393a1ae959fab78808d979ea840454bc1c6c9fbffffeef6f9c5ced5b7c2c8bfc9d2b1bec7
+a7b3bf97a5b0a5b3be9fadb8a0aeb99dabb6909ea788959e9ea8b1aab4bdadb7c0b2bcc5afb9c2
+abb5beb5bfc8b7c1cab6c0c9b6c0c9b7c1cab9c3ccb9c3ccbac4cdbac4cdb6c4cdc1d5e0b5cbd6
+9fb3bc9baeb599a8af707b7f5b63667b8083474b4cd5d6d8fcfcfcfffffff8f8fafffffff9f8fd
+feffffe5ecf2c6cfd6bac4cdbfc9d2b4c1caa2b0b99eacb5a0b1b891a0a76e7b815b696c748080
+414b4aeff8f5f8fefafbfffcfdfcf8fffefbfffffdfdfcfafffffdfefffffafefff8fefed4d9dd
+bec5cbbec7cebdc7d0aab4bea4aeba9eaab888929e646f757c86884e5659a2a7aafcfffff8fcfd
+fefffffefffffffffffdfdfffefffff6f9fed6d9e0bcc0c9b8bfc9bcc4cfacb5c499a5b3a6b2be
+a3b0b8a3b1b493a1a17988856b7a7534433c51625a63726f5161604c5a5d46555a4f5c64546169
+5a636a5c63695c63695e656b5f666c60676d61686e62696f656c725a61676a717770777d777e84
+d4dbe1f1f8fefcfffffffffafcfcf4fcfdf8fcfffdf4fdfcb7c2c4a7b6bba8b9c0a7bac19db0b7
+94a7ad9caeb2a0afb28d9b9c7e8c8c86908f7c82828f9190fefffff9fdfef5fafdfbfffff1f8fe
+e2e9efc5ced5bbc5cebfc9d3b1bec7929eaa8e9ca998a6b3909ea97079804a5157555c6241484e
+e0e5e9f8fdfff8fcfffcfffffefffffffffffffffdfffffdfffffdfffffdfffefbfffffbfffeff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefdf8fcfdf8fafcf9fbfffff1fbfde3f0f6bdcbd4b8cad6bbcddbb5c7d5a5b5c49fadb8aab4be
+a3acb5838c936a7179868f94394247d9e1e4f5fafdfcfffffcfefdfdfcfafffffdfffffbfffffb
+fffffbfffffbfffffffefffffdfffefcfefdfffffffefcfdfffffffdfffefbfcfefbfffff3f8fe
+d9e2e9c2cbd4b9c6cfbcc8d4a8b6c197a7b49cacb996a8b492a2af6f7c856871786b727a5f6468
+fcfffffdfefffffffffcfefdfdfffefcfffff9feffe6eff4c0cdd3b2c3cbbed0dab0c3d199acbd
+97a8ba96a7b7657582586671717e86414c50e7f1f3f7fffff9fffffcfefdfcfefdfefcfdfefcfd
+fffcfdfefdfbfbfaf6fefffbf6fbf7eef7f4f7ffffedfcffcbdde1b4c9ceb0c4cbadc1c8a5b7c1
+9fb0ba94a2ad919ba7818996969da72a2f35d3d8dcfbffffdee6e9c2c9d1bbc4cbc7d0d9b4bec7
+a8b2bc9ca9b2a3b0b9afbcc5a5b2bba1aeb79aa6b29eabb4939da69fa9b2b1bbc4afb9c2aeb8c1
+b6c0c9b4bec7b6c0c9bdc7d0bfc9d2c1cbd4c3cdd6c4ced7c4ced7c3cdd6c0cdd6c1d3dda8bcc7
+9eb0baa8b9c197a4ac626d715860638085883e4243fbfdfcf9f9f9fbfbfbfdfffefcfefdf9fafc
+fcffffdbe2e8c6cfd6c0cad3c3cdd6b4c1c9a5b4bba4b3baa9b8bf8a979f6a777d6d787c727c7d
+6c7574fafffef9fffbfbfdfafdfdfbfffffdfffffdfffefcfffffffefffffbfffff4fafacfd4d8
+bec5cbc6cfd6c4cdd6abb5bfaab4c0a2abba7c8692616a71858d90363e41d7dcdffcfffffafbfd
+fdfdfdfdfdfdfefefffdfdfffefffff1f4f9d3d6ddc0c4cdc0c7d1c3cbd6adb7c399a3afb2bcc6
+a6b1b795a0a4828e8e6b77753b47432d3a33707f78a8b4b2bbc9c9bfcaccc7d4dac6d0d9c7d2d8
+c3cad0c5cacec6cbcfc8cdd1c9ced2cacfd3cbd0d4ccd1d5d6dbdfc8cdd1d5daded2d7dbd8dde1
+f9fefffbfffffcfffffffffbfcfcf4fcfdf8fcfffff6feffbec9cdb0bdc3afc0c7afc0c8a4b5bd
+9cadb4a0b2b6a5b4b794a2a38792948d9798818787929697fcfffff9fdfef6fbfefbffffedf4fa
+d5dce2c1cad1bfc9d2bfc9d3a9b6bf939fab98a6b19eacb78c99a250596071787e3e43477e8387
+fbffffeef3f7fcfffffafefffefffffffffffffffdfffffdfffffdfffffdfffefbfffffbfffeff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffffbfefffafbfdfcfbfffff4feffdfeaf0c2d0d9c2d0dbbecedbb5c5d2a7b5c2a6b2beb0b9c2
+a2a9b18188906a717781888e30383bf7fcfff9fdfefafcfbfbfdfcfdfdfbfffffdfffffbfffffb
+fffffbfffffdfffffdfffffffdfffefcfefdfefefefffefffffffffcfcfefefffffcffffeff4f8
+d5dce2c2cbd2c1cbd4bcc9d2a7b5be9aa8b39fb0ba9bacb68b99a45f69727a81895a5f658e9196
+fefffff4f5f7fafafafffffffbfbfbfcfffffaffffdde6ebbdcad0bbc9d2bfd1dbb2c4d096a6b6
+9dadbd95a5b46775825f6c756f7982576065f9fffff9fefffafefffcfefdfdfdfdfefcfdfefcfd
+fffcfdfefcfdf9f9f7fefffdf6faf9eef7f6f7ffffebf9fcc6d8dcb8cbd1bacdd4b0c3caa7b8c0
+a6b4bd939fab86939c86909a888f993d4248e0e3e8f8fbffd9dee2d1d6dcbcc3cbc5ccd4b9c2c9
+a9b2bbacb6bf9ba5afb5bfc9a0adb696a3ac9ba7b393a0a9a8b2bbadb7c0c0cad3b5bfc8b6c0c9
+c6d0d9bac4cdbbc5ceb9c3ccbbc5cebec8d1c0cad3c1cbd4c1cbd4bfc9d2bcc9d1adbec893a5af
+8e9fa792a1a879848a545d62595e62767a7b565857fefffff8f8f8fdfdfdfefffffefffffafbfd
+f8fcffd1d8dec6cfd6c6cfd8c4ced7b4bec7a8b5bdaab7bfacb9bf8590966974787d8789586062
+adb3b3fbfffefcfffdfefffdfefefcfefefcfffffdfffffdfefffffefffffbffffeef2f3cfd4d7
+c2cacdcdd4dac7d0d7adb5c0aeb8c4a4aeba757d88656c7283888b3f4447f4f8fbf9fafcfcfdff
+f9f9f9fcfcfcfbfbfdfdfdfffcfdffecedf2d0d3dac6cad3caced9c8cfd9a6aebba7b1bdb8c1ca
+a0a9b0858f917680816f7978202b27ccd7d1f0fbf5f8fffff8fffff5fffff8fffff2fbfff3faff
+fbfffffbfffffbfefffafffffafdfff9fefff9fcfff8fdfffcfffff5fafdfcfffff0f5f8fcffff
+f9fefff1f4f9f4f5f7fffffbfdfcf7fdfdfbfcfffff7ffffc4cdd2b7c4cab5c4cbb6c4cdacbac3
+a3b2b9a6b5bca9b6bc9ba6aa8e979c949c9f858a8d909495fcfffffbfffff7fcfff7fcffe3eaf0
+ced5dbd0d9e0c1cad1b2bbc4a5afb9a2acb6a6b2be97a3af7380893e474e636a7044494debf0f4
+f2f7fbf9fefffcfffff8f9fbfffffffffffffffffdfffffdfffffdfffffdfffefbfffffbfffeff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fffffdfefefcfbfdfcfcfffff6feffd8e1e6c7d1dac6d3dcc2cfd8b5c2cba9b3bdadb6bfb4bbc3
+a1a6ac80858b707579767b7f4c5154f8fdfffcfffff8faf9fbfdfcfffffdfcfcfafffffbfffffb
+fffffdfffffdfffffdfffffdfdfffefcfefdfdfdfffffffffffefffcfbfffefffffcffffebf0f4
+d4dbe1c5ced3c6d1d7bcc7cda8b5bd9eacb5a3b4bc9daeb6818f985960688c8f9645484fc7c8cd
+fcfdfff7f7f9fcfafbfffffff8f8fafefffff9fdffd8dfe5c1ccd2c4d1dac0ced9b1c1ce9dabb8
+a4b2bf8896a16e7b84667079606970889196f9fffff9fefffafefffcfefdfdfdfdfefcfdfefcfd
+fffcfdfefcfdf8f8f8fefffff9fdfcf2fbfaf8ffffe9f4f8c6d5dabdcfd3bfd0d7afc0c7a4b5bc
+a7b6bd909da6808d958d97a07079806e7379f7fafffafdffdde0e5d7dadfbdc2c8c3c8cebcc1c7
+b1b5beb0b7bfa9b0baafb8c197a1ab959fa987919d7b858f7e87907a838a828b92717a81727b82
+828b92737c83717a817a838a7b848b7e878e818a91828b92818a917f888f7d87906e7b8465737c
+64717a606a73525b62565e61686d70727677959796fcfefdfafcfbfefffff6f8f7fefffffafefd
+f4f8f9ccd3d9c9d0d8cbd2dac4cdd4b4bdc4adb8beafbac0acb7bd7e878e6c737981898c404548
+e8ecedf5f7f6fefffdfefffdfffffffbfdfcfffffffefffffcfefdfefffffafefde6eaebd2d6d7
+cacfd2d1d6dac4cbd1adb4beb0b8c3a2aab5727b84767b81777b7e6f7376fcfdfff7f8faffffff
+fdfdfffffffffcfcfefffffffafbffe5e6ebcccfd6c9ced4cdd1dac3cad4a4abb5b3bcc5b3bac2
+9fa6ac889093747c7e7c8584464f4cf9fffdf9fffdeaf3f0ecf5f4eaf0f0f8fdfff5fafefbffff
+fcfffffcfffffefffffafefffbfcfff7fbfcf8f9fdf6fafbf8f9fdf7fbfcfeffffedf1f2feffff
+fcfffffefffffefffffffffdfffefafdfdfbfbfffff8fdffc8cfd5bdc8ceb7c4ccb9c6cfb1bec7
+a7b4bdaab4bdaeb7bea1a8ae949ba1999ea2888c8f888c8df3f9f9fbfffff7fcffeaeff2d7dfe2
+ced5dbdbe2eabec7cea8b1baa6afb8aab3bca0aab47d879158626b72797f404549999ea2fbffff
+edf1f4fcfffff4f5f9fefffffffffffffffffffffdfffffdfffffdfffffdfffefbfffffbfffeff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefefcfdfdfdf9fafcfafefff7fcffced6d9c7d0d5c9d2d9c5ced7b5bec5aab1b9b1b8beb6bbbf
+9c9fa4818286787c7f707477818586f8fcfdfcfffffefffffbfdfcfffffdfbfbf9fffffbfffffb
+fffffdfffffdfffffdfffffdfdfffefcfefdfdfdfffffefffdfcfffcfbfffffefffbfcffe4e8eb
+d7dcdfcbd3d6cad4d6b9c2c7acb7bba6b3b9a8b7bc9eadb477848a696e748c8d924a4b50efeef3
+fafafcfffefffffdfefcfafbf8f6f9fcfcfef5f9fcd5dadec6cfd6ccd6dfbdcad3b1bec7a8b2be
+aeb8c27a848e6e78816e777e52595fc2cacdf8fdfffbfffffdfefffefefefefefefffdfefffdfe
+fffdfefffdfef9f9f9fefffffcfffff9fffff8ffffe3edefcad5d9c4d2d5c6d5dab2c1c6a7b6bb
+acb9bf94a1a784919798a3a9596267979ca0fefffffeffffd8d9ddcacbcfc0c1c5c8c9cdbbbcc0
+b6b9beabaeb5bdc1caa5acb689929b8c96a06c7680717984838c937f888d80878f757e83798088
+838c9180878f7d868b787f877881867b828a7b84897d848c7b84897b828a79828967717a727c85
+78818a6d767d656c7271767a73777a636768dbdddcf7f9f8fefffffefffff0f2f1fefffffcffff
+eef2f3ced3d7ced3d9ced3d9c3c8ceb3bac0b2b9bfb2b9bfa7aeb4797e8471767a7a7e814c5051
+fefffff0f2f1fdfdfbfffffffefffff9fafcfefffffefffffafcfbfefffffafcf9dce1ddd4d6d5
+d1d5d6ced2d5bec3c7b0b4bdaeb5bd9aa1ab72798182858a666769a8a9abf8f9fbf8f8faffffff
+fffffffffffffffffffffffff9fafedfe0e5cbccd1cdd0d7cccfd6b9bdc6b1b5beb9bdc6acb1b7
+9ea3a78f94977377786a6e6d8d9190f0f5f1f6fbf7f5f9f8fcfffffcfffffafefff4f5f9fdfeff
+fcfdfffcfefdfdfdfffbfdfcfcfcfefafcfbfbfbfdfafcfbfafafcfdfffeffffffeceeedfcfcfe
+f8faf9fefefff8f8f8fffefcfffffdfdfdfdfbfcfef8fcffced3d9c2cbd2bcc5cebdc7d0b5bfc8
+abb3beabb4bdb1b5bea5aab09a9da49da0a58a8e8f888c8df6fafbfbfffff5fbfbe1e6e9d4d9dc
+d3dbdeced3d9b8bfc7abb2baadb6bfaab1bb8f98a16d767f58616a63686e6c7175ecf1f5f0f5f8
+f0f4f7f7fbfefefffffafbfdfffffffffffffffffdfffffdfffffdfffffdfffefbfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fdfdfdfcfcfcf9fafcfcfdfff9fdfecaced1c9ced2cbd0d4c8cbd0b7babfacafb4b5b9bcb7b8ba
+999a9c8181838282846b6c6eb0b2b1fefffffcfefdfffffff8f8f6fffffdfefefcfffffdfffffd
+fffffdfffffdfffffdfffffdfefefcfdfdfdfffdfffffefffcfafffdfbfffffefff2f2f4dddddf
+d9dadcd2d6d7ccd2d2b3bbbdadb7b8adb8baa9b7ba9ba9ac6e797d7d808579787e717076fffeff
+f9f7fafffdfffffdfefaf8f9fbf9faf8f8faf1f2f6d1d4d9cbd0d6d1d8e0bbc4cbb2bcc5a8b1b8
+b3bcc3737c836970767d848a52575beaeff3fbfffffbfffffdfefffefefefefefefefefefefefe
+fffdfefffdfefcfcfcfdfffefcfffffbfffff0f5f8dbe3e5cdd7d9cbd5d7cdd8dcb7c2c6a9b4b8
+adb8bc95a0a48590949aa3a83b4346c6c9cefafbfff4f3f8d0ced1c1bfc2c7c5c8cbc9ccb6b4b7
+b1b0b5b2b1b6afb0b59a9da4767d855c656e7078837b848d747b81798184787d837b83867f848a
+7e8689898e94878f92878c92858d90888d93868e91888d93868e91878c92858c927b8288858c92
+868b91797e8272777b74787b6c7071616264fcfefdf8faf9fdfffcfcfefbf8faf7fafcf9fefffd
+e3e5e4ced2d5cfd2d7cbced3bcbfc4b0b3b8b4b7bcb0b3b89ea1a675787d7a7e816d6e72848587
+fefefef7f7f7fcfafbfefefefefffff6fafbfcfffffcfffff7f9f8fefffdf8faf5d6d8d3d1d1cf
+d5d7d4cacccbb7bbbcb3b6bbabb0b690949d74777e86878b575759cfcfd1fbfbfbfdfbfcf5f3f4
+fefcfdfffefffcfcfefffffff5f5f7d9dadecacbcfd3d4d9cecfd4b2b5bab7babfb7babfb0b4b7
+8d9192888c8d7d7f7e494b4acdcfccf9fbf8fefffdfefffdf7f9f6fefffff8faf9fafafcffffff
+fffffffffffdfffefffffffdfffefffffffdfffefffffffdfefcfdfefefcfffdfef9f9f7fffeff
+fcfcfafffefffffefcfefdfbfffffdfdfdfdfafbfdf9fdffd2d7ddcaced7c1c8d0c0c7cfb9c0c8
+b0b4bfaeb2bbb4b4bca9aaaf9f9ea4a2a1a6898b8a8e908ffdfffefcffffedf1f0dbdfe0d3d7da
+d5dadebcbfc4b7bcc2b3b8beafb6bea4a8b1818890636a725c6069353a3eaeb2b5fcfffffcffff
+fefffffefffffefffff3f4f6fffffffffffffffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefefefefefefafcfbfefffffefefecbcbcbcdced0cfd0d2cacaccb8b8b8aeaeaeb8b8b6b7b6b4
+98979582817f888785646462c6c8c5fffffff0f0eefffffdf4f4f2fcfcfafffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfefefcfdfdfdfffefffffefffbf9fcfdfbfefffeffeceaebd6d5d3
+d9d9d7d4d6d3cacfcbafb3b2adb6b3afb9baa9b5b598a4a466707188898e6463689d9ba0fffeff
+f8f3f7f7f3f4fefafbfffcfdfffcfdf7f5f6eeeef0d1d1d3cfd0d4d5d8ddbbc0c4b5babea3a8ac
+b6bbbf777c80676c708f9396585c5ffbfffffefffffdfefffdfefffefefefefefefefefefefefe
+fffdfefefefefefefefafcfbfefffffeffffe9edeed8dcddd2d8d8d1d7d7ccd4d6b5bdbfa3adae
+a6b0b18c96977b85868d97991b2325f8fcfff7f7f9e6e4e7d1cdcec7c3c2cec8c8c7c1c1aea8a8
+a9a3a3c4c0c18d8b8e919297696c73232a32868d9789909a868b9192979b8d92969a9fa3969b9f
+8b90949ba0a49a9fa3999ea2999ea2999ea2989da1989da1989da1979ca0979ca09a9da29a9da2
+94979c969a9dabacb0c3c4c6dbdcdef1f3f2fbfdfcf8faf7fafcf9fcfefbfefffdf7f9f4fefffb
+d6d8d3cfcfcfcfcfd1c8c8cab7b7b9adadafb4b4b6aeaeb097979978787a848486656567bababa
+efefeffffffffdfbfcfbfbfdfefffff5f9fcfcfffffcfffff6f8f5fffffbf8f9f3d4d4cccfcec9
+d7d6d1c7c8c3b5b5b3b6b7b9aaabaf8a8b90747579878789515153e2e2e2fffffffffeffedebec
+fffdfefefcfdf7f7f7fbfbfbf1f1f1d6d6d6cbcbcdd9d9dbd2d2d4b3b3b5b2b2b4b5b5b5bdbdbd
+7d7d7d8181819494923c3c3af8f8f6fbfaf8fffffdfffffdf1f1effffffffffffffffffffdfdfd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfcfbf9fdfcfafffffdfffffd
+fcfbf9fffffdfffefcfdfcfafffefffdfdfffafafcfcfdffd6d9decfd2d9c7cad1c4c7cebdc0c7
+b3b3bbb1b2b7b6b4b9adabaea49fa3a4a2a381817f8e8f8afefefcf7f7f5e1e3e0d4d6d5cfd0d2
+d0d1d3babbbfbbbec3b6b9beabaeb5989ba27478815659624c4f5695989dd9dde0fcfffffcfdff
+f6f7f9f4f5f7f6f7f9fefffffffffffffffffffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fcfefdfbfdfcfdfdfdfffffdfbfaf8cbcac8c7c3c0c9c5c2c6c1beb4afabaaa5a1b3aea8aea9a3
+8e8983817c78928f8a53524ed8d9d4f4f3f1fefefcfffffdf9f9f7f8f8f6fffffdfefefcfefefc
+fffffdfffffdfffffdfffffdfffffdfffffdfefcfdfffefffefafbfffdfcfffdfae6e2dfd1cec9
+d9d6d1d6d5d0bdbeb8b4b5b0bcc1bbb2b8b6a0a9a68a94935f68678d8e924a484dd6d1d7fffcff
+fff9fdfef8fafffcfefffdfdfffdfdfaf6f5d5d1d0d2cecdd0cecfd0d0d0b4b4b4b2b3b5b9babc
+9a9b9d7b7c7e78797b7172748c8d8ff7f8faffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffdfcfcfafffffdfafaf8dadad8cdcdcbd7d9d8dddfdea1a5a4cbcfceb4baba
+9da3a3959e9d959e9d747c7e676d6dfafbfdfefcffe6e2e3cbc5c5cbc3c1bcb2b0ccc3beb4aba6
+afa6a1a59d9a8781818c8a8d6e6f747b7e85ebf2fcf5fcfffafffffbfffffbfffffbfffffbffff
+fbfffffbfffffbfffffbfffffbfffffbfffffafefffafefffafefffafefff9fdfffbfcfffffeff
+fffefffefffffcfcfefbfcfefafbfdf9fbfafdfffefefffdfffffdfdfef9fdfef9fffffaf4f5ef
+d6d7d1cccbc7cecac7c7c3c0b3afacaaa6a3b3afacaeaaa9959190736f6e8884834b4746e9e7e8
+fffefffbf9fafefcfff8f8faf9fdfff7fcfff4f8f9fefffffcfdf8fffffae2dfd6d1cec5d1cdc4
+c9c5bcc2bdb7b1aea9b2b1afa4a2a379777a767477878586666465f7f5f6fdfbfcfffefff9f7f8
+fefdfbfffffdf9f8f6fffffde6e5e3ccc8c7d9d5d2dbd7d4c0bcb9afaba8b6b2afa4a09daaa9a5
+79787471706c858482484745fffffdfffdfcfffefcfffefcfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffefcfffffdfffffdfffffdfffffd
+fffffdfffefcfefdfbfbfbfbfffffffefefffefefffcfdffe3e4e8cacbd0cccdd2c9c8cdbfbec3
+b7b5b8b5b3b6b6b0b2b0aaaaaba2a3a5a09d94918c85857dfffff8e6e5e0d3d2cddddcd8c4c4c2
+c8c8c6aeaeaeb2b3b5bcbdc1a1a2a777787d70737a6b6b735c5d62c0c1c5fcfdfffeffffececec
+fffffffffffffafafafffffffffffffffffffffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fdfffefdfffefdfdfbfcfbf7f8f5f0cac5c1c5beb8c6bfb7c4bbb4b1a89fa89f96b3aaa1aea59c
+8d867c837c7497938a484540f1f0ecfffffbfbfaf6fdfcfafffffdfffffdfcfcfafefefcfefefc
+fffffdfffffdfffffdfffffdfffffdfffefcfefaf9fffcfcfffcf9fff8f5f5edeaded7d1d0c9c1
+d3ccc4cfc8c0bdb9b0b9b6afb7b8b2b4b6b19ba29b7a807c757b796f6f71636166faf5f9fffdff
+fdf7f9fffdfdf2ececfdf8f5fffefbf4ece9cbc3c0c6bebbc8c3bfcdc8c4b4afabb0aca9b3b2b0
+969593787775878684565553bcbcbcfffefffdfdffffffffffffffffffffffffffffffffffffff
+fffffffffffdfdfef9fcfbf6fffef9f3f0ebd5d2cdcdcac5d6d2cfd4d3cfafaeaac0c1bcafb1ae
+a5a7a48a8e8d9399974f5555b9bdbefefefefffeffdbd5d5c3bbb8c2b9b4b6aca3bfb1a8ac9e95
+b3a69eaa9f99877d7b9b9595605e61a0a1a6f4f9fffbfffffcfffffcfffffcfffffcfffffcffff
+fcfffffcfffffcfffffafefffafefffafefffafefff9fdfff9fdfff9fdfffbfcfefefcfffffeff
+fefcfdfffffffafafcfdfdfdf9fbfafdfffefcfefbfefffdfffffbfdfef9fefff9fffffaeeede8
+d2cfc8cac3bdcbc4bcc2b9b2ada69ea7a098afa8a2a49d97867e7b6e6663827d7a5b5653eae6e5
+fdfbfcfffdfefffdfffefefff8fcfff8fefef9fdfcfbfdfafefdf8fffff6d7d3c8cdc7bbcdc5ba
+c2baafb7aea5aca39aaaa39d9d9894706b687c7877767273918f90fffefffefcfdfffefffbfaf8
+fffefcfefdfbfdfcf8fffbf8dfdcd7c7c2bcd5cec8d2cbc3b8b1a9aaa39bbdb6aca29b93afaba2
+79746e77726e7f7a7776726ffffefdfffdfcfffdfcfffefcfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffefcfffffdfffffdfffffdfffffd
+fffffdfffefcfefcfdfbfbfdfffffffefefffdfdfffdfdffe2e2e4c8c8cacac8c9c6c4c5bebab9
+b6b1aeb3aeaab4aca9afa6a1aa9f99a59c959d968c888478f6f0e4d1cdc2d7d3cacbc6c0bfbcb7
+bab6b3b4b3b1aba9aac0bec1908f946d6c715f60654b4a529e9fa4ebebedfffffffffffff3f3f3
+fffffffffffff7f7f7fffffffffffffffffffffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fdfffefefffdfefffaf8f7f2f2ede7c5beb4bfb7acbeb4a8c0b4a6ab9f91a39789b0a496a9a091
+89807183796d9e968b3c382ffffffafffffaf9f8f3fbfaf6fffffbfffffdfcfcfafdfffefdfffe
+fffffffffffffffffdfffffdfffefafffefafffaf7fffaf7fffdf7f9f0e9e0d5cfd0c6bdcec1b8
+c8beb2bbb1a5b8b0a5bbb4aab6b3aab4b4ac8e918a646661797b78555557a09fa4fffefffdfbfe
+f2eeeffffdfdf6f1eefff9f6fffef8e9e0dbc0b7b0bfb6afc9bfb6cac0b7b3a9a0aea59eb2ada7
+88858074706d96928f474340edeceafffefffbf9fafffefffffefffffffffffffffefffffeffff
+fefffffffffdfffff7fffff6fefaf1e4e0d7cbc4bcc8c1b9cbc4bcc1bab2c2bbb5b5b0aa9c9994
+9596917f817e878c88343837ebedecf8f7f5fefaf7c6bebbb5aca5bcafa6b7a99eb4a296ab998b
+af9f92a4968b7c7269a19a94454140cacaccf6f9fefbfffffbfffffdfefffdfefffdfefffdfeff
+fdfefffdfefffdfefffcfdfffcfdfffcfdfffcfdfffbfcfefbfcfefbfcfefbfcfefefdfbfffefc
+f7f6f4fffffdf7f7f7fcfcfaf7f7f5fffffdfdfdfbfefefcfffefafffefafffffafcf9f2e3e0d9
+cac3bbc5bbb1c8bcb0bbada2a79b8fa69a8eac9f96958b8172685f7d746d7c756f8c8784f5f1f0
+fcfafbfffffffbfbfdfdfefff6fafdfafefffefffff6f7f2fffef7fffff4c8c0b3c6bdaecec2b2
+bcb0a0b0a496aa9e92a399909a8f8969605b8b8380575352c6c5c3fffffdfcfcfafffffdfafaf8
+fffffdfaf9f5fffffaf4f1ead5cec6c5bcb3cfc5bbc7bdb1b1a597a89f90b5ac9d999282a29a8d
+6f675c7c756d645d57b0aba8fff9f9fffdfcfffdfefffdfefefefefffffdfefffdfffffdfffffd
+fffefffffefffffefffffefffffefffffefffffefffffefffffdfefffefffffefffffefffffeff
+fffdfefffdfefefcfdfcfcfefefffffdfdfdfdfdfdfcfcfce2e1dfc5c4c2c5c1bec3bebabbb4ae
+b2a9a2b0a89dafa59baba195a79b8da4988aa59989968d7cd5c9b9c1b8a9cbc3b8bcb3aab0a9a1
+ada6a0b3aeaaaea9a6a4a0a185838665636851505557545be6e5eafffefffffffdfaf9f7f9f8f6
+fffefcfcfbf9f9f8f6fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fbfdfcfffffdfffffaf9f6eff1eae0c0b8abbaaea0b7aa99bdae9ba89984a0917eac9f8ca79a87
+867b698276669e95863c352bfffcf6fef9f5fefbf6fefdf9fdfcf8f9f9f7fffffdfdfffefdfffe
+fffffffffffffffffdfefefcfffefafffdf8fffdf7fffaf3fffaf1eee4dacdc1b5c4b6a9cbbbac
+c3b3a4b0a393b3a799b2a89cb2aca0a7a39a7f7f7766675f71726d535355e4e4e6faf8fbfaf8f9
+f5f1f0f5f1eefffdfafff9f3fffdf6d9cfc6b8aba2c2b6aacdbfb2bfb2a2ab9e8eaa9e90afa69d
+7a736b77706a8e89855f5a56fffefbfdf9f8fffdfefffefffffefffffffffffffffefffffeffff
+fefffffffffbfffef5fffff3f6f0e4d5cdc2c1b7adc4bab0c5b8afb7aaa1beb4abbbb2ab9c958f
+8b8883868580696b66686a67fdfffcf7f6f2fcf7f3b6afa7aca298b7a99cb9a898ac9988ac9786
+a794859684767a6c619e958c332e2ae9e9e7f7fbfcf9fdfffcfdfffcfdfffcfdfffcfdfffcfdff
+fcfdfffcfdfffcfdfffdfefffdfefffdfefffdfefffdfefffcfdfffcfdfffdfdfdfffefafffbf8
+f4f3effffffdf7f7f5fcfcfaf4f4f2fffffdfefefcfffffdfffefafffffafffff8f7f3ead7d3ca
+c2baafc3b6a6c6b5a3b5a492a59484aa9a8aab9b8c8b7d70675b4f8d83795d544dc8c0bdfffdfa
+fcfafbfefffffafbfffbfffff3f7faf9fdfefffffdf3f2edfffef5fdf5e8beb2a2c4b7a6ccbdaa
+b8a996ac9c8cad9d8e9f918493857a685b538e857e403c39e9e9e7fffffdf9f9f7fffffdf7f8f3
+fffefafdfcf7fffff8e9e5dac9c1b6c2b9aaccc0b0c1b4a3b1a28fafa08ba398829e937f877c6a
+675e4f867c72453c35e0d8d5fffaf7fffdfcfffdfefefefefdfffefdfffefbfffcfbfffcfdfffc
+fffffffffffffffefffffffffffefffffffffffefffffffffffefffffffffffefffffffffffdfe
+fefefefefcfdfdfdfdfcfdfffefffffcfcfcfcfcfafdfcfae2dedbc2bfb8bfbbb2bfb8aeb5ada0
+ada495aca090ada08fa99c89a79883a59681a79883aa9b86b0a18cc0b19eb7aa9ab8ac9ea0968a
+aaa097ada49da8a09d827c7c6c6869706b6f47454aafacb3fffefffdfbfcf5f4f2f6f5f3fefdfb
+fffefcfcfbf9fcfbf9fefdfbfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+f9fbf8fefffafffff8fefaeff3ebdebdb4a3b4a794b5a490bdab93aa997fa19178ad9d84a79881
+887c648377619c917f4a4237fbf4ecfffbf5fffdf8fffefafbfaf6f9f9f7fefffffdfffefdfffe
+fefffffefffffefefcfefefcfefdf9fffcf7fffef4fff9effef2e6e5d7cac5b5a6c0af9fc7b4a3
+beac98ac9b89ad9d8da79a8aaba29391897e736f6476736c61605b787878fefffff8f8fafffeff
+fffefdfaf6f3fffef8fcf3ecfffcf2ccc0b4b0a295c2b2a3c9b8a6ae9c88a3917dac9b899d9183
+756d62837a716b645c948d87fffefaf9f3f3fffefdfffefffffefffffffffffffffefffffeffff
+fefffffffffbfdf9edfff8e8e8e1cfc8bfaebfb3a3c5b8a8c3b3a4b7a79a9f9186b0a39a948a81
+7f7870827f7a33322ebabab8fffffdfffef9fef9f3b7aea5afa397baaa9ab8a692aa947faa917b
+a9937e8f7a678272639d9185332f26fdfcf8fdfffef7fbfcfdfefffefefefefefefefefefefefe
+fefefefefefefefefefffffffffffffffffffffffffffffffefefefefefefefefcfffffafffef9
+fbf8f3fffffbfbfcf7fdfef9f7f7f5fdfdfbfffffdfffffdfffdfafffffafffef8f1eae2cdc4bd
+c0b4a8c2b19dc3af97b19d85a8937eb29d88a99783857462675748877b6d362c22eee7dffffdfa
+f8f8f8fbfcfefcfffffcfffff2f6f9f7fbfafffffbf6f3ecfffef2efe6d7baad9ac7b6a2c4b29c
+b2a08aa89682ad9a899a89798674666a594f80736b4f4c47fbfdfaf6f8f5f9fbf8fefffbf4f5f0
+fbfaf5fffff8fffff4e0dacec2b9aac2b7a5c9baa5bbab94af9e84b2a1879f9176b1a58b756953
+7166548e85763d342bf9f2ecfffefbfffdfcfffdfefefefffdfffefbfffefafffefafffcfafffc
+fefffffefffffffffffefffffffffffefffffffffffefffffffffffefffffefefffdfefffdfdff
+fcfdfffdfdfffcfdfffcfdfffefffffbfbfbfcfcfafefdf8e2dfd8bfbbb0bab4a6bdb4a5b4a997
+ab9e8ba99d85ac9e84aa9c81a99a7da9987ca9957ab5a188a7937bbaa892b09f8bb1a28fa29585
+aea296a79d94938a85776f6d514b4b645f63605b61fffcfffcf7fdfefcfdfbfaf8fffffdfffffd
+fcfbf9fdfcfafffffdfbfaf8fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fafcf7fafbf5fdfdf5fffdf1f6efdfbcb19db3a48db9a88ec0ac91b29f81aa9779b09f83ac9c82
+93856b8a7c62988d7963594defe6dffffef8fffbf5fdfaf5fefffafffffff9fbfafdfffefdfffe
+fefffffdfffefefefcfdfdfbfdfcf8fffaf4fffef3fff5e7f2e5d5ddcdbdc8b7a5c3b19dc2ad98
+b9a48fa6917ca4927eab9b8baa9d8d81776b665e53635f564a4742c2c2c2fefffffffffff9f9f7
+fdfcfafffffaf7f2ecfffcf3fff9edccbeb1bbaa9ac4b29ec5b09ba7917aab937bb6a18c847767
+7c72668e847a4a4138c8bfb8fffdfafffaf8fffcfcfffdfefffdfefefefffefefffdfffefdfffe
+fdfffefefff9fffef0fcf4e1ddd5c2c5baa6c4b5a2c1b09eb5a492af9e8ea391839e8e817b6d64
+72685f766f693c3934f2f1edf9f8f4fffdf6f7f3e8bbb3a8b6a999c2b19fbba78fb69e86b2987f
+b0967f8f79628a77669a8e803b342afffffafdfffcf9fbfaffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefefefefefefffefcfffffafffef7
+fffef9fefdf8fffffbfefffafefefcfdfdfbfefdfbfffffbfffdf8fffef8fffef8e8dfd8c4bab1
+c0b2a5c5b199c3ab8fb1997fad957bb9a189a58f78806b566e5d4b7c6f5f463c30fffbf3fffef9
+f8f8f8f6fafdfbfffffbfffff5f9faf9fbfafffffbfbf8effffbeeddd2c0beaf9acbb9a1c3af96
+b6a088ae9881b09b869a85747865567865576a5c51827f7afefffdf4f6f3fdfffafdfffaf4f7f0
+f8f8f0fffff6fefaeed8d2c2beb6a3c2b6a0c3b39ab4a387ad987bb09d7da59476bbad9074664c
+8478628478685b5147fff6f1fffdfafffdfcfefefefdfefffbfffff9fffff7fffdf7fffdf7fffd
+fbfffffdfefffdfefffdfefffdfefffdfefffdfefffdfefffefffffdfefffdfefffcfdfffcfdff
+fcfdfffcfdfffdfefffdfefffefffffafaf8fcfdf8fffef7e3dfd4bfb9adb6af9dbdb29eb3a78f
+ab9d83ab9c7fad9e7fae9e7cad9d7bb09d7cb19a7bb59c7eb59d83ae9880b8a48ca6947eae9d8b
+af9f929a8d84867b75655b595a5152474143b2adb1fffdfffdf8fefdfbfcfffefcfffffdfffefc
+f8f7f5fdfcfafffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfefefe
+fefefefefefefefefefefefefefefefefefefefefeffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefffbf7f8f2f9f9effffff3f9f0dfbaae98b4a48bc4b095c4af92bba687b39e7fb6a383b4a385
+a1927594866b988c767a7064cdc4bbfffcf4fffbf5fdfaf5f9faf5fffffff9fbfafdfefffdfeff
+fefffffdfffefefefcfefdfbfffbf8fffaf4fffbeffcefdee7d8c5d7c6b2d3c1abcbb79fbba58e
+b39d86ad9780a18c77b3a29097877770645660564c514a426a6762f6f6f6f8fcfdfefffff6f6f4
+f4f3f1fffffaf8f4ebfffef3f5e9dbd2c2b3cfbcabccb7a2c6ae96ae947bb79c81b8a08881725f
+85796b897d71483e34efe6dffff9f6fffdfbfff9f9fffdfefffdfefefefffefefffdfffefdfffe
+fbfffefefff9fffdedf2ead3d5cab4c8bca6cdbca8beac98ac9784ab9685b8a29497857776655b
+7568606259549a9591fffffbfcfbf6faf7eeede7dbc2b8acbcaf9ecab8a2bca68ec5ab92baa085
+b79d849781698977639c8f7e3d352afffef7f8faf5fdfffcfffffdfffffdfffffdfffffdfffffd
+fffffdfffffdfffffdfffefcfffefcfefdfbfefdfbfefdfbfefdfbfdfcfafdfcf8fffdf6fcf9f0
+fffff8eeede8fdfef9f9faf5fffffdfcfcfafcfbf7fffffbfefbf6fffcf6fffcf7e0d5cfbeb1a9
+c2b2a3cab49cc6ad8eb39a7cb59c7ebfa58aa28a707c6850796753756555908476fffef4fffffa
+fafcfbf5f9fcf9fffff5fdfffbfffffafcf9fcfbf6fffff6fff8e9c9bca9c5b59ed0bca3cab298
+c0a88eb8a086b69e869d8470715a488e776758483bbcb9b2fcfffdf4f9f3fbfffaf8faf5fbfef7
+fafaf2fffff4f5f2e3d2cbb9bfb4a0c4b59ec0af93b49f80b09979b29e7bb19e7dad9e7d807154
+978871685d4b978d81fff8f1fffaf7fffdfefefefffdfefffafefff7fffff6fffdf5fffcf6fffc
+fbfffffdfefffdfefffdfefffdfefffdfefffdfefffdfefffefffffdfefffcfdfffbfcfefbfcfe
+fbfcfefcfdfffdfefffbfffffefffffafaf8fdfef8fffff6e4e0d4beb8aab3ac99bdb29cb3a78d
+ac9f7fac9e7bb0a07cb4a27cb5a37db7a37eb99f7eb89b7bc0a588af957ab9a187aa947db2a08c
+a99789887a6f73665e5b524d574f4d7d7779fdf7fbf1ecf2fffdfffcf8f7fefaf7fffbf8fefaf7
+fdf9f6fffbf8fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfefefe
+fefefefefefefefefefefefefefefefefefefefefeffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefffbf6f7f1f7f7edfffff3f9f0dfb9ad97b7a58dccb79ac7b091c2ab8bbba482bda684bda988
+af9b7a9f8c6c9c8c728c7f6fa89e94f3e9e0fffbf4fffdf7f1eee9fffefcfffffdfefefefefefe
+fefefefefefcfefdfbfffbf8fffaf7fff9f2fff6e5fbebd4e1cfb7d8c4abdfcaafd0bba0b8a086
+b0987ec5ad95a49078b3a18d7565555b4d40665c525e554ebcb7b3f9f9f7f3f5f4f7f9f8feffff
+fcfcfaf7f6f1fffef5fcf4e7dfd2c1d1c0acdecab2d3bba1c7ae90b3987abba180ae967a8e7d69
+867a6a7a6e60594f45fffdf6faf2effff8f9fffafcfffdfefffdfefefefffefefefefefcfdfffc
+fdfffafffff5f7efdae7d9becdbda3cdbca2d9c5acc4b097ae9881b29c879b867586736578675d
+70635b382f2ae0dbd7f7f6f2fffffafffbf0ece4d7cdc1b1c4b5a2d0baa3baa085caac90b99c7e
+c3a589a68c718d7760a69581433729fffef4f8f8f0fffffafffbf8fffbf8fffbf8fffbf8fffbf8
+fffbf8fffbf8fffbf8fffbf8fffbf8fffbf8fffbf8fefaf7fefaf7fefaf7fefbf6fcf8eff7f3e8
+fffcf3e0e0d8f7f8f2f3f6effefffbf9fbf6f9faf5fffffafdfaf3fffaf2fff7eedfcec4bdaba1
+c7b2a1d2b89dcab08fb79d7cbca183c3aa8ca0886e7c664f816f5b665647cabeb2fff8eefffdf8
+fcfcfaf6fafbfbfffff5fafdfcfffffcfdf8fbf8f1fffff3fff6e4bfaf98cdb89dd5bc9eceb395
+c6ab8dbda285b69c819b836b68533e99847347392ce0ddd6fefffdf7fcf8f9fef8f3f5f0fefffa
+fefef4fffef1efead7cec6b1bfb399c6b798c3af8eb8a17fb6a07bb9a37ebaa8849c8d6c8f8063
+a5967f544736cec2b6fffdf6fffbf9fffdfefffdfffefdfffafefff9fffff6fffef6fffcf6fffc
+fbfffffdfefffdfefffdfefffefefffefefefefefefefefefffffffefefefdfdfdfbfdfcfbfdfc
+fbfcfefcfdfffdfefffefffffefffffafaf8fefdf8fffff6e7e1d3bfb8a5b5ab92c1b398b7a887
+afa079b09e76b5a278b8a57bb9a67cbda77ebfa37ebea17fbea483b89f80ae9479b49e86ae9984
+a3928280706354473f6a5f5b332b29f0eaecfffdfffffcfffdfbfefffdfefffefafffbfafffffb
+fffefdfefdfbf9f8f6fffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfffffdfefefe
+fefefefefefefefefefefefefefefefefefefefefeffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+fefffbfbfcf6f4f4ecfffef2fff7e6bfb39dc4b29ad7c2a7ceb797c5ad8bbfa582c4a883c7ab84
+bfa37cb59972ad947595836fac9c8fd4c6bbfaede4fffcf3fff7f0faf5f1fef9f5fbf7f4faf6f5
+fbf7f6fefaf7fffefbfffefafff9f6fff5ecfeead2e5ceaeddc6a6e7cfaddcc4a2c8b08ec1a987
+c2a98a9f876bc9b39b9f8d777161516153465a50475c534ef5f0ecfefdfbfefffff5f7f6fafcfb
+fefffdfffef9fffef3fff8e7decfb8d6c2a7e3caabd7bd9ac2a883c1a880b89e79aa937396866d
+857867645848978d83fff6effffcfafffdfff1ecf0f9f4f8f9f7faf8f8f8f9f9f7fdfef8fffff8
+fdfff4fdf8e5fbeccfe6cfade4cdabe3ccaac9b091bba485bda589b29c8499846f7766566a5c51
+6d635a6c655ffcf8f5fffffffaf9f5fff9edf4ebdacabba8c2b09ad8bea5cdaf93c5a485caa787
+c4a181c3a283ac8e74b09781847160ddcfc2fffcf1fffef4fffef6fffef8fffdf7fffdf7fffdf7
+fffdf7fffef8fffef8fffbf5fffef8fff9f3fff8f2fffef8fffef8fffcf6fffcf5fffdf1eae0d4
+ece6dad1cfc3eff1e6fcfff8eff6eef6fef3f6fcf2fefff6fbf9ecfffeeffff6e6d9c2b2c8ab99
+dbbda5d9bc9cd1b590b19776ceb395bea4898f79617d68558d7c6c4e4035eae0d7faf1ecfffbf7
+fffffdf4f4f4eeeff1fefffffefffdfefff9fffff4fffcebfaead1d0bb9cdfc5a2d8bb93d7b78e
+cfaf88c2a681b8a07e8a765b776750857865655d50f3f0ebfffffdf8faf9f9fdfcfbfffef7f9f4
+fdfdf3fffdece6dec7cec0a3cbbb99d4c09bc7b188bca67dbfa980beab83bdad89a293748a7a60
+a0917a3e3120eaded2fff7f1fbf1f0fff7f8fef8fcfdf8fefaf9fef9fafcf8fcfdf9fdfcf9fdfc
+fefffffdfefffefefefffffffffefffffffdfefaf9faf6f5f8f7f3f4f3effffffdf8f8f6dbdbd9
+fcfcfcfefffffefffffcfdfffcfcfcfefdfbfdfaf5fffbf3ece3d4c3b79fbfb091c9b791c4b186
+b7a273bca675bfa97abda579c5ac83c2a981c3a780c4a883baa07bc0a886baa183b39e83b6a28a
+92816f655546776a614037329c9492fffcfdfffdfffbfafffdfcfffffffffffffdfffefffffffd
+fffefffffffffefefefefefefefefefefefefefefefefefefefefefefefefefefefffffffcfcfc
+fdfdfdfefefefefefefefefefefefefefefefefefefffffffffffffefefefdfdfdfefefeffffff
+ffffffffffffffffffffffff
+f7f9f4fefffaf9f9f1f9f5eafffeefe1d4c1c6b69fccb89dd0b99acdb593c5a984c0a27cc4a47d
+c3a37ac4a47bc9ad88b59d85a69382ad9c8cd2c2b3f9ebdefffdf1fffdf4fef5ecfdf6eefef7f1
+fffaf4fffdf5fffdf6fffaf1fff6edfff2e2ecd7baedd3b0f1d7b2e9cfaad0b691bfa580bea481
+c0a886c8b1929c866e8977615e4e3e6f6156584e45c9bfbdf5edebfbfaf8fdfdfbf5f7f6f8fcfb
+fcfefbfdfcf7fffdf2f7efdcddcdb3d6c2a1e1c7a2d7bb93c4a97ec3ab7fbba27ab09a75928165
+92836c665744ccbeb1fffbf2fffdf8fef6f4fffdfdfffdfffffdfdfffefbfffff8fffff4fffff0
+f8f5e4f2ebd1ecd8b5e7cea6ebd1ace1c7a2c3a986bda583c0a98ab09b808e79645e4f3c786c60
+423930d9d4d0fbfaf8fcfcfcfffffbfff9edf9eedcc9b8a4bfab93d9bfa4d1b295c7a383cfa887
+caa382c09c7cc6a78ab49880a6907bc4b3a1f6e9d9f6eddefffaeefffcf0fffdf3fffdf1fffcf2
+fffbeffffcf2fffdf1fef4eafffdf1fffbf1fff7ebfffaf0fff8ecfaf0e6f9ede1eddfd2d5c7ba
+b7afa2e1ddd1f4f6ebfbfff8f3fdf2f7fff6fafff7fefff4faf7e8fffdebffeedbdec4add7b49e
+e0bea2dbba97c8aa84bfa280c8ad8faf957c8d7760816c5b8a786a55473ef4e9e3fffdf8fffaf6
+fffbfafcfcfafffffffffffdf8faf7fdfdf5fffcf0fffae6f2e1c7d4bc9ae0c39bddbc91dcba8d
+caa97cc8ab83af9773917e608173597a725d847e70f7f4effffffdfafcfbfafefdfcfffff9fbf6
+fffff5fffdeae5d9bfcfbf9ecfbb96d7c096c7b084bba478bfaa7fbeab81b8a884b5a685897a5d
+ac9e844d3e2bfff9ecfffcf3fffcf6fffcfafffdfbfffdfdfffdfdfffefdfffffbfffffbfffffb
+fefdf9fcfbf7faf9f5fdfaf5fffbf7fffcf8fffcf6fffbf5fbf6f0fefbf4fffff8f7f6f1dad9d5
+fffffbf9f9f7fefffffefefffffffffffefffffbf7fffcf5f1e5d5c9bba1c5b593d0bd92ccb685
+bda672bfa874c3a978bfa477c7ab83c4a881cbaf88c4a881bea47fc1a985b79e7fbda88bbaa68e
+7a69557464555d5047605752fffefbfdf9fafefdfffefffffeffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe
+fefefefffffffffffffffffffffffffffffffefefefffffffffffffefefefdfdfdfdfdfdfefefe
+ffffffffffffffffffffffff
+f1f6f2fefffbfefdf8f7f5e9fffef1f2e7d5bbac95c1b096c9b497ceb596c3a684bea27bccaf87
+ccaf87c4a47bc4a680c0a789a9937b9d8971ad9b85cebda9ebdcc9fcefdffff9e9fffcedfffdee
+fffbecfff6e5f4ebdae8e0cde0d5c3decfb8e8d1b2e9cface3c9a6d0b693bea481c1a784c6ae8c
+c0a788c6b194917d645544306c5f4f32251c90857ffffcfbfef8f8fbfaf8fcfcfcf9fafcfeffff
+fafaf8fffff7fffff3eee3d1dac9afd6bf9fdfc39ed3b68ec5aa7fc5ad81bea57db8a27dac9778
+baa68b907e66eeddc9fff7e8fff9eefaece3fffcf4fbeee8fbf1e8fbf1e7f9f0e1faefddf7ecd6
+efe5cce9dbbedfc9a4ddc49cdcc29dcdb38ebba37fc0a788bca5869e8a6f72604a7e6f5c65594d
+70695ffffffaf2f1effdfdfdfaf9f5fffcf0fff4e4c9baa7bba78fdcc2a7d5b89ac8a683cfab87
+c29e7abc9a77cdac8db59a7dad977f9e8c76c0b19ec9beacd5cab6dacfb9dfd4c0e1d6c0e1d6c2
+e2d7c1e5dac6e9dec8e1d6c2ede2cce9decae0d5bfe1d6c2e2d7c1dacfbbd6c9b6cabaaab9ab9e
+867c70f7f3e8fcfdf5f9fef7f9fff9f9fffbfbfff8fcfff2fbf8e9fffbe9f8e4ccdec4abe3c1a5
+e3c2a1d9b993bea07acbae8cc1a7869f856a87715a857261786859887b72fff6effffef8fefbf6
+faf9f7fffffdfffffffafcf9f3f3f1fdfdf5fffaeefff8e4e5d4bad7bf9dddbf99e1c095d8b689
+c09f74c9ab859f87658e7b5d897b61675f4ab0aa9cfcf9f4fffffdfbfdfcfdfffefdfffefbfcf6
+fffff4fff8e6e1d3b9d1be9dd4be97d9c097c6ae82bba375c0a97dc1ac81b2a078c1b28b998967
+ac9d7e74644afffbe6fff3e3fff6e9fff7eefff8eefff7eefff8edfff9edfcf9eafcf9eafcf9ea
+fffdf1fffdf1fffdf1fffbeefffbeefffbeffffbeffffbeffff7eafffbeff7f3e8dcd9d0d0d0c8
+fffffaf5f6f1fffffdfcfefdfffffffffefffffbf7fffbf4f2e6d6ccbea4caba98d4c196d0ba89
+c0a975c3a976c4a878bfa276c6a981c3a57fc5a781c7ab84c0a681c4ac8ac4ab8db7a2879b8671
+6958467c6e61382e24b8b1abfffefdf7f5f8fefdfff8f9fef9fafffefeffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefefefefefefefefffffffffffffefefefefefefdfdfdfdfdfd
+fefefefefefeffffffffffff
+f8fcfbfcfffdfffffafffff6fff9edf2e9d8baae98c2b299bda88dcab394c2a887bda17cc9ad86
+c8ab83be9e77bea07ac9af8ec1aa8bb8a386b4a183b4a085b8a78dc7b79ed5c6afddceb7dfd3bb
+e3d7bfe2d6bcddd1b7d8cdb1d6c8add7c6a8e5ceaed5bb98c3a988b99f7cbfa584cdb593c6ae8c
+a893748c785d6a5a41746552493d2f6b6158f3eae5f8f0eefaf6f5fcfafbfafafafbfcfefeffff
+f8f8f6fffef7fffef1e3d6c3d7c3a8d4bc9adabe97cdb088c4a97ec6ad84bfa87fc0a882ae9674
+b9a081af977bb6a186cfbaa5cbb9a5c9b6a7cdbbadd7c5b7d8c7b7d6c5b3d5c5aed9c8aedecdaf
+dfcfaedfcda9e6cea8d8bf97cab08bc2aa86c3ab89c9b292b39e8189755c69594271645352483c
+d9d2c8fbf8f3fffffdfefffffbfbf9fffbf1fff9e9cabba8b3a18bd5bda3d2b597c3a27fcaa682
+c5a27ccdab86c6a582ccaf8fb1997dad9980a79780bbad93beb093c5b898ccbf9fcec1a1cbbe9e
+cbbe9ecfc2a2d3c6a6cbbe9ed2c5a5c9bc9cbdb090c3b696cabd9dc3b696bbad92b6a794a39588
+847a6efffef4fffff8f8faf5fafffcfbfffdfcfffafcfef3fffcecfffbe7ebd7bed9c0a2e7c9a7
+e1c19bd3b68ebb9d77c9ad88bea483987e637d674f8875645a4a3bdacdc4fffcf5fcf7f1fffcf9
+fffffdf8faf7fefffff9fbfaf4f5f0fffff8fffbeffff4e2dbcab0dac3a1dcc099e1c198ccab80
+ba9970c0a27c927a587e6a4f887a60534b36d8d2c2fffdf6fffffdfcfcfcfdfffcfbfdfafcfbf6
+fffff3f9f1dedfcfb5d3bf9edac199dcc196c7ac7fbea376c3ab7fc3ac80ad9a6fbbaa7fb6a47c
+a18f6ba49473e2d1b5e5d5bce0d0b9e5d6c3e3d6c3e4d7c4e3d8c4e3d8c2e3d9c0e1dac0e3d9c0
+e3d7c1e7dbc5e8dcc6e6dac4e6d7c2e4d5c0e0d1bedbcebbe3d8c6ddd4c3d1cbbbb1ada1d5d3c7
+fffff8f9f9f1fffffbf9fbfafefffffffefffef9f6fff9f2f2e6d8cebfa8cbbb9ad4c197d1bb8a
+c0a975c2a875c2a676bc9f73c4a77fc2a47eba9c76c7ab86bda07ec1a987cbb4959f897175634d
+7b6b5b55473a71685ffffdfaebe7e6fefefffefffffbfefffbfefffcfdfffdfdfdfdfdfdfdfdfd
+fdfdfdfefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe
+fdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfffffffffffffffffffefefefefefefefefe
+fefefefefefeffffffffffff
+fbffffecf0effdfffcfffffaf8f4ebfff7ead3c8b6b8a994b6a289c1ac8fbfa687b69c7bb99d78
+bb9f78c2a47ed0b28cbca079bda37ec2aa86c7b18cc7b08ebfab8abaa786b6a685bfaf8ec4b492
+cbbb99cfbf9bd1c19dd2c29ed5c5a1d9c7a3d5be9cc5ad8bbca384bea684c0a788baa3839f8a6b
+7e6b4d57462c6c5d485447374b4137e8dfd8fffefaf4f0effffdfef9f9fbf6f6f8f6f7f9fbfdfc
+f3f3f1fbf8f1fff5e9dacbb6d1bc9fd2b895d4b690c5a87ec1a67bc4ab82bda67dc3ac83c6aa83
+c1a57ed5b896a88d6fc6ad8fc9b197c5ad95c4ae97c4ae97c5af97c7af95c6af90cbb391d1b993
+d6bd95d5bc93d9c098c5ac84b9a17bbda581c2aa88b7a081917d6269573f7e6f5a4a3e2e938b7e
+fffff6fffffafffffdf5f7f6fefefcfffbf3fffcedcdc0afb0a089c7b297c7ac8ebd9f7bc09e79
+bf9d77d0ae88bb9b75cfb38eb8a07ecab596b2a185c3b294cbbc95d3c199d7c59dd6c49cd1bf97
+cdbb93cfbd95d2c098d0be96d2c098c7b58dc0ae86cbb991cfbd95bfad85ae9e7cab9a868f8174
+aea198fffcf4fffffaf8f8f6f9fdfcfcfffffefffbfdfdf3ffffeffff8e2dfccaed2ba96e5c8a0
+daba91cbae86bd9f79bea27db9a17f947c60735f46857462463929fff9effffef6f8f5eefffffb
+fefffdf0f4f3fbfffff9fdfcf9f8f4fffff6fffceff8ebdad4c3a9dec7a7dbbf9addbd94c0a077
+bd9d76b397728b7351715d4281725b584d39f5eedefffef8fffffbfcfcfafffffdfbfcf7fffcf5
+fffff1f1e6d2ddccb0d7c09edec29bdcbf95c6a97dbfa276c6ab80c3ab7fb09b6cb4a274c2af84
+9f8c62beaa85bca885cbb898ccb99bc9b698c8b79bcab99bcabb9cccbd9ccdbf9ccdbf9acebe9a
+cbb897cfba9bcfba9bcab596c6b395c6b395c2ae93bbaa90bcad96aea28ca9a18e9a9383eae6da
+fffff4fffff5f5f6f0f7f9f8fdfffefffefffbf7f4fff9f1f0e7d8cebfa8caba99d2bf95cfb98a
+c0a977c2a875c1a575bd9e72c5a57ec4a47ebea07cbb9f7abda07ebea586b39b7f8c785f705f4b
+796c5c2e2216dbd4ccfffaf6fffefffbfafff5f6fbfcfffffcfffffcfdfffdfdfdfdfdfdfdfdfd
+fdfdfdfefefefefefefefefefffffffefefefefefefdfdfdfdfdfdfcfcfcfcfcfcfbfbfbfbfbfb
+fbfbfbfafafaf9f9f9f9f9f9fafafafafafafbfbfbfefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffff
+fbffffeaf0f0fbfffefffffbf5f2ebfffff3e8dfd0a09382b4a48daa967da78f73ae9576b99f7e
+c3a684c8ac87c7ab84b2966eb69a72bea27bc4ab83c9b289cdb790d1bb94d2bf97d0bd95d2bf97
+d4c197d4c197d1be94cdba8fcbb88dc9b68ec4ad8bbda687bca586b59e7f9d8667846f50746045
+66563c4f40295f5440483f30c5bcb3fcf7f1f3efecfffefffffefff7f7f9f4f4f6eeeff1f3f3f3
+f1f0ecf4f0e7f4e8dad3c3accdb697d0b48fceb189c0a077bea177c0a77eb6a079c1a983c4a880
+b99972cdaf89bb9d79ceb191d8bd9fc9ae91d5bc9ecfb698d4b99bd5bb9ad6ba95d4b890d5b88e
+d1b586ccaf83bea57cb79f79b8a07abea883b59e7e927f616e5d435b4c35584d397c7364e0dace
+fffff6fdfcf7eff1eef5f9f8f9fbf8fffff6fffef1d8ccbcb7a893bda98ec0a788bfa280bb9b75
+b89871c1a178b59870bb9f78b9a17bc6af8dbaa786bcaa86c8b58bceb98cd2bd90d2bd90cfba8d
+ceb98cd0bb8ed3be91ceb98ccdb88bc7b285c8b386d3be91c8b386a994678e7c569d8d76796b5e
+dbcec5fbf2edfffcf9f8f6f7f8f9fbfdfefff9f9f7fffdf6fffeeefaeed6d5c2a2ccb48edfc497
+d0b485c3a67cba9e77b39976ae95768b73576f5b437e6d5b5d5040faf0e6fffbf3fffff8fdfef9
+fcfefbedf3f1fbfffff7fbfafcfbf6fffef5fff9ece7dac9cebca4ddc6a7d7bd9ad1b38db79a72
+c1a37da58b68836c4c71604675664f7a6f5bfffdedfffcf6fefaf7fbfaf8fffffbfaf9f4fefaf1
+fff9ece4d8c2ddc8abd7bd9adcbe98d8b88fc3a478bfa074c4a77dbda579b49e6fb49f70b9a477
+ae996eb8a279b39d76c2ac87cfb896cab391ccb593cdb994cfbc94d1be94d2bf94d3c193d5c093
+d8bf96d9c098d4ba95cdb38ecdb390cfb795ccb595c3ae91b9a58ca3937c9285729c9382f6f0e2
+f8f4e9fffff6f2f3edf4f6f5fafcfbfcfcfaf7f3f0fef7eff1e7dbccbda8c4b395cebb93ccb78a
+bfa777c2a877c0a475bb9b72c3a37dbfa17dbc9e7aab8e6cc4aa89bea5878f7a5f7c6a526c5b49
+4a3e307f756cfff9f3e6e2dffffffff5f6faf4f7fcf3f8fef8fbfffefffffffffffffffffefefe
+fefefefefefefdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfafafaf9f9f9f7f7f7f6f6f6f6f6f6f6f6f6
+f5f5f5f4f4f4f3f3f3f4f4f4f5f5f5f6f6f6f7f7f7f8f8f8f9f9f9fbfbfbfdfdfdfefefeffffff
+ffffffffffffffffffffffff
+fbfffff4f9fcfafefffcfefbf9f8f3fffdf4f8f0e5b6aa9ab0a18e91816a917d62a28d70a89172
+b09778b79d7caf936ec1a57ec4a77fc3a67ebca078b99d75bca37bc7ae86d0b990cab389cbb68b
+cdb88dceb98cccb78ac6b184bfaa7db9a37aa790709e896c998467897658715d42635236625239
+5d51396d624e5c5342cfc7bcfffff6f4f1ecf4f3f1e2e0e3e7e6ebeeedf2ebeaefdedee0e3e3e3
+ebe7e4e7e0d6e0d3c3cab8a0c9b092ceb08ccbab84ba9a71bb9e74bba27aaf9974bca681c1a57e
+bb9e76bea07ac9ab87d3b792cfb592ccb291d0b695cdb390d1b794d4b893d1b48ccdb086ccad7f
+c7a778c1a274b29971b59f7ab7a07eb09c7b9a87697564486050375e523c453d2acfc7baf2eee3
+f6f6eeeaebe6eef0edfcfffffcfefbfffaf4fffbf0e0d4c6bcad9aae9c84b69e82bea481b39770
+b99c74bb9e74bda076bda179baa37abaa47dbaa681baa681c0a980c2ab81c4ad83c6af85c6af85
+c7b086cab389cbb48abfa87ebca57bb8a177bfa87ec3ac82ab946a826b416753308d7c686a5e52
+f2e8dff8f0edfdf7f7f5f3f6f7f6fbfaf9fef7f5f6fffbf5fcf5e5ebdfc7cdbd9bcab48bd7bf8f
+c8ae7dbb9f77b09671b49a799b8465756045705e4674655295897bf2eadffcf8effffffaf1f3ee
+f4f9f5e9efedf4fafaeff3f2f9f8f3f8f1e9faf0e4d2c5b4c8b69ed7c2a5d2ba98c2a583ae926d
+bc9f7d977f5d715c3f7f6e54635741aa9f8dfffaedf8f3edf4f0edf4f3f1fbfaf6f5f2edf8f4e9
+f8efe0d5c6b1dac2a6d4b795d7b791cfae85bb9a71b99970bea179b49b72b39c72b29b71af986e
+b8a178ab936db49c76c2aa84c7af89cbb38dcdb58fd0b990d2bb91d4be8fd5bf8ed5bf8ed5bd8d
+d4b78bd7b88cd3b38accaf87cdaf89ceb28dc3a988b49d7ea28d7297856f7d6e5bb4a89af5ede2
+e9e5dcfefbf6f3f2eeeff1f0f3f5f4f4f4f2edece8f8f3edece4d7c4b7a4b8a98cc7b591c7b287
+bca476bea376bb9e74b3966eb99b77b79977af916fae9171bea386a48c707a664d6a59455c4c3d
+554b3fe2d9d2fdf8f4f5f4f2f3f3f5fefffffcffffeff4fafcfffffdfefffefefefdfdfdfcfcfc
+fbfbfbfafafafafafafafafaf7f7f7f6f6f6f5f5f5f3f3f3f0f0f0eeeeeeedededecececebebeb
+eaeaeae9e9e9e9e9e9eaeaeaecececeeeeeeefefeff2f2f2f3f3f3f6f6f6f9f9f9fbfbfbfdfdfd
+fefefefefefeffffffffffff
+fbfffff9fefff9fdfffefffffffffbf8f5eefffff4eee6d9a69a8a8c7d6a92826b95846a7a654a
+7a6548a08769af9574b39772b39770b39772b79b74bda17abfa67ebda47cb9a078bba47bbba47a
+bca57bbca57bb8a177ae976ba0895f957f58806b4e735f4669583e65543a63533a695a43625640
+4e4631948b7aebe5d7fbf7ece5e2dbedece8e2e2e2dadadcdedde2e0dfe4dedee0cbcbcdd2d1cf
+ded9d5d6cec3cdbdadbfa991c9ac8ecfaf89c9a87fb6966db89b73b79f79a99572b6a27fb9a17d
+c2a681b49873b99f7cc7ad8aac9470c2aa88ac9472b59d79b79f7bb79d78b49871b2956bb29569
+b49567b093699d855f9f8b6a9782638471536c5b41514329473b254f4430a49d8df7f1e5fdfaf1
+eeeee6ecede8f7f9f6dbdfdee8eae7f2efeaf6efe5d9cfc3b5a8979e8e77a59176b69f7fa68e68
+a99068ae926ab1986fc0a77ead966daf9972af9b76baa681baa27ebaa07bb99f7cb79d78b69c79
+b59b76b59b78b49a75baa07db59b76b19774b89e79b79d7a987e596f55325a432485746265584f
+f9f0e9fcf4f1fcf6f8f4eff3f6f3faf7f5faf8f4f5fffaf6f3ebdeded4bbc9ba99c8b78cd3bf8d
+c2ac7bb49d74a68e6cb89f808d7559624e3573634c6d5d4dc6baacfff7eef9f5ecf4f3eee7e9e4
+f3f7f6e3e9e7e6efeee7ebeaf5f4efefe8e0f2e8dcc3b6a6c2b29bd1bda4cdb697b69d7ea58b6a
+b097788b74555f4b30897962554835cbbfaffaf2e5efeae4eae6e3ecebe9f5f4f0eeebe6f0ece1
+f1e5d7c8b7a3d6bea4cfb292d1b18bc8a680b4946db2946eb89c77ad9370b09872ab936daf9773
+b69e7aa78f6bb09874c1a784b49a77bba17cbda47cbfa67ec2aa7ec3ab7dc3ab7bc2aa7ac3a879
+c2a176c7a47ac7a67dc6a47ec6a680c1a37fad9070947b5d664e3676614e655546cec2b6faf1ea
+e4dfdbf2eeebe9e8e6e9ebeaeaeeedebedeae6e5e1f2ede7e8e0d5bdb09faf9f86c1ae8dc0ad85
+b69f75b89f76b59972ab8f6ab09373ae9173a6896bbca184a88e7378624a735e495c4d3a564a3c
+a69d94cfcac4fffefbf3f3f3ecedeff4f8fbfcfffff1f6fafcfffffafbfdfafafafafafaf9f9f9
+f7f7f7f6f6f6f5f5f5f5f5f5f0f0f0efefefedededebebebe8e8e8e5e5e5e4e4e4e2e2e2e1e1e1
+e0e0e0dfdfdfdfdfdfe0e0e0e3e3e3e5e5e5e7e7e7edededefefeff2f2f2f6f6f6f9f9f9fbfbfb
+fcfcfcfdfdfdffffffffffff
+f8fdfffafffffbfffffcfdfffffffdfffffbfffff8fffcf2d7cfc29a8e7e786b5891826da18f77
+89785e7a654a826a4e886f508c725194795b9e8463a38968a38b69a68e6caa926ea48c689e8662
+97815c917b5686704b7a643d745e39725e3d6b5a4062513d63543f6e5f4a5f523f4b402c6e6653
+b0aa9ae5dfd1e5e1d6e4e1dae1e0dbe0dfdddbdbdbd1d1d3c6c7cbcccccebebec0bab8b9bdb9b6
+bdb6aebdb4a5bfae9abca48ac7a889c8a681bb9a71b9986fb1956eb09874b4a180a39372927d5e
+a28b6b937c5c8e795a958061897656816e4e7d6a4a7d6a497d69487e67457e68437f67437f6540
+7e653d7b633d6f5b3a6857396a593d6252385f5039473c264d45329d9686dcd6c8f2eee3f3f0e9
+e2e1dce3e4dfeceeebe8eae9e0e0dee7e4dfe9e2dae0d8cdc3baaba49784998970938264897554
+8e7853937d569b835da08a63a48e69a5916ea79372a99475a68c71a88e73aa9077a88e73a58b72
+a3896ea38970a58b70a48a71987e639a8067a0866b8c7259664c31553b225b452e7c6c5fa1978e
+eae1dcfcf4f2ebe5e7e3dee2dfdce3e4e2e7e0dcddf1ece8e9e1d4c6bba5b7aa8ac2b38ac3b183
+b39e71af9974887354a38e7174604755432d796a574c3f2fd9cfc3f9f2eaece9e2e3e2dde2e4df
+dfe3e2d8dedcd4dddadadfdbdddad5e7e0d8dbd1c7c7bbabc6b7a4c6b49cb7a388a79275967f60
+9a85687f6b506250388a7b66534836e0d7c8e2dcd0ddd8d2dcd8d5dad9d7dcdbd7e2dfdae0dcd3
+d5c9bdc7b6a4c9b39bc4a98bbb9d7bb49370ae906eac8f6fa98e71a58e6f9f88699d86679c8566
+9d8667a0896aa38c6da68d6fa78e6fa68e6ca78f6ba98f6caa9169aa9168a99165a99165aa8f64
+b4946da2805ba58360ac8a67a78665a8896c92745a5f452e4c35238e7b6c625147ddd0caeee4e3
+e5dfdfe3dfe0dbdbdde5e9e8dde2dee3e5e2e8e7e3f0ede8e3dcd2c0b4a6b7a893baa98db09d7c
+a38f6a9e88619e8662a08768a78c71ae927aa68a72a2886f9b836b6f5a455e4b3a53463660564c
+c2bbb3dedbd6e7e6e4ecececebeceef1f5f8fcfffffcfffff1f5f8f6f7f9f6f6f6f4f4f4f1f1f1
+eeeeeeebebebe8e8e8e7e7e7eaeaeae5e5e5dfdfdfdbdbdbd9d9d9d7d7d7d4d4d4d2d2d2d2d2d2
+d3d3d3d3d3d3d4d4d4d6d6d6dadadadfdfdfe3e3e3e8e8e8ebebebefefeff3f3f3f6f6f6f9f9f9
+fcfcfcfefefefefefefefefe
+fafdfffcfffffcfffffdfefffdfffefffffdfefdf8fbf8f1fffff4efe7dab5ac9d796e5c6c604a
+84755e8b7b647d6b537a644c7964497c674c826a50826a4e7d65497a62467b6347786142765f40
+735e3f735e3f6d583b6350325f4c2e5e4d336354417265546458484d4131685f4eaea797e8e2d4
+fffcf0e5e3d7e4e4dae1e0dbdadbd6d3d3d1ccccccc1c2c4b7b8bab0b0b2a5a5a5a1a09ea19e99
+a19a90ac9f8fb8a38eb89f81c5a481c29f79b08d65b08f66b0946fab9472a7967898886e7b6a50
+8b7a607a694f71614778684e7363497161476d5f447264477162456f5e406b5a3c6a5739675434
+655031624f315b4a306a5c426d5e474f432d4d4230645b4a8e8679d3cdc1e5e1d6e4e1dad7d6d1
+cfcecad5d6d1d6d8d5cfcfcdcbcbc9cfcbc8d0cbc7d9d2cad0c8bdaa9e8e82766074664c716245
+6c5c3b715e3d7662417864437865457966467a664b7b674e7e6853826857836958826857806655
+7e6453806655826857806655745a49684e3d664c3b684e3d664c3b6046355741335f5249c7beb7
+d8d0cdeae5e2dcd8d9cdc8cceceaefdbd9dcd9d5d4e5e0dadbd3c8b7af9aa2977b9a8c697f7047
+5b49216d5a3a59452a67553d53412b5a49377d70605d5347f4ebe2e9e4dedddad5d3d2ced0d2cf
+cbcfcec3c9c7c1c7c5c5cac6cfccc7d9d2cac9c0b7afa397a194839584707b695166553b604c33
+604f3552422b4f402b827564605446d0c8bbd3ccc2c9c4c0c4c3bfc1c0bec1c0bec7c3c0c8c3bd
+c1b7abb8a898b9a38ca88e73927557806345795e41795f4677614976624a7d69517b674f79634c
+77614a77614a77614a7860487860487b63497c64487e65477e65467f67457f67437f6743806643
+7c5f3f7455387b5c3f7a5b3f6b4d336d51396b503b503625604a3c7d6b61665750e6dbd9cac1c2
+f2ecf0d1cfd4dddce1dbdfded0d5d1d8dad7dcddd8e0ddd8d9d2cab5ab9fa194839c8c73887759
+756241705d3c756041745f446c563f664d396c533d60473368513f5a47364d3d2e5b4f43817a72
+c3bebacdcccad4d4d4dddee0e6e7e9ebeff0eef2f3ecf0f1eaebedf0f0f0eeeeeeebebebe8e8e8
+e4e4e4e0e0e0dddddddbdbdbdadadad5d5d5d0d0d0cccccccbcbcbc9c9c9c7c7c7c5c5c5c4c4c4
+c5c5c5c8c8c8cbcbcbcececed3d3d3d9d9d9dddddde4e4e4e7e7e7edededf2f2f2f5f5f5f9f9f9
+fcfcfcfefefefefefefefefe
+fdfefffefffffefffffdfefffcfdfffdfdfdf8f8f6f3f2eefffef7faf6edf2ebe1dbd3c6aea594
+857a687e716090816e8b7c6987766485746087756185735d7f6d577b665179644f7a65507c6752
+7e6c5682705a806f5b7c6b57796854776a59534739584e42746c61aea69be3ddd1ebe7dcd9d7cb
+d0d0c6dedfd7dfe0dadbdcd7d0d0cec6c6c6bcbcbcb2b2b4a9a9ab9797978f8f8d8a8984858178
+877f729e8f7cb49e86ba9d7fc29e7abd9770a68059a6845ead906ea0896a91806682735e574737
+8b7b6b9e8e7e9d907f9c8f7c90857190856f8f8770938b7491897290856f8d826c8a7e68897a65
+877863867762847764756a58635747534a3b766e61b7b1a5dbd7ccece9e0e0ddd6d2d1ccc3c2be
+c3c2bec6c5c1b9bab5b0afabb6b5b3b6b2b1b6b1aec1b9b6c2b9b2a79f928e8574897e6a8b7f67
+87795f87795e86765c8373598070577d6d547c6b577d6a5976625778645b79655c77635a746057
+74605775615878645b69554c715d547561586e5a51634f465e4a416551486b5a52776c66e4dfdb
+cac5c1d8d4d1cbc7c8bbb9bad5d3d6c6c4c5bbb7b4c8c3bdc2bcb0a8a18f9c917b9c91758f8262
+7667487d6d5373634c6859445e4e3e7063537d71655f574ce1dad2d6d1cdcac6c3bebdb9b9bbb8
+b2b6b5a9afada8aeacaeb3afb9b5b2c6bfb9bbb2aba49a909a8e8091847380715e72634e71624d
+6a5b46645744665a4a847b6c696156aea59cb6b2a9b6b2afb1b0aeaeacadaeaeacb3b2aeb7b2ac
+b3a9a0ae9e8f9c877490786080664d7a60477c634d7e69567b6a5a766657736252726151716050
+7261517562537865567966557966557c6a567c6a547d69517d69507d69507d694e7e6a4f7f6951
+856d55816651876c578166516e5541715746745d4d6451435b493f54453ea89a97e2d8d7cdc7c9
+dad5dbcdcad3d7d8dddadeddced3cddee0dde2e2e0e1dddaddd8d2bab1a892897a8e826c87795f
+7e6f527b6c4f77674e72624b6e5d4b6d5a4b796453614c3b68554667574a50433a635a53938e8a
+a8a7a5bfbfbfc4c5c7d4d5d7e4e5e7e9eaece3e5e4e0e2e1e4e4e4e3e3e3e1e1e1dedededadada
+d5d5d5d1d1d1cecececcccccc8c8c8c5c5c5c1c1c1bfbfbfbebebebdbdbdbcbcbcbbbbbbbcbcbc
+c0c0c0c5c5c5cacacacfcfcfd5d5d5dbdbdbdededee1e1e1e5e5e5ecececf2f2f2f6f6f6f9f9f9
+fcfcfcfefefefefefefefefe
+fcfbfffffefffffefffdfefffcfdfffafbfdf4f6f5eeeeece4e5e0e1e0dbdcd9d2d7d5c9d1cbbf
+bab4a6968e81797061665d4e605748615547695c4c7164547767577969597b6b5b7f6e5e726253
+6151425245354f4136574b3f675b4f72685ea49d95c4c0b7dad6cdd9d6cdd4d4ccd5d5cddedfd9
+edf0e9d3d5d0d4d6d1d1d3d0c7c9c6bdbdbdb2b2b2a7a7a79e9e9e88888681827d7c7972767267
+7d74659a8975b59d83ba9b7cbe9873ba936ca37c55a4805ca78a6a91795f7b6953695c4b34261b
+44372e2e211821170b33291d423b2b474131403b283d3a273e3b2a423c2c433d2d463f2f4a4034
+4d43374f45394c44375e56498c867ab4ada3c6c2b9d5d2cbd6d3cecac9c5c7c6c2c3c2c0bdbcba
+bbbab6b4b3afa39f9c9b9794a6a29f9c9698a7a1a3b7afadb9b1aeaea79da49c8f8e8574706855
+685d49695e4a6a5d4a6b5e4d6c5f4e6f625275655878675d796861796a657b6965786964776561
+746560796763796a6574625e7465607866626c5d5855433f4b3c376f5d599a8c89b6aeabd6d2cf
+c5c1becccbc9bdbcbac1c0beacaba9b9b8b4b3b0abbfbcb5b8b4a99993858279688075617f735d
+786c56796c59766958655949675b4d74675e736960605951aaa5a1c1bdbab4b3b1abaaa8a6a6a4
+a0a2a1989c9b9a9e9da2a4a1a8a7a3b4afaba9a29c928982877d73807768776b5b716654706553
+685c4c6b62536d6357736b6060594f847d759c97919695919393919494949797979d9c9aa09d98
+9e958e9c8e83927f708d7664856f5a826b59806d5e7c6c5f72655c695f566c5f566c5e536c5e53
+6e605572645975675c79685e786a5d766659766657756556746454746454756653776757776757
+796858746152766354766354705d4f736052705e525f4e4673665e635854dfd5d3d4cecedfdade
+cbc9ced2d1d9d0d1d6d3d8d4c8cdc7dfe1dee4e4e2dfdedae3e0dbc2bbb390887b857c6b847965
+81755d7d71597669566f62516e6055736258705e52614f4362514765584f544b4466615d92918f
+969696b1b2b4bdbec0cdced0dadcdbdbdddcd8d8d6d7d8d3d9d8d4d2d2d2d0d0d0cecececbcbcb
+c7c7c7c4c4c4c2c2c2c0c0c0bebebebdbdbdbcbcbcbcbcbcbdbdbdbebebebebebebebebec1c1c1
+c5c5c5ccccccd2d2d2d8d8d8dddddde2e2e2e5e5e5e4e4e4e9e9e9f0f0f0f6f6f6fafafafcfcfc
+fdfdfdfefefefefefefefefe
+fbf9fcfefcfffffefffefefffcfdfffafbfff3f4f6ecedefdee0dfe9ebe8e2e3dec7c8c2bab7b0
+bab8acafaba09b978c95918689857a7d766c767064746c6172685c70665a70665a73675b7f756b
+948a81a79e95b8afaac3bcb6c8c3bfc9c5c2dad6d3efeeeaeeede9dadbd6dbddd8e7e9e4e0e5e1
+d3d8d4cacfcbcbd0cccacccbc1c3c2b6b6b4a9a9a79c9b9991908e7f807a75766e726f66746e62
+8074649c8a74b2987db59473b48c68b58b65a47d56a4805e9d7f63826c556d5c4a594b406d605a
+a59a96baafabb2a9a2a7a09697938795938694928598998b9d9d91a4a498aeaba2b7b4abc0bbb5
+c8c3bfcec9c5dcd7d1ccc7c1cfccc5dddad5d5d4d0d1d1cfd1d1cfc1c1c1b5b5b5b7b7b7b1afb0
+a7a6a4a29e9b96918e8f8a86948f8c938d8f918b8f938d8f938e8b999490aea79fb7b1a5aea89a
+a29a8d9d9588938b80867e737b71687166606f625c6d5f5c6d62606f6563736866736967746967
+746a68796e6c7b716f766b69726866786d6b8177758a7f7d968c8ab5aaa8d2cac8d1cbcbb8b7b5
+bab9b7c6c6c4b1b1afc7c8c3a6a7a1b9bab4acaca4bcb9b0bcb9b0a9a59a97918591897e90867a
+8b8175887e72887e74857b718e857c8b827b8c857f877f7c928d8aa9a5a49e9c9d979596969696
+94949290928f9597949ea09da9a8a6b7b2afb0aba7a0999399928a958d828e86798b83768c8477
+80786b837d71847d737c786f6f6a6476716b8b88838382808080808181838688878f8f8f989491
+9e9791a3968e9a8a7d998678958273907e708b7d74847b747e79767c77747f76717c716b746b64
+6e655e696059665d54645a516259505e544b5e544b60564d62584f655b52695f556b62596d645b
+786e65766c637b716782786e8a7d7490837a8f827a847973b9b0abb7afacd4cfccd1d0cecbcbcd
+e5e6ead0d1d5cfd3d4d6dbd5cdd2cbe2e4dfe5e5e3e2e1dff0ece9dad5cfb1aaa09a928587806e
+756d5a6b6350645d4d5a52454c433c4439334e3d3555443c50413a52474358504d686463868686
+939496a1a2a4b2b6b7c2c3c5c3c5c4c4c5c0cac9c4ccc9c2c8c5c0c2c1bfc1c1c1c0c0c0bfbfbf
+bebebebebebebdbdbdbcbcbcbdbdbdbebebec0c0c0c2c2c2c4c4c4c7c7c7c9c9c9cbcbcbcccccc
+d0d0d0d6d6d6dcdcdce0e0e0e4e4e4e8e8e8eaeaeaeeeeeef2f2f2f8f8f8fcfcfcfefefefefefe
+fffffffffffffefefefefefe
+fdf9fafffcfdfffcfffefcfffcfbfff9fafef1f5f8eaeef1e3e7eabdc1c2a5a9a8afb1aeb0b1ab
+999a9285857d82827a80817978797175756d79796f8481788f8b809c958ba59e94b6afa7bcb5ad
+c1bcb6c7c2bfcecac9d7d5d6dfdfe1e3e3e5d4d4d4d1d3d2d4d6d5dde1e0dadeddc3c9c7bec4c2
+d3d9d7c8ceccc7cdcbc2c6c5babcbbb1b1afa5a4a29793928a868387877f7a7a7079776b827c6e
+8e8170a08e76af9477b18f6caa805aaa7e59a57a57a17d5d8d6f5578624d6c5a4c4d40376e6361
+988c8ca096959e9693a099939592899090868c8e83919388999a92a4a59fafb0abb9b9b7c4c2c3
+cecccfd4d2d3d3d2d0dfdedae7e6e4dfdfddc5c5c5bebfc1cecfd3c4c5c9b3b4b8afb0b29f9d9e
+938f8e97919198908e9088858d8583999397958e95a09a9eaba5a7a39e9b9a97929d9892a19d94
+a09c939e99939a958f978f8c938b8993898a948a8b968c8d958d8b98938f9e9996a39e9aa7a29f
+aba6a2b0aba8b4afabbcb7b4c5c0bcc8c3c0c3bebac4bfbccac5c1c3bebbb4b0adc3c1c2bababa
+b7b7b7c5c5c3afb0abbebfb9bdbeb6bbbdb2acaca2b3b3a9bbb8afbebbb4c5c0bad0c9c3d1c9c6
+ccc5bfcdc6becac3bbcbc2bdcac3bdb5adaaafaaa7aba5a5918d8e9793948f8d8e8d8b8c8f8f8f
+91918f9292909b9b99a6a6a4aeadabc2bebdcac5c2cbc6c2d2cdc7d4d0c7d2cbc1cecabfd3ccc2
+bdb9aeb4b0a5aeaba2a5a29b94918c807d787c7b778383837e807f7f8082868789929294a09f9d
+afaaa6bcb1abc3b5accbbab0d3c2b8d7c9c0d4cbc6cfc9c9c8c8cac5c5c7beb9b6b8b1aba9a49e
+9c9791928d878a857f847f79817c76837e7886817b8b8682928d899a9591a09d98a6a39eaaa7a2
+adaaa5b4b1acbab7b2bfbab4c2bdb7cac3bdd1cac4d5d0cad2cdc9e8e5e0cccbc7d7d7d5c2c4c3
+e8ecebd2d6d7dce0dfe3e8e1dbe1d7e9ebe6e5e7e4e7e6e4faf9f7f2efeaded9d3d7d3c8c6c0b2
+ada797979183837d716f6b62615c585e56534f423c5d4e47584d49544a48635d5d6e6c6d79797b
+909497999da0acb0b1b9bbbab6b6b4b8b7b3c3c0b9c6c2b9bebbb4bbbab8bbbbbbbcbcbcbdbdbd
+bfbfbfc1c1c1c2c2c2c3c3c3c4c4c4c6c6c6cacacacdcdcdd0d0d0d3d3d3d7d7d7dadadadddddd
+e0e0e0e5e5e5e9e9e9ecececeeeeeef1f1f1f3f3f3f7f7f7fafafafefefeffffffffffffffffff
+fffffffffffffefefefefefe
+fffafafffefdfffefffefcfffbfafff8f9fdf0f3f8e8edf1d2d7dbc9ced1b9bfbfa6aaa9929793
+898b86898a858c8f88797e77757c74767b7480837a8d8e869b9b91adaaa1b9b6adbcb9b2c7c4bf
+d0cfcbd6d4d5d9d9dbdbdce0d8dbe0d4d7dce6eaeddee2e3cfd3d4ced4d4dde3e3dde6e5ccd5d4
+bfc8c7c4cdccc2c8c8bbbfbeb5b7b6b2b2b0acaba9a19d9a94918c9e9e94909084908e819b9585
+9e917ea38f76ab8e70ac8a67a77d57a17550a075529a73547a5c447059476c5c4f42352f7a6e6e
+9f93959f9596928a88877f7c79766f84847c94958d95968e9c9f98a8aaa5b3b5b4bbbcbec4c3c8
+cccbd1d2d1d7d9d9dbdadadacccccecdced0cecfd3c9cdd0c0c3c8999ca1b4b5baaeafb3a09ea1
+9995949f9999a199979d93919e9493a1989d9790979c979ba29e9f9b9798969593a39f9cadaaa5
+9a9693989491959190918b8d8e888c8f858d90868f90899096928f9a9a92a3a29daaaaa2afaea9
+b4b4acb8b7b2bcbcb4c2c1bcccccc4cac9c4bebeb6bdbcb7cacac2cfcec9c9c8c4c0c0c0cbccce
+bfc1c0c5c7c4c1c4bdc1c4bbc7c9bec5c7bac7c9bcc4c6bbc1c1b9c2bfbac5c1bec9c3c3c8c2c4
+c5bfc1d3cecad1ccc6cec6c3c4bfbcb7b1b1ada9aaa8a4a59691959893979290939391929b999a
+a09f9da4a4a2afb0abbcbcbabab9b7cdc9c8d6d2d1d8d4d1e0ddd8e0ddd6d8d3cdd3d0c7d3cfc6
+c1beb5b4b1a8a9a9a1a7a6a19a99958d8c8880807e8888888687898a8b8f9296979fa0a2ababab
+bab6b3c9c0bbc9bcb4cec0b7d5c6bfd6cbc5d0cbc8c4c2c5b4b7beabaeb3acaba9a6a39e999691
+8e8b8686837e827f7a7e7d787d7c7783847f8788838f8f8d999997a3a3a3acaeadb3b5b4b6b8b7
+c1c3c2caccc9cdcfcccecfcacfd0cbd0cfcad6d5d0e0dfdacac9c4ddded8d8d9d3e1e3ded2d7d1
+dbe1dddce2e0e4eae6e4eae0e3e9dfeceee9e4e6e3e7e6e4f2f1efede9e6e9e6e1cac7bec1bdb2
+aeaa9e9593877a777066625f605e5f6761615e535160524f685e5c645c5a706c6d7f7f817f8084
+999da09ea2a5aaaeafb6b8b7b9bab5bcbbb6c3c0b9c6c2b7c4c0b7c1c0bcc1c1c1c3c3c3c7c7c7
+cacacacdcdcdd0d0d0d1d1d1d2d2d2d6d6d6dbdbdbdededee0e0e0e3e3e3e6e6e6e9e9e9ededed
+efefeff2f2f2f5f5f5f6f6f6f8f8f8fafafafcfcfcfbfbfbfdfdfdffffffffffffffffffffffff
+fffffffffffffefefefefefe
+fffdfcfffefdfffefffefcfdfafafcf7f8fceff3f6e8ecefd4d8dbcaced1b8bcbda6aaa99b9f9e
+9698958b8d8a81837e848985818881858a848d928c989b94a4a59fb3b4aec0bfbac6c5c1cecdc9
+d5d5d3d7d7d9d9dadedce0e3dcdfe4d9dce1d2d6d9dfe3e4dce0e3d0d4d5cbd0d3c8cecec2cacc
+c4cdccc2c8c8bcc2c2b5b9b8b3b5b4b6b6b4b8b7b5b2aeada7a3a0b2b2aaa4a296a5a195aea594
+a998849f8971a38467a78361aa7f5c9d714e9a7151916c516d4e396a53436c5b51392c268c8281
+9990938c83848e88889f9a979e9a979e9d989fa09aa3a5a0acaea9b8bab7c1c2c4c8c9cdcecfd4
+d5d4dadad9dfd5d6dadddee0d0d1d3d2d3d7cacbcfbbbfc2c6c7ccbdbec3b0b1b5b2b1b6aeaeb0
+adabacafabaaa9a4a1aba3a0b7afadb6b0b4b9b4bac0bbbfbcbabdb6b4b5b6b4b5adacaa989795
+9e9d9b9d9b9c9a989998939795909695909797929999949aa09f9da6a7a1afb0aab6b7b1babbb5
+bdbeb8c1c2bcc3c4becacbc5c7c8c2c7c8c2cccdc7cfd0cacecfc9cdcec8cdcec9cbcdcccecfd1
+c8cac9c6c8c5dddfdad4d7d0c3c4bcd8dacfcecfc7d3d4ccd8d7d2d7d6d2d2d1cfcccacdcdc8ce
+cec9cdc9c5c4cac6c3bdb9b6b2aeadb4b0b1a6a4a5a19ca0a3a1a4a09ea19d9b9ea19fa0aba9aa
+b2b1afb8b7b5c4c4c2d2d2d0d2d0d1dedcdde2dedddedddbe4e0dddfdedad7d4cfd0cfcac9c6bf
+bfbfb7b2b1aca3a29da2a19d9998969e9e9c9898989a9c9b9c9d9fa6a7a9b4b5b7bdbec0c2c2c2
+cbc7c6d2cdcae0d7d2dad1ccd3cac5cfc7c4cac6c7c3c2c7b9bcc3b3b6bbabababa5a4a09b9a96
+93928e908f8b908f8b91928d929290989a979d9f9ca5a7a6afb1b0babbbdc1c5c6c9cacccbcfd0
+c8cccdced2d3ced2d1d3d5d4dadcd9d8dad5d5d6d1dadbd6dadbd6d5d6d1d1d3ceedefeadadfd9
+e7ece8e2e8e4e4ebe4ecf1eaf5f8f1fdfffaf5f5f3f4f4f4f5f3f4e6e5e3e9e8e4d6d3cec1c1b9
+adaaa39b9b93908f8b8584827c7c7e797778837b79716765857d7b7d79787e7c7d9191938b8c90
+9ea2a5a4a8aba7abacb3b5b4bfbfbdc4c3bfc4c1bac7c3bacac7c0cac9c7cacacacdcdcdd1d1d1
+d6d6d6dadadadddddddededee1e1e1e4e4e4e9e9e9ebebebedededefefeff2f2f2f5f5f5f2f2f2
+f4f4f4f6f6f6f8f8f8f9f9f9fafafafdfdfdfffffffcfcfcfdfdfdfffffffffffffefefefefefe
+fefefefffffffefefefefefe
+fffdfefffefffffefffffffffefefef8f8faeff0f2eaebeddbdcded0d1d3c1c3c2b4b6b5acaead
+a6a6a6a0a0a09b9b999a9c9b9b9d9a9ea09da4a6a3aeaeacb8b8b6c2c2c0c8c8c6cfcfcdd2d2d0
+d8d8d8dededee2e2e4e2e3e5e1e2e4e0e1e3dedfe1dddfdedbdcded7d9d8d3d4d6cfd1d0cccdcf
+cacccbc6c8c7c6c8c7c5c7c6c5c5c5c4c4c4c2c2c2c2c0c1c1c0becac7c2bebab1c2b8aed2c2b3
+a38c7a9a7e68a482669c7858946b4ba47b5d8c674c88664d674b365b4434513f334b3e3697928f
+9593948f8d8e9d9b9c9e9d9bafaeacadadabb2b2b0b8b8b6bebebcc7c7c7d0d0d0d7d7d9dadadc
+dadadcdadadcdfdfe1ddddddd9d9d9d5d5d7d0d0d2cacbcdc8c8cac6c6c8c1c1c3c0c0c2bfbfbf
+bebebec0bebfc1c0bec3c2c0c4c3c1c8c6c7cac8cbcccacbcacacac5c5c5bebebeb6b6b6b0b0b0
+acacaca8a8a8a4a4a4a19fa0a2a0a3a5a3a6aba9acaeacafb4b4b4b7b7b5bdbdbbc3c3c1c8c8c6
+cbcbc9cdcdcbcdcdcbd0d0ced1d1cfd2d2d0d3d3d1d4d4d2d5d5d3d6d6d4d6d6d4d7d7d7d8d8d8
+d9d9d9dadadadcdcdadddddbdfdfdddfe0dbe2e2e0e3e3e1e4e4e2e3e3e1e0e0e0dbdbdbd6d4d7
+d2d0d1d2d0d1cccbc9c4c3c1bbb9bab4b2b3b0b0b0b0aeafafafafaeaeaeb1b1b1b7b7b7bfbfbf
+c8c8c8d1d1d1d8d8d8dbdbdbe1e1e1e7e7e7edebececececebeae8e6e6e4dedddbd5d5d3cecdcb
+c7c7c5bebebcb5b5b3afafadacacacacacacadadadb1b1b1b6b6b6bebebec7c7c7cfcfcfd5d5d5
+d9d7d8dbdad8dfdbdadedad9dbd7d6d5d4d2cecccdc5c5c7bdbec0b9babcb0b0b0acacaaa7a7a5
+a3a3a1a1a19fa2a2a0a5a5a3a7a7a7adadadb2b2b2bababac2c2c2c8c8c8cdcfced2d2d2d4d6d5
+d5d7d6d6d8d7d7d9d8d9d9d9dadadadbdbd9dcdcdadcdcdadddddbdddddbdfdfdde1e1dfe4e6e3
+e8eae9edefeeeff1eef1f3f0f7f7f5f9f9f7f8f8f8f7f7f7f5f5f5eeeeeee5e5e3d8d7d5cbcbc9
+bdbcbaafafada4a4a29a9a9a9696969696969695939894939796949a98999e9e9ea4a4a4aaaaac
+acadafb4b5b7babcbbc3c3c3c9c9c9cbcbc9cfceccd4d3cfd8d7d5d8d8d8dadadadddddde1e1e1
+e5e5e5e8e8e8eaeaeaebebebefefeff0f0f0f2f2f2f3f3f3f5f5f5f7f7f7f9f9f9f9f9f9f8f8f8
+f9f9f9fafafafbfbfbfcfcfcfdfdfdfefefefefefefefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffffffffffffffffffffefefef9f9f9f3f3f3eeeeeee7e7e7dededed3d3d3cbcbcbc6c6c6
+c2c2c2bdbdbdbababab7b7b7b8b8b8babababebebec5c5c5cdcdcdd4d4d4d8d8d8dbdbdbdddddd
+e1e1e1e5e5e5e8e8e8e8e8e8e7e7e7e6e6e6e5e5e5e4e4e4e2e2e2dfdfdfdcdcdcd8d8d8d5d5d5
+d3d3d3d1d1d1d2d2d2d3d3d3d4d4d4d5d5d5d5d5d5d5d5d5d6d5d3d8d5d0e2dbd3ddd0c7d4c2b4
+87705e9d7f679f7d61a07b5e936c4d7c573a7b593e81634b624734523d2c645244766960aca8a5
+b2b2b2b3b3b3bcbcbcb2b2b2bbbbbbbcbcbcc6c6c6ccccccd0d0d0d7d7d7dededee3e3e3e5e5e5
+e5e5e5e4e4e4e4e4e4e3e3e3e0e0e0dcdcdcd8d8d8d4d4d4d1d1d1d0d0d0cfcfcfcfcfcfd0d0d0
+d1d1d1d3d3d3d7d7d7dadadadcdcdcdedededfdfdfe0e0e0dfdfdfdadadad4d4d4cdcdcdc8c8c8
+c3c3c3c0c0c0bdbdbdbababababababdbdbdc2c2c2c5c5c5cacacacdcdcdd2d2d2d6d6d6dadada
+dcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdddddddedededfdfdfe0e0e0e0e0e0e2e2e2e3e3e3
+e4e4e4e6e6e6e8e8e8eaeaeaebebebecececeeeeeeefefefefefefeeeeeeebebebe7e7e7e2e2e2
+dfdfdfd9d9d9d5d5d5cececec7c7c7c3c3c3c1c1c1c1c1c1c1c1c1c7c7c7cacacacfcfcfd6d6d6
+dddddde3e3e3e8e8e8ebebebefefeff4f4f4f7f7f7f4f4f4f0f0f0ebebebe3e3e3dbdbdbd6d6d6
+d2d2d2ccccccc6c6c6c3c3c3c3c3c3c4c4c4c6c6c6cccccccfcfcfd6d6d6dddddde3e3e3e8e8e8
+eaeaeaebebebe9e9e9e7e7e7e4e4e4dededed7d7d7d0d0d0c9c9c9c6c6c6c5c5c5c2c2c2bebebe
+bbbbbbbabababcbcbcbfbfbfc1c1c1c5c5c5cacacad0d0d0d5d5d5dadadadddddde0e0e0e2e2e2
+e0e0e0e0e0e0e1e1e1e2e2e2e3e3e3e3e3e3e4e4e4e4e4e4e4e4e4e5e5e5e6e6e6e9e9e9ececec
+f0f0f0f3f3f3f5f5f5f7f7f7fbfbfbfdfdfdfbfbfbfbfbfbfafafaf3f3f3ececece0e0e0d6d6d6
+cbcbcbc4c4c4bdbdbdb7b7b7b5b5b5b7b7b7b8b8b8b8b8b8b8b8b8babababdbdbdc1c1c1c6c6c6
+c8c8c8c5c5c5cbcbcbd3d3d3d7d7d7d9d9d9dcdcdce0e0e0e4e4e4e7e7e7e8e8e8ebebebeeeeee
+f1f1f1f4f4f4f6f6f6f7f7f7f8f8f8f8f8f8f9f9f9fbfbfbfcfcfcfefefefffffffffffffcfcfc
+fdfdfdfdfdfdfefefefffffffffffffffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffffffffffffffffffffefefefbfbfbf7f7f7f4f4f4efefefeaeaeae3e3e3dedededcdcdc
+dadadad7d7d7d5d5d5d3d3d3d4d4d4d5d5d5d7d7d7dbdbdbdfdfdfe3e3e3e5e5e5e9e9e9eaeaea
+ecececedededeeeeeeededededededecececebebebebebebe9e9e9e7e7e7e5e5e5e2e2e2e0e0e0
+dededee0e0e0e1e1e1e3e3e3e5e5e5e7e7e7e8e8e8e9e9e9eae9e7e2dfdaeae6dbf4eaded1c1b1
+8b76619b8168947558916e508865457f5c3e8a6b4e54391e6148326856424636269a9086bcb8b5
+c5c5c5c9c9c9d5d5d5cdcdcdd6d6d6d6d6d6dfdfdfdfdfdfe2e2e2e5e5e5e9e9e9ebebebececec
+ebebebebebebebebebeaeaeae8e8e8e5e5e5e3e3e3e0e0e0dfdfdfdddddddededededededfdfdf
+e1e1e1e4e4e4e8e8e8ebebebedededefefefefefefefefefedededeaeaeae5e5e5e0e0e0dddddd
+d9d9d9d7d7d7d5d5d5d4d4d4d4d4d4d6d6d6d8d8d8dadadadededee0e0e0e3e3e3e6e6e6e8e8e8
+e8e8e8e7e7e7e7e7e7e9e9e9e9e9e9e9e9e9eaeaeaeaeaeaeaeaeaebebebebebebeaeaeaebebeb
+ecececeeeeeef1f1f1f3f3f3f4f4f4f5f5f5f7f7f7f7f7f7f6f6f6f5f5f5f3f3f3efefefececec
+eaeaeae5e5e5e2e2e2ddddddd9d9d9d7d7d7d6d6d6d8d8d8d9d9d9dededee0e0e0e5e5e5eaeaea
+eeeeeef2f2f2f4f4f4f6f6f6f7f7f7fbfbfbfcfcfcf8f8f8f4f4f4f0f0f0eaeaeae3e3e3e3e3e3
+e1e1e1ddddddd9d9d9d8d8d8dadadadddddddfdfdfe2e2e2e4e4e4e9e9e9eeeeeef1f1f1f4f4f4
+f5f5f5f5f5f5f3f3f3f1f1f1eeeeeee9e9e9e4e4e4dfdfdfdbdbdbd9d9d9d8d8d8d7d7d7d4d4d4
+d2d2d2d2d2d2d5d5d5d8d8d8dadadadcdcdcdfdfdfe3e3e3e5e5e5e7e7e7e8e8e8e9e9e9eaeaea
+ebebebebebebebebebececececececededededededededededededeeeeeeefefeff2f2f2f4f4f4
+f6f6f6f8f8f8f9f9f9fbfbfbfefefefefefefefefefefefefdfdfdf9f9f9f4f4f4e9e9e9e2e2e2
+dbdbdbd9d9d9d7d7d7d4d4d4d4d4d4d6d6d6d0d0d0cfcfcfcecececfcfcfd0d0d0d3d3d3d5d5d5
+d7d7d7dbdbdbdfdfdfe5e5e5e8e8e8eaeaeaecececefefeff2f2f2f1f1f1f3f3f3f5f5f5f7f7f7
+f9f9f9fbfbfbfcfcfcfdfdfdfbfbfbfbfbfbfcfcfcfcfcfcfdfdfdfefefeffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffffffffefefefefefefefefefefefefbfbfbfafafaf5f5f5f2f2f2eeeeeeebebebeaeaea
+e8e8e8e5e5e5e3e3e3e5e5e5e6e6e6e7e7e7e8e8e8e9e9e9eaeaeaececececececf2f2f2f2f2f2
+f2f2f2f2f2f2f2f2f2f2f2f2f1f1f1f1f1f1f1f1f1f0f0f0f0f0f0efefefedededebebebeaeaea
+e9e9e9edededeeeeeeeeeeeeefefeff0f0f0f0f0f0f0f0f0efefedfffff8e8e4d9f6eee1a79a89
+887660856d53876c4f7d5e3f75543586674885674b4e341b4d39213e2d192e2110d2cabdd3cfcc
+d4d4d4d2d2d2e2e2e2e3e3e3f1f1f1ebebebecececedededeeeeeeefefeff0f0f0f1f1f1f1f1f1
+f1f1f1f1f1f1f0f0f0f0f0f0efefefeeeeeeedededebebebebebebeaeaeaebebebebebebececec
+ecececeeeeeef0f0f0f2f2f2f3f3f3f5f5f5f5f5f5f4f4f4f2f2f2f0f0f0ececece9e9e9e8e8e8
+e7e7e7e7e7e7e6e6e6e5e5e5e6e6e6e6e6e6e8e8e8e9e9e9eaeaeaebebebedededefefefefefef
+efefefeeeeeeeeeeeef2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f0f0f0f1f1f1
+f2f2f2f5f5f5f7f7f7f9f9f9fbfbfbfbfbfbfbfbfbfbfbfbfafafaf8f8f8f6f6f6f5f5f5f3f3f3
+f2f2f2eeeeeeecececeaeaeae7e7e7e7e7e7e7e7e7e9e9e9eaeaeaebebebedededf1f1f1f5f5f5
+f8f8f8f9f9f9fafafafafafaf9f9f9fcfcfcfdfdfdfafafaf8f8f8f8f8f8f5f5f5f0f0f0efefef
+edededeaeaeae7e7e7e6e6e6e8e8e8eaeaeaebebebecececeeeeeef1f1f1f4f4f4f6f6f6f7f7f7
+f7f7f7f7f7f7f6f6f6f5f5f5f3f3f3f0f0f0eeeeeeedededecececececece7e7e7e5e5e5e4e4e4
+e3e3e3e3e3e3e5e5e5e7e7e7e9e9e9ebebebecececeeeeeeefefefeeeeeeedededeeeeeeeeeeee
+f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f3f3f3f5f5f5f7f7f7f9f9f9
+fafafafbfbfbfbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfefefefdfdfdfbfbfbf8f8f8f3f3f3eeeeee
+eaeaeaeaeaeae9e9e9e6e6e6e6e6e6e7e7e7e8e8e8e7e7e7e6e6e6e6e6e6e6e6e6e7e7e7e9e9e9
+eaeaeaebebebeeeeeef1f1f1f4f4f4f5f5f5f6f6f6f8f8f8fafafaf7f7f7f8f8f8f9f9f9fbfbfb
+fcfcfcfdfdfdfefefefefefefbfbfbfbfbfbfcfcfcfcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfefefe
+fefefefefefefefefefdfdfdfdfdfdfdfdfdfdfdfdfefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffffffffefefefefefefffffffffffffffffffefefefafafaf9f9f9f8f8f8f7f7f7f6f6f6
+f4f4f4f2f2f2f1f1f1f3f3f3f4f4f4f4f4f4f5f5f5f5f5f5f6f6f6f6f6f6f6f6f6f7f7f7f7f7f7
+f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f6f6f6f5f5f5f4f4f4f3f3f3
+f3f3f3f6f6f6f6f6f6f6f6f6f7f7f7f6f6f6f6f6f6f5f5f5f4f4f2fffff8efede1f6f0e2938b78
+7869526f5b426a5236513819462b0d5d442541290d3d291035250e504330877c6ae6e0d2efeeea
+efefefe8e8e8f3f3f3f0f0f0fafafaf4f4f4f4f4f4f8f8f8f8f8f8f8f8f8f8f8f8f9f9f9f9f9f9
+fafafafafafaf7f7f7f6f6f6f6f6f6f6f6f6f6f6f6f5f5f5f5f5f5f5f5f5f8f8f8f8f8f8f8f8f8
+f8f8f8f8f8f8f9f9f9f9f9f9fafafafcfcfcfcfcfcfbfbfbf9f9f9f7f7f7f6f6f6f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f5f5f5f5f5f5f5f5f5f4f4f4f5f5f5f7f7f7f8f8f8f9f9f9
+f9f9f9f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f7f7f7f7f7f7f7f7f7f7f7f7f9f9f9fafafa
+fbfbfbfcfcfcfefefefffffffffffffffffffffffffffffffefefefdfdfdfcfcfcfcfcfcfbfbfb
+fbfbfbf6f6f6f5f5f5f4f4f4f3f3f3f2f2f2f3f3f3f4f4f4f4f4f4f5f5f5f7f7f7fafafafdfdfd
+fffffffffffffffffffffffffdfdfdfffffffffffffefefefefefefffffffffffffcfcfcf7f7f7
+f6f6f6f4f4f4f2f2f2f1f1f1f2f2f2f3f3f3f4f4f4f6f6f6f7f7f7f9f9f9fbfbfbfcfcfcfdfdfd
+fdfdfdfdfdfdfafafaf8f8f8f7f7f7f5f5f5f5f5f5f5f5f5f6f6f6f7f7f7f5f5f5f4f4f4f3f3f3
+f2f2f2f2f2f2f3f3f3f4f4f4f5f5f5f6f6f6f8f8f8f9f9f9f8f8f8f7f7f7f6f6f6f6f6f6f7f7f7
+f7f7f7f7f7f7f7f7f7f7f7f7f6f6f6f6f6f6f6f6f6f6f6f6f7f7f7f8f8f8f9f9f9fafafafcfcfc
+fcfcfcfdfdfdfdfdfdfffffffdfdfdfcfcfcfdfdfdfefefefefefefdfdfdfcfcfcfdfdfdf8f8f8
+f6f6f6f7f7f7f7f7f7f4f4f4f3f3f3f4f4f4f5f5f5f5f5f5f4f4f4f4f4f4f5f5f5f6f6f6f7f7f7
+f8f8f8f5f5f5f6f6f6f8f8f8f9f9f9fafafafbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfefefeffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffffffffefefefefefefffffffffffffffffffffffffbfbfbfcfcfcfcfcfcfdfdfdfdfdfd
+fcfcfcfcfcfcfcfcfcfafafafafafafafafafbfbfbfbfbfbfcfcfcfcfcfcfcfcfcfbfbfbfbfbfb
+fbfbfbfbfbfbfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfbfbfb
+fafafafbfbfbfbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbf1f2ecfdfdf3f3efe39e9787
+62554267574059452c4631144830143c270a3d29103b2b12473b25776c5a8e8775e2ded2f9f8f4
+fffffffcfcfcfffffff2f2f2f9f9f9f8f8f8fffffffcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfd
+fefefefefefefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfdfdfd
+fefefefefefefffffffffffffffffffffffffffffffffffffffffffefefefdfdfdfdfdfdfcfcfc
+fafafafbfbfbfbfbfbfbfbfbfbfbfbfcfcfcfcfcfcfcfcfcfafafafbfbfbfcfcfcfdfdfdfefefe
+fefefefefefefefefefdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfdfdfdfd
+fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefefefefe
+fefefefdfdfdfcfcfcfcfcfcfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfcfcfcfefefeffffff
+fffffffffffffffffffffffffffffffffffffffffffdfdfdfcfcfcfffffffefefefcfcfcfcfcfc
+fcfcfcfbfbfbfbfbfbfbfbfbfcfcfcfefefefefefefdfdfdfdfdfdfefefeffffffffffffffffff
+fffffffffffffffffffefefefdfdfdfbfbfbfafafafafafafbfbfbfbfbfbfdfdfdfdfdfdfcfcfc
+fbfbfbfbfbfbfafafafafafafbfbfbfcfcfcfdfdfdfefefefdfdfdfcfcfcfbfbfbfcfcfcfefefe
+fcfcfcfcfcfcfcfcfcfcfcfcfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfcfcfcfcfcfcfdfdfdfefefe
+fffffffffffffffffffffffffefefefdfdfdfffffffffffffffffffefefefffffffefefefafafa
+f9f9f9fbfbfbfcfcfcfafafafafafafcfcfcf7f7f7f7f7f7f7f7f7f7f7f7f8f8f8f8f8f8f9f9f9
+fafafafcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfefefefffffffffffffefefefefefeffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffefefefdfdfdfefefefffffffffffffffffffffffffbfbfbfcfcfcfdfdfdfefefefefefe
+fefefefefefefffffffbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfdfdfd
+fefefefefefefefefefefefefefefefdfdfdfefefefffffffffffffffffffffffffefefefefefe
+fdfdfdfdfdfdfdfdfdfefefefffffffffffffffffffffffffffffdfdfef9fffff8f1eee5b8b2a6
+736a5b605342594a355b4b3462503a3e2c1662513d5e513e564a3a3b3424767062fffcf3f9f8f4
+fffffffdfdfdfffffff6f6f6fcfcfcfcfcfcfffffffcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfd
+fdfdfdfdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffcfcfcfdfdfdfdfdfd
+fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfdfdfdfdfdfd
+fefefefefefefefefefffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfdfdfd
+fdfdfdfcfcfcfcfcfcfbfbfbfbfbfbfbfbfbfcfcfcfcfcfcfdfdfdfefefefefefefefefefdfdfd
+fdfdfdfffffffffffffffffffffffffffffffffffffefefefefefefdfdfdfdfdfdfdfdfdfdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfffffffefefefbfbfbfafafafcfcfcfcfcfcfafafaffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffefefefdfdfdfdfdfdfdfdfdfefefefdfdfdfdfdfdfefefe
+fefefefdfdfdfdfdfdfdfdfdfcfcfcfdfdfdfefefefefefefdfdfdfcfcfcfcfcfcfdfdfdfefefe
+fffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfdfdfdfdfdfdfdfdfdfefefe
+fffffffffffffffffffffffffffffffefefefffffffffffffffffffefefefffffffdfdfdf9f9f9
+f8f8f8fbfbfbfdfdfdfcfcfcfcfcfcfefefeffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffefefefdfdfdfdfdfdfefefefffffffffffffffffffdfdfdfdfdfdfefefefefefe
+fefefefefefefdfdfdfdfdfdfefefefefefefefefefefefefefefefdfdfdfdfdfdfdfdfdffffff
+fffffffffffffffffffffffffffffffffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+fffffffefefefdfdfdfefefeffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffefefefdfdfdfdfdfdfcfcfcfcfcfcfdfdfdfefefefffffffcfcfcfdfdfd
+fefefefefefefefefefdfdfdfcfcfcfbfbfbfdfdfdfefefefefefefefefefefefefdfdfdfdfdfd
+fcfcfcfdfdfdfefefefefefefefefefefefefdfdfdfdfdfdfcfcfcf6f8f5f8f9f4fffffaf7f4ed
+c7c0b6655b4f463a2c3e31213e2e1f403323382c1e392f23342c213a3329fffff6fffffafffffd
+fffffff9f9f9fffffffdfdfdfffffffcfcfcfcfcfcffffffffffffffffffffffffffffffffffff
+fffffffffffffefefefefefefefefefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdffffffffffffffffff
+fffffffffffffffffffefefefdfdfdfefefefefefeffffffffffffffffffffffffffffffffffff
+fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefefefefefffffffefefefefefefefefefefefefefefe
+fefefefefefefffffffefefefefefefefefeffffffffffffffffffffffffffffffffffffffffff
+fefefefdfdfdfcfcfcfbfbfbfafafaf9f9f9fcfcfcfdfdfdffffffffffffffffffffffffffffff
+fffffffefefefefefefffffffffffffffffffffffffffffffffffffffffffffffffefefefdfdfd
+fdfdfdfcfcfcfcfcfcfcfcfcfdfdfdfffffffffffffdfdfdfdfdfdfffffffffffffefefeffffff
+fffffffffffffffffffffffffefefefefefefefefefffffffffffffffffffffffffefefefdfdfd
+fdfdfdfdfdfdfcfcfcfcfcfcfdfdfdfefefefefefefffffffffffffffffffefefefefefeffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffefefefdfdfdfdfdfdfefefeffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfcfcfcfbfbfbfbfbfbfcfcfc
+fefefefffffffffffffffffffefefefdfdfdfffffffffffffefefefdfdfdfefefefffffffefefe
+fcfcfcfefefefffffffffffffffffffffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefffffffefefefcfcfcfdfdfdfefefefefefefefefefefefeffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe
+fefefefffffffffffffffffffffffffffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefefefeffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffdfdfdfefefefefefefffffffffffffefefefefefefdfdfdfffffffefefe
+fefefefefefefefefefdfdfdfdfdfdfdfdfdfffffffefefefefefefdfdfdfdfdfdfefefefefefe
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfefffdfefffefefefffffd
+fffefbfffef8fffdf6fffdf6fffcf6fffcf6fffdf6fffef8fffefafffefbfffefdfffffdffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffefefefcfcfcfbfbfbfafafafafafaffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefcfcfcfefefefefefe
+fefefefefefefefefefefefefffffffffffffbfbfbfbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfcfcfc
+fcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffefefefafafaf9f9f9fefefe
+fffffffffffffefefefefefefffffffffffffefefefefefefefefefefefefdfdfdfdfdfdfefefe
+fefefefffffffffffffffffffffffffefefefefefefefefefefefefefefefdfdfdfdfdfdfdfdfd
+fdfdfdfcfcfcfefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fefefefefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefdfdfdfdfdfd
+fefefefefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fdfdfdfefefefffffffefefefcfcfcfafafafbfbfbf9f9f9fafafafbfbfbfbfbfbf9f9f9fbfbfb
+fffffffefefefffffffffffffefefefdfdfdfcfcfcfdfdfdffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffefefefefefefefefefefefefefefefefefefefefefefefeffffffffffff
+fefefefefefefefefefefefefdfdfdfdfdfdfffffffefefefefefefdfdfdfdfdfdfefefefefefe
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfefffdfefffefdfffffeff
+fffefffffdfdfffdfdfffefbfffdfbfffdfbfffdfdfffdfdfffefffffefffffefffffeffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffefefefdfdfdfcfcfcfcfcfcfbfbfbffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefcfcfcfcfcfcfcfcfc
+fcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfbfbfbfcfcfcfdfdfdfdfdfdfefefefefefefdfdfd
+fdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffefefefbfbfbfafafafefefe
+fffffffefefefdfdfdfdfdfdfffffffffffffefefefefefefefefefefefefdfdfdfdfdfdfefefe
+fefefefffffffffffffffffffffffffefefefefefefefefefefefefefefefdfdfdfdfdfdfdfdfd
+fdfdfdfdfdfdfefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fefefefefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefdfdfdfdfdfd
+fefefefefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fefefefefefefffffffefefefcfcfcfafafafffffffdfdfdfdfdfdfffffffefefefdfdfdffffff
+fffffffefefefffffffffffffefefefdfdfdfcfcfcfdfdfdffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffefefefefefefefefefefefefefefefefefefefefefefefeffffffffffff
+fffffffefefefefefefefefefefefefefefefffffffffffffefefefefefefefefefefefeffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffefdfffefdfffefdfffffeff
+fffefffffdfffffdfffffdfffffdfffffdfffffdfffffefffffefffffefffffefffffeffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefefefefefefefefefefeffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefcfcfcfcfcfcfcfcfc
+fdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfdfdfdfdfdfdfefefefefefefffffffffffffefefe
+fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcfbfbfbffffff
+fffffffdfdfdfcfcfcfcfcfcfffffffffffffefefefefefefefefefefefefdfdfdfdfdfdfefefe
+fefefefffffffffffffffffffffffffefefefefefefefefefefefefefefefefefefdfdfdfdfdfd
+fdfdfdfdfdfdfefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fefefefefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefdfdfdfdfdfd
+fefefefefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fefefefefefefffffffefefefcfcfcfafafafcfcfcfafafafafafafcfcfcfbfbfbf9f9f9fcfcfc
+fffffffefefefffffffffffffefefefdfdfdfcfcfcfdfdfdffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff
+showpage
+%%Trailer
+end
+%%EOF

Added: packages/openev/branches/upstream/current/doc/openevreport.cls
===================================================================
--- packages/openev/branches/upstream/current/doc/openevreport.cls	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/openevreport.cls	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,132 @@
+\NeedsTeXFormat{LaTeX2e}
+
+\ProvidesClass{openevreport}[1998/01/13 OpenEV report class]
+
+% Switch off some of the report options.
+ 
+\DeclareOption{a4paper}{\OptionNotUsed}
+\DeclareOption{a5paper}{\OptionNotUsed}
+\DeclareOption{b5paper}{\OptionNotUsed}
+\DeclareOption{letterpaper}{\OptionNotUsed}
+\DeclareOption{legalpaper}{\OptionNotUsed}
+\DeclareOption{executivepaper}{\OptionNotUsed}
+\DeclareOption{landscape}{\OptionNotUsed}
+\DeclareOption{twoside}{\OptionNotUsed}
+\DeclareOption{titlepage}{\OptionNotUsed}
+\DeclareOption{onecolumn}{\OptionNotUsed}
+
+% The default action for any option is to pass it to the report class.
+ 
+\DeclareOption*{\PassOptionsToClass{\CurrentOption}{report}}
+ 
+% Execute the options.
+ 
+\ProcessOptions
+ 
+% Load the report class.
+ 
+\LoadClass[oneside]{report}
+
+% Load the graphics package (for including figures)
+
+\RequirePackage{graphicx}
+
+% Load the PostScript fonts package
+
+\RequirePackage{pslatex}
+
+% Load the fancy headings package
+
+\RequirePackage{fancyhdr}
+
+% Load the latex2html package
+
+\RequirePackage{html}
+
+% Define the heading fonts
+
+\newcommand{\@headingfont}{\fontfamily{phv}\selectfont}
+
+\newcommand{\@chapterfont}{%
+   \@headingfont\bfseries\huge
+}
+\newcommand{\@sectionfont}{%
+   \@headingfont\bfseries\Large
+}
+\newcommand{\@subsectionfont}{%
+   \@headingfont\bfseries\slshape\large
+}
+\newcommand{\@subsubsectionfont}{%
+   \@headingfont\bfseries\normalsize
+}
+
+% Redefine headings
+
+\renewcommand\section{\@startsection {section}{1}{\z@}%
+                                     {-3.5ex \@plus -1ex \@minus -.2ex}%
+                                     {2.3ex \@plus.2ex}%
+                                     {\@sectionfont}}
+\renewcommand\subsection{\@startsection{subsection}{2}{\z@}%
+                                     {-3.25ex\@plus -1ex \@minus -.2ex}%
+                                     {1.5ex \@plus .2ex}%
+                                     {\@subsectionfont}}
+\renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}%
+                                     {-3.25ex\@plus -1ex \@minus -.2ex}%
+                                     {1.5ex \@plus .2ex}%
+                                     {\@subsubsectionfont}}
+
+\renewcommand{\chapter}{%
+   \if at openright\cleardoublepage\else\clearpage\fi
+   \global\@topnum\z@
+   \@afterindentfalse
+   \secdef\@chapter\@schapter}
+
+\def\@makechapterhead#1{%
+  \vspace*{30\p@}%
+  {\parindent \z@ \raggedright \normalfont
+    \ifnum \c at secnumdepth >\m at ne
+        \@chapterfont \@chapapp\space \thechapter.\hspace*{20\p@}%
+    \fi
+    \@chapterfont #1\par\nobreak
+    \vskip 40\p@
+  }}
+
+% Page margins
+
+\setlength{\textwidth}{6.0in}
+\setlength{\oddsidemargin}{0.25in}
+\setlength{\textheight}{8.0in}
+\setlength{\topmargin}{0.0in}
+
+\newcommand{\verticaloffset}[1]{\setlength{\voffset}{#1}}
+
+% Headers and footers.
+
+\pagestyle{fancy}
+
+\lhead{\scshape OpenEV}
+\chead{}
+\rhead{\scshape\leftmark}
+
+\lfoot{\slshape\today}
+\cfoot{}
+\rfoot{\thepage}
+
+\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
+
+\renewcommand{\headrulewidth}{0.4pt}
+\renewcommand{\footrulewidth}{0.4pt}
+
+\newcommand{\headtext}[1]{\rhead{\scshape #1}}
+\newcommand{\foottext}[1]{\lfoot{\slshape #1}}
+
+% Set maximum number of figures/tables at top of page to 1
+
+\setcounter{topnumber}{1}
+\setcounter{totalnumber}{2}
+
+% Other useful commands
+
+% That's it!
+
+\endinput

Added: packages/openev/branches/upstream/current/doc/openevreport.perl
===================================================================
--- packages/openev/branches/upstream/current/doc/openevreport.perl	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/openevreport.perl	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,16 @@
+package main;
+
+&do_require_package('report');
+&do_require_package('graphicx');
+&do_require_package('html');
+
+sub do_env_members {
+    &do_env_description;
+}
+
+&ignore_commands( <<_IGNORED_CMDS_);
+headtext # {}
+foottext # {}
+_IGNORED_CMDS_
+
+1;   # Must be last line

Added: packages/openev/branches/upstream/current/doc/phaseclip.fig
===================================================================
--- packages/openev/branches/upstream/current/doc/phaseclip.fig	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/phaseclip.fig	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,39 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 2700 3600 1200 1200 2700 3600 3900 3600
+1 3 0 1 0 0 100 0 20 0.000 1 0.0000 4825 2700 50 50 4825 2700 4875 2700
+1 3 0 1 0 0 100 0 20 0.000 1 0.0000 3925 3075 50 50 3925 3075 3975 3075
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 600 3600 4800 3600
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 2700 5700 2700 1500
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+	 1500 2400 3900 2400 3900 4800 1500 4800 1500 2400
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 2700 3600 4800 2700
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4125 2700 4050 2925
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4290 4455 3990 4605
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 1950 4875 2025 4650
+4 0 0 100 0 22 12 0.0000 4 135 150 2850 1500 Q\001
+4 0 0 100 0 22 12 0.0000 4 135 60 4875 3525 I\001
+4 0 0 100 0 20 12 0.0000 4 180 585 5100 2625 sample\001
+4 0 0 100 0 20 12 0.0000 4 180 405 5100 2850 point\001
+4 0 0 100 0 20 12 0.0000 4 180 300 4125 2400 clip\001
+4 0 0 100 0 20 12 0.0000 4 180 405 4125 2625 point\001
+4 0 0 100 0 20 12 0.0000 4 180 1185 4200 4350 LUT boundary\001
+4 0 0 100 0 20 12 0.0000 4 135 750 1650 5100 maximum\001
+4 0 0 100 0 20 12 0.0000 4 180 855 1650 5325 magnitude\001
+4 0 0 100 0 22 12 0.0000 4 135 150 2775 3825 O\001

Added: packages/openev/branches/upstream/current/doc/toolbar.eps
===================================================================
--- packages/openev/branches/upstream/current/doc/toolbar.eps	                        (rev 0)
+++ packages/openev/branches/upstream/current/doc/toolbar.eps	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,3931 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: GIMP PostScript file plugin V 1.06 by Peter Kirchgessner
+%%Title: /home/srawlin/devel/doc/technote/gview/toolbar.eps
+%%CreationDate: Fri Jun 16 11:22:21 2000
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%Pages: 1
+%%BoundingBox: 14 14 202 99
+%%EndComments
+%%BeginProlog
+% Use own dictionary to avoid conflicts
+5 dict begin
+%%EndProlog
+%%Page: 1 1
+% Translate for offset
+14.400000 14.400000 translate
+% Translate to begin of first scanline
+0.000000 84.071856 translate
+187.200000 -84.071856 scale
+% Variable to keep one line of raster data
+/scanline 334 3 mul string def
+% Image geometry
+334 150 8
+% Transformation matrix
+[ 334 0 0 150 0 0 ]
+{ currentfile scanline readhexstring pop } false 3
+colorimage
+8080804b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b5f5b53776e5e575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+575757575757575757575757575757575757575757575757575757575757575757575757575757
+57575757575757575757575770695c6762564b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b808080
+4b4b4b464646858585858585858585858585858585858585858585858585858585858585858585
+858585858585858585858585858585858585858585858585858585858585858585858585858585
+8585856f6e6e7a7771a29c91878787878787878787878787878787878787878787878787878787
+878787878787878787878787878787878787878787878787878787878787878787878787878787
+878787878787878787878787878787878787878787878787878787878787878787878787898989
+8b8b8b8b8b8b8b8b8b8b8b8b8989898888888787878787878787878989898a8a8a8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
+8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8a8a8a898989878787878787
+8787878888888989898b8b8b8b8b8b8b8b8b8a8a8a898989878787878787878787878787878787
+878787878787878787878787878787878787878787878787878787878787878787878787878787
+878787878787878787878787878787878787878787878787878787878787878787878787878787
+878787878787878787878787837e74837e76858585858585858585858585858585858585858585
+858585858585858585858585858585858585858585858585858585858585858585858585858585
+858585858585858585858585858585858585858585858585858585858585858585858585858585
+858585858585858585858585858585858585858585858585858585858585858585858585858585
+858585858585858585858585858585858585858585858585858585858585858585858585858585
+8585858585858585858585858585858585855f5f5e1515154b4b4b
+4b4b4b858585f8f8f8edededededededededededededededededededededededededededededed
+ededededededededededededededededededededededededededededededededededededededed
+edededc7c7c6cac9c9f7f6f7d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9
+d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9
+d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9e4dde5f0eef0
+f5f5f5f6f6f6f6f6f6e6e2e8dcd6ded7d2dad7d2d9d7d2d9e3dde4efedf0f5f5f5f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6
+f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f5f5f5efedf0e6dfe6d9d3db
+d7d2d9d7d2dadcd7dee6e2e8f6f6f6f6f6f6f5f5f5f0edf0e4dde4d7d2d9d7d2d9d7d2d9d7d2d9
+d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9
+d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9d7d2d9
+d7d2d9d7d2d9d7d2d9d7d2d9bab8bad0d0cfffffffedededededededededededededededededed
+ededededededededededededededededededededededededededededededededededededededed
+ededededededededededededededededededededededededededededededededededededededed
+ededededededededededededededededededededededededededededededededededededededed
+ededededededededededededededededededededededededededededededededededededededed
+ededededededededededededededededededadacab2726264b4b4b
+4b4b4b858585edededcbcbcbc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7a9a8a8bababaebe8ed9e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea5
+9e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea5
+9e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea5a090a7c9b8cbe3dee4
+ececececececeae8e9baadbfa495ab9e8ea59f8fa6a090a7c7b7cae2dce3ececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ececececececececececececececececececececececececececececececececececececececec
+ecececececececececececececececececececececececececececececece2dce2d0bfd1a595ac
+9f8fa69e8ea5a595acbaaec0eae8e9ecebebececece3dde3c8b8cba090a79e8ea59e8ea59e8ea5
+9e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea5
+9e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea59e8ea5
+9e8ea59e8ea59e8ea59e8ea5959098c2c2c2ffffffc9c9c8c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7
+c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c79b9b9b2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b2afb3b2b0b2b1afb2b1afb2b2afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92839b92839a92849a92839a92849a92839b92839b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b9d8ca4dbd9dce6e6e6
+e6e6e6e5e5e5dacbd49e8fa393849b93849b96869d9d8ca4d9d6dae6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6a495ab
+95869d93849b93849b9f90a4dacbd4e2dde0e6e6e6e6e6e6dad8db9d8ca493849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1b0b3b2b0b2b1b0b2b1afb2b1b0b2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb2b1b0b2b1afb2b1b0b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839b92839a92839a92839b92839a92839a92839b92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b94859bad9eb3dedbdee6e6e6
+e6e6e6e4e4e4b7a8b998899f93849b93849b9a8ca2ad9eb3dcd9dde6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6b3a6b9
+9a8ba293849b93849b998aa0b7a8b9d7d3d8e6e6e6e6e6e6dddbdead9eb393849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1b0b2b1afb2b1afb3b2b0b2b1b0b2b1b0
+b2b1b0b2b1afb2b1afb2b1afb2b1b0b2b1b0b3b2b0b2b1afb2b1afb2b1afb2b1b0b2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839a92849a92839a92839a92839a92839a93839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b97889fcdc6d0e2e1e2e6e6e6
+e4e4e4e2e2e29c8da494859c93849b93849ca699adcdc6d0e1e0e2e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6d0cad3
+a598ac93849c93849b94859c9c8da4cec9d0e5e5e5e6e6e6e2e1e2cdc6d096879f93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92839a92839a92849a92839a92849a92839b92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b95869da091a8e6e6e6e6e6e6e6e6e6
+dcdaddd1ccd393849b93849b93849b9888a0b3a8b9e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6afafaf9b9b9b9b9b9b9b9b9b9b9b9bd1d1d1e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e3e3e39b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9bcccccce6e6e6e6e6e69b9b9bd8d8d8
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6b7b7b7bcbcbce6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+b2a7b89888a093849b93849b93849bbfb7c3dcdadde6e6e6e6e6e6e6e6e69f90a895869d93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afafaeacafaeacafaeac
+afaeadb2b1afafaeacafaeacafaeacb0afaeb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb93849a92849a93839a93849b92849a93849a92849a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b9889a0af9fb5e6e6e6e6e6e6e6e6e6
+cbc4cfb0a2b793849b93849b93849b9f90a6beb3c2e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6c8c8c84c4c4c202020202020202020202020969696d6d6d6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+dedede0000001b1b1b202020202020202020202020202020a1a1a1e6e6e6e6e6e6000000bcbcbc
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6565656656565e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+bdb2c29f8fa693849b93849b93849ba799afccc5cfe6e6e6e6e6e6e6e6e6af9eb49889a093849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1afafaeacafaeacafaeacafaeacafaeacafaeac
+afaeacafaeacafaeacb0afadb2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b1b0aeafaeacafaeacafaeacb1b0afb3b2b0b3b2b0b3b2b0afaeadafaeacb1b0aeb3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1af999999989898989898
+9c9c9cb2b1af9a9a9a989898989898a3a3a2b3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92839a93839b92839b92839b92839a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b9a8ca2b8abbee6e6e6e6e6e6e6e6e6
+c6becaa596ad93849b93849b93849ba495abc5bbc9e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6131313a8a8a8e6e6e6e6e6e6e6e6e6e6e6e64141417b7b7be6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+dedede000000c3c3c3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6000000bcbcbc
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6565656656565e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+c4bac8a395aa93849b93849b93849ba091a8c6bfcae6e6e6e6e6e6e6e6e6b8aabe9a8ba293849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af999999999999999999999999999999999999
+999999999999999999a09f9fb2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a4a4a3999999999999999999abaaa9b3b2b0b3b2b0b3b2b09c9c9c999999a6a5a4b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1b0b3b2b0b2b1afb2b1afb2b1b0b2b1af999999252525060606
+262626fcfbfb9d9d9d2727270606066b6b6bfcfbfbb7b6b4b2b1afb2b1afb2b1b0b2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92839a92839a92839a92839a92839a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849ba293a7d9cad3e6e6e6e6e6e6e6e6e6
+c1b9c59d8ca493849b93849b93849bb4a5b6dcd2d8e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6909090090909cacacae3e3e3e6e6e6e6e6e6e6e6e6e6e6e6dfdfdf6b6b6b575757e6e6e6
+e6e6e6090909bdbdbd9a9a9a090909090909e1e1e1e6e6e6e6e6e6e6e6e6e6e6e6101010090909
+090909666666e6e6e6e6e6e6e6e6e6646464636363b3b3b3090909090909878787e6e6e6e6e6e6
+dedede000000c3c3c3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6dcdcdc2f2f2f
+969696e6e6e6e6e6e6e6e6e63434348d8d8de0e0e0e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+dcd1d8b3a4b593849b93849b93849b9a89a1c2bac6e6e6e6e6e6e6e6e6e6d8cad3a192a693849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af9999991e1e1e060606060606060606060606
+060606060606060606494949f5f4f4b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a4a4a38f8f8f060606060606b2b2b2d5d5d4aaa9a89a9999191919060606828282e4e3e3b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1af999999202020000000
+212121fefefe9d9d9d222222000000686868ffffffb7b6b5b2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839b92839a92839a92839a92839a92839a92839b92839b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849ba396a9ded7dbe6e6e6e6e6e6e6e6e6
+bfb7c39888a093849b93849b93849bb7abb9e0dbdee6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e68c8c8c000000d2d2d2e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e66a6a6a525252e6e6e6
+e6e6e60000006060607474746f6f6f6f6f6f767676cfcfcfe6e6e6d5d5d57676766f6f6f6f6f6f
+6f6f6f727272767676e0e0e0e6e6e65e5e5e3030307474746f6f6f6f6f6f737373a2a2a2e6e6e6
+dedede000000c3c3c3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6292929
+939393e6e6e6e6e6e6e6e6e62d2d2d8f8f8fe6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e0dbdeb6aab993849b93849b93849b97879ec0b8c4e6e6e6e6e6e6e6e6e6ded7dba295a893849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af999999191919000000000000000000000000
+000000000000000000464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a4a4a38f8f8f0000000000005c5c5cafafaea8a8a74e4e4e0a0a0a000000818181e6e5e5b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb3b1afb2b1afb2b1afa1a1a05f5e5e4d4d4d
+646464fefefea5a5a46060604d4d4d969696ffffffb7b6b5b2b1afb2b1afb3b1afb3b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839b92839a92839a92839a92839a92839a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849ba499aae3e3e3e6e6e6e6e6e6e6e6e6
+bdb5c194849c93849b93849b93849bbab2bee4e4e4e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e68c8c8c000000d2d2d2e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e66a6a6a525252e6e6e6
+e6e6e60000003838387c7c7ce6e6e6e6e6e6040404b8b8b8e6e6e6c4c4c4000000dddddde6e6e6
+e6e6e6848484000000dadadae6e6e65e5e5e1c1c1c6a6a6ae6e6e6e5e5e56262625a5a5ae6e6e6
+dedede000000888888a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0cdcdcde6e6e6e6e6e6e6e6e6292929
+939393e6e6e6e6e6e6e6e6e62d2d2d8f8f8fe6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e4e4e4b9b1bd93849b93849b93849b93849bbeb6c2e6e6e6e6e6e6e6e6e6e3e3e3a397aa93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af9999991919193232324d4d4d4d4d4d4d4d4d
+4d4d4d3e3e3e1d1d1d464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a9a8a79999982e2e2e0505050000005c5c5c7474740000000000003535359b9b9bd6d6d5b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b2afb3b2b0b2b1afb2b1afb2b1afb2b1afafafade4e4e3f2f2f2
+f2f2f2f5f5f5b3b2b0e3e3e2f2f2f2f3f3f3f5f5f5b7b6b4b2b1afb2b2afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92849a92849a92849a92849a92849a92849a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849ba599abe5e5e5e6e6e6e6e6e6e6e6e6
+bcb5c093849b93849b93849b93849bbab3bee5e5e5e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e68c8c8c000000d2d2d2e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e66a6a6a525252e6e6e6
+e6e6e6000000bbbbbbe6e6e6e6e6e6e6e6e6040404b8b8b8e6e6e6c4c4c4000000c2c2c2c9c9c9
+c9c9c9747474000000dadadae6e6e65e5e5e5d5d5de6e6e6e6e6e6e5e5e56262625a5a5ae6e6e6
+dedede0000001717171c1c1c1c1c1c1c1c1c1c1c1c1c1c1ca0a0a0e6e6e6e6e6e6e6e6e6404040
+8b8b8bd6d6d6dededec9c9c93e3e3e9a9a9ae6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e5e5e5b9b1be93849b93849b93849b93849bbdb6c1e6e6e6e6e6e6e6e6e6e5e5e5a498aa93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af999999191919a6a6a6fafafaf5f5f5f5f5f5
+f5f5f5c9c8c75f5f5e464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b1b0ae9c9c9b2222220505050000000000000000001a1a1ab2b2b2d3d3d2b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1af999999989898989898
+9c9c9cb2b1af9a9a9a999999989898a3a3a2b3b2b0b2b2afb2b1afb2b2afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839a92839a92839a92839a92849a92849a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849ba498aae3e3e3e6e6e6e6e6e6e6e6e6
+bdb5c194849c93849b93849b93849bb9b1bde3e3e3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e68c8c8c000000d2d2d2e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e66a6a6a525252e6e6e6
+e6e6e6000000bbbbbbe6e6e6e6e6e6e6e6e6040404b8b8b8e6e6e6c4c4c4000000000000000000
+000000000000000000dadadae6e6e65e5e5e5d5d5de6e6e6e6e6e6e5e5e56262625a5a5ae6e6e6
+dedede000000c3c3c3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+525252696969a5a5a5000000b8b8b8e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e3e3e3b8b0bc93849b93849b93849b94849cbeb6c2e6e6e6e6e6e6e6e6e6e3e3e3a397a993849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af999999191919a6a6a6dbdad9b3b2b0b3b2b0
+b3b2b0a2a2a1535353464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b09c9b9b2d2d2d000000000000000000ddddddcacac8b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1af999999272727090909
+292929fafafa9d9d9d2929290909096c6c6cfafafab7b6b4b2b1afb2b1afb3b1afb2b1afb3b1af
+b2b1af878786a0a0a0e9e6eb92839b92839a92839a92839b92839b92839b92839b92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849ba293a7dacdd5e6e6e6e6e6e6e6e6e6
+c1b8c49c8ba393849b93849b93849bb5a6b6ddd4dae6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e68c8c8c000000d2d2d2e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e66a6a6a525252e6e6e6
+e6e6e6000000bbbbbbe6e6e6e6e6e6e6e6e6040404b8b8b8e6e6e6c4c4c4000000d0d0d0d8d8d8
+d8d8d8d8d8d8d8d8d8e5e5e5e6e6e65e5e5e5d5d5de6e6e6e6e6e6e5e5e56262625a5a5ae6e6e6
+dedede000000c3c3c3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+525252696969a5a5a5000000b8b8b8e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+ddd3d9b4a6b693849b93849b93849b9989a0c2bac5e6e6e6e6e6e6e6e6e6dacdd5a192a693849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+9f9f9f9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9b9b9bb3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af999999191919a6a6a6dbdad9b3b2b0b3b2b0
+b3b2b0a2a2a1535353464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b1b0ae9a9a9a1a19190202020000000000000000000d0d0db0b0b0d6d5d4b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1af999999202020000000
+212121ffffff9d9d9d222222000000686868ffffffb7b6b5b2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839b92839a92839a92839a93839a92839a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b9c8da3c0b3c3e6e6e6e6e6e6e6e6e6
+c5bdc8a393ab93849b93849b93849ba899aecbc1cde6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6d0d0d0aeaeae414141b7b7b7e6e6e6e6e6e6e6e6e6e6e6e6686868777777c2c2c2e6e6e6
+e6e6e60000002d2d2d727272e6e6e6e6e6e6040404b8b8b8e6e6e6c4c4c4000000dddddde6e6e6
+e6e6e69b9b9b373737dddddde6e6e65e5e5e5d5d5de6e6e6e6e6e6e5e5e56262625a5a5ae6e6e6
+dedede000000c3c3c3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+c2c2c2777777585858aeaeaedbdbdbe6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+cac0cca799ad93849b93849b93849b9e8fa6c5bec9e6e6e6e6e6e6e6e6e6c0b2c39c8da393849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+9e9e9e5353532424242424242424242424242424242424242424242d2d2decececbab9b8b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af99999919191980807fb3b2b19f9f9e9f9f9e
+9f9f9e9b9b9b535353464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a8a7a69797962424240404040000006565657f7f7f0000000000002a2a2a969695d9d9d8b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afa0a0a05a5a5a484848
+606060fefefea4a4a45c5c5c484848939393ffffffb7b6b5b2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb93849a93849a93849a93849a93849a93839a93849b92849a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b9a8ba1b5a7bbe6e6e6e6e6e6e6e6e6
+c7c0cba99ab093849b93849b93849ba293a9c3b8c7e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e64f4f4f8a8a8aa4a4a4a4a4a4a4a4a4a4a4a45d5d5d999999e6e6e6e6e6e6
+e6e6e6000000353535636363a4a4a4a4a4a4434343c5c5c5e6e6e6cecece414141a1a1a1a4a4a4
+a4a4a47a7a7a414141dddddde6e6e65e5e5e5d5d5de6e6e6e6e6e6e5e5e56262625a5a5ae6e6e6
+dedede0000008c8c8ca4a4a4a4a4a4a4a4a4a4a4a4a4a4a4cfcfcfe6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e67c7c7c404040e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+c2b7c6a293a993849b93849b93849ba293aac8c1cce6e6e6e6e6e6e6e6e6b5a6bb998aa193849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+9e9e9e3d3d3d0000000000000000000000000000000000000000000b0b0bffffffbdbcbab3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af9999991919195353527777766d6d6d6d6d6d
+6d6d6d6d6d6d3b3b3b464646f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a4a4a38f8f8f0000000000003232329c9c9cb2b2b1484848090909000000818181e6e5e5b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb3b1afb2b1afb2b1afb3b2b0e8e8e7f7f7f6
+f7f7f6f7f7f6b6b5b3e7e7e6f7f7f6f7f7f6f7f7f6b7b6b4b2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92849a92849a92849a92839a92849a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b98889fac9cb2e6e6e6e6e6e6e6e6e6
+cec8d2b6aabd93849b93849b93849b9e8ea5bcb0c0e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e64e4e4e171717171717171717171717abababe6e6e6e6e6e6e6e6e6
+e6e6e6000000bbbbbb9f9f9f171717171717e1e1e1e6e6e6e6e6e6e6e6e6e6e6e61e1e1e171717
+1717176f6f6fe6e6e6e6e6e6e6e6e66c6c6c6b6b6be6e6e6e6e6e6e5e5e56f6f6f686868e6e6e6
+dfdfdf1717171717171717171717171717171717171717179e9e9ee6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6878787515151e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+bbafbf9d8da493849b93849b93849bac9fb3cfc9d2e6e6e6e6e6e6e6e6e6ab9bb197889f93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a0a09f4d4d4d1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a242424ffffffbdbcbab3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1af9b9b9b2f2f2f1a1a1a1a1a1a1a1a1a1a1a1a
+1a1a1a1a1a1a1a1a1a595959f7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+a6a5a49393931a1a1a1a1a1ab5b5b5d3d2d1cac9c8f7f7f63737371a1a1a8e8e8ee6e5e5b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb93839a92839a92839a92839a92839a92839a93839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849c9a8ba3e6e6e6e6e6e6e6e6e6
+e4e4e4e0dfe093849b93849b93849b94859daea4b5e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6000000bbbbbbe6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+ada3b494859d93849b93849b93849bc9c5cce4e4e4e6e6e6e6e6e6e6e6e6998aa293849c93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0e0e0dfffffffffffffffffffffffffffffffffffffffffffffffffffffffbdbcbab3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0f2f2f1ffffffffffffffffffffffff
+fffffffffffffffffffffffff7f7f7b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b7b6b5ffffffffffffc9c8c7b3b2b0b3b2b0b3b2b0f4f4f4ffffffffffffe6e5e5b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb3b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb93839a92839a92839a92839a93839a92839a93839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b94859cb8abbedfdde0e6e6e6
+e5e5e5e4e3e4a596ac95869d93849b93849b9e90a6b8abbededbdfe6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6000000bbbbbbe6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6bdb2c2
+9e8fa593849b93849b96879da596acd2cdd4e5e5e5e6e6e6dfdddfb8abbe94859c93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b6b5b3b8b8b6b8b8b6b8b8b6b8b8b6b8b8b6b8b8b6b8b8b6b8b8b6b8b8b6b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b7b7b5b8b8b6b8b8b6b8b8b6b8b8b6
+b8b8b6b8b8b6b8b8b6b8b8b6b8b7b5b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b8b8b6b8b8b6b4b3b1b3b2b0b3b2b0b3b2b0b8b7b5b8b8b6b8b8b6b7b6b4b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b2afb2b1afb2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb3b1afb3b1afb2b2afb2b2afb3b2b0b2b1afb2b1afb2b1afb3b1afb2b1afb3b2af
+b2b2af878786a0a0a0e9e6eb92849b92849b92849a92839b92849b92849b92849b92849a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849ba393a9dcdadde6e6e6
+e6e6e6e5e5e5ccbdca9c8da293849b93849b97889fa393a9dad7dbe6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6000000bbbbbbe6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6aa9cb0
+97889f93849b93849b9c8da2ccbdcaded9dde6e6e6e6e6e6dbd9dca393a993849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92849a92849a92839a92839a92839a93839b92839b92839b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b97879ec2b8c6dcdadd
+e6e6e6e5e5e5e0dadeaa9db197889f93849b94859c97879ec0b7c4dbd8dde6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6808080d3d3d3e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6dbd8dcc9c1cc9c8da3
+94859c93849b9788a0ab9eb2e0dadee4e2e3e6e6e6dcd9ddc1b8c597879e93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b2afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb93839a92839a93839a93839a92839a92839a93839b92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849ba495abcfcbd2
+e4e4e4e6e6e6e6e6e6c7bfcca497ac94859c93849b93849ba394aacdc8d0e4e4e4e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e4e4e4cdc7cfa697ad95869d
+93849b94859ca598acc8c0cce6e6e6e6e6e6e4e4e4cfcad1a394aa93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb3b1afb2b1afb2b1afb3b2b0b2b1afb3b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b3b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1af878786a0a0a0e9e6eb92839a92839a92839a92839a92839a92839a92839a92839a93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849bc6c1c9
+e2e2e2e6e6e6e6e6e6e6e6e6b4aab997889f93849b93849b93849bc4bec6e2e2e2e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e5e5e5e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e2e2e2c3bdc693849b93849b
+93849b97889fb5abbae6e6e6e6e6e6e6e6e6e2e2e2c5c0c893849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b93849b
+93849b93849b93849b93849b767179abababffffffb4b3b1b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b6c6c6773726dafacae83758b83758b83758b83758b83758b83758b83758b83758b83758b
+83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b
+83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b8a7c92
+94869d9c8da49c8da49c8da495859c8b7c9283758b83758b83758b8a7c9294869c9c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da4
+9c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da49c8da494869c8a7c9283758b83758b
+83758b8b7c9295869c9c8da49c8da49c8da494869c8a7c9283758b83758b83758b83758b83758b
+83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b
+83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b83758b
+83758b83758b83758b83758b5d585b7b7b75bab9b79c9c9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b
+9b9b9b9b9b9ba7a6a5b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999996060602a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a1d1d1c332e245c5242302c32302c32302c32302c32302c32302c32302c32302c32302c32
+302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32
+302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32312d33
+332f36353138353138353138353037322e34302c32302c32302c32312d33332f35353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138353138353138353138353138
+353138353138353138353138353138353138353138353138353138332f35312d33302c32302c32
+302c32322e34353037353138353138353138332f35312d33302c32302c32302c32302c32302c32
+302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32
+302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32302c32
+302c32302c32302c32302c323f372a3e382a3231312a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a
+2a2a2a2a2a2a767676bfbebdb3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b2424246f6f6f717171717171707070707070
+7070706f6f6f6f6f6f6f6f6f6f6f6f6f6f6f707070707070707070707070717171727272727272
+7272727272727e7b768e887d787878787878777777767676747474737373727272737373747474
+7575757878787a7a7a7b7b7b7c7c7c7c7c7c7b7b7b797979787878787878787878797979787878
+767676757575747474737373727272747474757575757575747474737373737373737373747474
+757575757575767676777777787878797979797979797979777777757575747474757575757575
+7676767777777878787979797a7a7a7a7a7a787878777777767676767676757575757575757575
+757575747474737373747474757575767676767676767676767676787878787878767676747474
+737373737373747474757575757575757575747474737373737373737373737373737373737373
+747474757575757575727272727272737373747474747474737373727272727272727272737373
+7474747575757777777878787a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7979797979797a7a7a
+7a7a7a7a7a7a7a7a7a7b7b7b7b7b7b7c7c7c7c7c7c7c7c7c7a7a7a767676757575747474737373
+737373737373747474757575757575757575757575757575757575767676767676797979797979
+787878787878777777767676767676767676767676777777787878787878787878787878787878
+7a7a7a7a7a7a7a7a7a797979797979797979787878787878777777757575747474737373727272
+7373737474747676767878787a7a7a7b7b7b7c7c7c7c7c7c7b7b7b797979787878787878787878
+797979797979777777757575747474737373727272747474757575757575747474737373737373
+737373737373747474767676767676777777787878787878797979797979777777757575747474
+7474747575757676767777777878787979797a7a7a7a7a7a787878777777767676767676757575
+757575757575757575747474737373747474757575767676767676767676767676787878787878
+777777747474737373737373858078807c756e6e6e6e6e6e6c6c6c6b6b6b6b6b6b6b6b6b6b6b6b
+6b6b6b6b6b6b6c6c6c6d6d6d6d6d6d6a6a6a6a6a6a6b6b6b6c6c6c6c6c6c6b6b6b6a6a6a6a6a6a
+6a6a6a6b6b6b6c6c6c6d6d6d6f6f6f717171727272727272727272727272727272727272717171
+7171717272727272727272727272727373737373737474747575757474747272726f6f6f6d6d6d
+6c6c6c6b6b6b6b6b6b6b6b6b6c6c6c6d6d6d6e6e6e6e6e6e6e6e6e6e6e6e6d6d6d6e6e6e6f6f6f
+717171030303646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b434343cdcdcdcfcfcfd0d0d0d0d0d0cfcfcf
+cfcfcfcdcdcdcdcdcdcdcdcdcdcdcdcecececfcfcfcfcfcfcfcfcfcfcfcfd0d0d0d2d2d2d3d3d3
+d4d4d4d3d3d3d3d3d3d2d2d2d1d1d1d0d0d0cfcfcfcdcdcdcacacac8c8c8c7c7c7c7c7c7c9c9c9
+cbcbcbcfcfcfd3d3d3d5d5d5d7d7d7d7d7d7d7d7d7d3d3d3d1d1d1d1d1d1d1d1d1d2d2d2d2d2d2
+cecececccccccacacac8c8c8c6c6c6c9c9c9cbcbcbcacacac9c9c9c7c7c7c7c7c7c8c8c8c9c9c9
+cbcbcbcccccccecececfcfcfd1d1d1d3d3d3d3d3d3d3d3d3cecececbcbcbcacacacbcbcbcccccc
+cecececfcfcfd0d0d0d2d2d2d3d3d3d3d3d3d1d1d1cecececdcdcdcdcdcdcccccccccccccccccc
+cccccccbcbcbc9c9c9cbcbcbcccccccecececfcfcfcfcfcfcececed0d0d0d1d1d1cfcfcfcbcbcb
+c9c9c9c8c8c8c9c9c9cacacacccccccccccccacacac9c9c9c9c9c9c8c8c8c8c8c8c7c7c7c6c6c6
+c8c8c8cacacacacacac6c6c6c6c6c6c7c7c7c9c9c9c9c9c9c8c8c8c6c6c6c6c6c6c6c6c6c8c8c8
+c9c9c9cacacacdcdcdd0d0d0d3d3d3d3d3d3d3d3d3d2d2d2d2d2d2d2d2d2d0d0d0d0d0d0d2d2d2
+d3d3d3d3d3d3d2d2d2d4d4d4d4d4d4d5d5d5d6d6d6d5d5d5d2d2d2cecececdcdcdcbcbcbcacaca
+cacacacacacacbcbcbcbcbcbcccccccbcbcbcbcbcbcbcbcbcacacacbcbcbcccccccfcfcfd0d0d0
+d0d0d0cfcfcfcececececececdcdcdcdcdcdcececececececfcfcfcfcfcfcfcfcfcfcfcfd0d0d0
+d2d2d2d3d3d3d4d4d4d3d3d3d3d3d3d2d2d2d1d1d1d0d0d0cfcfcfcccccccacacac8c8c8c7c7c7
+c7c7c7c9c9c9cccccccfcfcfd3d3d3d5d5d5d7d7d7d7d7d7d6d6d6d3d3d3d1d1d1d1d1d1d1d1d1
+d2d2d2d2d2d2cecececcccccc9c9c9c8c8c8c6c6c6c9c9c9cacacacacacac9c9c9c7c7c7c7c7c7
+c8c8c8c9c9c9cacacacccccccecececfcfcfd1d1d1d2d2d2d4d4d4d3d3d3cecececbcbcbcacaca
+cacacacccccccdcdcdcfcfcfd0d0d0d1d1d1d3d3d3d3d3d3d1d1d1cfcfcfcdcdcdcdcdcdcccccc
+cccccccccccccccccccbcbcbc9c9c9cbcbcbcccccccecececfcfcfcfcfcfcececed1d1d1d1d1d1
+cfcfcfcbcbcbc9c9c9c8c8c8c9c9c9cbcbcbcccccccccccccbcbcbc9c9c9c9c9c9c8c8c8c8c8c8
+c7c7c7c6c6c6c8c8c8cacacacacacac6c6c6c6c6c6c7c7c7c8c8c8c9c9c9c8c8c8c6c6c6c6c6c6
+c6c6c6c8c8c8c9c9c9cacacacdcdcdd1d1d1d3d3d3d3d3d3d3d3d3d3d3d3d2d2d2d1d1d1d0d0d0
+d0d0d0d1d1d1d2d2d2d3d3d3d3d3d3d4d4d4d4d4d4d5d5d5d6d6d6d6d6d6d2d2d2cfcfcfcccccc
+cbcbcbcacacacacacacacacacbcbcbcbcbcbcccccccccccccbcbcbcbcbcbcacacacbcbcbcdcdcd
+cfcfcf060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9cbcbcbcdcdcdcdcdcdcdcdcd
+cdcdcdcdcdcdcccccccdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcfcfcfcecececececed0d0d0d1d1d1
+d3d3d3d4d4d4d4d4d4d3d3d3d3d3d3d2d2d2d0d0d0cfcfcfcccccccbcbcbc9c9c9c8c8c8c9c9c9
+cacacacececed1d1d1d3d3d3d6d6d6d7d7d7d8d8d8d5d5d5d3d3d3d1d1d1d1d1d1d2d2d2d2d2d2
+cfcfcfcdcdcdcbcbcbc9c9c9c7c7c7c8c8c8c9c9c9c9c9c9c9c9c9c8c8c8c8c8c8c9c9c9cacaca
+cccccccdcdcdcdcdcdd0d0d0d3d3d3d5d5d5d6d6d6d5d5d5cfcfcfcccccccbcbcbcccccccecece
+cecececfcfcfd0d0d0d1d1d1d1d1d1d2d2d2d1d1d1cecececdcdcdcccccccccccccccccccdcdcd
+cececececececccccccecececfcfcfd1d1d1d1d1d1d1d1d1d0d0d0d0d0d0d1d1d1d0d0d0cecece
+cbcbcbc8c8c8c9c9c9cacacacccccccccccccecececececececececccccccacacac7c7c7c6c6c6
+c8c8c8c9c9c9c9c9c9c7c7c7c6c6c6c7c7c7c8c8c8c9c9c9c9c9c9c8c8c8c8c8c8c8c8c8c9c9c9
+c9c9c9c9c9c9cbcbcbcdcdcdd0d0d0d2d2d2d1d1d1d0d0d0cfcfcfcfcfcfcdcdcdcdcdcdcdcdcd
+cfcfcfd0d0d0d0d0d0d1d1d1d0d0d0d0d0d0d1d1d1d1d1d1d0d0d0d0d0d0d0d0d0cfcfcfcecece
+cececed0d0d0cececececececccccccccccccbcbcbcacacac9c9c9c8c8c8c9c9c9cbcbcbcdcdcd
+cdcdcdcecececececececececccccccdcdcdcecececdcdcdcdcdcdcdcdcdcfcfcfcececececece
+cfcfcfd1d1d1d4d4d4d4d4d4d4d4d4d3d3d3d3d3d3d2d2d2d1d1d1cecececdcdcdcbcbcbcacaca
+c9c9c9c9c9c9cbcbcbcececed1d1d1d3d3d3d5d5d5d7d7d7d7d7d7d5d5d5d2d2d2d1d1d1d1d1d1
+d2d2d2d2d2d2cfcfcfcccccccbcbcbcacacac7c7c7c8c8c8c9c9c9c9c9c9c9c9c9c8c8c8c8c8c8
+c8c8c8cacacacbcbcbcdcdcdcdcdcdcfcfcfd2d2d2d5d5d5d6d6d6d5d5d5cecececbcbcbcbcbcb
+cccccccececececececfcfcfd0d0d0d1d1d1d1d1d1d2d2d2d1d1d1cfcfcfcdcdcdcccccccccccc
+cccccccdcdcdcececececececccccccecececfcfcfd1d1d1d1d1d1d1d1d1d0d0d0d0d0d0d1d1d1
+d1d1d1cecececbcbcbc9c9c9c9c9c9cacacacccccccccccccdcdcdcecececdcdcdcccccccacaca
+c7c7c7c6c6c6c7c7c7c9c9c9c9c9c9c7c7c7c6c6c6c6c6c6c8c8c8c9c9c9c9c9c9c8c8c8c8c8c8
+c8c8c8c9c9c9c9c9c9c9c9c9cbcbcbcdcdcdd0d0d0d2d2d2d1d1d1d0d0d0d0d0d0cfcfcfcdcdcd
+cdcdcdcdcdcdcececed0d0d0d0d0d0d1d1d1d0d0d0d0d0d0d1d1d1d2d2d2d0d0d0d0d0d0d0d0d0
+cfcfcfcecececececed0d0d0cfcfcfcdcdcdcccccccccccccbcbcbcacacac9c9c9c8c8c8c9c9c9
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c5c5c5c7c7c7cbcbcbcdcdcdcdcdcd
+cecececdcdcdcccccccdcdcdcdcdcdcdcdcdcccccccbcbcbcdcdcdcdcdcdcdcdcdcecececfcfcf
+d2d2d2d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d1d1d1cdcdcdcdcdcdcccccccccccc
+cccccccecececececed0d0d0d3d3d3d6d6d6d9d9d9d8d8d8d4d4d4d2d2d2d2d2d2d2d2d2d1d1d1
+cecececccccccccccccbcbcbc9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9cacacacacacacacaca
+cacacacbcbcbccccccd0d0d0d4d4d4d7d7d7d8d8d8d6d6d6d1d1d1cdcdcdcccccccccccccecece
+cecececfcfcfd0d0d0d1d1d1d1d1d1d2d2d2d0d0d0cecececdcdcdcccccccccccccccccccecece
+d0d0d0d1d1d1d1d1d1d1d1d1d2d2d2d4d4d4d5d5d5d4d4d4d3d3d3d1d1d1d1d1d1d2d2d2d1d1d1
+cecececccccccbcbcbcbcbcbcccccccccccccfcfcfd1d1d1d1d1d1d0d0d0ccccccc8c8c8c7c7c7
+c8c8c8c9c9c9c9c9c9c8c8c8c7c7c7c7c7c7c8c8c8cacacacbcbcbcbcbcbcbcbcbcbcbcbcacaca
+cacacac9c9c9c9c9c9cacacaccccccd0d0d0cecececececececececdcdcdcbcbcbcbcbcbcbcbcb
+cdcdcdcecececececececececccccccdcdcdcecececececed0d0d0d0d0d0d1d1d1d0d0d0d0d0d0
+d0d0d0d2d2d2d2d2d2d1d1d1d0d0d0cdcdcdcccccccacacac7c7c7c5c5c5c5c5c5c8c8c8cbcbcb
+cdcdcdcecececececececececccccccdcdcdcecececdcdcdcccccccccccccdcdcdcececececece
+cececed0d0d0d2d2d2d3d3d3d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d2d2d2d1d1d1cececececece
+cccccccccccccccccccececececececfcfcfd2d2d2d6d6d6d8d8d8d8d8d8d4d4d4d3d3d3d2d2d2
+d2d2d2d2d2d2cecececccccccccccccbcbcbcacacac9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9
+cacacacacacacacacacbcbcbcccccccfcfcfd3d3d3d7d7d7d8d8d8d6d6d6d0d0d0cccccccccccc
+cccccccececececececfcfcfd0d0d0d1d1d1d1d1d1d1d1d1d0d0d0cecececdcdcdcccccccccccc
+cccccccececed0d0d0d1d1d1d1d1d1d1d1d1d2d2d2d4d4d4d5d5d5d4d4d4d3d3d3d1d1d1d1d1d1
+d2d2d2d2d2d2cecececccccccbcbcbcbcbcbcccccccccccccfcfcfd1d1d1d1d1d1cfcfcfcccccc
+c8c8c8c7c7c7c7c7c7c9c9c9c8c8c8c8c8c8c7c7c7c7c7c7c8c8c8cacacacbcbcbcbcbcbcbcbcb
+cbcbcbcacacacacacac9c9c9c9c9c9cacacaccccccd0d0d0cecececececececececdcdcdcbcbcb
+cbcbcbcbcbcbcccccccecececdcdcdcecececccccccccccccecececececed0d0d0d0d0d0d1d1d1
+d0d0d0d0d0d0d0d0d0d1d1d1d2d2d2d1d1d1cfcfcfcecececccccccacacac7c7c7c5c5c5c5c5c5
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c3c3c3c5c5c5c9c9c99a9a9a8d8d8d
+8d8d8d8d8d8d8c8c8c8d8d8d8d8d8d8d8d8d8c8c8c8c8c8c8d8d8d8d8d8d8d8d8d8d8d8d8e8e8e
+8e8e8e8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8e8e8e8d8d8d8d8d8d8d8d8d8c8c8c
+8d8d8d8d8d8d8d8d8d8e8e8e8f8f8f9090909090909090908f8f8f8e8e8e8e8e8e8e8e8e8e8e8e
+8d8d8d8d8d8d8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c
+8c8c8c8c8c8c8c8c8c8e8e8e8f8f8f9090909090908f8f8f8e8e8e8d8d8d8c8c8c8d8d8d8d8d8d
+8d8d8d8d8d8d8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8d8d8d8d8d8d8c8c8c8c8c8c8c8c8c8d8d8d
+8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f8f8f8f8f8f8f8f8f8f8f8f8e8e8e8e8e8e8e8e8e8e8e8e
+8d8d8d8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8e8e8e8e8e8e8e8e8e8e8e8e8c8c8c8b8b8b8b8b8b
+8b8b8b8c8c8c8c8c8c8b8b8b8b8b8b8b8b8b8b8b8b8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c
+8c8c8c8c8c8c8c8c8c8c8c8c8d8d8d8e8e8e8d8d8d8d8d8d8d8d8d8d8d8d8c8c8c8c8c8c8c8c8c
+8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e
+8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8d8d8d8c8c8c8c8c8c8b8b8b8a8a8a8a8a8a8b8b8b8c8c8c
+8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8c8c8c8c8c8c8d8d8d8d8d8d8d8d8d
+8d8d8d8e8e8e8e8e8e8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8e8e8e8d8d8d8d8d8d
+8d8d8d8c8c8c8d8d8d8d8d8d8d8d8d8e8e8e8e8e8e8f8f8f9090909090908f8f8f8f8f8f8e8e8e
+8e8e8e8e8e8e8d8d8d8d8d8d8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c
+8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8d8d8d8f8f8f9090909090908f8f8f8e8e8e8d8d8d8c8c8c
+8d8d8d8d8d8d8d8d8d8d8d8d8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8d8d8d8d8d8d8c8c8c8c8c8c
+8c8c8c8d8d8d8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f8f8f8f8f8f8f8f8f8f8f8f8e8e8e8e8e8e
+8e8e8e8e8e8e8d8d8d8d8d8d8c8c8c8c8c8c8c8c8c8c8c8c8d8d8d8e8e8e8e8e8e8e8e8e8c8c8c
+8b8b8b8b8b8b8b8b8b8c8c8c8c8c8c8b8b8b8b8b8b8b8b8b8b8b8b8c8c8c8c8c8c8c8c8c8c8c8c
+8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8e8e8e8d8d8d8d8d8d8d8d8d8d8d8d8c8c8c
+8c8c8c8c8c8c8d8d8d8d8d8d8d8d8d8d8d8d8c8c8c8d8d8d8d8d8d8d8d8d8e8e8e8e8e8e8e8e8e
+8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8d8d8d8d8d8d8c8c8ca8a8a8c4c4c4c3c3c3
+c5c5c5060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b3f3f3fc2c2c2c4c4c4c6c6c6848484878787
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5
+b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5bcbcbcc3c3c3c2c2c2
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c3c3c3c3c3c3c5c5c58383839a9a9a
+f3f3f3f2f2f2f1f1f1f2f2f2f2f2f2f1f1f1f1f1f1f1f1f1f2f2f2f1f1f1f0f0f0f1f1f1f2f2f2
+f1f1f1f1f1f1f0f0f0f1f1f1f2f2f2f1f1f1f2f2f2f2f2f2f1f1f1f2f2f2f1f1f1f1f1f1f1f1f1
+f1f1f1f1f1f1f0f0f0f0f0f0f0f0f0efefefefefefefefeff1f1f1f2f2f2f2f2f2f2f2f2f0f0f0
+f1f1f1f1f1f1f1f1f1f0f0f0efefeff0f0f0f0f0f0efefefefefefeeeeeeefefefefefefefefef
+efefefefefefeeeeeeeeeeeeeeeeeeeeeeeeefefeff0f0f0f0f0f0f0f0f0f0f0f0f0f0f0eeeeee
+eeeeeeeeeeeeeeeeeeefefefefefefeeeeeeeeeeeeeeeeeeefefefefefefeeeeeeeeeeeeeeeeee
+ededededededeeeeeeefefefeeeeeeeeeeeeeeeeeeeeeeeef0f0f0efefefefefefeeeeeeededed
+eeeeeeeeeeeeeeeeeeedededecececedededececececececededededededededededededeeeeee
+ecececebebebeaeaeaebebebebebebebebebebebebebebebebebebebebebebebebebebebebebeb
+ebebebebebebebebebebebebebebebeaeaeaebebebecececebebebebebebecececebebebebebeb
+eaeaeaebebebebebebebebebebebebebebebebebebeaeaeaeaeaeaeaeaeaebebebebebebebebeb
+ecececebebebe9e9e9e9e9e9ebebebececececececebebebe9e9e9e9e9e9eaeaeae9e9e9e8e8e8
+eaeaeaeaeaeae9e9e9eaeaeaeaeaeae9e9e9e9e9e9eaeaeaeaeaeae8e8e8e8e8e8e9e9e9eaeaea
+e9e9e9e9e9e9e8e8e8eaeaeaeaeaeaeaeaeaeaeaeae9e9e9eaeaeae9e9e9e9e9e9eaeaeae9e9e9
+e9e9e9e8e8e8e8e8e8e8e8e8e8e8e8e7e7e7e7e7e7e8e8e8e9e9e9eaeaeaeaeaeae9e9e9e8e8e8
+e9e9e9eaeaeae8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e7e7e7e6e6e6e6e6e6e7e7e7e7e7e7e7e7e7
+e7e7e7e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e7e7e7e8e8e8e8e8e8e9e9e9e8e8e8e6e6e6e6e6e6
+e6e6e6e6e6e6e7e7e7e7e7e7e7e7e7e7e7e7e6e6e6e7e7e7e7e7e7e6e6e6e5e5e5e5e5e5e6e6e6
+e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e7e7e7e7e7e7e6e6e6e5e5e5e5e5e5
+e6e6e6e6e6e6e5e5e5e4e4e4e5e5e5e5e5e5e4e4e4e4e4e4e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5
+e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3
+e3e3e3e3e3e3e2e2e2e3e3e3e2e2e2e2e2e2e3e3e3e4e4e4e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3
+e2e2e2e2e2e2e3e3e3e3e3e3e3e3e3e3e3e3e2e2e2e2e2e2e3e3e3e3e3e3e3e3e3e3e3e3e4e4e4
+e3e3e3e3e3e3e0e0e0e3e3e3e4e4e4e4e4e4e3e3e3e1e1e1cbcbcbe6e6e6dbdbdbc3c3c3c3c3c3
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c3c3c3c5c5c58383839a9a9a
+f3f3f3efefefecececefefefeeeeeeedededededededededefefefecececeaeaeaebebebefefef
+ecececebebebebebebecececf0f0f0edededefefefeeeeeeedededf0f0f0ecececedededececec
+ebebebebebebebebebeaeaeae9e9e9e8e8e8e7e7e7e8e8e8ebebebeeeeeeefefefeeeeeee9e9e9
+ecececececececececeaeaeae7e7e7e9e9e9e8e8e8e7e7e7e6e6e6e3e3e3e5e5e5e5e5e5e5e5e5
+e5e5e5e6e6e6e5e5e5e3e3e3e3e3e3e4e4e4e5e5e5e8e8e8e8e8e8eaeaeaeaeaeae8e8e8e4e4e4
+e3e3e3e3e3e3e4e4e4e5e5e5e5e5e5e4e4e4e4e4e4e5e5e5e5e5e5e5e5e5e4e4e4e3e3e3e2e2e2
+e2e2e2e2e2e2e4e4e4e5e5e5e4e4e4e3e3e3e3e3e3e3e3e3e8e8e8e6e6e6e5e5e5e3e3e3e0e0e0
+e2e2e2e3e3e3e2e2e2e0e0e0dddddde0e0e0dfdfdfdfdfdfdfdfdfe1e1e1e1e1e1e2e2e2e2e2e2
+dddddddadadad9d9d9dbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdadadadadadadbdbdbdbdbdbdbdbdb
+dcdcdcdcdcdcd9d9d9d9d9d9dadadad8d8d8dadadadcdcdcdbdbdbdbdbdbdcdcdcdbdbdbd9d9d9
+d9d9d9d9d9d9dadadad9d9d9dadadadadadad9d9d9d8d8d8d9d9d9d8d8d8d9d9d9dadadadadada
+dcdcdcd9d9d9d5d5d5d7d7d7dbdbdbdcdcdcdddddddadadad6d6d6d6d6d6d7d7d7d3d3d3d2d2d2
+d5d5d5d6d6d6d6d6d6d8d8d8d6d6d6d4d4d4d4d4d4d6d6d6d8d8d8d3d3d3d2d2d2d4d4d4d6d6d6
+d4d4d4d4d4d4d3d3d3d6d6d6d7d7d7d6d6d6d7d7d7d5d5d5d7d7d7d6d6d6d4d4d4d7d7d7d3d3d3
+d3d3d3d3d3d3d3d3d3d2d2d2d1d1d1d0d0d0d0d0d0d1d1d1d4d4d4d8d8d8d8d8d8d5d5d5d2d2d2
+d4d4d4d6d6d6d2d2d2d0d0d0d0d0d0d1d1d1d1d1d1cfcfcfcccccccccccccdcdcdcdcdcdcdcdcd
+cecececdcdcdcccccccbcbcbcbcbcbcccccccececed0d0d0d1d1d1d4d4d4d2d2d2cdcdcdcccccc
+cccccccccccccdcdcdcdcdcdcdcdcdcdcdcdcccccccdcdcdcdcdcdcccccccacacacacacacacaca
+cacacacbcbcbcccccccdcdcdcbcbcbcccccccbcbcbcdcdcdd0d0d0cecececbcbcbc8c8c8c9c9c9
+cbcbcbcbcbcbc9c9c9c6c6c6c8c8c8c8c8c8c7c7c7c7c7c7c9c9c9c9c9c9c9c9c9cacacac9c9c9
+c4c4c4c2c2c2c3c3c3c4c4c4c3c3c3c3c3c3c3c3c3c3c3c3c2c2c2c3c3c3c3c3c3c3c3c3c4c4c4
+c4c4c4c3c3c3c1c1c1c2c2c2c1c1c1c1c1c1c2c2c2c6c6c6c3c3c3c4c4c4c4c4c4c3c3c3c1c1c1
+c1c1c1c1c1c1c2c2c2c3c3c3c4c4c4c2c2c2c0c0c0c0c0c0c2c2c2c2c2c2c2c2c2c2c2c2c4c4c4
+c3c3c3c1c1c1bcbcbcc3c3c3c4c4c4c5c5c5c3c3c3bdbdbd7c7c7ccbcbcbdbdbdbc4c4c4c4c4c4
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c3c3c3c5c5c58383839a9a9a
+f2f2f2e8e8e8efefefededededededededededededededededededecececeaeaeaeaeaeaeeeeee
+ecececededededededececececececebebebecececededededededefefefedededeeeeeeececec
+ebebebebebebedededebebebeaeaeaeaeaeaecececeaeaeae9e9e9eaeaeaedededededede6e6e6
+e7e7e7e7e7e7e8e8e8e8e8e8e6e6e6e5e5e5e6e6e6e8e8e8e9e9e9e3e3e3e3e3e3e1e1e1e3e3e3
+e5e5e5e8e8e8e9e9e9e5e5e5e3e3e3e1e1e1e2e2e2e6e6e6e7e7e7eaeaeaeaeaeae7e7e7e4e4e4
+e4e4e4e4e4e4e3e3e3e3e3e3e3e3e3e4e4e4e4e4e4e5e5e5e4e4e4e3e3e3e3e3e3e2e2e2e2e2e2
+e2e2e2e2e2e2e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e6e6e6e6e6e6e4e4e4e1e1e1dfdfdf
+e2e2e2e1e1e1e3e3e3e4e4e4e1e1e1dcdcdce3e3e3e0e0e0dfdfdfdddddde4e4e4e4e4e4e0e0e0
+dbdbdbdbdbdbdfdfdfdfdfdfdddddddbdbdbdbdbdbdadadadadadadcdcdcdddddddddddddddddd
+dddddddcdcdcdbdbdbdbdbdbdbdbdbd9d9d9d9d9d9dcdcdcdbdbdbdadadadcdcdcdbdbdbd8d8d8
+d9d9d9dadadadadadad9d9d9dbdbdbd9d9d9d8d8d8d8d8d8d9d9d9d8d8d8d9d9d9dadadadadada
+d9d9d9d6d6d6dbdbdbdbdbdbd8d8d8d6d6d6d5d5d5d5d5d5d8d8d8d8d8d8d6d6d6d1d1d1d5d5d5
+d2d2d2d4d4d4d7d7d7d5d5d5d6d6d6d5d5d5d5d5d5d6d6d6d5d5d5d3d3d3d2d2d2d3d3d3d5d5d5
+d5d5d5d7d7d7d4d4d4d4d4d4d5d5d5d5d5d5d6d6d6d5d5d5d7d7d7d7d7d7d7d7d7d8d8d8d4d4d4
+d3d3d3d4d4d4d5d5d5d3d3d3d3d3d3d4d4d4d3d3d3d1d1d1d1d1d1d5d5d5d7d7d7d3d3d3cecece
+cececed1d1d1d1d1d1cecececcccccccccccd0d0d0d2d2d2cdcdcdcbcbcbcacacacacacacbcbcb
+cfcfcfd1d1d1cfcfcfcbcbcbcacacac9c9c9cbcbcbcececed0d0d0d4d4d4d1d1d1cdcdcdcccccc
+cccccccccccccccccccccccccccccccccccccccccccccccccccccccacacacacacacacacacacaca
+cacacacacacacbcbcbcbcbcbcbcbcbcccccccbcbcbcccccccecececfcfcfcbcbcbc8c8c8c9c9c9
+cacacac9c9c9cdcdcdcbcbcbc7c7c7c8c8c8ccccccc8c8c8c6c6c6c9c9c9cccccccacacac7c7c7
+c2c2c2c4c4c4c7c7c7c7c7c7c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c5c5c5c6c6c6c6c6c6c6c6c6
+c5c5c5c3c3c3c3c3c3c3c3c3c2c2c2c1c1c1c1c1c1c6c6c6c2c2c2c3c3c3c4c4c4c2c2c2c1c1c1
+c2c2c2c2c2c2c2c2c2c2c2c2c4c4c4c0c0c0c0c0c0c1c1c1c2c2c2c2c2c2c3c3c3c2c2c2c2c2c2
+bfbfbfbdbdbdc5c5c5c1c1c1c0c0c0bebebebfbfbfc3c3c37c7c7ccbcbcbdbdbdbc4c4c4c4c4c4
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c4c4c4c5c5c58383839a9a9a
+f2f2f2eaeaeae9e9e9ecececebebebeaeaeaeaeaeaedededececececececeaeaeae7e7e7eaeaea
+e7e7e7e5e5e5e5e5e5e7e7e7e8e8e8e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ebebebeaeaeaeaeaea
+eaeaeaeaeaeae4e4e4e6e6e6e8e8e8eaeaeaeaeaeae9e9e9ebebebededededededebebebe7e7e7
+e6e6e6e5e5e5e5e5e5e7e7e7e7e7e7e5e5e5e3e3e3e4e4e4e7e7e7e6e6e6e6e6e6e5e5e5e4e4e4
+e5e5e5e6e6e6e5e5e5e1e1e1e0e0e0e2e2e2e6e6e6e8e8e8e6e6e6e8e8e8e5e5e5e1e1e1e2e2e2
+e2e2e2e2e2e2e3e3e3e3e3e3e4e4e4e4e4e4e4e4e4e4e4e4e3e3e3e2e2e2e1e1e1e1e1e1e1e1e1
+e2e2e2e2e2e2e2e2e2e1e1e1e1e1e1e0e0e0dfdfdfe1e1e1e4e4e4e0e0e0e1e1e1e1e1e1dddddd
+e1e1e1e0e0e0dedededededee2e2e2dadadadcdcdcdededededededcdcdcdcdcdcdfdfdfe0e0e0
+dbdbdbd9d9d9ddddddddddddd9d9d9d8d8d8d8d8d8d9d9d9d9d9d9dbdbdbdedededcdcdcd8d8d8
+dddddddbdbdbdcdcdcdcdcdcdcdcdcdcdcdcdadadadddddddbdbdbd8d8d8d7d7d7d7d7d7d7d7d7
+d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d8d8d8d9d9d9dadadad8d8d8d9d9d9d8d8d8d7d7d7
+d7d7d7d6d6d6d5d5d5d5d5d5d5d5d5d2d2d2d6d6d6d7d7d7d6d6d6d5d5d5d6d6d6cecececfcfcf
+d1d1d1d2d2d2d2d2d2d5d5d5d4d4d4d2d2d2d3d3d3d5d5d5d4d4d4d3d3d3cececed0d0d0d1d1d1
+cecececccccccfcfcfd0d0d0d1d1d1d1d1d1d1d1d1d1d1d1d2d2d2d2d2d2d3d3d3d2d2d2d2d2d2
+d2d2d2cfcfcfcdcdcdd0d0d0d2d2d2d2d2d2d1d1d1d2d2d2d5d5d5d5d5d5d5d5d5d3d3d3d0d0d0
+cdcdcdcccccccdcdcdcfcfcfcdcdcdcccccccccccccfcfcfcecececdcdcdcdcdcdcbcbcbcbcbcb
+cdcdcdcdcdcdcbcbcbc9c9c9c8c8c8cacacacecececfcfcfcececed2d2d2c9c9c9c9c9c9cacaca
+cbcbcbcccccccccccccccccccccccccccccccccccccccccccbcbcbcacacacacacacacacac9c9c9
+cacacacacacacacacacacacac9c9c9c8c8c8c8c8c8cacacacbcbcbc8c8c8cbcbcbc6c6c6c6c6c6
+c9c9c9c9c9c9c5c5c5cacacac5c5c5c4c4c4c7c7c7c7c7c7c5c5c5c4c4c4c5c5c5c8c8c8c8c8c8
+c1c1c1c3c3c3c5c5c5c3c3c3c0c0c0c0c0c0c0c0c0c1c1c1c1c1c1c5c5c5c7c7c7c1c1c1c3c3c3
+c4c4c4c3c3c3c5c5c5c5c5c5c4c4c4c3c3c3c1c1c1c6c6c6c2c2c2c0c0c0bfbfbfbfbfbfc0c0c0
+c0c0c0c0c0c0c0c0c0bfbfbfbfbfbfc0c0c0c1c1c1c2c2c2c2c2c2c1c1c1c3c3c3c0c0c0c0c0c0
+bebebebdbdbdbdbdbdbebebebcbcbcbdbdbdbebebebdbdbd7b7b7bcbcbcbdbdbdbc4c4c4c4c4c4
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c4c4c4c6c6c68383839a9a9a
+f2f2f2e8e8e8e8e8e8eaeaeaebebebebebebeaeaeaebebebebebebebebebe9e9e9e7e7e7e7e7e7
+e6e6e6e5e5e5e5e5e5e6e6e6e6e6e6e7e7e7e7e7e7e6e6e6e6e6e6e7e7e7e5e5e5e7e7e7e7e7e7
+e7e7e7e7e7e7e8e8e8eaeaeae8e8e8e5e5e5e2e2e2e5e5e5e8e8e8e8e8e8e7e7e7e7e7e7eaeaea
+e9e9e9e8e8e8e7e7e7e5e5e5e4e4e4e2e2e2e2e2e2e4e4e4e5e5e5e4e4e4e6e6e6e5e5e5e5e5e5
+e4e4e4e3e3e3e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e6e6e6e4e4e4e3e3e3e2e2e2e0e0e0e0e0e0
+e0e0e0e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e2e2e2e3e3e3e2e2e2e1e1e1dedededfdfdf
+dededededededddddddfdfdfdedededddddddedededfdfdfdededee0e0e0e1e1e1dfdfdfdddddd
+dddddde0e0e0e0e0e0dededededededcdcdcdedededbdbdbdadadad9d9d9d8d8d8d9d9d9dadada
+d9d9d9d8d8d8d8d8d8d8d8d8d8d8d8d9d9d9dadadadadadad9d9d9dadadad9d9d9d8d8d8d8d8d8
+d9d9d9dadadadbdbdbdbdbdbdbdbdbd9d9d9dadadadadadad7d7d7d5d5d5d6d6d6d6d6d6d6d6d6
+d6d6d6d5d5d5d4d4d4d5d5d5d4d4d4d5d5d5d6d6d6d7d7d7d7d7d7d7d7d7d6d6d6d4d4d4d3d3d3
+d3d3d3d3d3d3d5d5d5d5d5d5d5d5d5d5d5d5d4d4d4d4d4d4d2d2d2d1d1d1d1d1d1cdcdcdcecece
+cfcfcfd0d0d0d1d1d1d2d2d2d3d3d3d2d2d2d2d2d2d3d3d3d4d4d4d2d2d2cecececececececece
+cdcdcdcdcdcdcececececececececed0d0d0cececececececfcfcfcecececdcdcdcececed0d0d0
+d0d0d0d0d0d0d1d1d1d2d2d2cecececccccccccccccececed0d0d0cfcfcfcdcdcdd0d0d0d2d2d2
+d1d1d1cfcfcfcecececdcdcdcbcbcbcacacacbcbcbcecececccccccdcdcdcdcdcdcdcdcdcccccc
+cccccccccccccbcbcbcbcbcbcbcbcbcbcbcbcdcdcdcdcdcdcccccccbcbcbc9c9c9c8c8c8c8c8c8
+c8c8c8c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9cbcbcbcbcbcbc8c8c8c7c7c7c8c8c8cacaca
+c7c7c7c6c6c6c7c7c7c8c8c8c6c6c6c6c6c6c7c7c7c7c7c7c7c7c7c9c9c9c9c9c9c5c5c5c5c5c5
+c7c7c7c9c9c9c7c7c7c6c6c6c6c6c6c6c6c6c7c7c7c2c2c2c1c1c1c0c0c0c1c1c1c2c2c2c2c2c2
+c1c1c1c1c1c1c1c1c1c0c0c0c0c0c0c3c3c3c2c2c2c2c2c2c2c2c2c3c3c3c0c0c0c0c0c0c1c1c1
+c2c2c2c3c3c3c5c5c5c3c3c3c2c2c2c2c2c2c2c2c2c3c3c3bdbdbdbdbdbdbdbdbdbdbdbdbfbfbf
+bdbdbdbcbcbcbcbcbcbdbdbdbdbdbdbdbdbdbfbfbfbfbfbfbfbfbfbfbfbfbebebebcbcbcbcbcbc
+bbbbbbbbbbbbbebebebebebebebebebdbdbdbbbbbbb9b9b97b7b7bcbcbcbdbdbdbc4c4c4c4c4c4
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c4c4c4c7c7c78484849a9a9a
+f2f2f2e7e7e7e7e7e7e8e8e8e9e9e9eaeaeae9e9e9eaeaeaebebebeaeaeae9e9e9e7e7e7e7e7e7
+e6e6e6e6e6e6e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e4e4e4e6e6e6e4e4e4e6e6e6e6e6e6
+e6e6e6e6e6e6e8e8e8eaeaeae7e7e7e4e4e4e1e1e1e4e4e4e6e6e6e5e5e5e4e4e4e4e4e4e8e8e8
+e9e9e9e8e8e8e6e6e6e5e5e5e5e5e5e4e4e4e3e3e3e4e4e4e4e4e4e3e3e3e4e4e4e4e4e4e4e4e4
+e3e3e3e3e3e3e3e3e3e4e4e4e4e4e4e4e4e4e5e5e5e6e6e6e3e3e3e1e1e1e0e0e0dfdfdfdfdfdf
+dfdfdfe0e0e0e1e1e1e1e1e1dfdfdfdedededfdfdfe1e1e1e2e2e2e1e1e1e1e1e1dededededede
+dddddddcdcdcdbdbdbdededededededddddddddddddedededcdcdce0e0e0e1e1e1dfdfdfdcdcdc
+dcdcdcdfdfdfdedededddddddcdcdcdddddddddddddbdbdbdadadad9d9d9d7d7d7d8d8d8d8d8d8
+d8d8d8d7d7d7d6d6d6d7d7d7d8d8d8dadadadadadadadadad9d9d9d9d9d9d8d8d8d8d8d8d9d9d9
+d8d8d8dadadadadadad9d9d9d8d8d8d7d7d7d9d9d9d7d7d7d5d5d5d4d4d4d5d5d5d5d5d5d5d5d5
+d5d5d5d4d4d4d3d3d3d4d4d4d3d3d3d3d3d3d4d4d4d5d5d5d6d6d6d6d6d6d5d5d5d3d3d3d3d3d3
+d3d3d3d3d3d3d5d5d5d5d5d5d4d4d4d4d4d4d3d3d3d3d3d3d1d1d1cfcfcfcfcfcfcecececdcdcd
+cecececfcfcfcfcfcfd0d0d0d2d2d2d2d2d2d2d2d2d3d3d3d4d4d4d2d2d2cfcfcfcececececece
+cececececececdcdcdcdcdcdcdcdcdcecececdcdcdcccccccecececdcdcdcccccccececececece
+cfcfcfcfcfcfd0d0d0d2d2d2cecececbcbcbcbcbcbcdcdcdcfcfcfcdcdcdcbcbcbcdcdcdd0d0d0
+d0d0d0cfcfcfcdcdcdcdcdcdcccccccccccccbcbcbcccccccbcbcbcbcbcbcccccccccccccbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcccccccccccccdcdcdcdcdcdcbcbcbc9c9c9c8c8c8c7c7c7c7c7c7
+c8c8c8c9c9c9c9c9c9c8c8c8c7c7c7c7c7c7c7c7c7c9c9c9cacacac8c8c8c6c6c6c6c6c6c8c8c8
+c5c5c5c4c4c4c5c5c5c7c7c7c6c6c6c6c6c6c7c7c7c7c7c7c6c6c6c9c9c9c9c9c9c5c5c5c5c5c5
+c5c5c5c7c7c7c6c6c6c4c4c4c5c5c5c6c6c6c6c6c6c2c2c2c1c1c1c0c0c0c0c0c0c1c1c1c0c0c0
+c0c0c0bfbfbfbfbfbfc0c0c0c1c1c1c3c3c3c2c2c2c2c2c2c1c1c1c1c1c1bfbfbfc1c1c1c1c1c1
+c1c1c1c2c2c2c3c3c3c1c1c1c0c0c0c0c0c0c1c1c1c0c0c0bcbcbcbcbcbcbcbcbcbcbcbcbdbdbd
+bcbcbcbbbbbbbbbbbbbcbcbcbcbcbcbcbcbcbdbdbdbebebebebebebebebebdbdbdbbbbbbbbbbbb
+bbbbbbbbbbbbbebebebdbdbdbdbdbdbbbbbbbababab9b9b97b7b7bcbcbcbdbdbdbc3c3c3c4c4c4
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b2af9999994b4b4b404040c5c5c5c6c6c6c7c7c78484849a9a9a
+f1f1f1e4e4e4e3e3e3e5e5e5e7e7e7e8e8e8e8e8e8e9e9e9d2d2d2cacacac9c9c9c8c8c8c8c8c8
+c7c7c7c6c6c6d1d1d1e3e3e3e4e4e4c3c3c3dededee2e2e2dbdbdbc4c4c4e3e3e3e6e6e6e5e5e5
+e4e4e4e5e5e5e4e4e4e7e7e7e7e7e7e5e5e5e4e4e4e3e3e3e4e4e4e3e3e3e2e2e2e1e1e1e3e3e3
+e4e4e4e3e3e3e3e3e3e4e4e4e7e7e7e8e8e8e6e6e6e4e4e4e1e1e1e0e0e0e0e0e0e1e1e1e2e2e2
+e2e2e2e1e1e1c2c2c2c3c3c3c4c4c4c6c6c6c6c6c6c6c6c6c3c3c3d4d4d4dedededddddddddddd
+dfdfdfe0e0e0e1e1e1d8d8d8c1c1c1dbdbdbdcdcdcc8c8c8c8c8c8dfdfdfdfdfdfc4c4c4d4d4d4
+dcdcdcdbdbdbdadadadcdcdcdddddddddddddddddddedededbdbdbdfdfdfe0e0e0dfdfdfdcdcdc
+dadadadbdbdbdbdbdbdadadadadadadcdcdcdadadadddddddddddddadadad8d8d8d8d8d8d8d8d8
+d8d8d8d7d7d7d5d5d5d7d7d7dadadadadadad9d9d9d8d8d8d8d8d8d7d7d7d7d7d7d8d8d8d9d9d9
+d8d8d8d7d7d7d6d6d6d5d5d5d4d4d4d5d5d5d5d5d5d2d2d2d3d3d3d4d4d4d4d4d4d2d2d2d2d2d2
+d3d3d3d3d3d3d2d2d2d3d3d3d2d2d2d2d2d2d2d2d2d3d3d3d4d4d4d5d5d5d4d4d4d3d3d3d3d3d3
+d3d3d3d6d6d6d6d6d6d5d5d5d2d2d2cfcfcfd1d1d1d0d0d0d0d0d0cfcfcfcecececfcfcfcccccc
+cbcbcbcccccccccccccececed0d0d0d0d0d0d1d1d1d2d2d2d3d3d3d2d2d2d0d0d0d0d0d0d0d0d0
+cfcfcfcdcdcdcbcbcbcbcbcbcbcbcbcbcbcbcccccccacacacbcbcbcbcbcbcbcbcbcdcdcdcccccc
+cdcdcdcccccccdcdcdcfcfcfcecececccccccbcbcbcccccccdcdcdccccccc9c9c9cacacacbcbcb
+cbcbcbcbcbcbcbcbcbcececed0d0d0d0d0d0cecececacacac9c9c9c9c9c9c8c8c8c9c9c9cacaca
+cbcbcbcacacacacacacbcbcbcccccccccccccdcdcdccccccc9c9c9c6c6c6c7c7c7c7c7c7c7c7c7
+c8c8c8c9c9c9cacacac7c7c7c5c5c5c4c4c4c5c5c5c7c7c7c8c8c8c8c8c8c6c6c6c4c4c4c4c4c4
+c4c4c4c3c3c3c4c4c4c5c5c5c6c6c6c5c5c5c6c6c6c6c6c6c5c5c5c8c8c8c9c9c9c5c5c5c4c4c4
+c3c3c3c4c4c4c3c3c3c2c2c2c4c4c4c4c4c4c2c2c2c6c6c6c3c3c3c1c1c1c0c0c0c1c1c1c0c0c0
+c0c0c0bebebebebebec1c1c1c3c3c3c2c2c2aaaaaab1b1b1c1c1c1bebebec0c0c0c1c1c1c0c0c0
+bababaa6a6a6bfbfbfbdbdbdbcbcbcbdbdbdbcbcbcbabababcbcbcbcbcbcbbbbbbbababab7b7b7
+a1a1a1b1b1b1bbbbbbbcbcbcbabababbbbbbbbbbbbbbbbbbbcbcbcbcbcbcbcbcbcbbbbbbbbbbbb
+bcbcbcbdbdbdbdbdbdbbbbbbb9b9b9b9b9b9b9b9b9bababa7b7b7bcbcbcbdbdbdbc4c4c4c5c5c5
+c6c6c6060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c6c6c6c7c7c78484849a9a9a
+f1f1f1e4e4e4e3e3e3e5e5e5e7e7e7e8e8e8e7e7e7e6e6e6383838000000000000000000000000
+000000000000555555e2e2e2dfdfdf000000bfbfbfe3e3e3b8b8b8000000e1e1e1e3e3e3e2e2e2
+e2e2e2e5e5e5e3e3e3e4e4e4e5e5e5e5e5e5e4e4e4e0e0e0e1e1e1e3e3e3e5e5e5e4e4e4e2e2e2
+e1e1e1dedededddddddddddde1e1e1e4e4e4e4e4e4e5e5e5e5e5e5e0e0e0e5e5e5e5e5e5e1e1e1
+dfdfdfd9d9d90000000000000000000000000000000000000000008e8e8edededee0e0e0e2e2e2
+e1e1e1dddddddcdcdca0a0a0131313dddddddddddd3b3b3b383838dfdfdfdfdfdf1717179d9d9d
+dfdfdfe1e1e1dfdfdfd9d9d9d8d8d8d8d8d8dadadadcdcdcd7d7d7dddddde0e0e0dfdfdfdadada
+d9d9d9dbdbdbdcdcdcdbdbdbd9d9d9dadadad9d9d9d9d9d9d9d9d9d9d9d9dadadad8d8d8dddddd
+dbdbdbd7d7d7d4d4d4d1d1d1d8d8d8dbdbdbdadadad8d8d8d8d8d8d6d6d6d6d6d6d6d6d6d7d7d7
+d6d6d6d4d4d4d3d3d3d3d3d3d4d4d4d5d5d5d5d5d5d2d2d2d2d2d2d2d2d2d2d2d2cececed0d0d0
+d1d1d1d1d1d1d1d1d1d1d1d1d0d0d0d2d2d2d4d4d4d2d2d2d1d1d1d1d1d1d2d2d2d3d3d3d3d3d3
+d3d3d3d6d6d6d7d7d7d6d6d6d3d3d3cccccccccccccccccccfcfcfcfcfcfcececececececbcbcb
+cbcbcbcccccccccccccececed0d0d0cfcfcfcecececfcfcfcececececececccccccccccccbcbcb
+c9c9c9c9c9c9c9c9c9cbcbcbcbcbcbcbcbcbcccccccacacacacacacbcbcbcbcbcbcccccccacaca
+cbcbcbcccccccccccccececececececccccccacacac8c8c8cbcbcbcecececccccccbcbcbcacaca
+c8c8c8c6c6c6c5c5c5c7c7c7cacacacccccccecececececec9c9c9cacacacccccccececec8c8c8
+c8c8c8cacacacccccccdcdcdccccccc9c9c9c9c9c9c9c9c9c7c7c7c6c6c6c8c8c8cacacacacaca
+c8c8c8c5c5c5c5c5c5c4c4c4c4c4c4c5c5c5c6c6c6c7c7c7c8c8c8c6c6c6c5c5c5c5c5c5c5c5c5
+c9c9c9c8c8c8c4c4c4c1c1c1c1c1c1c2c2c2c3c3c3c2c2c2c0c0c0c8c8c8c9c9c9c4c4c4c3c3c3
+c2c2c2c4c4c4c4c4c4c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c2c2c2c1c1c1c1c1c1c7c7c7
+c3c3c3bdbdbdbabababcbcbcc3c3c3c5c5c51717174d4d4dbfbfbfbdbdbdbdbdbdbfbfbfbdbdbd
+959595050505bbbbbbbbbbbbbcbcbcbdbdbdbcbcbcbababababababbbbbbb9b9b9b8b8b8a4a4a4
+000000767676babababababab9b9b9bdbdbdbbbbbbbababababababbbbbbbbbbbbbbbbbbbbbbbb
+bcbcbcbdbdbdc0c0c0bdbdbdb8b8b8b6b6b6b7b7b7bababa7b7b7bcbcbcbdcdcdcc6c6c6c6c6c6
+c6c6c6060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c6c6c6c7c7c78484849a9a9a
+f1f1f1e4e4e4e2e2e2e0e0e0e1e1e1e3e3e3e4e4e4e5e5e53737377e7e7edbdbdbd9d9d9d9d9d9
+d9d9d9d8d8d8dbdbdbe1e1e1e1e1e1d7d7d7dcdcdcddddddb4b4b4000000e0e0e0dfdfdfdddddd
+dddddddfdfdfe1e1e1dfdfdfdfdfdfdedededddddddfdfdfdddddddfdfdfdfdfdfdddddddddddd
+dddddddddddddedededfdfdfdddddddcdcdcdddddddedededfdfdfe1e1e1e3e3e3e1e1e1dedede
+ddddddd8d8d8000000b5b5b5d8d8d8d6d6d6d3d3d3d3d3d3d3d3d3d8d8d8dbdbdbdadadadadada
+dbdbdbdbdbdbdbdbdb9f9f9f131313dcdcdcdcdcdcd5d5d5d5d5d5dcdcdcdbdbdb1717179b9b9b
+dbdbdbdcdcdcd9d9d9d9d9d9d9d9d9d9d9d9dbdbdbddddddd7d7d7d7d7d7d8d8d8d9d9d9dadada
+d9d9d9d9d9d9d9d9d9d8d8d8d6d6d6dadadad9d9d9d4d4d4d3d3d3d5d5d5d6d6d6d4d4d4d6d6d6
+d6d6d6d5d5d5d5d5d5d7d7d7dadadad9d9d9d9d9d9d8d8d8d2d2d2d4d4d4d5d5d5d5d5d5d6d6d6
+d6d6d6d4d4d4d3d3d3d2d2d2d1d1d1d0d0d0cececececececdcdcdcccccccccccccccccccfcfcf
+d0d0d0cfcfcfcecececccccccccccccdcdcdcececececececfcfcfcececececececdcdcdcccccc
+cecececececececececfcfcfd1d1d1cdcdcdcdcdcdc9c9c9cacacacbcbcbcbcbcbcacacacbcbcb
+cbcbcbcbcbcbc9c9c9c9c9c9cacacacbcbcbcccccccecececececececececccccccccccccbcbcb
+cacacac9c9c9c9c9c9cacacac9c9c9c8c8c8c5c5c5c6c6c6c6c6c6c7c7c7c8c8c8c7c7c7c5c5c5
+c7c7c7c8c8c8c8c8c8c7c7c7c9c9c9c7c7c7c7c7c7c7c7c7c5c5c5c9c9c9c7c7c7c7c7c7c6c6c6
+c5c5c5c6c6c6c7c7c7c7c7c7c5c5c5c4c4c4c5c5c5c7c7c7c8c8c8cacacacacacacacacac6c6c6
+c5c5c5c6c6c6c7c7c7c9c9c9cbcbcbc6c6c6c5c5c5c6c6c6c5c5c5c4c4c4c2c2c2c2c2c2c2c2c2
+c4c4c4c4c4c4c3c3c3c4c4c4c4c4c4c5c5c5c5c5c5c6c6c6c6c6c6c2c2c2c0c0c0c3c3c3c6c6c6
+c5c5c5c2c2c2c1c1c1c2c2c2c1c1c1c2c2c2c5c5c5c4c4c4bfbfbfbfbfbfc1c1c1c1c1c1c2c2c2
+c2c2c2c2c2c2c1c1c1bfbfbfc1c1c1c2c2c2c1c1c1bbbbbbbcbcbcbdbdbdbcbcbcbcbcbcbfbfbf
+bdbdbdbdbdbdbebebec1c1c1c2c2c2c2c2c21717174b4b4bbbbbbbbdbdbdbdbdbdbdbdbdbdbdbd
+959595050505bbbbbbbababab8b8b8b7b7b7b7b7b7b6b6b6b5b5b5b4b4b4b5b5b5b6b6b6a4a4a4
+000000757575b7b7b7b5b5b5b5b5b5b8b8b8b7b7b7b7b7b7b7b7b7b6b6b6b7b7b7b6b6b6b6b6b6
+b6b6b6b6b6b6b6b6b6bababab8b8b8b6b6b6b5b5b5b3b3b37a7a7acbcbcbdededec8c8c8c7c7c7
+c6c6c6060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9c6c6c6c5c5c58383839a9a9a
+f1f1f1e0e0e0e0e0e0e0e0e0e0e0e0e1e1e1e0e0e0e1e1e1363636838383e4e4e4e5e5e5e3e3e3
+e2e2e2e0e0e0dededededededcdcdc323232c3c3c3dfdfdfb6b6b6000000dfdfdfe1e1e1dedede
+c0c0c03232323232323232329c9c9cdcdcdcdddddddbdbdbdcdcdcdddddddddddddddddddddddd
+dddddddddddddddddddfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdddddddadadad9d9d9
+d9d9d9d5d5d5000000b8b8b8dcdcdcdcdcdcdbdbdbdddddddbdbdbdadadadbdbdbdcdcdccbcbcb
+3131313131317c7c7c9d9d9d131313d9d9d9dadada5f5f5f5c5c5ccccccc313131050505222222
+888888d6d6d6d5d5d5d6d6d6d7d7d7d9d9d9dadadadadadadadadad9d9d9dadadadbdbdbdbdbdb
+dbdbdbd9d9d9d8d8d8d7d7d7d5d5d5d7d7d7d3d3d3d2d2d2d2d2d2d4d4d4d6d6d6d4d4d4d4d4d4
+d6d6d6d6d6d6d4d4d4d5d5d5d9d9d9d7d7d7d6d6d6d7d7d7d2d2d2d3d3d3d3d3d3d3d3d3d3d3d3
+d2d2d2d2d2d2d1d1d1d1d1d1d1d1d1d0d0d0cecececccccccccccccccccccccccccccccccccccc
+cdcdcdcdcdcdcecececdcdcdcccccccccccccccccccccccccecececacacacbcbcbcbcbcbcbcbcb
+cccccccccccccdcdcdcdcdcdcdcdcdcccccccfcfcfcecececbcbcbcacacacacacac8c8c8c8c8c8
+c8c8c8c8c8c8c8c8c8c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9cccccccdcdcdcccccccbcbcb
+c9c9c9c7c7c7c7c7c7c8c8c8c8c8c8c8c8c8c7c7c7c8c8c8c7c7c7c7c7c7c8c8c8c9c9c9c5c5c5
+c4c4c4c5c5c5c5c5c5c5c5c5c4c4c4c4c4c4c4c4c4c4c4c4c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5
+c5c5c5c6c6c6c7c7c7c8c8c8c7c7c7c7c7c7c7c7c7c7c7c7c8c8c8c6c6c6c4c4c4c3c3c3c1c1c1
+c2c2c2c2c2c2c3c3c3c3c3c3c4c4c4c3c3c3c4c4c4c4c4c4c4c4c4c2c2c2c4c4c4c5c5c5c5c5c5
+c4c4c4c3c3c3c0c0c0c0c0c0c1c1c1c2c2c2c3c3c3c2c2c2c1c1c1c1c1c1c1c1c1c0c0c0bdbdbd
+bdbdbdbdbdbdbebebebebebec0c0c0c2c2c2c3c3c3c3c3c3c2c2c2c2c2c2c4c4c4c3c3c3c3c3c3
+c3c3c3c2c2c2c0c0c0bdbdbdbebebebdbdbdbbbbbbbabababbbbbbbcbcbcbcbcbcbcbcbcbbbbbb
+bebebebcbcbcbcbcbcbebebec1c1c1bebebe1616164b4b4bbbbbbbbbbbbbbbbbbbbbbbbbbababa
+939393050505bababab9b9b9b8b8b8727272292929282828282828848484b5b5b5b5b5b5a0a0a0
+000000757575b7b7b78f8f8f363636b5b5b53434342929294d4d4db2b2b2b3b3b3b3b3b3b4b4b4
+b4b4b4b4b4b4b7b7b7b7b7b7b6b6b6b7b7b7b5b5b5b2b2b27a7a7acbcbcbdededec9c9c9c9c9c9
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacac8c8c8c5c5c58383839a9a9a
+f1f1f1e1e1e1e1e1e1e0e0e0e0e0e0e0e0e0dfdfdfdfdfdf363636818181e1e1e1e1e1e1e2e2e2
+e0e0e0dedededededededededadada000000b9b9b9ddddddb4b4b4000000dddddddddddd888888
+606060848484848484828282686868939393dcdcdcdadadadadadadadadadbdbdbdbdbdbdbdbdb
+dadadadadadadbdbdbdcdcdcdcdcdcdcdcdcdbdbdbdbdbdbdbdbdbdbdbdbd9d9d9d6d6d6d7d7d7
+d8d8d8d4d4d4000000b6b6b6d8d8d8d8d8d8d9d9d9dadadadadadadadadadadadab6b6b65e5e5e
+8181818181816e6e6e3f3f3f131313d6d6d6d6d6d63a3a3a363636cfcfcf8080800d0d0d595959
+aaaaaad2d2d2d0d0d0d3d3d3d2d2d2d4d4d4d6d6d6d6d6d6d6d6d6d9d9d9dbdbdbdadadad9d9d9
+d9d9d9d5d5d5d6d6d6d6d6d6d4d4d4d4d4d4d2d2d2d4d4d4d4d4d4d3d3d3d3d3d3d3d3d3d2d2d2
+d1d1d1d3d3d3d5d5d5d2d2d2d3d3d3d4d4d4d4d4d4d4d4d4d1d1d1d3d3d3d3d3d3d3d3d3d3d3d3
+d2d2d2d1d1d1d0d0d0d0d0d0d0d0d0cfcfcfcccccccccccccbcbcbcbcbcbcccccccccccccbcbcb
+cacacacacacacbcbcbcbcbcbcacacacbcbcbcbcbcbcbcbcbcacacac9c9c9cacacacacacacacaca
+cacacacccccccccccccbcbcbcbcbcbcbcbcbcdcdcdd0d0d0cecececdcdcdccccccc9c9c9c8c8c8
+c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c8c8c8c8c8c8c7c7c7c6c6c6c9c9c9c9c9c9cacacac9c9c9
+c8c8c8c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c6c6c6c6c6c6c5c5c5c5c5c5c5c5c5c6c6c6c5c5c5
+c5c5c5c5c5c5c5c5c5c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3
+c2c2c2c3c3c3c4c4c4c5c5c5c4c4c4c4c4c4c3c3c3c3c3c3c3c3c3c2c2c2c0c0c0bebebebfbfbf
+c1c1c1c1c1c1c0c0c0c0c0c0c0c0c0c1c1c1c2c2c2c2c2c2c2c2c2c2c2c2c3c3c3c3c3c3c3c3c3
+c3c3c3c1c1c1bebebebfbfbfbfbfbfbebebebfbfbfbdbdbdbebebebfbfbfbfbfbfbcbcbcb9b9b9
+bababab9b9b9babababbbbbbbabababebebebebebebfbfbfc0c0c0c3c3c3c3c3c3c1c1c1c1c1c1
+c1c1c1bdbdbdbebebebdbdbdbcbcbcbbbbbbbabababdbdbdbbbbbbbabababbbbbbbbbbbbb9b9b9
+babababbbbbbbbbbbbbabababcbcbcbdbdbd1616164b4b4bbabababcbcbcbbbbbbbcbcbcbbbbbb
+939393050505b8b8b8b7b7b76f6f6f5a5a5a6b6b6b6b6b6b6a6a6a5555557c7c7cb4b4b49f9f9f
+000000727272b4b4b48282820707074949496767676a6a6a616161505050b3b3b3b3b3b3b3b3b3
+b3b3b3b4b4b4b5b5b5b4b4b4b4b4b4b5b5b5b6b6b6b6b6b67b7b7bcbcbcbdfdfdfcbcbcbcacaca
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cbcbcbc8c8c8c6c6c68383839a9a9a
+f1f1f1e2e2e2e2e2e2e1e1e1e0e0e0e0e0e0dfdfdfdfdfdf363636717171c5c5c5c5c5c5c6c6c6
+c5c5c5d2d2d2dddddddfdfdfdadada000000b8b8b8dbdbdbb2b2b2000000dadadadadada4e4e4e
+252525dededededededbdbdb515151626262dbdbdbdadadad9d9d9d9d9d9d9d9d9d9d9d9d9d9d9
+d8d8d8d9d9d9dadadadadadadadadadadadad9d9d9d8d8d8d8d8d8d8d8d8d8d8d8d5d5d5d7d7d7
+d8d8d8d3d3d3000000a0a0a0bdbdbdbebebebebebebfbfbfc0c0c0d1d1d1dadada9b9b9b161616
+d8d8d8d8d8d8828282121212131313d4d4d4d4d4d4393939353535d4d4d4d7d7d7161616979797
+d2d2d2d1d1d1cececed1d1d1cfcfcfd0d0d0d3d3d3d4d4d4d3d3d3d7d7d7d9d9d9d8d8d8d6d6d6
+d6d6d6d3d3d3d4d4d4d5d5d5d4d4d4d2d2d2d3d3d3d6d6d6d5d5d5d2d2d2d1d1d1d2d2d2d0d0d0
+cfcfcfd1d1d1d5d5d5d1d1d1d0d0d0d2d2d2d4d4d4d2d2d2d0d0d0d3d3d3d3d3d3d3d3d3d4d4d4
+d2d2d2d0d0d0cfcfcfcfcfcfcfcfcfcecececccccccbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+c9c9c9c9c9c9cacacacacacac8c8c8cacacacbcbcbcacacac9c9c9c9c9c9cacacacacacacacaca
+cacacacbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcccccccecececfcfcfcecececccccccacacac9c9c9
+cacacacacacac9c9c9c9c9c9c9c9c9c8c8c8c7c7c7c7c7c7c5c5c5c7c7c7c6c6c6c7c7c7c8c8c8
+c7c7c7c6c6c6c7c7c7c6c6c6c6c6c6c5c5c5c4c4c4c4c4c4c3c3c3c3c3c3c3c3c3c3c3c3c4c4c4
+c5c5c5c6c6c6c5c5c5c3c3c3c3c3c3c3c3c3c3c3c3c2c2c2c2c2c2c2c2c2c1c1c1c1c1c1c1c1c1
+c0c0c0c1c1c1c2c2c2c2c2c2c2c2c2c1c1c1c1c1c1c0c0c0c0c0c0c0c0c0bfbfbfbdbdbdbfbfbf
+c1c1c1c0c0c0bfbfbfc0c0c0bfbfbfc0c0c0c0c0c0c1c1c1c1c1c1c2c2c2c2c2c2c1c1c1c1c1c1
+c1c1c1c0c0c0bcbcbcbebebebdbdbdbcbcbcbcbcbcbabababcbcbcbebebebdbdbdbababab9b9b9
+b9b9b9b8b8b8b8b8b8b9b9b9b7b7b7babababbbbbbbcbcbcbdbdbdc1c1c1c1c1c1bfbfbfbfbfbf
+bfbfbfbbbbbbbcbcbcbcbcbcbbbbbbbababababababfbfbfbbbbbbb9b9b9b9b9b9b9b9b9b8b8b8
+b7b7b7babababbbbbbb9b9b9b8b8b8bcbcbc161616424242a3a3a3a6a6a6a4a4a4a6a6a6a5a5a5
+828282050505b7b7b7b7b7b73c3c3c585858b4b4b4b4b4b4b3b3b33e3e3e555555b4b4b49f9f9f
+000000717171b2b2b2818181010101141414a5a5a5b2b2b28383830e0e0eb2b2b2b2b2b2b2b2b2
+b3b3b3b4b4b4b4b4b4b4b4b4b3b3b3b4b4b4b6b6b6b8b8b87b7b7bcbcbcbdfdfdfcccccccbcbcb
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cbcbcbc7c7c7c7c7c78484849a9a9a
+f1f1f1e2e2e2e2e2e2e1e1e1e0e0e0e0e0e0e1e1e1e0e0e0363636000000000000000000000000
+0000007b7b7bdbdbdbdadadad6d6d6000000b7b7b7dadadab2b2b2000000d9d9d9dadada4e4e4e
+242424dbdbdbd8d8d8dbdbdb525252616161dadadad8d8d8d9d9d9d9d9d9d8d8d8d7d7d7d7d7d7
+d8d8d8d8d8d8d8d8d8d9d9d9dadadadadadad8d8d8d7d7d7d7d7d7d8d8d8d9d9d9d7d7d7d7d7d7
+d7d7d7d2d2d20000000000000000000000000000000000000000008b8b8bd6d6d6989898161616
+d7d7d7d6d6d6d4d4d4999999121212d2d2d2d1d1d1383838353535d2d2d2d4d4d4161616959595
+d2d2d2d2d2d2d2d2d2cececed0d0d0cfcfcfcecececececed2d2d2d0d0d0d0d0d0d0d0d0d0d0d0
+cececed7d7d7d4d4d4d0d0d0d0d0d0d0d0d0d1d1d1d1d1d1d0d0d0d0d0d0d1d1d1d0d0d0d0d0d0
+d1d1d1d2d2d2d4d4d4d4d4d4d5d5d5d4d4d4d4d4d4d3d3d3d0d0d0cdcdcdcececed0d0d0cfcfcf
+cececececececececececececfcfcfcecececccccccbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cacacacacacaccccccccccccc9c9c9c9c9c9c9c9c9c9c9c9cacacac9c9c9c8c8c8c8c8c8cacaca
+cacacac9c9c9cbcbcbcccccccbcbcbcbcbcbccccccc6c6c6c4c4c4c4c4c4c5c5c5cacacacacaca
+cbcbcbcacacac9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c8c8c8c5c5c5c5c5c5c5c5c5c6c6c6c6c6c6
+c5c5c5c5c5c5c3c3c3c3c3c3c4c4c4c4c4c4c4c4c4c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3
+c3c3c3c2c2c2c2c2c2c4c4c4c4c4c4c2c2c2c1c1c1c1c1c1c2c2c2c2c2c2bfbfbfbfbfbfbfbfbf
+c0c0c0c1c1c1c1c1c1c1c1c1c2c2c2c1c1c1c1c1c1bfbfbfbfbfbfc1c1c1c1c1c1c0c0c0bfbfbf
+bfbfbfbfbfbfc0c0c0c1c1c1c1c1c1c0c0c0c0c0c0c0c0c0bfbfbfbfbfbfbfbfbfbfbfbfbfbfbf
+bebebebdbdbdbbbbbbbabababababab9b9b9b9b9b9b9b9b9b9b9b9bcbcbcbcbcbcbabababdbdbd
+bbbbbbbababab8b8b8b6b6b6b9b9b9b7b7b7b7b7b7b8b8b8b9b9b9b9b9b9b9b9b9b9b9b9b8b8b8
+b8b8b8c1c1c1b8b8b8b9b9b9b8b8b8b8b8b8bababab9b9b9b8b8b8b8b8b8b8b8b8b8b8b8b9b9b9
+b9b9b9babababbbbbbbdbdbdbcbcbcbbbbbb161616000000000000000000000000000000000000
+000000050505b7b7b7b7b7b73d3d3d575757b3b3b3b3b3b3b3b3b33e3e3e545454b3b3b39f9f9f
+000000737373b4b4b4828282101010b1b1b1b1b1b1b1b1b18484840d0d0db0b0b0b1b1b1b3b3b3
+b2b2b2b2b2b2b4b4b4b4b4b4b4b4b4b4b4b4b1b1b1acacac797979cbcbcbe0e0e0cdcdcdcbcbcb
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8c7c7c7c7c7c78484849a9a9a
+f1f1f1dddddde2e2e2e3e3e3e2e2e2e1e1e1e1e1e1dddddd353535777777cfcfcfcecececccccc
+ccccccd5d5d5d9d9d9d6d6d6d2d2d2000000b7b7b7dadadab1b1b1000000d9d9d9d9d9d94d4d4d
+0202020d0d0d0d0d0d0d0d0d050505606060d8d8d8dadadadbdbdbdadadad9d9d9d9d9d9d8d8d8
+d8d8d8d8d8d8d7d7d7d7d7d7d7d7d7d8d8d8d8d8d8d8d8d8d7d7d7d7d7d7d6d6d6d4d4d4d4d4d4
+d5d5d5d0d0d0000000a9a9a9cbcbcbcacacac7c7c7c7c7c7c7c7c7d1d1d1d6d6d6989898161616
+d2d2d2d4d4d4d3d3d3999999121212d0d0d0cecece373737343434cfcfcfcccccc151515919191
+cfcfcfd2d2d2d1d1d1cccccccfcfcfcfcfcfcdcdcdccccccd1d1d1d3d3d3d4d4d4d2d2d2d0d0d0
+d1d1d1cecececdcdcdcccccccbcbcbc9c9c9d1d1d1d3d3d3d2d2d2cdcdcdc7c7c7c8c8c8cccccc
+cccccccbcbcbcccccccccccccfcfcfcfcfcfcecececececececececbcbcbcecececfcfcfcecece
+cfcfcfd0d0d0cecececdcdcdcbcbcbcacacacbcbcbcbcbcbcacacac9c9c9c8c8c8c9c9c9cbcbcb
+cacacac9c9c9c8c8c8c9c9c9cacacac9c9c9c9c9c9c9c9c9c7c7c7c6c6c6c7c7c7c7c7c7c5c5c5
+c4c4c4c6c6c6c4c4c4c5c5c5c8c8c8cacacacdcdcdc3c3c3c2c2c2c3c3c3c3c3c3c6c6c6c7c7c7
+c6c6c6c8c8c8cbcbcbcbcbcbcacacac9c9c9c7c7c7c6c6c6c6c6c6c5c5c5c5c5c5c4c4c4c3c3c3
+c2c2c2c4c4c4bfbfbfbdbdbdbebebec1c1c1c2c2c2c2c2c2c2c2c2c1c1c1c1c1c1c1c1c1c1c1c1
+c2c2c2c1c1c1c1c1c1c1c1c1c0c0c0c0c0c0c1c1c1c3c3c3c3c3c3c2c2c2c1c1c1c1c1c1c1c1c1
+c0c0c0c1c1c1bfbfbfbfbfbfbfbfbfc0c0c0c1c1c1c0c0c0bfbfbfbfbfbfbdbdbdbbbbbbbcbcbc
+bdbdbdbcbcbcbcbcbcc0c0c0c1c1c1bebebebdbdbdbdbdbdbdbdbdbfbfbfbdbdbdbabababababa
+babababdbdbdbababab7b7b7b7b7b7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b7b7b7b7b7b7bbbbbb
+bbbbbbbababab8b8b8b6b6b6b8b8b8b6b6b6b5b5b5b6b6b6b9b9b9bcbcbcbababab9b9b9b9b9b9
+bababab7b7b7b5b5b5b5b5b5b2b2b2b5b5b5babababababab7b7b7b1b1b1afafafb3b3b3b8b8b8
+b3b3b3b3b3b3b4b4b4b6b6b6b8b8b8b7b7b7161616444444a9a9a9a7a7a7adadadaaaaaaababab
+888888050505b7b7b7b3b3b33b3b3b0505050b0b0b0b0b0b0b0b0b030303535353b2b2b29f9f9f
+000000707070b1b1b1808080101010b1b1b1b2b2b2b0b0b08181810d0d0db0b0b0aeaeaeacacac
+adadadaeaeaeacacacb0b0b0b1b1b1b4b4b4b1b1b1aaaaaa797979cbcbcbdfdfdfcacacac8c8c8
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b2af9999994b4b4b414141c7c7c7c6c6c6c7c7c78484849a9a9a
+f0f0f0d9d9d9d9d9d9d9d9d9dadadadadadadadadadadada3535357d7d7ddcdcdcddddddd6d6d6
+d7d7d7d9d9d9d9d9d9d6d6d6d3d3d3000000b6b6b6d9d9d9b0b0b0000000d8d8d8d9d9d94d4d4d
+1b1b1ba6a6a6a4a4a4a6a6a6a6a6a6bdbdbddadadad8d8d8d5d5d5d7d7d7d9d9d9d8d8d8d8d8d8
+d7d7d7d7d7d7d7d7d7d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d1d1d1d1d1d1d3d3d3
+d4d4d4cfcfcf000000afafafd2d2d2d1d1d1cfcfcfcfcfcfd1d1d1d0d0d0d1d1d1959595151515
+cdcdcdcfcfcfd0d0d0979797121212cecececccccc373737343434cecececdcdcd151515929292
+cdcdcdcdcdcdcececececececfcfcfcfcfcfcdcdcdcbcbcbcacacacdcdcdcecececdcdcdcccccc
+cbcbcbcfcfcfcdcdcdcbcbcbcacacac9c9c9cdcdcdcdcdcdcccccccbcbcbc5c5c5c5c5c5c6c6c6
+cbcbcbcecececdcdcdd0d0d0cfcfcfcfcfcfd0d0d0d0d0d0cecececacacacbcbcbcdcdcdcccccc
+cccccccdcdcdcccccccbcbcbcacacac9c9c9c9c9c9cacacac9c9c9c9c9c9c8c8c8c7c7c7c6c6c6
+c5c5c5c5c5c5c5c5c5c7c7c7c9c9c9c8c8c8c7c7c7c8c8c8c7c7c7c7c7c7c7c7c7c7c7c7c6c6c6
+c7c7c7c9c9c9c9c9c9c7c7c7c3c3c3c2c2c2c4c4c4c0c0c0c1c1c1c1c1c1c2c2c2c2c2c2c4c4c4
+c3c3c3c2c2c2c1c1c1c2c2c2c2c2c2c3c3c3c2c2c2c2c2c2c3c3c3c2c2c2c7c7c7c3c3c3bfbfbf
+c1c1c1c3c3c3c0c0c0bfbfbfc0c0c0c1c1c1c2c2c2c0c0c0c0c0c0bfbfbfc0c0c0c1c1c1c2c2c2
+c3c3c3c3c3c3c2c2c2c3c3c3c4c4c4c3c3c3c1c1c1bfbfbfbdbdbdc1c1c1c1c1c1c0c0c0c0c0c0
+bfbfbfbfbfbfbfbfbfbfbfbfbebebebdbdbdbdbdbdbdbdbdbdbdbdbbbbbbb9b9b9b8b8b8bbbbbb
+bbbbbbb9b9b9b8b8b8b9b9b9bbbbbbb9b9b9b8b8b8b9b9b9bababab8b8b8b9b9b9b8b8b8b7b7b7
+b6b6b6b9b9b9b7b7b7b6b6b6b7b7b7b7b7b7b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b5b5b5b4b4b4
+b4b4b4b6b6b6b7b7b7b8b8b8b8b8b8b6b6b6b5b5b5b4b4b4b4b4b4b6b6b6b5b5b5b5b5b5b5b5b5
+b4b4b4b7b7b7b5b5b5b3b3b3b1b1b1b3b3b3b7b7b7b5b5b5b5b5b5afafafadadadaeaeaeaeaeae
+b4b4b4b6b6b6b7b7b7b8b8b8b6b6b6b8b8b8161616494949b5b5b5b0b0b0b6b6b6b4b4b4b4b4b4
+8f8f8f050505b5b5b5b2b2b23b3b3b414141858585878787868686858585999999afafaf9b9b9b
+0000006f6f6faeaeae7e7e7e101010b0b0b0b1b1b1b0b0b08181810d0d0db0b0b0afafafafafaf
+b0b0b0b1b1b1b1b1b1adadadabababababababababaaaaaa797979cbcbcbddddddc7c7c7c7c7c7
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c8c8c8c9c9c98484849a9a9a
+f0f0f0d6d6d6d6d6d6d7d7d7d7d7d7d7d7d7d6d6d6d6d6d63434347c7c7cdadadadcdcdcd3d3d3
+d6d6d6d8d8d8d9d9d9d8d8d8d4d4d4000000b6b6b6d8d8d8afafaf000000d6d6d6d8d8d84d4d4d
+242424d7d7d7d7d7d7d8d8d88b8b8b939393d6d6d6d7d7d7d4d4d4d5d5d5d7d7d7d7d7d7d6d6d6
+d6d6d6d6d6d6d5d5d5d5d5d5d6d6d6d6d6d6d5d5d5d4d4d4d4d4d4d5d5d5d1d1d1d0d0d0d1d1d1
+d1d1d1cbcbcb000000adadadd0d0d0cfcfcfcececed0d0d0cfcfcfcdcdcdcecece959595151515
+cdcdcdcfcfcf9a9a9a3f3f3f121212cecececdcdcd373737333333cccccccbcbcb151515939393
+cccccccacacacccccccdcdcdcdcdcdcdcdcdcccccccbcbcbc8c8c8cacacacbcbcbcbcbcbc9c9c9
+cacacacdcdcdcdcdcdcbcbcbc8c8c8c8c8c8c9c9c9cbcbcbcacacac7c7c7c8c8c8cacacac5c5c5
+c9c9c9cbcbcbcacacacacacacbcbcbc7c7c7c8c8c8cececec8c8c8c8c8c8c9c9c9cacacac9c9c9
+cdcdcdcdcdcdcacacac9c9c9c7c7c7c9c9c9c9c9c9c9c9c9c8c8c8c7c7c7c6c6c6c6c6c6c6c6c6
+c6c6c6c5c5c5c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c5c5c5c5c5c5c6c6c6c6c6c6c5c5c5c4c4c4
+c4c4c4c5c5c5c4c4c4c3c3c3c2c2c2c0c0c0c1c1c1c0c0c0bfbfbfbfbfbfc0c0c0bfbfbfc0c0c0
+bebebebebebebfbfbfc0c0c0bfbfbfbfbfbfbebebebfbfbfc0c0c0c0c0c0c6c6c6c2c2c2bcbcbc
+c0c0c0c3c3c3c1c1c1c1c1c1c1c1c1c0c0c0c2c2c2c0c0c0bebebebebebebfbfbfc1c1c1c0c0c0
+bfbfbfbfbfbfc0c0c0c0c0c0c1c1c1bfbfbfbebebebebebebcbcbcbebebebfbfbfbfbfbfbebebe
+bdbdbdbdbdbdbdbdbdbdbdbdbebebebebebebdbdbdbbbbbbbcbcbcbbbbbbb9b9b9b8b8b8b9b9b9
+b9b9b9b7b7b7b6b6b6b6b6b6b9b9b9b7b7b7b7b7b7b8b8b8b7b7b7b5b5b5bababab8b8b8b7b7b7
+b6b6b6b9b9b9b7b7b7b7b7b7b7b7b7b7b7b7b6b6b6b6b6b6b5b5b5b4b4b4b5b5b5b5b5b5b2b2b2
+b2b2b2b4b4b4b5b5b5b6b6b6b6b6b6b5b5b5b5b5b5b4b4b4b1b1b1b3b3b3b4b4b4b1b1b1b1b1b1
+b2b2b2b5b5b5b5b5b5b2b2b2b0b0b0b1b1b1b2b2b2b3b3b3afafafafafafb1b1b1b0b0b0acacac
+b2b2b2b3b3b3b3b3b3b2b2b2b3b3b3acacac141414474747b0b0b0b0b0b0b2b2b2b0b0b0b3b3b3
+8f8f8f050505b2b2b2b0b0b03a3a3a565656b1b1b1b1b1b1afafaf6e6e6e797979aeaeae9b9b9b
+0000006e6e6eaeaeae7e7e7e0707074a4a4aa5a5a5adadad8080800d0d0daeaeaeacacacacacac
+acacacadadadacacacabababa9a9a9a9a9a9a8a8a8a7a7a7797979cbcbcbdcdcdcc6c6c6c7c7c7
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8c9c9c9c9c9c98484849a9a9a
+efefefd5d5d5d6d6d6d7d7d7d6d6d6d5d5d5d4d4d4d3d3d3333333797979d6d6d6d9d9d9d3d3d3
+d5d5d5d7d7d7d8d8d8d7d7d7d4d4d4000000b6b6b6d8d8d8afafaf000000d3d3d3d6d6d6828282
+5b5b5b8080808181818181816363638b8b8bd1d1d1d4d4d4d3d3d3d2d2d2d2d2d2d2d2d2d2d2d2
+d1d1d1d3d3d3d5d5d5d6d6d6d6d6d6d5d5d5d5d5d5d3d3d3d1d1d1d3d3d3d2d2d2d2d2d2d0d0d0
+cfcfcfcacaca0000006969697e7e7e7d7d7d7c7c7c7f7f7f7d7d7db0b0b0cdcdcdababab555555
+7c7c7c7e7e7e6969693a3a3a121212cecececccccc373737333333cacacac9c9c9151515595959
+a5a5a5c9c9c9cacacacccccccccccccccccccccccccacacac8c8c8c8c8c8c8c8c8c8c8c8c6c6c6
+c8c8c8c9c9c9cacacacacacac8c8c8c9c9c9c9c9c9cbcbcbc9c9c9c6c6c6cacacaccccccc7c7c7
+c6c6c6c6c6c6c6c6c6c4c4c4c7c7c7c3c3c3c3c3c3c9c9c9c4c4c4c6c6c6c6c6c6c6c6c6c7c7c7
+ccccccc9c9c9c8c8c8c6c6c6c5c5c5c9c9c9cacacac9c9c9c6c6c6c5c5c5c4c4c4c5c5c5c7c7c7
+c6c6c6c6c6c6c6c6c6c5c5c5c4c4c4c5c5c5c5c5c5c4c4c4c4c4c4c4c4c4c3c3c3c2c2c2c1c1c1
+c0c0c0c2c2c2c0c0c0c0c0c0c0c0c0bfbfbfc0c0c0c0c0c0bfbfbfbebebebfbfbfbebebebfbfbf
+bebebebebebebfbfbfbfbfbfbdbdbdbdbdbdbcbcbcbcbcbcbdbdbdbcbcbcc1c1c1bfbfbfbcbcbc
+bebebec1c1c1c0c0c0c1c1c1c1c1c1bfbfbfc1c1c1bfbfbfbcbcbcbcbcbcbcbcbcbfbfbfbebebe
+bcbcbcbcbcbcbdbdbdbcbcbcbcbcbcbabababbbbbbbcbcbcbababababababbbbbbbababab9b9b9
+b9b9b9bcbcbcbebebebebebebdbdbdbdbdbdbdbdbdb9b9b9babababababababababababab8b8b8
+b8b8b8b6b6b6b5b5b5b6b6b6b6b6b6b6b6b6b7b7b7b7b7b7b5b5b5b5b5b5b8b8b8b7b7b7b7b7b7
+b6b6b6b8b8b8b6b6b6b6b6b6b6b6b6b7b7b7b5b5b5b5b5b5b3b3b3b1b1b1b1b1b1b3b3b3b1b1b1
+b2b2b2b1b1b1b3b3b3b5b5b5b5b5b5b5b5b5b4b4b4b3b3b3b1b1b1b0b0b0b1b1b1adadadaeaeae
+afafafb1b1b1b4b4b4b1b1b1b1b1b1b1b1b1b1b1b1b3b3b3aeaeaeb0b0b0b3b3b3b2b2b2adadad
+aeaeaeaeaeaeaeaeaeadadadafafafa7a7a7141414454545adadadafafafaeaeaeaeaeaeb2b2b2
+8e8e8e040404afafafacacac6767675858586b6b6b6a6a6a696969505050757575aeaeae9c9c9c
+0000006e6e6eadadad7e7e7e0606064444446565656868685e5e5e4a4a4aabababa9a9a9a8a8a8
+a9a9a9aaaaaaa7a7a7a8a8a8a8a8a8a8a8a8a8a8a8a7a7a7797979cbcbcbdbdbdbc5c5c5c8c8c8
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8c9c9c9cacaca8484849a9a9a
+f0f0f0d8d8d8d6d6d6d5d5d5d4d4d4d3d3d3d4d4d4d2d2d2333333767676d0d0d0d4d4d4d5d5d5
+d5d5d5d6d6d6d5d5d5d4d4d4d3d3d3000000b6b6b6d7d7d7adadad000000cfcfcfd3d3d3d4d4d4
+b1b1b1000000000000000000828282d0d0d0d0d0d0d0d0d0d0d0d0cecececdcdcdcccccccbcbcb
+cbcbcbd0d0d0d5d5d5d7d7d7d6d6d6d4d4d4d4d4d4d2d2d2cececed1d1d1d2d2d2d3d3d3cfcfcf
+cfcfcfcccccc000000000000000000000000000000000000000000858585cdcdcdccccccb8b8b8
+0000000000005b5b5b949494121212cecececbcbcb363636333333cacacac9c9c9151515000000
+6b6b6bc8c8c8c7c7c7cdcdcdcccccccdcdcdccccccc9c9c9cacacac7c7c7c5c5c5c4c4c4c4c4c4
+c5c5c5c6c6c6c7c7c7c9c9c9c9c9c9cccccccbcbcbcbcbcbcacacacacacacacacacacacac8c8c8
+c5c5c5c3c3c3c4c4c4c4c4c4c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c4c4c4c4c4c4c6c6c6
+cacacac3c3c3c5c5c5c4c4c4c3c3c3c9c9c9cacacacacacac6c6c6c4c4c4c4c4c4c5c5c5c7c7c7
+c6c6c6c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c3c3c3c0c0c0bfbfbfbfbfbfbfbfbf
+c0c0c0c3c3c3bfbfbfbfbfbfbebebebdbdbdbfbfbfbfbfbfc0c0c0c0c0c0c0c0c0c0c0c0c0c0c0
+c0c0c0c0c0c0bebebebdbdbdbbbbbbbcbcbcbcbcbcbbbbbbbababab7b7b7babababcbcbcbebebe
+bdbdbdbfbfbfbdbdbdbfbfbfc0c0c0bfbfbfc1c1c1bdbdbdbababab9b9b9b9b9b9bdbdbdbdbdbd
+bbbbbbbbbbbbbcbcbcbababab8b8b8b8b8b8b8b8b8b8b8b8b7b7b7b7b7b7b5b5b5b5b5b5b4b4b4
+b3b3b3bbbbbbbfbfbfc0c0c0bdbdbdbbbbbbbdbdbdb6b6b6b8b8b8b9b9b9bbbbbbbdbdbdb7b7b7
+b8b8b8b6b6b6b4b4b4b5b5b5b3b3b3b5b5b5b6b6b6b5b5b5b4b4b4b8b8b8b5b5b5b5b5b5b6b6b6
+b6b6b6b5b5b5b5b5b5b4b4b4b5b5b5b6b6b6b4b4b4b4b4b4b3b3b3aeaeaeacacacaeaeaeb0b0b0
+b0b0b0afafafb1b1b1b7b7b7b5b5b5b7b7b7b3b3b3b1b1b1b1b1b1adadadacacacacacacacacac
+acacacaeaeaeb0b0b0b1b1b1b3b3b3b4b4b4b4b4b4b3b3b3b3b3b3b2b2b2b2b2b2b1b1b1b0b0b0
+ababababababacacacaeaeaeaeaeaeaeaeae141414464646aeaeaeadadadacacacadadadb0b0b0
+8c8c8c040404aeaeaeaaaaaaaeaeae5a5a5a000000000000000000707070acacacaeaeae9c9c9c
+0000006d6d6dacacac7e7e7e101010aeaeae0e0e0e0000002b2b2ba8a8a8a8a8a8a7a7a7a8a8a8
+aaaaaaabababa7a7a7a7a7a7a7a7a7a7a7a7a8a8a8a9a9a9797979cbcbcbdbdbdbc5c5c5c8c8c8
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c9c9c9c9c9c98484849a9a9a
+efefefd0d0d0d3d3d3d1d1d1d3d3d3d4d4d4d4d4d4d5d5d5c9c9c9cbcbcbcecececdcdcdd2d2d2
+d3d3d3d0d0d0cfcfcfd1d1d1d2d2d2c1c1c1d3d3d3d6d6d6d0d0d0bebebed0d0d0d0d0d0cfcfcf
+ccccccc0c0c0bbbbbbbebebecacacad1d1d1d0d0d0cccccccccccccccccccccccccbcbcbcbcbcb
+cbcbcbcacacacccccccdcdcdc7c7c7cccccccccccccbcbcbcacacac9c9c9c9c9c9cccccccdcdcd
+cfcfcfcfcfcfbcbcbcbebebebdbdbdbcbcbcbbbbbbbbbbbbbbbbbbc5c5c5cbcbcbcccccccacaca
+bbbbbbbbbbbbc2c2c2c7c7c7bcbcbccccccccbcbcbbcbcbcbcbcbccbcbcbc8c8c8b7b7b7b7b7b7
+c3c3c3cdcdcdcdcdcdc9c9c9c3c3c3c6c6c6cbcbcbcdcdcdcacacac8c8c8c6c6c6c5c5c5c4c4c4
+c3c3c3c2c2c2c3c3c3c4c4c4c5c5c5c7c7c7c4c4c4c5c5c5c5c5c5c6c6c6c6c6c6c5c5c5c6c6c6
+c7c7c7c6c6c6c2c2c2c3c3c3c4c4c4c5c5c5c5c5c5c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4
+c4c4c4c6c6c6c3c3c3c2c2c2c2c2c2c8c8c8c3c3c3c3c3c3c2c2c2c1c1c1c5c5c5c7c7c7c7c7c7
+c5c5c5c2c2c2c1c1c1c2c2c2c4c4c4c5c5c5c3c3c3c1c1c1c0c0c0bfbfbfbfbfbfbfbfbfc0c0c0
+c0c0c0bfbfbfbfbfbfbebebebdbdbdbcbcbcbbbbbbbcbcbcbdbdbdbdbdbdbdbdbdbfbfbfbcbcbc
+bababababababababab8b8b8bbbbbbbcbcbcbcbcbcbdbdbdbebebeb8b8b8b4b4b4b7b7b7bbbbbb
+bababab6b6b6b9b9b9babababbbbbbbbbbbbc0c0c0bdbdbdbababab9b9b9b9b9b9b9b9b9b7b7b7
+b9b9b9b8b8b8b7b7b7b8b8b8b9b9b9b8b8b8b6b6b6b5b5b5b4b4b4b4b4b4b4b4b4b3b3b3b3b3b3
+b3b3b3b3b3b3b6b6b6b1b1b1b2b2b2b5b5b5b4b4b4b2b2b2b1b1b1b1b1b1b3b3b3b6b6b6b6b6b6
+b7b7b7b7b7b7b7b7b7b8b8b8b6b6b6b5b5b5b5b5b5b5b5b5b5b5b5b4b4b4b4b4b4b4b4b4b5b5b5
+b5b5b5b5b5b5b5b5b5b4b4b4b5b5b5b5b5b5b5b5b5b1b1b1b3b3b3b0b0b0afafafb1b1b1b2b2b2
+b4b4b4b5b5b5b3b3b3afafafabababb2b2b2b4b4b4b4b4b4b2b2b2afafafadadadacacacababab
+aaaaaaaaaaaaacacacacacacadadadadadadacacacacacacaeaeaeaeaeaeadadadacacacaeaeae
+b0b0b0adadadaaaaaaaaaaaaacacacaeaeae9f9f9fa3a3a3acacacacacacacacacacacacacacac
+a9a9a99f9f9faaaaaaaaaaaaaeaeaea6a6a69b9b9b9b9b9b9b9b9ba7a7a7aeaeaeafafafacacac
+9d9d9da5a5a5aaaaaa7b7b7b101010aeaeae9d9d9d9b9b9b9d9d9da8a8a8a7a7a7a8a8a8a9a9a9
+a9a9a9a8a8a8a7a7a7a6a6a6a5a5a5a5a5a5a5a5a5a6a6a6787878cbcbcbdbdbdbc4c4c4c6c6c6
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c6c6c6c8c8c88484849a9a9a
+efefefd2d2d2d1d1d1d1d1d1d2d2d2d2d2d2d2d2d2d3d3d3d1d1d1cfcfcfcfcfcfcfcfcfcfcfcf
+cfcfcfcfcfcfcecececdcdcdcccccccdcdcdcececed0d0d0d3d3d3d1d1d1d2d2d2cfcfcfcecece
+cdcdcdccccccc9c9c9cdcdcdd0d0d0d0d0d0cecececbcbcbcbcbcbcccccccbcbcbcacacac9c9c9
+cbcbcbcbcbcbcbcbcbcbcbcbc9c9c9cbcbcbcacacacacacac9c9c9c8c8c8c9c9c9c9c9c9c9c9c9
+cacacacbcbcbcececed0d0d0cecececacacac8c8c8c8c8c8c8c8c8c8c8c8c7c7c7c6c6c6c7c7c7
+c8c8c8c6c6c6c4c4c4c3c3c3c5c5c5c9c9c9c9c9c9c6c6c6c6c6c6c7c7c7c5c5c5c3c3c3c6c6c6
+c8c8c8c9c9c9ccccccc9c9c9c7c7c7c6c6c6c7c7c7c8c8c8c8c8c8c8c8c8c7c7c7c6c6c6c5c5c5
+c5c5c5c4c4c4c4c4c4c3c3c3c1c1c1bfbfbfc0c0c0c3c3c3c3c3c3c1c1c1c0c0c0c4c4c4c3c3c3
+c4c4c4c3c3c3c2c2c2c1c1c1c0c0c0c3c3c3c4c4c4c2c2c2c2c2c2c1c1c1c2c2c2c2c2c2c2c2c2
+c1c1c1c3c3c3c2c2c2c2c2c2c2c2c2c4c4c4c1c1c1c0c0c0bebebebebebec3c3c3c5c5c5c2c2c2
+c1c1c1c1c1c1c1c1c1bfbfbfc2c2c2c3c3c3c1c1c1bfbfbfbdbdbdbdbdbdbdbdbdbdbdbdbcbcbc
+bcbcbcbfbfbfbfbfbfbebebebdbdbdbbbbbbb9b9b9bbbbbbbbbbbbbbbbbbbabababdbdbdbcbcbc
+bbbbbbbabababababab9b9b9bbbbbbbbbbbbbabababababab8b8b8b7b7b7b7b7b7b7b7b7b7b7b7
+b8b8b8b7b7b7b6b6b6b5b5b5b5b5b5b6b6b6b8b8b8bbbbbbbababababababababab7b7b7b7b7b7
+b5b5b5b3b3b3b3b3b3b6b6b6bababab8b8b8b5b5b5b3b3b3b4b4b4b4b4b4b2b2b2b2b2b2b2b2b2
+b3b3b3b3b3b3b3b3b3b2b2b2b3b3b3b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b2b2b2
+b3b3b3b5b5b5b7b7b7b8b8b8b4b4b4b2b2b2b1b1b1b1b1b1b1b1b1afafafaeaeaeafafafafafaf
+afafafaeaeaeabababacacacafafafb1b1b1b1b1b1adadadafafafadadadacacacafafafb1b1b1
+b1b1b1b3b3b3b4b4b4b1b1b1aeaeaeafafafb0b0b0b1b1b1b1b1b1afafafaeaeaeadadadadadad
+acacacacacacacacacaaaaaaa8a8a8a8a8a8a9a9a9abababaaaaaaa9a9a9aaaaaaabababababab
+abababaaaaaaa9a9a9a9a9a9a8a8a8aeaeaeaaaaaaaaaaaaaaaaaaa9a9a9acacacaaaaaaaaaaaa
+aaaaaaabababaaaaaaaaaaaaabababaaaaaaa9a9a9a8a8a8a7a7a7a9a9a9acacacadadadaaaaaa
+a9a9a9a9a9a9a9a9a97a7a7a101010abababa8a8a8a6a6a6a5a5a5a6a6a6a6a6a6a5a5a5a6a6a6
+a6a6a6a7a7a7a7a7a7a6a6a6a4a4a4a2a2a2a2a2a2a4a4a4787878cbcbcbdbdbdbc3c3c3c4c4c4
+c6c6c6060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b404040c3c3c3c5c5c5c7c7c78484849a9a9a
+efefefd0d0d0cecececfcfcfcfcfcfcfcfcfcfcfcfd1d1d1cececececececfcfcfcececed0d0d0
+cdcdcdcecececfcfcfcececececececdcdcdcdcdcdcdcdcdcecececfcfcfd0d0d0cecececccccc
+cccccccececececececfcfcfcecececdcdcdcccccccbcbcbcbcbcbcccccccbcbcbcbcbcbcbcbcb
+cdcdcdcccccccccccccccccccdcdcdcbcbcbcacacacacacacbcbcbc9c9c9cccccccacacac8c8c8
+c8c8c8c9c9c9c9c9c9cccccccecececccccccacacacacacacacacac9c9c9c9c9c9c9c9c9c8c8c8
+c6c6c6c4c4c4c4c4c4c5c5c5c5c5c5c9c9c9c8c8c8c7c7c7c6c6c6c5c5c5c5c5c5c4c4c4c4c4c4
+c4c4c4c4c4c4c7c7c7c6c6c6c7c7c7c7c7c7c6c6c6c4c4c4c6c6c6c5c5c5c5c5c5c5c5c5c4c4c4
+c5c5c5c2c2c2c2c2c2c2c2c2c0c0c0bfbfbfbebebec1c1c1c1c1c1bebebec1c1c1c1c1c1c1c1c1
+c0c0c0c0c0c0c1c1c1c0c0c0bfbfbfc0c0c0c1c1c1c2c2c2c1c1c1c0c0c0c0c0c0bfbfbfc0c0c0
+c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c1c1c1c1c1c1c0c0c0bebebebdbdbdbfbfbfc2c2c2c1c1c1
+bfbfbfc0c0c0c1c1c1bfbfbfbebebebebebebfbfbfbfbfbfbebebebebebebdbdbdbcbcbcbcbcbc
+bcbcbcbdbdbdbdbdbdbdbdbdbcbcbcbabababababab9b9b9b9b9b9b9b9b9b9b9b9b9b9b9bababa
+b9b9b9b8b8b8b7b7b7b7b7b7b8b8b8b8b8b8b9b9b9b8b8b8b6b6b6b8b8b8b6b6b6b7b7b7b8b8b8
+b6b6b6b8b8b8b6b6b6b6b6b6b6b6b6b6b6b6b7b7b7b6b6b6b7b7b7b8b8b8b8b8b8b7b7b7b5b5b5
+b6b6b6b6b6b6b6b6b6b7b7b7b7b7b7b6b6b6b4b4b4b3b3b3b4b4b4b5b5b5b3b3b3b3b3b3b4b4b4
+b5b5b5b5b5b5b5b5b5b5b5b5b4b4b4b3b3b3b2b2b2b3b3b3b2b2b2b3b3b3b4b4b4b2b2b2b0b0b0
+b1b1b1b2b2b2b3b3b3b5b5b5b6b6b6b3b3b3b3b3b3b3b3b3b3b3b3b1b1b1b2b2b2b1b1b1b0b0b0
+adadadacacacaeaeaeadadadafafafb1b1b1b1b1b1aeaeaeaeaeaeacacacacacacacacacaeaeae
+adadadadadadaeaeaeaeaeaeaeaeaeafafafaeaeaeadadadadadadacacacacacacacacacacacac
+acacacabababaaaaaaa9a9a9a8a8a8a7a7a7a7a7a7aaaaaaa8a8a8a8a8a8a8a8a8a9a9a9a8a8a8
+a8a8a8a8a8a8a8a8a8a7a7a7a7a7a7a9a9a9a9a9a9aaaaaaa9a9a9a8a8a8a8a8a8a8a8a8a9a9a9
+a9a9a9a8a8a8a8a8a8a8a8a8a9a9a9a9a9a9a9a9a9a8a8a8a6a6a6a7a7a7a9a9a9aaaaaaa8a8a8
+a8a8a8a9a9a9a9a9a97a7a7a101010a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a6a6a6a5a5a5a5a5a5
+a6a6a6a6a6a6a5a5a5a4a4a4a3a3a3a2a2a2a2a2a2a2a2a2787878cbcbcbdbdbdbc3c3c3c3c3c3
+c5c5c5060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c4c4c4c6c6c68383839a9a9a
+eeeeeecdcdcdcbcbcbcccccccdcdcdcdcdcdcececed0d0d0cdcdcdcdcdcdcdcdcdcbcbcbcecece
+cccccccccccccdcdcdcecececdcdcdcccccccccccccbcbcbcacacacbcbcbcbcbcbcccccccbcbcb
+cbcbcbcececed1d1d1cecececbcbcbcacacacacacac9c9c9cbcbcbcbcbcbcbcbcbcccccccdcdcd
+cecececccccccbcbcbcbcbcbcdcdcdcccccccacacacbcbcbccccccc9c9c9cececec8c8c8c5c5c5
+c6c6c6c7c7c7c5c5c5c7c7c7cbcbcbcccccccbcbcbcbcbcbcacacacbcbcbcccccccbcbcbc8c8c8
+c3c3c3c2c2c2c4c4c4c7c7c7c6c6c6c8c8c8c7c7c7c6c6c6c6c6c6c5c5c5c5c5c5c8c8c8c5c5c5
+c2c2c2c1c1c1c2c2c2c2c2c2c4c4c4c6c6c6c6c6c6c3c3c3c4c4c4c2c2c2c2c2c2c2c2c2c1c1c1
+c3c3c3c0c0c0c1c1c1c2c2c2c0c0c0bebebebdbdbdbfbfbfbfbfbfbdbdbdc1c1c1bfbfbfc1c1c1
+c0c0c0bfbfbfbfbfbfc0c0c0bfbfbfbebebebebebebfbfbfbebebebebebebdbdbdbdbdbdbebebe
+bebebebebebebdbdbdbdbdbdbdbdbdbdbdbdbebebebebebebdbdbdbcbcbcbebebebfbfbfbfbfbf
+bebebebebebebebebebebebebabababbbbbbbcbcbcbebebebebebebdbdbdbcbcbcbbbbbbbbbbbb
+bbbbbbbbbbbbbbbbbbbabababababab9b9b9b9b9b9b8b8b8b8b8b8b8b8b8b8b8b8b7b7b7b8b8b8
+b7b7b7b5b5b5b4b4b4b4b4b4b5b5b5b6b6b6b7b7b7b7b7b7b4b4b4b7b7b7b3b3b3b4b4b4b6b6b6
+b5b5b5b5b5b5b6b6b6b6b6b6b6b6b6b5b5b5b6b6b6b3b3b3b3b3b3b4b4b4b4b4b4b4b4b4b2b2b2
+b5b5b5b7b7b7b8b8b8b5b5b5b3b3b3b2b2b2b2b2b2b2b2b2b3b3b3b5b5b5b4b4b4b4b4b4b5b5b5
+b5b5b5b4b4b4b3b3b3b5b5b5b4b4b4b4b4b4b3b3b3b4b4b4b2b2b2b4b4b4b4b4b4b1b1b1adadad
+aeaeaeaeaeaeadadadb0b0b0b6b6b6b4b4b4b3b3b3b3b3b3b3b3b3b5b5b5b5b5b5b1b1b1aeaeae
+abababaaaaaab0b0b0aeaeaeafafafb0b0b0afafafaeaeaeaeaeaeafafafaeaeaeabababababab
+aaaaaaa9a9a9a9a9a9aaaaaaacacacb0b0b0adadadababababababaaaaaaabababaaaaaaaaaaaa
+aaaaaaa9a9a9aaaaaaa9a9a9a7a7a7a6a6a6a6a6a6a8a8a8a6a6a6a8a8a8a8a8a8a8a8a8a9a9a9
+a8a8a8a8a8a8a7a7a7a8a8a8a7a7a7a6a6a6a7a7a7a7a7a7a7a7a7a6a6a6a6a6a6a6a6a6a6a6a6
+a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a7a7a7a7a7a7a7a7a7
+a7a7a7a7a7a7a7a7a7898989464646a4a4a4a5a5a5a6a6a6a6a6a6a6a6a6a4a4a4a4a4a4a4a4a4
+a4a4a4a4a4a4a3a3a3a2a2a2a1a1a1a2a2a2a1a1a1a0a0a0787878cbcbcbdcdcdcc4c4c4c4c4c4
+c5c5c5060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c5c5c5c6c6c68383839a9a9a
+eeeeeecccccccacacacacacacbcbcbcccccccdcdcdcecececacacacacacacacacac8c8c8c8c8c8
+c9c9c9c9c9c9c9c9c9cacacac8c8c8cacacacacacac9c9c9c9c9c9c7c7c7c7c7c7c8c8c8c9c9c9
+c9c9c9c7c7c7cccccccacacac9c9c9c8c8c8c8c8c8c9c9c9c9c9c9cacacacacacacacacacacaca
+cccccccacacac8c8c8c6c6c6c8c8c8cacacacacacacacacac9c9c9c8c8c8cacacac4c4c4c1c1c1
+c2c2c2c4c4c4c4c4c4c4c4c4c7c7c7c8c8c8c8c8c8c6c6c6c8c8c8c9c9c9c9c9c9c7c7c7c2c2c2
+c0c0c0c0c0c0c1c1c1c3c3c3c3c3c3c4c4c4c3c3c3c3c3c3c3c3c3c3c3c3c5c5c5c8c8c8c6c6c6
+c3c3c3c2c2c2c2c2c2c2c2c2c1c1c1c4c4c4c6c6c6c4c4c4c2c2c2c1c1c1c1c1c1c0c0c0bdbdbd
+c0c0c0c0c0c0c1c1c1c1c1c1bfbfbfbbbbbbbfbfbfbfbfbfbebebebcbcbcbebebec0c0c0c3c3c3
+c1c1c1bfbfbfbfbfbfc1c1c1c1c1c1bfbfbfbcbcbcbbbbbbbababab9b9b9bbbbbbbcbcbcbababa
+bbbbbbbbbbbbbbbbbbbbbbbbbabababababab9b9b9bababababababbbbbbbebebebcbcbcbcbcbc
+bcbcbcbcbcbcbbbbbbbbbbbbb9b9b9b9b9b9babababababababababababab9b9b9b8b8b8b8b8b8
+b8b8b8b9b9b9b8b8b8b8b8b8b8b8b8b7b7b7b7b7b7b7b7b7b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6
+b5b5b5b4b4b4b3b3b3b3b3b3b4b4b4b5b5b5b6b6b6b5b5b5b1b1b1b3b3b3b0b0b0b0b0b0b1b1b1
+b3b3b3b0b0b0b3b3b3b1b1b1b0b0b0b2b2b2b2b2b2b1b1b1afafafafafafb0b0b0b1b1b1b1b1b1
+b0b0b0b2b2b2b4b4b4b2b2b2b2b2b2b0b0b0b0b0b0b1b1b1b1b1b1b3b3b3b2b2b2b2b2b2b3b3b3
+b4b4b4b1b1b1adadadb0b0b0b2b2b2b3b3b3b3b3b3b1b1b1b0b0b0b1b1b1afafafacacaca9a9a9
+abababacacacacacacacacacb0b0b0b1b1b1afafafaeaeaeb1b1b1b3b3b3b2b2b2abababa9a9a9
+a8a8a8a8a8a8ababababababacacacadadadababababababacacacafafafafafafacacacaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeaeaeadadadabababaaaaaaa9a9a9a9a9a9a6a6a6a6a6a6
+a8a8a8a9a9a9aaaaaaa8a8a8a5a5a5a5a5a5a7a7a7a7a7a7a5a5a5a6a6a6a7a7a7a9a9a9ababab
+a9a9a9a8a8a8a7a7a7a8a8a8a8a8a8a6a6a6a4a4a4a4a4a4a2a2a2a1a1a1a6a6a6a3a3a3a3a3a3
+a3a3a3a4a4a4a4a4a4a3a3a3a3a3a3a2a2a2a2a2a2a2a2a2a4a4a4a5a5a5a5a5a5a5a5a5a5a5a5
+a5a5a5a5a5a5a4a4a4a3a3a3a1a1a1a2a2a2a2a2a2a3a3a3a3a3a3a2a2a2a0a0a0a0a0a0a1a1a1
+a1a1a1a1a1a1a0a0a0a0a0a0a0a0a09f9f9f9f9f9f9e9e9e777777cbcbcbddddddc6c6c6c6c6c6
+c5c5c5060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c7c7c7c8c8c88484849a9a9a
+eeeeeecbcbcbcdcdcdcbcbcbcacacacacacacacacacacacac9c9c9c9c9c9c9c9c9c9c9c9c9c9c9
+c8c8c8c8c8c8c8c8c8c9c9c9cacacacacacacacacac9c9c9c7c7c7c7c7c7c7c7c7c9c9c9c8c8c8
+c7c7c7c6c6c6c6c6c6c7c7c7cbcbcbcdcdcdcdcdcdcdcdcdc6c6c6c5c5c5c6c6c6c6c6c6c5c5c5
+c7c7c7c5c5c5c4c4c4c3c3c3c3c3c3c4c4c4c5c5c5c4c4c4c3c3c3c3c3c3c3c3c3c3c3c3c5c5c5
+c5c5c5c5c5c5c5c5c5c5c5c5c4c4c4c4c4c4c5c5c5c4c4c4c6c6c6c5c5c5c4c4c4c5c5c5c3c3c3
+c3c3c3c2c2c2c1c1c1c2c2c2c1c1c1c1c1c1c1c1c1c0c0c0bfbfbfbfbfbfc1c1c1c1c1c1c1c1c1
+c4c4c4c6c6c6c6c6c6c5c5c5c2c2c2c1c1c1c2c2c2c2c2c2bdbdbdbfbfbfc0c0c0bfbfbfbfbfbf
+bfbfbfbdbdbdbcbcbcbdbdbdbfbfbfc1c1c1c0c0c0c0c0c0bfbfbfbababababababebebebebebe
+bdbdbdbebebec0c0c0bbbbbbbfbfbfbfbfbfbebebebcbcbcbdbdbdb9b9b9b9b9b9b9b9b9b7b7b7
+bababababababababababababababab9b9b9b9b9b9b9b9b9bababab9b9b9b6b6b6b9b9b9bababa
+babababcbcbcbebebebcbcbcbbbbbbbababab9b9b9b9b9b9b8b8b8b6b6b6b4b4b4b3b3b3b4b4b4
+b7b7b7bcbcbcb8b8b8b8b8b8b8b8b8b6b6b6b5b5b5b4b4b4b4b4b4b4b4b4b3b3b3b4b4b4b3b3b3
+b3b3b3b4b4b4b5b5b5b3b3b3b3b3b3b3b3b3b3b3b3b2b2b2b1b1b1b1b1b1b2b2b2b2b2b2b2b2b2
+b0b0b0b0b0b0b0b0b0b2b2b2b2b2b2b2b2b2b1b1b1b1b1b1b0b0b0afafafb0b0b0b0b0b0b0b0b0
+aeaeaeaeaeaeaeaeaeafafafb6b6b6b6b6b6b6b6b6b3b3b3acacacaeaeaeaeaeaeaeaeaeaeaeae
+afafafacacacacacacacacacacacacacacacaeaeaeaaaaaaabababaaaaaaaaaaaaacacacadadad
+adadadadadadadadadababababababadadadadadadaeaeaeafafafacacacadadadacacacababab
+abababa9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a8a8a8a8a8a8a7a7a7a8a8a8a9a9a9aaaaaaababab
+adadadafafafafafafadadada9a9a9a9a9a9abababa9a9a9a6a6a6a8a8a8a8a8a8a7a7a7a7a7a7
+a7a7a7a6a6a6a4a4a4a6a6a6a8a8a8a9a9a9a9a9a9a8a8a8a4a4a4a2a2a2a4a4a4a6a6a6a6a6a6
+a6a6a6a7a7a7a6a6a6a5a5a5a8a8a8a7a7a7a6a6a6a5a5a5a4a4a4a0a0a0a4a4a49f9f9fa1a1a1
+a3a3a3a4a4a4a3a3a3a3a3a3a2a2a2a1a1a1a1a1a1a2a2a2a2a2a2a0a0a0a0a0a0a2a2a2a3a3a3
+a3a3a3a6a6a6a6a6a6a4a4a4a3a3a3a1a1a1a2a2a2a2a2a29f9f9f9c9c9c9b9b9b9b9b9b9e9e9e
+a3a3a3a5a5a59f9f9fa0a0a0a0a0a09e9e9e9c9c9c9c9c9c777777cbcbcbdededec9c9c9c7c7c7
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacacacacacbcbcb8585859a9a9a
+eeeeeec9c9c9c8c8c8c9c9c9cacacacacacacacacac8c8c8cacacac9c9c9c9c9c9cbcbcbc9c9c9
+cbcbcbc8c8c8c5c5c5c7c7c7c8c8c8c3c3c3c3c3c3c3c3c3c4c4c4c6c6c6c7c7c7c5c5c5c5c5c5
+c6c6c6cacacac8c8c8c7c7c7c6c6c6c4c4c4c3c3c3c2c2c2c5c5c5c9c9c9cbcbcbc9c9c9c5c5c5
+c5c5c5c6c6c6c6c6c6c6c6c6c6c6c6c7c7c7c6c6c6c6c6c6c6c6c6c6c6c6c8c8c8c8c8c8c6c6c6
+c4c4c4c3c3c3c4c4c4c4c4c4c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c1c1c1bfbfbfbdbdbd
+bdbdbdbfbfbfbfbfbfbdbdbdbdbdbdbdbdbdbdbdbdbebebebfbfbfc0c0c0c1c1c1c1c1c1c1c1c1
+bebebebdbdbdbdbdbdbebebebcbcbcbbbbbbbbbbbbbcbcbcc2c2c2c1c1c1c0c0c0bfbfbfbcbcbc
+b9b9b9b9b9b9b9b9b9bbbbbbbababab9b9b9b9b9b9babababbbbbbbbbbbbbdbdbdbdbdbdbdbdbd
+bebebebdbdbdb9b9b9b7b7b7b9b9b9bbbbbbbbbbbbbbbbbbbdbdbdbbbbbbbebebebfbfbfbcbcbc
+bababab7b7b7b5b5b5b5b5b5b7b7b7b8b8b8b8b8b8babababbbbbbbababab7b7b7b9b9b9b8b8b8
+b8b8b8b8b8b8b9b9b9b4b4b4b5b5b5b6b6b6b7b7b7b9b9b9b8b8b8b6b6b6b6b6b6b6b6b6b6b6b6
+b8b8b8bbbbbbb7b7b7b7b7b7b8b8b8b7b7b7b6b6b6b3b3b3b2b2b2b2b2b2b3b3b3b4b4b4b3b3b3
+b2b2b2b1b1b1b1b1b1b2b2b2b3b3b3b2b2b2b1b1b1b1b1b1b3b3b3b0b0b0b3b3b3b3b3b3b2b2b2
+b4b4b4aeaeaeaeaeaeb0b0b0afafafacacacacacacacacacadadadafafafafafafacacacadadad
+b0b0b0b1b1b1afafafaeaeaeaeaeaeabababaaaaaaabababaeaeaeb4b4b4b3b3b3afafafacacac
+acacacaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeafafafb0b0b0b1b1b1aeaeae
+acacacacacacacacacabababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacacaca9a9a9a7a7a7a7a7a7
+a7a7a7a9a9a9a6a6a6a6a6a6a6a6a6a5a5a5a5a5a5a6a6a6a7a7a7a8a8a8a9a9a9a8a8a8a6a6a6
+a5a5a5a5a5a5a6a6a6a6a6a6a4a4a4a4a4a4a4a4a4a6a6a6a9a9a9a8a8a8a8a8a8a6a6a6a4a4a4
+a1a1a1a1a1a1a2a2a2a4a4a4a2a2a2a1a1a1a2a2a2a3a3a3a4a4a4a5a5a5a6a6a6a6a6a6a5a5a5
+a7a7a7a3a3a3a0a0a0a0a0a0a3a3a3a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a9a9a9a5a5a5a3a3a3
+a2a2a29e9e9e9d9d9d9e9e9ea0a0a0a1a1a1a1a1a1a4a4a4a4a4a4a0a0a0a0a0a0a2a2a2a0a0a0
+a0a0a0a0a0a09f9f9f9d9d9d9e9e9e9d9d9da0a0a0a1a1a1a1a1a19e9e9e9d9d9d9d9d9d9f9f9f
+a2a2a2a3a3a39f9f9f9f9f9fa0a0a09e9e9e9b9b9b999999777777cbcbcbe0e0e0cdcdcdcbcbcb
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cccccccccccccdcdcd858585848484
+ababab9a9a9a9999999a9a9a9b9b9b9b9b9b9a9a9a9a9a9a9b9b9b9a9a9a9a9a9a9b9b9b9a9a9a
+9b9b9b9999999898989999999a9a9a989898979797979797989898999999999999989898989898
+9999999a9a9a9999999999999999999898989797979797979898989a9a9a9b9b9b9a9a9a989898
+9898989999999999999999999999999999999999999999999999999999999999999a9a9a999999
+989898979797989898989898979797979797979797979797979797979797969696959595949494
+949494969696959595949494949494949494949494959595959595969696969696969696969696
+959595949494949494959595949494939393939393949494979797969696969696959595949494
+929292929292939393939393939393929292939393939393939393949494949494949494949494
+959595949494929292929292939393939393939393949494949494949494959595969696949494
+939393929292919191919191929292929292929292939393939393939393929292939393929292
+929292929292929292909090919191919191929292929292929292929292919191919191919191
+9292929393939292929292929292929292929191919090908f8f8f8f8f8f909090909090909090
+8f8f8f8f8f8f8f8f8f8f8f8f9090909090908f8f8f8f8f8f9090908e8e8e909090909090909090
+9090908d8d8d8d8d8d8e8e8e8e8e8e8c8c8c8c8c8c8c8c8c8d8d8d8e8e8e8e8e8e8c8c8c8d8d8d
+8f8f8f8f8f8f8e8e8e8d8d8d8d8d8d8c8c8c8c8c8c8c8c8c8e8e8e9090909090908e8e8e8c8c8c
+8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8e8e8e8e8e8e8f8f8f8e8e8e
+8d8d8d8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8b8b8b8a8a8a8a8a8a
+8a8a8a8b8b8b8a8a8a8a8a8a8a8a8a8a8a8a8989898a8a8a8a8a8a8b8b8b8b8b8b8b8b8b8a8a8a
+8989898989898a8a8a8a8a8a8989898989898989898a8a8a8b8b8b8b8b8b8b8b8b8a8a8a898989
+8787878787878888888989898888888888888888888989898989898989898a8a8a8a8a8a898989
+8a8a8a8989898787878787878888888989898989898989898989898989898b8b8b898989898989
+888888868686858585868686878787878787878787898989898989878787878787888888878787
+878787878787868686868686868686858585878787888888878787868686858585858585868686
+888888898989868686878787878787868686858585848484747474cbcbcbe1e1e1cecececccccc
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cececececececececea9a9a9a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0d9d9d9e1e1e1cfcfcfcecece
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b444444d0d0d0d0d0d0d0d0d0e7e7e7ededed
+edededededededededededededededededededededecececececececececececececececededed
+ededededededededededededededededededededededededececececececededededededededed
+edededededededededecececececececececededededededededededededededededededededed
+ededededededededededededecececededededededededededededececececececedededededed
+ededededededededededededededededededededededededededededededededededededececec
+ecececececececececececececececededededededededededededededededededecececececec
+ececececececececececececececececececececececececededededededeeeeeeedededededed
+ededededededededededededeeeeeeededededededededededededeeeeeeeeeeeeeeeeeeeeeeee
+eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeededed
+edededededededededecececececececececececececececececececececececececececececec
+ecececececececececececececececececececececececececececedededededededededededed
+ededededededededededededededededededededededededededededededededededededededed
+edededededededededededededededededededededededededededecececececececececececec
+ecececededededededededededededededededededededededededecececececececececededed
+edededededededededededededededecececececececececededededededededededededededed
+ededededededededededededededededededecececededededededededededededecececececec
+ededededededededededededededededededededededededededededededededededededededed
+edededececececececececececececececececececedededededededededededededededededed
+ececececececececececececececececececececececececececececececededededededeeeeee
+eeeeeeededededededededededededeeeeeeeeeeeeedededededededededeeeeeeeeeeeeeeeeee
+eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+eeeeeeededededededededededededecececececececececececececececececececececececec
+ecececececececececececececececececececececececececececececececececedededededed
+ededededededededededededededededededededededededededededededdededed0d0d0d0d0d0
+d0d0d0060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b444444d0d0d0d0d0d0d1d1d1d0d0d0d0d0d0
+d0d0d0d0d0d0d0d0d0cecececccccccccccccccccccbcbcbcacacacacacacacacacbcbcbcccccc
+cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcccccccbcbcbcacacacbcbcbcccccccdcdcdcecece
+cececececececccccccbcbcbcbcbcbcbcbcbcccccccccccccccccccdcdcdcececececececfcfcf
+cececececececdcdcdcbcbcbcbcbcbcccccccccccccccccccbcbcbcbcbcbcbcbcbcbcbcbcdcdcd
+cececececececececed0d0d0d1d1d1d0d0d0cfcfcfcecececfcfcfd0d0d0cfcfcfcdcdcdcacaca
+cacacac8c8c8c8c8c8c8c8c8c9c9c9ccccccd0d0d0d1d1d1d0d0d0cdcdcdcccccccacacac7c7c7
+c6c6c6c6c6c6c6c6c6c6c6c6c7c7c7c9c9c9cacacacbcbcbcececed0d0d0d2d2d2d1d1d1cfcfcf
+cccccccdcdcdcfcfcfd1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d2d2d2d4d4d4d4d4d4d5d5d5
+d5d5d5d5d5d5d5d5d5d5d5d5d4d4d4d3d3d3d3d3d3d4d4d4d6d6d6d5d5d5d3d3d3d2d2d2d0d0d0
+d0d0d0cecececccccccbcbcbc9c9c9c8c8c8c8c8c8c8c8c8c9c9c9cacacacbcbcbcacacacacaca
+cacacac9c9c9c9c9c9c9c9c9cacacacacacacbcbcbcbcbcbcbcbcbcbcbcbcdcdcdcececececece
+cececed0d0d0d0d0d0d0d0d0d0d0d0cdcdcdcccccccccccccdcdcdcfcfcfd1d1d1d1d1d1d1d1d1
+d1d1d1d1d1d1d1d1d1d1d1d1d0d0d0cecececdcdcdcccccccbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcccccccdcdcdcecececececececececececececececccccccbcbcbcbcbcbcbcbcbcbcbcb
+cdcdcdcececececececdcdcdcccccccbcbcbcbcbcbcbcbcbcccccccccccccccccccccccccecece
+cececed0d0d0cececececececccccccbcbcbcbcbcbcbcbcbcccccccccccccbcbcbcbcbcbcbcbcb
+cbcbcbcdcdcdcececececececececed0d0d0d1d1d1d0d0d0cfcfcfcecececfcfcfd0d0d0cfcfcf
+cdcdcdcbcbcbcacacac8c8c8c8c8c8c8c8c8c9c9c9cbcbcbcfcfcfd1d1d1cfcfcfcdcdcdcccccc
+cacacac8c8c8c6c6c6c6c6c6c6c6c6c7c7c7c7c7c7c8c8c8cacacacbcbcbcececed1d1d1d2d2d2
+d1d1d1d0d0d0cccccccdcdcdcfcfcfd1d1d1d2d2d2d1d1d1d1d1d1d1d1d1d1d1d1d2d2d2d4d4d4
+d4d4d4d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d4d4d4d3d3d3d3d3d3d4d4d4d6d6d6d5d5d5d3d3d3
+d2d2d2d0d0d0d0d0d0cecececccccccbcbcbc9c9c9c8c8c8c8c8c8c8c8c8c9c9c9cacacacacaca
+cacacacacacacacacac9c9c9c9c9c9cacacacacacacacacacbcbcbcbcbcbcbcbcbcbcbcbcdcdcd
+cecececececececececfcfcfd0d0d0d0d0d0cfcfcfcecececccccccccccccdcdcdcececed1d1d1
+d1d1d1070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cececed1d1d1d3d3d3d3d3d3d3d3d3
+d2d2d2d1d1d1d1d1d1d1d1d1cfcfcfcdcdcdcccccccbcbcbcbcbcbcbcbcbcacacacacacacacaca
+cacacacbcbcbcccccccccccccccccccccccccbcbcbcbcbcbcacacacbcbcbcbcbcbcbcbcbcdcdcd
+cececececececdcdcdcccccccdcdcdcdcdcdcccccccccccccccccccccccccccccccececececece
+cfcfcfcecececdcdcdcbcbcbcbcbcbcccccccdcdcdcdcdcdcbcbcbcccccccdcdcdcdcdcdd0d0d0
+d0d0d0d0d0d0d0d0d0d0d0d0d1d1d1d0d0d0d0d0d0cfcfcfcfcfcfd0d0d0cfcfcfcdcdcdcacaca
+c8c8c8c7c7c7c6c6c6c6c6c6c6c6c6c9c9c9cbcbcbcdcdcdcdcdcdcccccccccccccbcbcbc8c8c8
+c5c5c5c5c5c5c5c5c5c5c5c5c6c6c6c8c8c8cacacacacacacacacacbcbcbcdcdcdcececececece
+cfcfcfcecececececed0d0d0d0d0d0d0d0d0d0d0d0cecececececececececececed1d1d1d4d4d4
+d6d6d6d7d7d7d7d7d7d7d7d7d6d6d6d4d4d4d3d3d3d3d3d3d3d3d3d3d3d3d1d1d1d0d0d0d0d0d0
+d0d0d0cfcfcfcecececdcdcdcbcbcbcacacacacacacacacacacacacacacacbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbc9c9c9c9c9c9c9c9c9cacacacbcbcbcacacacacacacbcbcbcdcdcdcececececece
+d1d1d1d1d1d1d1d1d1d1d1d1cfcfcfcacacac7c7c7c9c9c9cccccccecececececed1d1d1d3d3d3
+d3d3d3d3d3d3d2d2d2d1d1d1d1d1d1d1d1d1cfcfcfcdcdcdcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cacacacacacacacacacbcbcbcccccccccccccccccccccccccbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcccccccecececdcdcdcdcdcdcccccccccccccdcdcdcccccccccccccccccccccccccccccc
+cececececececfcfcfcecececccccccbcbcbcbcbcbcbcbcbcdcdcdcccccccbcbcbcbcbcbcdcdcd
+cdcdcdcfcfcfd0d0d0d0d0d0d0d0d0d0d0d0d1d1d1d0d0d0d0d0d0cfcfcfcfcfcfd0d0d0cfcfcf
+cdcdcdcbcbcbc8c8c8c7c7c7c6c6c6c6c6c6c6c6c6c8c8c8cbcbcbcdcdcdcdcdcdcccccccccccc
+cbcbcbc8c8c8c6c6c6c5c5c5c5c5c5c5c5c5c6c6c6c8c8c8cacacacacacacacacacbcbcbcccccc
+cececececececfcfcfcecececfcfcfd0d0d0d0d0d0d0d0d0d0d0d0cececececececececececece
+d1d1d1d3d3d3d5d5d5d7d7d7d7d7d7d7d7d7d6d6d6d5d5d5d3d3d3d3d3d3d3d3d3d3d3d3d1d1d1
+d0d0d0d0d0d0d0d0d0cfcfcfcecececdcdcdcbcbcbcacacacacacacacacacacacacacacacbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbc9c9c9c9c9c9c9c9c9cacacacbcbcbcacacacacacacbcbcbcdcdcd
+cecececececed0d0d0d1d1d1d1d1d1d1d1d1cecececbcbcbc7c7c7c9c9c9cccccccececececece
+d1d1d1070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cececed1d1d1d4d4d4d5d5d5d5d5d5
+d4d4d4d3d3d3d3d3d3d2d2d2d0d0d0cecececccccccbcbcbcbcbcbcacacacacacac9c9c9c9c9c9
+c9c9c9c9c9c9cacacacbcbcbcbcbcbcacacacacacacacacacacacacacacac9c9c9cacacacccccc
+cecececfcfcfd0d0d0d0d0d0d1d1d1d0d0d0d0d0d0cfcfcfcdcdcdcecececfcfcfcfcfcfcfcfcf
+cececececececdcdcdcbcbcbcbcbcbcccccccdcdcdcdcdcdcccccccdcdcdd0d0d0d1d1d1d2d2d2
+d2d2d2d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d0d0d0d0d0d0d0d0d0d0d0d0cfcfcfcdcdcdcacaca
+c8c8c8c7c7c7c6c6c6c5c5c5c5c5c5c7c7c7cacacacbcbcbcccccccccccccccccccbcbcbc8c8c8
+c6c6c6c5c5c5c4c4c4c4c4c4c6c6c6c9c9c9cbcbcbcbcbcbcacacac9c9c9cacacacbcbcbcccccc
+cececed0d0d0d0d0d0cfcfcfcececececececececececececdcdcdcbcbcbc9c9c9cacacacecece
+d2d2d2d5d5d5d8d8d8d7d7d7d5d5d5d5d5d5d4d4d4d3d3d3d2d2d2d2d2d2d1d1d1d1d1d1d0d0d0
+cecececececececececdcdcdcdcdcdcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcdcdcd
+cccccccbcbcbcacacacacacacacacacacacacacacac9c9c9c8c8c8cbcbcbcbcbcbcdcdcdcecece
+cececed1d1d1d2d2d2d2d2d2d0d0d0cacacac7c7c7c8c8c8cbcbcbcdcdcdcececed2d2d2d4d4d4
+d5d5d5d5d5d5d4d4d4d3d3d3d3d3d3d2d2d2d0d0d0cecececccccccccccccbcbcbcacacacacaca
+cacacac9c9c9c9c9c9cacacacacacacbcbcbcbcbcbcacacacacacacacacacacacacacacac9c9c9
+cacacacccccccecececfcfcfd0d0d0d0d0d0d1d1d1d0d0d0cfcfcfcfcfcfcdcdcdcecececfcfcf
+cfcfcfcfcfcfcececececececccccccbcbcbcbcbcbcbcbcbcdcdcdcccccccccccccdcdcdd0d0d0
+d1d1d1d2d2d2d2d2d2d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d0d0d0d0d0d0d0d0d0d0d0d0cfcfcf
+cdcdcdcbcbcbc8c8c8c7c7c7c6c6c6c5c5c5c5c5c5c7c7c7c9c9c9cbcbcbcccccccccccccccccc
+cbcbcbc9c9c9c7c7c7c5c5c5c4c4c4c5c5c5c6c6c6c9c9c9cbcbcbcbcbcbcacacac9c9c9c9c9c9
+cbcbcbcccccccececed0d0d0d0d0d0cfcfcfcececececececececececececccccccacacac9c9c9
+cacacacdcdcdd1d1d1d5d5d5d7d7d7d7d7d7d5d5d5d5d5d5d5d5d5d3d3d3d2d2d2d2d2d2d1d1d1
+d1d1d1d0d0d0cecececececececececdcdcdcdcdcdcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cccccccdcdcdcccccccbcbcbcacacacacacacacacacacacacacacac9c9c9c8c8c8cacacacccccc
+cdcdcdcecececececed0d0d0d2d2d2d2d2d2cfcfcfcbcbcbc7c7c7c8c8c8cacacacccccccecece
+d2d2d2070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b2af9999994b4b4b434343cdcdcdd0d0d0d4d4d4c4c4c4bfc0bf
+bfbfbfbebebebebebebdbdbdbcbcbcbabbbab9b9b9b9b9b9b8b9b8b8b8b8b7b8b7b7b8b7b7b7b7
+b7b7b7b7b7b7b8b8b8b8b8b8b8b8b8b7b8b7b7b8b7b7b8b7b7b8b7b7b8b7b7b7b7b8b8b8b9bab9
+babbbabbbcbbbcbcbcbdbdbdbdbdbdbcbdbcbcbcbcbbbcbbbabbbabbbbbbbbbcbbbbbcbbbbbcbb
+bababababababababab9b9b9b9b9b9b9b9b9babababababab9b9b9babbbabcbcbcbdbebdbdbebd
+bdbdbdbdbdbdbdbdbdbdbdbdbcbdbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbbbbbbbababab8b8b8
+b6b6b6b6b6b6b5b5b5b5b5b5b4b5b4b5b6b5b7b8b7b8b9b8b9b9b9b9b9b9b9b9b9b8b8b8b7b7b7
+b5b6b5b5b5b5b3b4b3b4b4b4b5b6b5b7b8b7b9b9b9b9b9b9b7b8b7b7b7b7b7b7b7b8b8b8b9b9b9
+babababcbcbcbcbcbcbbbbbbbababababababababababababababab8b8b8b6b6b6b7b7b7b9bab9
+bcbdbcbfbfbfc1c1c1c0c0c0bfbfbfbfbfbfbfbfbfbebebebdbdbdbdbdbdbdbdbdbdbdbdbcbcbc
+bababababababababababababababab9b9b9b9b9b9b8b9b8b8b8b8b8b8b8b8b8b8b9b9b9bababa
+b9bab9b8b9b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b7b7b7b6b7b6b8b8b8b8b9b8b9bab9bababa
+babababcbcbcbdbebdbdbebdbcbcbcb8b8b8b5b6b5b7b7b7b8b8b8b9bab9babababdbdbdbebfbe
+bfc0bfbfbfbfbfbfbfbebebebebebebdbebdbcbcbcbbbbbbb9b9b9b9b9b9b8b8b8b8b8b8b7b8b7
+b7b8b7b7b7b7b7b7b7b7b8b7b8b8b8b8b8b8b8b8b8b8b8b8b7b8b7b7b8b7b7b8b7b7b8b7b7b7b7
+b8b8b8b9b9b9bbbbbbbcbcbcbcbcbcbdbdbdbdbdbdbdbdbdbcbcbcbbbcbbbabbbabbbbbbbbbcbb
+bbbcbbbbbcbbbabbbabababab9bab9b9b9b9b9b9b9b9b9b9bababab9bab9b9bab9babababcbcbc
+bdbdbdbdbebdbdbebdbdbdbdbdbdbdbdbdbdbcbdbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbbbbbb
+bababab8b8b8b6b7b6b6b6b6b5b5b5b5b5b5b4b4b4b5b6b5b7b7b7b8b9b8b9b9b9b9b9b9b9b9b9
+b8b9b8b7b7b7b6b6b6b4b5b4b3b4b3b4b4b4b5b6b5b7b7b7b9b9b9b9b9b9b7b8b7b7b7b7b7b7b7
+b8b8b8b9b9b9babababcbcbcbcbcbcbbbbbbbabababababababababababab9bab9b8b8b8b6b6b6
+b7b7b7b9b9b9bcbcbcbfbfbfc1c1c1c0c0c0bfbfbfbfbfbfbfbfbfbebebebdbdbdbdbdbdbdbdbd
+bdbdbdbcbcbcbababababababababababababababab9b9b9b9b9b9b8b9b8b8b8b8b8b8b8b8b9b8
+b9b9b9bababab9bab9b9b9b9b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b7b7b7b6b6b6b8b8b8b9b9b9
+b9bab9bababababababcbcbcbdbdbdbdbdbdbbbbbbb8b8b8b6b6b6b6b7b6c1c1c1cccccccdcdcd
+d0d0d0070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b2af9999994b4b4b434343cccccccdcdcdd2d2d2a1a2a1989998
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a29495949fa09fa2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a29c9d9c979897a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a29596959f9f9f
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a29c9d9c979897a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a29596959e9f9ea2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a29d9e9d949594a1a1a1
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a29a9b9a999a99a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2949594a0a1a0a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2
+a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2a2a3a2b6b7b6cbcbcbcccccc
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cccccccdcdcdd0d0d0a0a1a0b2b2b2
+f3f3f3f3f3f3f4f4f4f3f3f3f4f4f4f3f3f3f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3
+f3f3f3f3f3f3f3f3f3f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4969796dfe0dff4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4c8c9c8adaeadf4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f49a9b9adbdcdb
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4cccdcca9aaa9f4f4f4f3f3f3f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f49e9f9ed7d8d7f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4d0d1d0949594e9e9e9
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4bfbfbfb7b8b7f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f1f1f1949594e5e5e5f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4dededec9c9c9cccccc
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b424242cbcbcbcbcbcbcccccc9fa09fb2b2b2
+f3f3f3f1f1f1ebebebebebebedededeaeaeae6e6e6e6e6e6e7e7e7e7e7e7e6e6e6e5e5e5e2e2e2
+dedededfdfdfe3e3e3e4e4e4e2e2e2dbdbdbd7d7d7d6d6d6d5d5d5d2d2d2d0d0d0d4d4d4d2d2d2
+d0d0d0cdcdcdcfcfcfcfcfcfabacabc0c0c0f4f4f4969796dfe0dff2f2f2eeeeeeebebebececec
+eeeeeee9e9e9e6e6e6e6e6e6e8e8e8e6e6e6e6e6e6e4e4e4e1e1e1dddddde1e1e1e4e4e4e3e3e3
+dfdfdfdbdbdbd6d6d6d6d6d6d3d3d3d1d1d1d2d2d2d5d5d5d1d1d1cececececececfcfcfc9c9c9
+969796d1d1d1c8c9c8adaeadf3f3f3f1f1f1ebebebebebebecececededede6e6e6e6e6e6e7e7e7
+e7e7e7e6e6e6e6e6e6e2e2e2dfdfdfdfdfdfe2e2e2e4e4e4e2e2e2ddddddd9d9d9d6d6d6d5d5d5
+d2d2d2d1d1d1d3d3d3d4d4d4d0d0d0cdcdcdcecececfcfcfbebebea0a1a0f4f4f49a9b9adbdcdb
+f2f2f2eeeeeeebebebecececededede9e9e9e6e6e6e6e6e6e8e8e8e6e6e6e6e6e6e4e4e4e1e1e1
+dddddde1e1e1e4e4e4e4e4e4e1e1e1dbdbdbd6d6d6d6d6d6d4d4d4d2d2d2d0d0d0d5d5d5d2d2d2
+cecececdcdcdcfcfcfcfcfcfa3a4a3cdcdcdcccdcca9aaa9f3f3f3f1f1f1edededebebebececec
+ededede6e6e6e6e6e6e7e7e7e7e7e7e6e6e6e6e6e6e3e3e3dfdfdfdfdfdfe1e1e1e5e5e5e3e3e3
+dededed9d9d9d6d6d6d5d5d5d3d3d3d1d1d1d3d3d3d4d4d4d0d0d0cdcdcdcecececfcfcfc1c1c1
+9c9d9cf4f4f49e9f9ed7d8d7f3f3f3f0f0f0ebebebecececedededeaeaeae6e6e6e6e6e6e8e8e8
+e7e7e7e6e6e6e5e5e5e1e1e1dddddde0e0e0e3e3e3e4e4e4e1e1e1dbdbdbd7d7d7d6d6d6d5d5d5
+d2d2d2d0d0d0d5d5d5d2d2d2cecececdcdcdcfcfcfcfcfcfa6a6a6c9cac9d0d1d0949594e9e9e9
+f1f1f1edededebebebecececeeeeeee8e8e8e6e6e6e7e7e7e8e8e8e6e6e6e6e6e6e3e3e3e0e0e0
+dededee1e1e1e5e5e5e3e3e3dedededbdbdbd6d6d6d5d5d5d3d3d3d1d1d1d2d2d2d4d4d4d0d0d0
+cececececececfcfcfc3c3c3989998f4f4f4bfbfbfb7b8b7f3f3f3f1f1f1ebebebecececececec
+ececece6e6e6e6e6e6e7e7e7e7e7e7e6e6e6e6e6e6e2e2e2dededee0e0e0e3e3e3e4e4e4e2e2e2
+ddddddd8d8d8d6d6d6d5d5d5d2d2d2d0d0d0d5d5d5d3d3d3d0d0d0cdcdcdcfcfcfcfcfcfa8a9a8
+aaaaaaf1f1f1949594e5e5e5f2f2f2eeeeeeebebebecececededede9e9e9e6e6e6e6e6e6e8e8e8
+e6e6e6e6e6e6e4e4e4e1e1e1dededee1e1e1e5e5e5e4e4e4e0e0e0dbdbdbd6d6d6d5d5d5d3d3d3
+d1d1d1d1d1d1d5d5d5d1d1d1cececececececfcfcfcfcfcf9e9e9ed6d7d6ddddddc9c9c9cbcbcb
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacacacacacacaca9fa09fb2b2b2
+f2f2f2e8e8e8eeeeeeedededececece8e8e8e5e5e5e8e8e8e6e6e6e6e6e6e6e6e6e6e6e6e4e4e4
+e0e0e0dededee0e0e0e3e3e3dcdcdcd6d6d6dadadad6d6d6d0d0d0d6d6d6ccccccd2d2d2d0d0d0
+cecececccccccccccccccccca9a9a9bebfbef4f4f4969796dfe0dfecececebebebeeeeeeececec
+ebebebe7e7e7e6e6e6e7e7e7e6e6e6e6e6e6e7e7e7e6e6e6e3e3e3dfdfdfdddddde3e3e3dfdfdf
+d9d9d9d6d6d6dadadad1d1d1d4d4d4d1d1d1cfcfcfd4d4d4cfcfcfccccccccccccccccccc6c6c6
+949594d0d0d0c8c9c8adaeadf2f2f2e8e8e8eeeeeeedededecececdddddd2f2f2f2f2f2f303030
+3030302f2f2f2f2f2f2f2f2f2e2e2e2e2e2e2f2f2f2f2f2f2f2f2f2e2e2e2d2d2d2c2c2c2c2c2c
+2b2b2b2b2b2b2b2b2b2c2c2c3f3f3fccccccccccccccccccbcbcbc9e9f9ef4f4f49a9b9adbdcdb
+edededeaeaeaeeeeeeecececebebebe8e8e8e6e6e6e7e7e7e6e6e6e6e6e6e6e6e6e6e6e6e3e3e3
+dfdfdfdddddde3e3e3e2e2e2dbdbdbd6d6d6dadadad1d1d1d3d3d3d5d5d5ccccccd3d3d3cfcfcf
+cccccccccccccccccccccccca1a1a1cccccccccdcca9aaa9f3f3f3e8e8e8ecececeeeeeeececec
+ebebebe5e5e5e7e7e7e7e7e7e6e6e6e6e6e6e7e7e7e4e4e4e1e1e1dedededddddde5e5e5dddddd
+d8d8d8d8d8d8d7d7d7cfcfcfd5d5d5d0d0d0d0d0d0d2d2d2cecececcccccccccccccccccbebebe
+9a9b9af4f4f49e9f9ed7d8d7f1f1f1e8e8e8eeeeeeedededebebebe8e8e8e5e5e5e8e8e8e6e6e6
+e6e6e6e6e6e6e6e6e6e3e3e3dfdfdfdededee1e1e1e2e2e2dbdbdbd6d6d6dadadad5d5d5d1d1d1
+d5d5d5ccccccd3d3d3cfcfcfcdcdcdcccccccccccccccccca3a4a3c8c8c8d0d1d0949594e9e9e9
+ebebebebebebeeeeeeecececebebebe7e7e7e6e6e6e7e7e7e6e6e6e6e6e6e7e7e7e5e5e5e2e2e2
+dfdfdfdddddde4e4e4dededed8d8d8d6d6d6dadadad0d0d0d5d5d5d0d0d0cfcfcfd2d2d2cecece
+ccccccccccccccccccc0c1c0969796f4f4f4bfbfbfb7b8b7f1f1f1e8e8e8eeeeeeedededececec
+eaeaeae5e5e5e8e8e8e6e6e6e6e6e6e6e6e6e7e7e7e4e4e4e0e0e0dededee0e0e0e2e2e2dcdcdc
+d7d7d7d8d8d8d6d6d6d1d1d1d6d6d6ccccccd3d3d3d1d1d1cdcdcdcccccccccccccccccca5a6a5
+a8a8a8f1f1f1949594e5e5e5ecececebebebeeeeeeecececebebebe7e7e7e6e6e6e7e7e7e6e6e6
+e6e6e6e7e7e7e6e6e6e2e2e2dfdfdfdddddde4e4e4e1e1e1dadadad6d6d6dadadad0d0d0d4d4d4
+d1d1d1cdcdcdd4d4d4cfcfcfcccccccccccccccccccccccc9b9c9bd5d6d5ddddddc8c8c8cacaca
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacac9c9c9c9c9c99fa09fb2b2b2
+f2f2f2e8e8e8e9e9e9eaeaeaeaeaeae7e7e7e3e3e3e1e1e1e3e3e3e3e3e3e3e3e3e2e2e2dedede
+dfdfdfdfdfdfe0e0e0e0e0e0d8d8d8d4d4d4d8d8d8d6d6d6d2d2d2d4d4d4ccccccd1d1d1cecece
+cccccccccccccccccccbcbcba8a9a8bebfbef4f4f4969796dfe0dfececece9e9e9eaeaeaeaeaea
+e9e9e9e5e5e5e2e2e2c1c1c15959595959595a5a5a595959585858575757565656585858565656
+5454545353535555555151515252528d8d8dcececed2d2d2cdcdcdcccccccccccccbcbcbc4c5c4
+949594d0d0d0c8c9c8adaeadf2f2f2e8e8e8e9e9e9eaeaeaeaeaead8d8d80000004545454e4e4e
+4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e434343
+000000000000000000000000191919cccccccccccccbcbcbbabbba9e9f9ef4f4f49a9b9adbdcdb
+ededede9e9e9e9e9e9eaeaeae9e9e99999995959595a5a5a5959595959595a5a5a5a5a5a585858
+5757575656565858585757575454545353535555555151515252525353534f4f4f525252505050
+9b9b9bcccccccccccccacacaa0a1a0cccccccccdcca9aaa9f3f3f3e8e8e8e9e9e9eaeaeaeaeaea
+e9e9e9e3e3e3e1e1e1e2e2e2e3e3e3e3e3e3e3e3e3dfdfdfdfdfdfdfdfdfdfdfdfe2e2e2dadada
+d6d6d6d7d7d7d7d7d7d1d1d1d3d3d3cfcfcfcfcfcfd1d1d1cccccccccccccccccccbcbcbbdbdbd
+9a9b9af4f4f49e9f9ed7d8d7f1f1f1e8e8e8e9e9e9eaeaeaeaeaeae6e6e6e3e3e3e1e1e1e3e3e3
+e3e3e3e3e3e3e2e2e2dededee0e0e0dfdfdfe0e0e0dfdfdfd8d8d8d5d5d5d9d9d9d5d5d5d2d2d2
+d3d3d3ccccccd2d2d2cecececccccccccccccccccccacacaa3a3a3c8c8c8d0d1d0949594e9e9e9
+ebebebe9e9e9eaeaeaeaeaeae9e9e9e5e5e5e2e2e2e2e2e2e3e3e3e3e3e3e3e3e3dfdfdfdedede
+dfdfdfdfdfdfe2e2e2dadadad6d6d6d5d5d5d9d9d9d2d2d2d3d3d3cfcfcfcececed1d1d1cccccc
+cccccccccccccbcbcbbfbfbf969796f4f4f4bfbfbfb7b8b7f1f1f1e8e8e8e9e9e9eaeaeaeaeaea
+e8e8e8e3e3e3e1e1e1e3e3e3e3e3e3e3e3e3e3e3e3dedededfdfdfdfdfdfe0e0e0dfdfdfd8d8d8
+d5d5d5d7d7d7d6d6d6d2d2d2d3d3d3ccccccd1d1d1d0d0d0cccccccccccccccccccacacaa5a6a5
+a8a8a8f1f1f1949594e5e5e5ececece9e9e9e9e9e9eaeaeae9e9e9e5e5e5e2e2e2e2e2e2e3e3e3
+e3e3e3e3e3e3e1e1e1dedededfdfdfdfdfdfe2e2e2dededed7d7d7d5d5d5d9d9d9d2d2d2d3d3d3
+cfcfcfccccccd2d2d2cdcdcdcccccccccccccbcbcbcacaca9b9c9bd5d6d5ddddddc8c8c8cacaca
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b2af9999994b4b4b424242c9c9c9c9c9c9cacaca9fa09fb2b2b2
+f2f2f2e8e8e8e6e6e6e8e8e8e9e9e9e6e6e6e2e2e2dededee0e0e0e0e0e0dfdfdfdedededcdcdc
+dddddddedededddddddbdbdbd8d8d8d4d4d4d6d6d6d6d6d6d3d3d3d1d1d1cececed0d0d0cdcdcd
+cbcbcbcbcbcbcacacacacacaa8a8a8bebfbef4f4f4969796dfe0dfededede7e7e7e7e7e7e9e9e9
+e9e9e9e5e5e5e0e0e0aaaaaa0505056d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d
+6d6d6d6d6d6d6d6d6d6d6d6d242424636363cfcfcfd1d1d1cccccccbcbcbcacacacacacac4c4c4
+949594d0d0d0c8c9c8adaeadf2f2f2e8e8e8e6e6e6e8e8e8e9e9e9d8d8d8000000979797a9a9a9
+a8a8a8a7a7a7a7a7a7a9a9a9a7a7a7a7a7a7a6a6a6a5a5a5a7a7a7a4a4a4a4a4a4a4a4a48d8d8d
+000000343434363636262626191919cbcbcbcacacacacacabababa9e9f9ef4f4f49a9b9adbdcdb
+ededede7e7e7e6e6e6e9e9e9e9e9e9686868141414363636363636363636363636363636363636
+3636363636363636363636363636363636363636363636363636362e2e2e000000000000000000
+7a7a7acacacacacacacacacaa0a1a0cccccccccdcca9aaa9f3f3f3e8e8e8e7e7e7c0c0c0858585
+8484848181817e7e7e7f7f7f8181818181818080807d7d7d7e7e7e8080808080808080807b7b7b
+7a7a7a7a7a7a7a7a7a797979787878757575757575767676747474747474c1c1c1cacacabcbdbc
+9a9b9af4f4f49e9f9ed7d8d7f1f1f1e8e8e8e6e6e6e9e9e9e9e9e9e5e5e5e1e1e1dededee0e0e0
+e0e0e0dfdfdfdedededcdcdcdddddddedededddddddbdbdbd8d8d8d4d4d4d6d6d6d5d5d5d3d3d3
+d1d1d1cececed0d0d0cccccccbcbcbcbcbcbcacacacacacaa2a3a2c8c8c8d0d1d0949594e9e9e9
+ebebebe7e7e7e7e7e7e9e9e9e9e9e9e4e4e4e0e0e0dfdfdfe0e0e0dfdfdfdfdfdfdddddddcdcdc
+dddddddedededcdcdcd9d9d9d5d5d5d4d4d4d7d7d7d4d4d4d2d2d2cfcfcfcfcfcfcfcfcfcbcbcb
+cbcbcbcacacacacacabfbfbf969796f4f4f4bfbfbfb7b8b7f1f1f1e8e8e8e6e6e6e8e8e8e9e9e9
+e8e8e8e2e2e2dededee0e0e0e0e0e0dfdfdfdfdfdfdcdcdcdddddddedededddddddbdbdbd8d8d8
+d5d5d5d5d5d5d6d6d6d3d3d3d1d1d1cececed0d0d0cecececbcbcbcbcbcbcacacacacacaa5a5a5
+a8a8a8f1f1f1949594e5e5e5ececece7e7e7e6e6e6e9e9e9e9e9e9e4e4e4e0e0e0dfdfdfe0e0e0
+dfdfdfdfdfdfdedededcdcdcdddddddedededcdcdcdadadad7d7d7d4d4d4d7d7d7d4d4d4d2d2d2
+cfcfcfcececed1d1d1cbcbcbcbcbcbcacacacacacacacaca9b9c9bd5d6d5ddddddc8c8c8c9c9c9
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b2af9999994b4b4b424242c9c9c9c9c9c9cacaca9fa09fb2b2b2
+f2f2f2e6e6e6e7e7e7e9e9e9e9e9e9e6e6e6e2e2e2e0e0e0dfdfdfdedededcdcdcdddddddfdfdf
+dcdcdcdadadad9d9d9d7d7d7dadadad2d2d2d5d5d5d5d5d5d4d4d4d0d0d0d0d0d0d0d0d0cccccc
+cacacacacacac8c8c8cacacaa8a9a8bebfbef4f4f4969796dfe0dfececece7e7e7e7e7e7eaeaea
+eaeaeae5e5e5e1e1e1ababab0d0d0dffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff565656646464d0d0d0d0d0d0cbcbcbcacacac9c9c9c8c8c8c5c5c5
+949594d0d0d0c8c9c8adaeadf2f2f2e7e7e7e7e7e7e9e9e9eaeaead8d8d8000000c8c8c8e0e0e0
+dedededddddddddddddfdfdfdcdcdcdbdbdbd9d9d9d7d7d7dbdbdbd5d5d5d4d4d4d5d5d5b6b6b6
+0000007b7b7b808080585858191919cacacac8c8c8cacacabbbbbb9e9f9ef4f4f49a9b9adbdcdb
+ececece7e7e7e7e7e7e9e9e9eaeaea6868682f2f2f808080808080808080808080808080808080
+8080808080808080808080808080808080808080808080808080806d6d6d000000000000000000
+7a7a7ac9c9c9c8c8c8cacacaa0a1a0cccccccccdcca9aaa9f3f3f3e6e6e6e7e7e78c8c8c000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000b4b4b4c9c9c9bdbdbd
+9a9b9af4f4f49e9f9ed7d8d7f0f0f0e7e7e7e7e7e7e9e9e9eaeaeae5e5e5e1e1e1e0e0e0e0e0e0
+dddddddddddddddddddfdfdfdbdbdbdbdbdbd9d9d9d8d8d8dadadad3d3d3d5d5d5d5d5d5d3d3d3
+d1d1d1d1d1d1d0d0d0cbcbcbcacacac9c9c9c8c8c8cacacaa3a3a3c8c8c8d0d1d0949594e9e9e9
+eaeaeae7e7e7e8e8e8eaeaeaeaeaeae4e4e4e0e0e0e0e0e0dfdfdfdddddddddddddfdfdfdedede
+dbdbdbdbdbdbd7d7d7dadadad6d6d6d3d3d3d6d6d6d5d5d5d1d1d1d1d1d1d0d0d0cecececacaca
+cacacac8c8c8c9c9c9bfbfbf969796f4f4f4bfbfbfb7b8b7f1f1f1e7e7e7e7e7e7e9e9e9eaeaea
+e8e8e8e2e2e2e0e0e0e0e0e0dedededddddddddddddfdfdfdcdcdcdbdbdbd9d9d9d8d8d8dbdbdb
+d4d4d4d4d4d4d5d5d5d4d4d4d1d1d1d1d1d1d0d0d0cdcdcdcacacac9c9c9c8c8c8cacacaa5a6a5
+a8a8a8f1f1f1949594e5e5e5ebebebe7e7e7e7e7e7eaeaeaeaeaeae4e4e4e1e1e1e0e0e0dfdfdf
+dddddddddddddededededededadadadbdbdbd7d7d7d8d8d8d8d8d8d3d3d3d6d6d6d5d5d5d2d2d2
+d1d1d1d0d0d0d0d0d0cacacacacacac8c8c8c9c9c9cbcbcb9b9c9bd5d6d5ddddddc8c8c8cacaca
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9cacacacbcbcb9fa09fb2b2b2
+f1f1f1e3e3e3e1e1e1e5e5e5e7e7e7e6e6e6e4e4e4e0e0e0dddddddadadadadadadcdcdcdbdbdb
+dbdbdbdadadad6d6d6d3d3d3d6d6d6dbdbdbd2d2d2d0d0d0d0d0d0d0d0d0d1d1d1cecececacaca
+c9c9c9cbcbcbc3c3c3c7c7c7a7a8a7bebfbef4f4f4969796dfe0dfe9e9e9e2e2e2e2e2e2e6e6e6
+e8e8e8e6e6e6e2e2e2ababab0d0d0dffffff2a2a2a0c0c0c414141ffffffffffffffffffffffff
+ffffffffffffffffffffffff565656646464d0d0d0cececec9c9c9cacacac7c7c7c4c4c4c2c2c2
+949594d0d0d0c8c9c8adaeadf2f2f2e3e3e3e1e1e1e4e4e4e6e6e6d6d6d6000000c8c8c8dddddd
+dadadad9d9d9dddddddbdbdbdbdbdbdadadad7d7d7d3d3d3d6d6d6d9d9d9d6d6d6919191191919
+797979c9c9c9a7a7a7585858191919cbcbcbc3c3c3c6c6c6b9b9b99e9f9ef4f4f49a9b9adbdcdb
+eaeaeae2e2e2e1e1e1e5e5e5e7e7e7676767515151dadadad7d7d7d5d5d5d6d6d6d7d7d7d6d6d6
+d7d7d7d4d4d4d0d0d0d0d0d0d2d2d2d7d7d7cdcdcd2d2d2d5151518a8a8ac7c7c77c7c7c1a1a1a
+7b7b7bc8c8c8c3c3c3c7c7c7a0a0a0cccccccccdcca9aaa9f3f3f3e3e3e3e2e2e28888882e2e2e
+dcdcdcdadadad6d6d6d3d3d3d1d1d1cececed2d2d2d0d0d0d1d1d1d0d0d0cececec9c9c9cbcbcb
+cfcfcfccccccc6c6c6c6c6c6c6c6c6c6c6c6c5c5c5c3c3c3afafaf000000b0b0b0c5c5c5bbbbbb
+9a9b9af4f4f49e9f9ed7d8d7f0f0f0e3e3e3e1e1e1e5e5e5e7e7e7e6e6e6e4e4e4e0e0e0dcdcdc
+dadadadbdbdbdcdcdcdbdbdbdbdbdbdadadad6d6d6d4d4d4d6d6d6dbdbdbd1d1d1d0d0d0d0d0d0
+d0d0d0d1d1d1cecececacacacacacacbcbcbc3c3c3c7c7c7a2a3a2c8c8c8d0d1d0949594e9e9e9
+e8e8e8e2e2e2e2e2e2e6e6e6e8e8e8e5e5e5e2e2e2dedededbdbdbd9d9d90f0f0f0a0a0a0a0a0a
+262626d9d9d9d3d3d3d5d5d5d9d9d9dadadad1d1d1d0d0d0d0d0d0d0d0d0cfcfcfccccccc9c9c9
+cbcbcbc7c7c7c5c5c5bdbdbd969796f4f4f4bfbfbfb7b8b7f0f0f0e3e3e3e1e1e1e5e5e5e6e6e6
+e7e7e7e4e4e4e0e0e0dcdcdcdadada7b7b7b0a0a0a0a0a0a0a0a0a898989d6d6d6d3d3d3d6d6d6
+dadadad5d5d5d0d0d0d0d0d0d0d0d0d1d1d1cecececcccccc9c9c9cbcbcbc3c3c3c7c7c7a4a5a4
+a8a8a8f1f1f1949594e5e5e5e8e8e8e2e2e2e1e1e1e6e6e6e7e7e75757570a0a0a0a0a0a0a0a0a
+0a0a0a909090dcdcdcdbdbdbc7c7c70a0a0a0a0a0a0a0a0a0a0a0a0909090a0a0a0a0a0a090909
+090909161616cececec9c9c9cbcbcbc7c7c7c4c4c4c8c8c89a9b9ad5d6d5ddddddc8c8c8c9c9c9
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9cacacacbcbcb9fa09fb2b2b2
+f1f1f1e2e2e2e1e1e1e5e5e5e5e5e5e3e3e3e1e1e1dbdbdbdbdbdbd9d9d9d9d9d9dbdbdbd9d9d9
+dbdbdbd9d9d9d7d7d7b6b6b63030303232323030306b6b6bd3d3d3d2d2d2d0d0d0cccccccccccc
+cbcbcbc7c7c7c4c4c4c7c7c7a7a8a7bebfbef4f4f4969796dfe0dfe9e9e9e2e2e2e3e3e3e6e6e6
+e4e4e4e2e2e2dededea7a7a70d0d0dffffffccccccc4c4c4d1d1d1ffffffffffffffffffffffff
+ffffffffffffffffffffffff565656646464cfcfcfcbcbcbccccccc9c9c9c6c6c6c5c5c5c2c2c2
+949594d0d0d0c8c9c8adaeadf2f2f2e2e2e2e1e1e1e5e5e5e6e6e6d3d3d3000000c4c4c4dbdbdb
+dadadad9d9d9dbdbdbd9d9d9dbdbdbd9d9d9d7d7d7d6d6d6cfcfcfd4d4d48383834040406f6f6f
+c0c0c0d0d0d0a9a9a9585858191919c7c7c7c4c4c4c7c7c7b8b9b89e9f9ef4f4f49a9b9adbdcdb
+e9e9e9e2e2e2e1e1e1e6e6e6e5e5e5666666515151dbdbdbdbdbdbd9d9d9dadadadadadad9d9d9
+dcdcdcd6d6d6d7d7d7d4d4d4d1d1d1d7d7d73232325858589c9c9cc2c2c2d1d1d18282821b1b1b
+787878c6c6c6c4c4c4c7c7c7a0a0a0cccccccccdcca9aaa9f3f3f3e2e2e2e1e1e1898989303030
+dfdfdf343434333333323232525252d9d9d9dbdbdbd9d9d9dbdbdbd9d9d9d6d6d6d7d7d7d1d1d1
+d4d4d4d7d7d7d6d6d6d3d3d34f4f4f2f2f2f2f2f2f585858bbbbbb000000b1b1b1c6c6c6bbbbbb
+9a9b9af4f4f49e9f9ed7d8d7efefefe2e2e2e1e1e1e6e6e6e5e5e5e2e2e2e0e0e0dbdbdbdbdbdb
+d9d9d9dadadadadadad9d9d9dcdcdcd8d8d8d7d7d7d5d5d5d0d0d0d7d7d7d7d7d7d5d5d5d3d3d3
+d2d2d2d1d1d1ccccccccccccc9c9c9c7c7c7c4c4c4c7c7c7a2a2a2c8c8c8d0d1d0949594e9e9e9
+e7e7e7e1e1e1e3e3e3e6e6e6e4e4e4e2e2e2dededea2a2a2323232313131a5a5a5a7a7a7a8a8a8
+999999313131303030acacacd4d4d4d7d7d7d7d7d7d4d4d4d2d2d2d1d1d1cecececbcbcbcccccc
+c8c8c8c5c5c5c5c5c5bdbdbd969796f4f4f4bfbfbfb7b8b7f0f0f0e2e2e2e1e1e1e5e5e5e6e6e6
+e4e4e4e1e1e1dbdbdb515151313131676767a8a8a8a7a7a7a9a9a95f5f5f303030595959cfcfcf
+d5d5d5d7d7d7d5d5d5d3d3d3d2d2d2d1d1d1cccccccccccccbcbcbc7c7c7c4c4c4c7c7c7a4a5a4
+a8a8a8f1f1f1949594e5e5e5e8e8e8e2e2e2e1e1e1e6e6e6525252929212c4c400c4c400c4c400
+c4c4006767207e7e7edadadac7c7c7000000b4b4b4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4
+5a5a5a0202022f2f2fbababac8c8c8c5c5c5c5c5c5c8c8c89a9b9ad5d6d5ddddddc8c8c8c9c9c9
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacac9c9c9c9c9c99fa09fb2b2b2
+f1f1f1e2e2e2e1e1e1e2e2e2e2e2e2e2e2e2e1e1e1dedededadadad9d9d9d8d8d8d8d8d8d6d6d6
+d7d7d7d5d5d5a4a4a45f5f5f7a7a7a7878787b7b7b6e6e6e6d6d6dcfcfcf555555b5b5b5cacaca
+c9c9c9c5c5c5c6c6c6c6c6c6a6a7a6bebfbef4f4f4969796dfe0dfe9e9e9e2e2e2e1e1e1e2e2e2
+e2e2e2e1e1e1dfdfdfa9a9a90d0d0dffffff7b7b7b686868686868686868686868686868686868
+686868686868686868e7e7e7565656636363ccccccc9c9c9cacacac7c7c7c6c6c6c6c6c6c0c1c0
+949594d0d0d0c8c9c8adaeadf2f2f2e2e2e2e1e1e1e2e2e2e2e2e2d1d1d1000000c6c6c6dbdbdb
+d9d9d9d8d8d8d8d8d8d6d6d6d7d7d7d5d5d5d2d2d2d1d1d1cfcfcf7d7d7d5151516c6c6cb6b6b6
+d0d0d0cececea8a8a8585858181818c5c5c5c6c6c6c6c6c6b7b7b79e9f9ef4f4f49a9b9adbdcdb
+eaeaeae2e2e2e1e1e1e2e2e2e2e2e2656565525252ddddddd9d9d9d9d9d9d8d8d8d7d7d7d6d6d6
+d8d8d8d3d3d3d2d2d2d1d1d1bbbbbb5757574b4b4b9d9d9dc4c4c4cfcfcfcecece8282821b1b1b
+777777c5c5c5c6c6c6c6c6c69fa09fcccccccccdcca9aaa9f3f3f3e2e2e2e1e1e1888888303030
+dbdbdb000000000000303030919191d8d8d8d8d8d8d6d6d6d7d7d7d5d5d5d3d3d3d2d2d2cfcfcf
+cfcfcfd2d2d2d4d4d4d5d5d58b8b8b2d2d2d000000353535b9b9b9000000b3b3b3c6c6c6b9bab9
+9a9b9af4f4f49e9f9ed7d8d7efefefe2e2e2e1e1e1e2e2e2e2e2e2e1e1e1dcdcdc595959595959
+afafafd8d8d8d7d7d7d6d6d6d8d8d8d4d4d4d2d2d2d1d1d1cfcfcfd0d0d0d4d4d4d4d4d4d3d3d3
+c3c3c3555555535353abababc7c7c7c5c5c5c6c6c6c6c6c6a1a2a1c8c8c8d0d1d0949594e9e9e9
+e7e7e7e1e1e1e1e1e1e2e2e2e2e2e2e1e1e19b9b9b666666808080808080d6d6d6ea7373e88181
+cbcbcb7b7b7b7979795c5c5c9e9e9ed0d0d0d4d4d4d4d4d4d1d1d1cecececcccccc9c9c9cacaca
+c6c6c6c6c6c6c6c6c6bbbcbb969796f4f4f4bfbfbfb7b8b7f0f0f0e2e2e2e1e1e1e2e2e2e2e2e2
+e2e2e2e1e1e15a5a5a787878808080a8a8a8d8d8d8d6d6d6d7d7d79f9f9f7a7a7a707070545454
+b4b4b4d2d2d2d4d4d4d3d3d3cfcfcfcecececacacacacacac9c9c9c5c5c5c6c6c6c6c6c6a3a4a3
+a8a8a8f1f1f1949594e5e5e5e8e8e8e2e2e2dfdfdf5e5e5e8d8d0fdada00b7b700686800797900
+ffff00bcbc007a7a287c7c7cc3c3c3000000e9e9e9ffffffffffffffffffffffffffffffffffff
+7575750909099696965c5c5ca3a3a3c6c6c6c6c6c6c6c6c69a9b9ad5d6d5ddddddc9c9c9cacaca
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cbcbcbc8c8c8c8c8c89f9f9fb2b2b2
+f1f1f1e1e1e1dfdfdfdededee0e0e0e1e1e1e1e1e1dedededadadad9d9d9d8d8d8d6d6d6d3d3d3
+d3d3d3d1d1d1a0a0a06c6c6ccfcfcfcececed1d1d1a3a3a3595959797979000000a6a6a6c9c9c9
+c8c8c8c4c4c4c5c5c5c4c4c4a5a6a5bebfbef4f4f4969796dfe0dfe8e8e8e0e0e0dfdfdfdedede
+e2e2e2e1e1e1e0e0e0aaaaaa0d0d0dffffff7b7b7b686868686868686868686868686868686868
+686868686868686868e7e7e7565656626262cbcbcbcacacac9c9c9c5c5c5c4c4c4c5c5c5bebebe
+949594d0d0d0c8c9c8adaeadf2f2f2e1e1e1dfdfdfdedededfdfdfd1d1d1000000c6c6c6dadada
+d9d9d9d9d9d9d7d7d7d3d3d3d3d3d3d1d1d1cececec0c0c07a7a7a4a4a4a6b6b6bb0b0b0cfcfcf
+cbcbcbcbcbcba7a7a7585858181818c4c4c4c5c5c5c4c4c4b5b6b59e9f9ef4f4f49a9b9adbdcdb
+e9e9e9e0e0e0dfdfdfdededee0e0e0656565525252ddddddd9d9d9d9d9d9d8d8d8d5d5d5d3d3d3
+d3d3d3cfcfcfcdcdcdb1b1b16f6f6f3434349f9f9fc6c6c6cdcdcdcbcbcbcccccc8282821b1b1b
+767676c4c4c4c5c5c5c3c3c39f9f9fcccccccccdcca9aaa9f3f3f3e1e1e1e0e0e08787872f2f2f
+dadada0000004c4c4c686868919191d9d9d9d7d7d7d3d3d3d3d3d3d1d1d1cfcfcfcdcdcdcecece
+cececed0d0d0d1d1d1d0d0d08a8a8a6161612e2e2e343434b8b8b8000000b2b2b2c4c4c4b7b8b7
+9a9b9af4f4f49e9f9ed7d8d7efefefe1e1e1dfdfdfdededea0a0a0858585808080000000000000
+939393d8d8d8d6d6d6d3d3d3d3d3d3d1d1d1cecececdcdcdcfcfcfcececed2d2d2ababab7c7c7c
+6d6d6d000000000000959595c6c6c6c4c4c4c5c5c5c3c3c3a1a1a1c8c8c8d0d1d0949594e9e9e9
+e6e6e6e0e0e0dedededededee2e2e2e1e1e16d6d6d484848d9d9d9d9d9d9d7d7d7f62c2cf14343
+d3d3d3cfcfcfcdcdcd2d2d2d7b7b7bcfcfcfd2d2d2d0d0d0cccccccbcbcbcbcbcbc9c9c9c8c8c8
+c5c5c5c4c4c4c4c4c4b9bab9969796f4f4f4bfbfbfb7b8b7f0f0f0e1e1e1dfdfdfdedededfdfdf
+e1e1e1e1e1e1010101b0b0b0d9d9d9d8d8d8d7d7d7d3d3d3d3d3d3d1d1d1cecece989898000000
+a1a1a1d0d0d0d1d1d1cecececbcbcbcccccccacacac9c9c9c7c7c7c4c4c4c5c5c5c4c4c4a3a3a3
+a8a8a8f1f1f1949594e5e5e5e7e7e7e0e0e0dcdcdc000000d4d400baba0079792a5959595f5f4e
+969600d8d8008a8a003a3a3abfbfbf000000e9e9e9ffffffffffffffffffffffffffffffffffff
+7575750909099696961111115151519e9e9ec4c4c4c3c3c39a9b9ad5d6d5dededecacacacbcbcb
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacac9c9c9c8c8c89f9f9fb2b2b2
+f1f1f1dfdfdfdddddddcdcdcdedededfdfdfdededeacacaca7a7a7a8a8a8bbbbbbd6d6d6d2d2d2
+d2d2d2d0d0d0cdcdcdcbcbcbcdcdcdd1d1d1d0d0d0bdbdbd7e7e7e000000000000a6a6a6c9c9c9
+c8c8c8c2c2c2c1c1c1c0c0c0a4a5a4bebfbef4f4f4969796dfe0dfe7e7e7dedededddddddddddd
+e0e0e0dfdfdfdcdcdca6a6a60d0d0dffffffccccccc4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4
+c4c4c4c4c4c4c4c4c4f5f5f5565656616161cacacacacacac9c9c9c5c5c5c2c2c2c1c1c1bbbbbb
+949594d0d0d0c8c9c8adaeadf2f2f2dfdfdfddddddddddddddddddcfcfcf000000c2c2c2d9d9d9
+dadadadadadad8d8d8d3d3d3d2d2d2d0d0d0bebebe8989891d1d1d6c6c6cb0b0b0cdcdcdc8c8c8
+c6c6c6cacacaa6a6a6585858181818c2c2c2c1c1c1c0c0c0b3b3b39e9f9ef4f4f49a9b9adbdcdb
+e8e8e8dedededddddddcdcdcdfdfdf646464515151d9d9d9d9d9d9dadadad9d9d9d6d6d6d2d2d2
+d2d2d2cececea6a6a67171712f2f2f919191cfcfcfc9c9c9c7c7c7c7c7c7cacaca8282821b1b1b
+757575c2c2c2c1c1c1c0c0c09e9f9ecccccccccdcca9aaa9f3f3f3dfdfdfdedede8686862f2f2f
+dadada323232c1c1c19a9a9a494949a9a9a9d8d8d8d3d3d3d2d2d2d0d0d0cecececbcbcbcdcdcd
+cfcfcfd0d0d0c1c1c19b9b9b4242428f8f8f848484555555b8b8b8000000aeaeaec1c1c1b5b5b5
+9a9b9af4f4f49e9f9ed7d8d7efefefdfdfdfdddddddcdcdc444444000000000000000000000000
+939393d9d9d9d6d6d6d2d2d2d2d2d2b0b0b09e9e9eabababcdcdcdd1d1d1d0d0d06f6f6f000000
+000000000000000000959595c5c5c5c2c2c2c1c1c1c0c0c0a0a0a0c8c8c8d0d1d0949594e9e9e9
+e5e5e5dedededddddddddddde0e0e0bcbcbc6d6d6d696969d9d9d9dbdbdbd8d8d8f62c2cf04242
+d1d1d1cecececbcbcb515151737373a8a8a8d0d0d0c8c8c8c7c7c7c9c9c9cacacac9c9c9c9c9c9
+c4c4c4c2c2c2c1c1c1b7b7b7969796f4f4f4bfbfbfb7b8b7f0f0f0dfdfdfdddddddcdcdcdedede
+d7d7d7acacac313131b9b9b9dadadad9d9d9d8d8d8d2d2d2d2d2d2d0d0d0cdcdcda3a3a32e2e2e
+898989bebebecdcdcdc7c7c7c7c7c7cacacacacacac9c9c9c7c7c7c2c2c2c1c1c1c0c0c0a2a2a2
+a8a8a8f1f1f1949594e5e5e5e6e6e6dedededadada000000a3a300444400666666d9d9d9c1c1c1
+000000a2a2008a8a00393939bebebe000000e9e9e9ffffffffffffffffffffffffffffffffffff
+9494943a3a3a3a3a3a3a3a3a111111656565c1c1c1c0c0c0999a99d5d6d5dfdfdfcccccccbcbcb
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cbcbcbcbcbcbcbcbcb9fa09fb2b2b2
+f1f1f1e2e2e2e0e0e0dcdcdcdbdbdbdadadadbdbdb0d0d0d000000000000555555d7d7d7d3d3d3
+d5d5d5d3d3d3cdcdcdc9c9c9cbcbcbcacacac8c8c87f7f7f000000000000000000a1a1a1c5c5c5
+c5c5c5c2c2c2c0c0c0bbbbbba2a3a2bebfbef4f4f4969796dfe0dfe8e8e8e1e1e1dedededbdbdb
+dbdbdbdbdbdbd9d9d9a4a4a40d0d0dffffff1f1f1f000000000000000000000000000000000000
+000000000000000000d6d6d65656565f5f5fc6c6c6c4c4c4c5c5c5c3c3c3c1c1c1bebebeb6b6b6
+949594d0d0d0c8c9c8adaeadf2f2f2e2e2e2e0e0e0dddddddbdbdbcacaca000000c0c0c0d9d9d9
+dadadadadadad9d9d9d4d4d4d5d5d5d3d3d38a8a8a141414808080b3b3b3c9c9c9c7c7c7c5c5c5
+c3c3c3c6c6c6a4a4a4585858181818c2c2c2c0c0c0bcbcbcafafaf9e9f9ef4f4f49a9b9adbdcdb
+e9e9e9e1e1e1e0e0e0dcdcdcdbdbdb616161505050d7d7d7dadadadadadad9d9d9d7d7d7d4d4d4
+d6d6d6d1d1d12626262b2b2b8c8c8ccbcbcbc8c8c8c6c6c6c4c4c4c3c3c3c7c7c78282821b1b1b
+757575c1c1c1c0c0c0bbbbbb9d9d9dcccccccccdcca9aaa9f3f3f3e1e1e1e0e0e08888882e2e2e
+dbdbdbdbdbdbd7d7d7d8d8d8afafaf030303d9d9d9d4d4d4d5d5d5d3d3d3d1d1d1c8c8c8cbcbcb
+cbcbcbc9c9c99090900000009c9c9cc5c5c5c5c5c5c4c4c4b5b5b5000000adadadbdbdbdb1b1b1
+9a9b9af4f4f49e9f9ed7d8d7efefefe1e1e1e0e0e0dcdcdc444444000000000000000000000000
+939393dadadad7d7d7d4d4d4d6d6d64848480000003c3c3ccbcbcbcbcbcbc8c8c86b6b6b000000
+000000000000000000929292c3c3c3c1c1c1c0c0c0bbbbbb9e9f9ec8c8c8d0d1d0949594e9e9e9
+e7e7e7e0e0e0dedededbdbdbdbdbdb4242426e6e6ed8d8d8dadadadbdbdbd9d9d9f72c2cf14444
+d5d5d5d1d1d1c9c9c9cbcbcb525252181818c8c8c8c6c6c6c3c3c3c5c5c5c5c5c5c4c4c4c6c6c6
+c3c3c3c0c0c0bebebeb3b3b3969796f4f4f4bfbfbfb7b8b7f0f0f0e1e1e1e0e0e0dcdcdcdbdbdb
+b5b5b5000000d5d5d5d9d9d9dadadadadadad9d9d9d4d4d4d5d5d5d2d2d2cdcdcdc9c9c9cccccc
+2c2c2c787878c7c7c7c5c5c5c3c3c3c7c7c7c4c4c4c4c4c4c5c5c5c1c1c1c0c0c0bbbbbba0a1a0
+a8a8a8f1f1f1949594e5e5e5e7e7e7e1e1e1dddddd000000000000000000656565d7d7d7c1c1c1
+000000a2a2008a8a003a3a3ac1c1c1000000e9e9e9ffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff4b4b4b646464bebebebbbbbb999a99d5d6d5e0e0e0cdcdcdcccccc
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cdcdcdcdcdcdcbcbcb9fa09fb2b2b2
+f1f1f1e0e0e0dddddddedededcdcdc979797363636b7b703bfbf00bfbf00898915353535343434
+343434343434333333323232323232323232aeaeaeb4b4b4949494929292949494bbbbbbc2c2c2
+c1c1c1bfbfbfbcbcbcbbbbbba2a3a2bebfbef4f4f4969796dfe0dfe8e8e8dfdfdfdddddddedede
+dbdbdbd9d9d99494942828280d0d0dffffffc7c7c7bfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbf
+bfbfbfbfbfbfbfbfbff5f5f55656561717175d5d5dc4c4c4c2c2c2c0c0c0bebebebbbbbbb6b6b6
+949594d0d0d0c8c9c8adaeadf2f2f2e0e0e0dddddddedededdddddcacaca000000bebebed7d7d7
+d8d8d8d8d8d8d4d4d4d2d2d2d1d1d17f7f7f4242426e6e6eb9b9b9c4c4c4c8c8c8c6c6c6c5c5c5
+c3c3c3c5c5c5a3a3a3585858181818bfbfbfbcbcbcbbbbbbafafaf9e9f9ef4f4f49a9b9adbdcdb
+e8e8e8dfdfdfdddddddedededbdbdb6161614f4f4fd5d5d5d7d7d7d8d8d8d6d6d6d3d3d3d2d2d2
+d1d1d1cdcdcd2525252b2b2b8b8b8bcacacac6c6c6c6c6c6c4c4c4c3c3c3c5c5c58282821b1b1b
+737373bebebebcbcbcbbbbbb9d9d9dcccccccccdcca9aaa9f3f3f3e0e0e0dedede8686862e2e2e
+dadadad7d7d7d5d5d5d6d6d6cdcdcda3a3a3d4d4d4d2d2d2d1d1d1cfcfcfcdcdcdcbcbcbcbcbcb
+cacacac8c8c8b8b8b8949494b9b9b9c4c4c4c4c4c4c3c3c3b2b2b2000000a9a9a9bbbbbbb1b1b1
+9a9b9af4f4f49e9f9ed7d8d7efefefe0e0e0dddddddededeb6b6b6a3a3a39b9b9b000000000000
+929292d6d6d6d3d3d3d2d2d2d1d1d14747470000003c3c3ccbcbcbcacacac6c6c6afafaf949494
+838383000000000000909090c0c0c0bfbfbfbcbcbcbbbbbb9e9f9ec8c8c8d0d1d0949594e9e9e9
+e6e6e6dfdfdfdddddddddddddbdbdb4242426d6d6de0a0a0f53636f63636f53535fd0b0bfb1010
+f43434f33333f13131d4a9a9525252181818c6c6c6c6c6c6c3c3c3c4c4c4c4c4c4c3c3c3c2c2c2
+c0c0c0bdbdbdbbbbbbb3b3b3969796f4f4f4bfbfbfb7b8b7f0f0f0e0e0e0dddddddedededddddd
+b5b5b5000000d3d3d3ef5353f53636f53636f53535f43434f43434f43434f23232e75959cccccc
+2c2c2c777777c6c6c6c5c5c5c3c3c3c5c5c5c4c4c4c3c3c3c1c1c1bfbfbfbcbcbcbbbbbba0a1a0
+a8a8a8f1f1f1949594e5e5e5e6e6e6dfdfdfdddddda7a7a7a5a5a5a2a2a2bababaa7a7a7454530
+bfbf00e7e7008a8a00393939bdbdbd000000e9e9e9ffffffcdcdcd3f3f3f3f3f3f3f3f3fc9c9c9
+ffffffffffffffffffffffff4b4b4b626262bbbbbbbbbbbb999a99d5d6d5e1e1e1cecececdcdcd
+cdcdcd060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cecececdcdcdcccccc9fa09fb2b2b2
+f1f1f1dedededfdfdfdddddddcdcdc818181000000eeee00fefe00fefe00d3d300919100919100
+9191009191009191009191008e8e00000000a5a5a5c4c4c4c4c4c4c4c4c4c4c4c4c3c3c3c0c0c0
+bebebebfbfbfb9b9b9b8b8b8a1a2a1bebfbef4f4f4969796dfe0dfe6e6e6dededededededddddd
+dcdcdcd9d9d97e7e7e0000000505056d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d
+6d6d6d6d6d6d6d6d6d6d6d6d2424240000003a3a3ac2c2c2bfbfbfbebebebcbcbcb9b9b9b4b4b4
+949594d0d0d0c8c9c8adaeadf2f2f2dedededfdfdfdddddddcdcdccbcbcb000000bebebed4d4d4
+d5d5d5d5d5d5d1d1d1cecece7c7c7c5050506a6a6ab1b1b1ccccccc9c9c9c6c6c6c4c4c4c4c4c4
+c4c4c4c4c4c4a3a3a3585858171717bfbfbfbababab8b8b8aeaeae9e9f9ef4f4f49a9b9adbdcdb
+e7e7e7dedededfdfdfdddddddcdcdc6262624f4f4fd4d4d4d4d4d4d5d5d5d3d3d3d0d0d0cecece
+cdcdcd5757574b4b4b6a6a6ab0b0b0c8c8c8c4c4c4c4c4c4c4c4c4c4c4c4c5c5c58282821b1b1b
+737373bcbcbcb9b9b9b8b8b89c9d9ccccccccccdcca9aaa9f2f2f2dededededede8787872e2e2e
+dbdbdbd7d7d7d5d5d5d4d4d4d4d4d4d5d5d5d1d1d1cfcfcfcecececdcdcdcdcdcdcdcdcdcccccc
+c9c9c9c7c7c7c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c1c1c1aeaeae000000a7a7a7b9b9b9afafaf
+9a9b9af4f4f49e9f9ed7d8d7eeeeeededededfdfdfdddddddcdcdcd9d9d9cfcfcf000000000000
+909090d3d3d3d0d0d0cecececdcdcd9393937575758f8f8fcbcbcbc8c8c8c5c5c5c4c4c4c4c4c4
+b0b0b00000000000008d8d8dbebebebebebeb9b9b9b8b8b89e9e9ec8c8c8d0d1d0949594e9e9e9
+e4e4e4dededededededddddddcdcdc4343436d6d6ddab6b6e57878e67979e47676f91818f52525
+e27474e37575e37575d1b8b8525252171717c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c1c1c1bebebe
+bebebebcbcbcb9b9b9b1b1b1969796f4f4f4bfbfbfb7b8b7efefefdedededfdfdfdddddddcdcdc
+b6b6b6000000d3d3d3e28989e67878e57777e47676e17474e17474e27575e37575dd8b8bcccccc
+2c2c2c767676c4c4c4c4c4c4c4c4c4c5c5c5c3c3c3c1c1c1bebebebebebeb9b9b9b8b8b89fa09f
+a8a8a8f1f1f1949594e5e5e5e5e5e5dedededfdfdfdddddddcdcdcd9d9d99c9c9c6b6b409d9d00
+ffff00a2a2007070358d8d8dbababa000000e9e9e9ffffffe2e2e2919191919191919191e0e0e0
+ffffffffffffffffffffffff4b4b4b616161b9b9b9b8b8b8989998d5d6d5e2e2e2cfcfcfcecece
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cdcdcdcccccccbcbcb9fa09fb2b2b2
+f0f0f0dadadadcdcdcd9d9d9dbdbdb818181000000eeee00fefe00fefe00fefe00fefe00fefe00
+fefe00fefe00fefe00fefe00fafa00000000a4a4a4c2c2c2c1c1c1c3c3c3c1c1c1c0c0c0bdbdbd
+bcbcbcbdbdbdb7b7b7b5b5b5a0a1a0bebfbef4f4f4969796dfe0dfe4e4e4dbdbdbdbdbdbd3d3d3
+8686868585854d4d4d0b0b0b343434636363636363636363585858333333636363636363636363
+4a4a4a333333636363636363636363333333232323767676737373a3a3a3bababab6b6b6b1b2b1
+949594d0d0d0c8c9c8adaeadf1f1f1dadadadcdcdcd9d9d9d9d9d9cccccc000000c0c0c0d5d5d5
+d3d3d3d1d1d1cfcfcf8686864848486a6a6aadadadcbcbcbcacacac8c8c8c5c5c5c2c2c2c1c1c1
+c3c3c3c1c1c1a1a1a1585858171717bdbdbdb7b7b7b5b5b5abacab9e9f9ef4f4f49a9b9adbdcdb
+e5e5e5dbdbdbdcdcdcd9d9d9dbdbdb626262505050d7d7d7d4d4d4d2d2d2d0d0d0cecececdcdcd
+cacaca000000686868999999c9c9c9c7c7c7c3c3c3c1c1c1c2c2c2c2c2c2c1c1c18282821b1b1b
+727272bababab7b7b7b5b5b59b9c9bcccccccccdcca9aaa9f2f2f2dadadadbdbdb8686862e2e2e
+dcdcdcd9d9d9d8d8d8d6d6d6d4d4d4d1d1d1cfcfcfcecececbcbcbcccccccecececbcbcbcacaca
+c8c8c8c5c5c5c3c3c3c1c1c1c2c2c2c1c1c1c0c0c0bebebeacacac000000a5a5a5b6b6b6adadad
+9a9b9af4f4f49e9f9ed7d8d7eeeeeedadadadcdcdcd9d9d9dbdbdbdbdbdbd2d2d2000000000000
+8d8d8dd0d0d0cecececdcdcdcacacacccccccccccccbcbcbc9c9c9c7c7c7c3c3c3c2c2c2c1c1c1
+afafaf0000000000008b8b8bbcbcbcbcbcbcb7b7b7b5b5b59d9d9dc8c8c8d0d1d0949594e9e9e9
+e2e2e2dbdbdbdbdbdbd9d9d9dcdcdc7f7f7f6c6c6c9d9d9dd4d4d4d1d1d1cfcfcff52a2aee4040
+cacacacecececbcbcb8e8e8e323232171717c3c3c3c1c1c1c2c2c2c2c2c2c0c0c0bfbfbfbbbbbb
+bcbcbcb9b9b9b6b6b6aeaeae969796f4f4f4bfbfbfb7b8b7efefefdadadadcdcdcd9d9d9dadada
+c5c5c5565656818181c5c5c5d2d2d2d0d0d0cfcfcfcecececbcbcbccccccccccccb7b7b77c7c7c
+1b1b1b757575c2c2c2c1c1c1c3c3c3c1c1c1c0c0c0bebebebcbcbcbcbcbcb7b7b7b5b5b59e9f9e
+a8a8a8f1f1f1949594e5e5e5e3e3e3dbdbdbdcdcdcd8d8d8dbdbdba2a2a2747445909000f3f300
+9b9b006b6b33898989cdcdcdb7b7b7000000e9e9e9ffffffe5e5e59b9b9b9b9b9b9b9b9be3e3e3
+ffffffffffffffffffffffff4b4b4b5f5f5fb6b6b6b5b5b5989998d5d6d5e2e2e2d0d0d0cdcdcd
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cbcbcbcacacacacaca9fa09fb2b2b2
+f0f0f0d7d7d7d6d6d6d4d4d4d7d7d7808080000000eeee00fefe00fefe00fefe00f3f300c9c900
+c9c900c9c900c9c900c9c900c5c500000000818181999999979797979797939393959595acacac
+b9b9b9b9b9b9b4b4b4b3b3b39fa09fbebfbef4f4f4969796dfe0dfe2e2e2d7d7d7cbcbcb9d9d9d
+1a1a1a2a2a2a2a2a2a3838389e9e9ee4e4e4fbfbfbf4f4f4d4d4d49d9d9de4e4e4fbfbfbededed
+bfbfbf9c9c9ce4e4e4fafafaededed8f8f8f2d2d2d1a1a1a303030717171a2a2a2b4b4b4afafaf
+949594d0d0d0c8c9c8adaeadf1f1f1d7d7d7d6d6d6d4d4d4d5d5d5cacaca000000c2c2c2d7d7d7
+d2d2d2cbcbcba3a3a32b2b2b6d6d6dafafafcbcbcbc7c7c7c7c7c7c7c7c7c5c5c5c2c2c2bfbfbf
+bebebeb9b9b99d9d9d585858171717b9b9b9b5b5b5b3b3b3a9aaa99e9f9ef4f4f49a9b9adbdcdb
+e3e3e3d7d7d7d6d6d6d4d4d4d8d8d8616161515151d9d9d9d6d6d6d0d0d0cdcdcdcececec8c8c8
+9e9e9e1b1b1b797979a2a2a2c7c7c7c7c7c7c3c3c3c0c0c0bebebebdbdbdb9b9b98282821b1b1b
+707070b7b7b7b4b4b4b2b2b29b9b9bcccccccccdcca9aaa9f2f2f2d7d7d7d7d7d78282822e2e2e
+dadadadadadad9d9d9d8d8d8d5d5d5cecececdcdcdd0d0d0cccccccbcbcbccccccc7c7c7c7c7c7
+c7c7c7c5c5c5c2c2c2bfbfbfbebebebbbbbbbabababbbbbba9a9a9000000a3a3a3b3b3b3ababab
+9a9b9af4f4f49e9f9ed7d8d7edededd7d7d7d6d6d6d4d4d4d8d8d8dadadad2d2d2000000000000
+8b8b8bcecececececed0d0d0c9c9c9b0b0b0a0a0a0aaaaaac7c7c7c7c7c7c3c3c3c1c1c1bfbfbf
+aaaaaa000000000000898989b9b9b9b9b9b9b4b4b4b2b2b29c9d9cc8c8c8d0d1d0949594e9e9e9
+dfdfdfd7d7d7d5d5d5d4d4d4dadadadadada6a6a6a393939b0b0b0cecececdcdcdf42a2aee4040
+cacacaccccccc8c8c82c2c2c1818183c3c3cc3c3c3bfbfbfbebebebbbbbbbabababbbbbbb9b9b9
+b9b9b9b6b6b6b4b4b4acacac969796f4f4f4bfbfbfb7b8b7eeeeeed7d7d7d6d6d6d4d4d4d6d6d6
+dadadadadada0101018a8a8ac1c1c1cecececdcdcdd1d1d1cbcbcbcbcbcbcacaca949494000000
+202020858585c1c1c1bfbfbfbdbdbdb9b9b9bbbbbbbbbbbbb9b9b9b9b9b9b4b4b4b3b3b39d9e9d
+a8a8a8f1f1f1949594e5e5e5e1e1e1d7d7d7d6d6d6d3d3d3d9d9d94c4c4c787800efef00b8b804
+2b2b2b929292cfcfcfcfcfcfb6b6b6000000e9e9e9ffffffcacaca353535353535353535c6c6c6
+ffffffffffffffffffffffff4b4b4b5e5e5eb4b4b4b2b2b2989898d5d6d5e2e2e2cfcfcfcbcbcb
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b424242c8c8c8c8c8c8c8c8c89f9f9fb2b2b2
+f0f0f0dadadad9d9d9d4d4d4d4d4d47d7d7d000000eeee00fefe00fefe00fcfc00c3c300030300
+030300030300030300030300030300030300030300030300030300030300000000040404777777
+b7b7b7b8b8b8b7b7b7b3b3b39f9f9fbebfbef4f4f4969796dfe0dfe4e4e4dadadaa3a3a3090909
+838383cdc8c8cbc4c4a09999f8f1f1838383efefefcdcdcd9e9d9dfaf3f3837c7cede6e6aba4a4
+c1bdbdfcfcfc838383ebebebada8a8bfb8b8d9d4d4838383e6e6e6585858555555b6b6b6aeaeae
+949594d0d0d0c8c9c8adaeadf1f1f1dadadad9d9d9d4d4d4d3d3d3c4c4c4000000bbbbbbd1d1d1
+cdcdcdbebebe030303727272b9b9b9ccccccc8c8c8c8c8c8c8c8c8c7c7c7c5c5c5c3c3c3c0c0c0
+bababab4b4b49a9a9a585858171717b8b8b8b7b7b7b3b3b3a9a9a99e9f9ef4f4f49a9b9adbdcdb
+e5e5e5dadadad9d9d9d3d3d3d4d4d45f5f5f4e4e4ed2d2d2d1d1d1d0d0d0cdcdcdcccccca9a9a9
+0000007f7f7fbababac8c8c8c7c7c7c6c6c6c4c4c4c1c1c1bdbdbdb9b9b9b4b4b48181811b1b1b
+6f6f6fb8b8b8b7b7b7b2b2b29a9b9acccccccccdcca9aaa9f2f2f2dadadadadada8484842d2d2d
+d4d4d4d1d1d1d1d1d1d2d2d2d0d0d0cacacacccccccdcdcdcfcfcfccccccc8c8c8c8c8c8c8c8c8
+c7c7c7c5c5c5c2c2c2bbbbbbbbbbbbb6b6b6b6b6b6b8b8b8a7a7a7000000a5a5a5b5b5b5aaaaaa
+9a9b9af4f4f49e9f9ed7d8d7eeeeeedadadad9d9d9d3d3d3d4d4d4d2d2d2c9c9c9000000000000
+8c8c8ccdcdcdcccccccececed0d0d04747470000003b3b3bc7c7c7c6c6c6c5c5c5c3c3c3bfbfbf
+a7a7a7000000000000878787b8b8b8b8b8b8b7b7b7b2b2b29c9c9cc8c8c8d0d1d0949594e9e9e9
+e2e2e2dadadad7d7d7d3d3d3d4d4d4d2d2d26868680303031f1f1fcacacaccccccf32e2eef4646
+cececec2c2c2c2c2c22b2b2b737373c1c1c1c3c3c3c1c1c1bcbcbcb6b6b6b6b6b6b8b8b8b7b7b7
+b8b8b8b8b8b8b5b5b5ababab969796f4f4f4bfbfbfb7b8b7efefefdadadad9d9d9d4d4d4d3d3d3
+d4d4d4d1d1d10707070101017f7f7fcbcbcbcccccccececed0d0d0c7c7c7c2c2c2909090000000
+979797c2c2c2c2c2c2bfbfbfbababab4b4b4b8b8b8b8b8b8b7b7b7b8b8b8b7b7b7b2b2b29d9e9d
+a8a8a8f1f1f1949594e5e5e5e3e3e3dadadad9d9d9d3d3d3d4d4d44a4a4a747400afaf00171717
+cfcfcfcdcdcdcccccccececebcbcbc000000e9e9e9fffffffdfdfdf7f7f7f7f7f7f7f7f7fdfdfd
+ffffffffffffffffffffffff4b4b4b606060b5b5b5b1b1b1979897d5d6d5e0e0e0ccccccc9c9c9
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c8c8c8c8c8c8c8c8c89f9f9fb2b2b2
+efefefd2d2d2d2d2d2d2d2d2cecece787878000000eeee00fefe00ffff009a9a001b1b007f7f00
+7f7f007f7f007f7f007f7f007f7f007f7f007f7f007f7f007f7f007f7f00000000999999bababa
+bababab8b8b8b3b3b3aeaeae9d9e9dbebfbef4f4f4969796dfe0dfdfdfdfd2d2d29d9d9d121212
+ffffffff6767ff0000ff0000ff0d0dffffffffffffffffffffc7c7ff0000ff0000ff0000ff0000
+ff7f7fffffffffffffffffffff5656ff0000ff4d4dffffff9595952d2d2d535353b1b1b1ababab
+949594d0d0d0c8c9c8adaeadf1f1f1d3d3d3d2d2d2d2d2d2d1d1d1bcbcbc000000c0c0c0cfcfcf
+5f5f5f070707808080c6c6c6cdcdcdcccccccacacac8c8c8c9c9c9c4c4c4c3c3c3c1c1c1bebebe
+b8b8b8b6b6b69b9b9b585858171717b8b8b8b3b3b3afafafa6a6a69e9f9ef4f4f49a9b9adbdcdb
+e1e1e1d2d2d2d2d2d2d2d2d2cdcdcd5b5b5b505050d4d4d4cccccccacacacccccccececeababab
+0000007f7f7fbababac8c8c8c8c8c8c3c3c3c3c3c3bfbfbfbababab7b7b7b6b6b68181811b1b1b
+6f6f6fb5b5b5b3b3b3aeaeae9a9a9acccccccccdcca9aaa9f2f2f2d2d2d2d2d2d27f7f7f2b2b2b
+ccccccd3d3d3d6d6d6d2d2d2a3a3a3030303cecececfcfcfcdcdcdccccccccccccc7c7c7c8c8c8
+c5c5c5c3c3c38c8c8c000000939393b6b6b6b7b7b7bababaababab000000a1a1a1b0b0b0a7a7a7
+9a9b9af4f4f49e9f9ed7d8d7ecececd2d2d2d2d2d2d2d2d2cecececfcfcfcbcbcb000000000000
+888888cbcbcbcecececfcfcfcccccc4545450000003c3c3cc8c8c8c3c3c3c3c3c3c1c1c1bdbdbd
+a5a5a50000000000008a8a8ab9b9b9b7b7b7b3b3b3aeaeae9b9b9bc8c8c8d0d1d0949594e9e9e9
+dcdcdcd2d2d2d2d2d2d1d1d1ccccccd0d0d0d5d5d58f8f8f000000000000c8c8c8cfcfcfcecece
+afafaf0000000000000000000000000f0f0f808080bbbbbbb9b9b9b6b6b6b7b7b7babababbbbbb
+b8b8b8b5b5b5b1b1b1a8a8a8969796f4f4f4bfbfbfb7b8b7edededd2d2d2d2d2d2d2d2d2d0d0d0
+cdcdcdd3d3d3d6d6d62828280000005d5d5dcecececfcfcfcccccc4e4e4e000000000000000000
+0000004c4c4c9a9a9abdbdbdb7b7b7b6b6b6b9b9b9babababababab7b7b7b3b3b3aeaeae9c9c9c
+a8a8a8f1f1f1949594e5e5e5ddddddd2d2d2d2d2d2d3d3d3cdcdcd474747000000000000161616
+cacacacccccccecececececeb8b8b8000000e9e9e9ffffffbcbcbc000000000000000000b7b7b7
+ffffffffffffffffffffffff4b4b4b5d5d5db1b1b1aeaeae979897d5d6d5dededec9c9c9c9c9c9
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c8c8c8c8c8c8c9c9c99fa09fb2b2b2
+efefefd3d3d3d3d3d3d3d3d3d1d1d17a7a7a000000eeee00fefe00aeae006161005252007f7f00
+7f7f007f7f007f7f007f7f007f7f007f7f007f7f007f7f00737300393900636363a6a6a6b7b7b7
+b7b7b7b6b6b6b1b1b1b0b0b09e9f9ebebfbef4f4f4969796dfe0dfe0e0e0d3d3d39e9e9e0d0d0d
+b9b9b9e29e9ee16f6fc95656fb8e8eb9b9b9f6f6f6e3e3e3c8afaffc8989b94646f58282cf5d5d
+dba2a2fdfdfdb9b9b9f3f3f3d08484da6868e99999b9b9b9c3c3c3454545525252b1b1b1adadad
+949594d0d0d0c8c9c8adaeadf1f1f1d3d3d3d3d3d3d3d3d3d2d2d2c0c0c0000000bcbcbc7d7d7d
+5050504c4c4ca4a4a4c9c9c9cbcbcbc8c8c8c4c4c4c0c0c0c1c1c1c0c0c0c2c2c2c2c2c2bebebe
+b8b8b8b5b5b59b9b9b585858161616b6b6b6b2b2b2b0b0b0a8a8a89e9f9ef4f4f49a9b9adbdcdb
+e1e1e1d3d3d3d3d3d3d3d3d3d0d0d05c5c5c4e4e4ed2d2d2d0d0d0ccccccc9c9c9a1a1a1595959
+464646a4a4a4bbbbbbc0c0c0c1c1c1bfbfbfc4c4c4c0c0c0bababab7b7b7b5b5b58181811b1b1b
+6e6e6eb4b4b4b1b1b1b0b0b09a9b9acccccccccdcca9aaa9f2f2f2d3d3d3d3d3d38080802c2c2c
+cccccc5e5e5ec1c1c1a6a6a65f5f5f717171c7c7c7cdcdcdcbcbcbc8c8c8c6c6c6c0c0c0c1c1c1
+c0c0c0c1c1c1ababab6969695757579090908a8a8a6e6e6ea8a8a8000000a0a0a0b1b1b1a9a9a9
+9a9b9af4f4f49e9f9ed7d8d7ecececd3d3d3d3d3d3d3d3d3d1d1d1cececec6c6c6000000000000
+888888c9c9c9cacacacececec9c9c98b8b8b696969828282c1c1c1bfbfbfc4c4c4c2c2c2bdbdbd
+a5a5a5000000000000888888b7b7b7b5b5b5b1b1b1b0b0b09b9c9bc8c8c8d0d1d0949594e9e9e9
+dcdcdcd3d3d3d3d3d3d2d2d2cfcfcfcececed0d0d0b3b3b37474746f6f6f5c5c5c5d5d5d5c5c5c
+4e4e4e000000626262161616000000070707393939969696b1b1b1b6b6b6b5b5b5b5b5b5b8b8b8
+b6b6b6b3b3b3b1b1b1aaaaaa969796f4f4f4bfbfbfb7b8b7edededd3d3d3d3d3d3d3d3d3d2d2d2
+cfcfcfcdcdcdd3d3d38686867171716767675c5c5c5d5d5d5b5b5b2323232c2c2c4c4c4c000000
+000000222222636363a2a2a2b7b7b7b5b5b5b5b5b5b6b6b6b7b7b7b6b6b6b1b1b1b0b0b09c9d9c
+a8a8a8f1f1f1949594e5e5e5dededed3d3d3d3d3d3d3d3d3d0d0d09090907070707373737e7e7e
+cacacac8c8c8cbcbcbcdcdcdb6b6b6000000e9e9e9ffffffe1e1e18c8c8c8c8c8c8c8c8cdedede
+ffffffffffffffffffffffff4b4b4b5d5d5db1b1b1b0b0b0979897d5d6d5dededec9c9c9c9c9c9
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9c9c9c9cacaca9fa09fb2b2b2
+efefefd2d2d2d3d3d3d2d2d2d0d0d0797979000000eeee00b7b7005f5f004f4f007f7f007f7f00
+7f7f007f7f007f7f007f7f007f7f007f7f007f7f006e6e004e4e0c464646b9b9b9b4b4b4b5b5b5
+b6b6b6b4b4b4b2b2b2b2b2b29f9f9fbebfbef4f4f4969796dfe0dfdfdfdfd2d2d29e9e9e0c0c0c
+aeaeaededededdddddb6b6b6cececeaeaeaef4f4f4dfdfdfb6b6b6cececeaeaeaef3f3f3c8c8c8
+bfbfbfcfcfcfaeaeaef2f2f2c9c9c9bebebec6c6c6aeaeaef1f1f15a5a5a535353b2b2b2aeaeae
+949594d0d0d0c8c9c8adaeadf0f0f0d2d2d2d3d3d3d2d2d2d1d1d1bfbfbf000000757575474747
+6969699f9f9fc2c2c2c8c8c8c7c7c7c4c4c4c0c0c0bbbbbbbbbbbbbcbcbcbfbfbfbfbfbfbababa
+b9b9b9b9b9b99d9d9d585858161616b4b4b4b2b2b2b2b2b2a9a9a99e9f9ef4f4f49a9b9adbdcdb
+e0e0e0d2d2d2d3d3d3d1d1d1cfcfcf5c5c5c4c4c4ccfcfcfd2d2d2cececea0a0a05f5f5f414141
+989898c2c2c2bcbcbcbbbbbbbbbbbbbdbdbdc1c1c1bbbbbbbababab9b9b9b9b9b98282821b1b1b
+6d6d6db3b3b3b2b2b2b2b2b29a9b9acccccccccdcca9aaa9f2f2f2d2d2d2d2d2d28080802b2b2b
+c8c8c80000006f6f6f6e6e6e666666cbcbcbc2c2c2c8c8c8c7c7c7c4c4c4c2c2c2bbbbbbbbbbbb
+bcbcbcbebebebfbfbfbababa5c5c5c6161613f3f3f303030a7a7a7000000a1a1a1b2b2b2aaaaaa
+9a9b9af4f4f49e9f9ed7d8d7ecececd2d2d2d3d3d3d1d1d1cfcfcfcdcdcdc5c5c5000000000000
+898989c7c7c7c4c4c4c9c9c9c6c6c6c3c3c3bfbfbfbbbbbbbbbbbbbdbdbdc0c0c0bebebebababa
+a6a6a6000000000000878787b5b5b5b4b4b4b2b2b2b2b2b29c9c9cc8c8c8d0d1d0949594e9e9e9
+dcdcdcd2d2d2d2d2d2d1d1d1cecececdcdcdcdcdcdcfcfcfd1d1d1cccccc4a4a4a474747474747
+484848484848b6b6b64e4e4e1313130000000000004b4b4b8f8f8fb0b0b0b7b7b7b4b4b4b6b6b6
+b5b5b5b3b3b3b2b2b2abacab969796f4f4f4bfbfbfb7b8b7edededd2d2d2d3d3d3d2d2d2d0d0d0
+cecececccccccececed2d2d2cecece9090904747474747474848484848487a7a7a9797972f2f2f
+0a0a0a000000212121646464a4a4a4b9b9b9b4b4b4b4b4b4b6b6b6b4b4b4b2b2b2b2b2b29d9e9d
+a8a8a8f1f1f1949594e5e5e5ddddddd2d2d2d3d3d3d1d1d1cfcfcf9a9a9a8080808383838d8d8d
+ccccccc5c5c5c5c5c5c8c8c8b3b3b3000000e9e9e9ffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffff4b4b4b5d5d5db2b2b2b2b2b2979897d5d6d5dededec9c9c9c9c9c9
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9cacacacbcbcb9fa09fb2b2b2
+efefefd0d0d0cfcfcfcecececacaca757575000000c1c1004444005353007f7f007f7f007f7f00
+7f7f007f7f007f7f007f7f007f7f007f7f006b6b004e4e0c3e3e3ebcbcbcbfbfbfb8b8b8b6b6b6
+b5b5b5b2b2b2b3b3b3b1b1b19e9f9ebebfbef4f4f4969796dfe0dfdededecfcfcf9b9b9b0e0e0e
+cececececececececeb6b6b66d6d6dcececececececececeb8b8b86b6b6bcecececececececece
+9b9b9b6a6a6acecececececececece9d9d9d868686cececececece494949535353b2b2b2adadad
+949594d0d0d0c8c9c8adaeadf0f0f0d0d0d0cfcfcfcecececcccccb9b9b90000000000004a4a4a
+898989a6a6a69c9c9c9d9d9d9d9d9d9e9e9e9c9c9c999999979797989898979797949494929292
+9999999c9c9c828282484848161616b2b2b2b3b3b3b1b1b1a8a8a89e9f9ef4f4f49a9b9adbdcdb
+dfdfdfcfcfcfcfcfcfcececec9c9c95959593d3d3da5a5a5a9a9a9b9b9b96c6c6c494949959595
+c3c3c3c1c1c1bdbdbdbbbbbbbbbbbbbcbcbcb8b8b8b4b4b4b9b9b9bcbcbcbfbfbf8282821b1b1b
+6b6b6bb2b2b2b3b3b3b1b1b19a9b9acccccccccdcca9aaa9f2f2f2d0d0d0cfcfcf7e7e7e2a2a2a
+c1c1c10000000000003f3f3fb0b0b0cdcdcdc1c1c1c0c0c0c2c2c2c2c2c2c1c1c1bcbcbcbbbbbb
+bbbbbbbababab7b7b7b3b3b39e9e9e3939390000002f2f2fa6a6a6000000a2a2a2b2b2b2a9a9a9
+9a9b9af4f4f49e9f9ed7d8d7ebebebd0d0d0cfcfcfcececec9c9c9cccccccacaca252525242424
+969696c7c7c7c1c1c1c1c1c1c3c3c3c2c2c2bfbfbfbcbcbcbbbbbbbcbcbcb8b8b8b6b6b6b5b5b5
+acacac2222222121218f8f8fb3b3b3b2b2b2b3b3b3b1b1b19b9c9bc8c8c8d0d1d0949594e9e9e9
+dadadacfcfcfcfcfcfcdcdcdc8c8c8cecececdcdcdcbcbcbcecececdcdcdc1c1c1c0c0c0c1c1c1
+c3c3c3c1c1c1bcbcbc9696964646461515150000000000004f4f4fa2a2a2c3c3c3b6b6b6b6b6b6
+b3b3b3b2b2b2b2b2b2aaabaa969796f4f4f4bfbfbfb7b8b7edededd0d0d0cfcfcfcecececbcbcb
+c9c9c9d0d0d0cacacacdcdcdcdcdcdc8c8c8c1c1c1c0c0c0c2c2c2c2c2c2bfbfbfafafaf8b8b8b
+3131310909090000001818186d6d6dccccccb9b9b9b6b6b6b5b5b5b2b2b2b3b3b3b1b1b19d9d9d
+a8a8a8f1f1f1949594e5e5e5dcdcdccfcfcfcfcfcfcececec9c9c9454545161600222200161616
+cdcdcdc5c5c5c1c1c1c1c1c1b0b0b0000000bdbdbdcececececececececececececececececece
+cececececececececececece3d3d3d5d5d5db2b2b2b0b0b0979897d5d6d5dfdfdfcacacac9c9c9
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacacacacacbcbcb9fa09fb2b2b2
+efefefd0d0d0cecececdcdcdcbcbcb7676760000000000006060007e7e007e7e007e7e007e7e00
+7e7e007e7e007e7e007e7e007e7e007e7e00151501424242b6b6b6bababab7b7b7b2b2b2afafaf
+adadadaaaaaaadadadacacac9c9d9cbebfbef4f4f4969796dfe0dfdededecfcfcf9a9a9a010101
+000000000000000000000000010101010101010101010101010101010101010101010101010101
+010101010101010101010101010101000000000000000000000000000000515151adadada9a9a9
+949594d0d0d0c8c9c8adaeadf0f0f0d0d0d0cecececdcdcdccccccbababa010101010101010101
+010101010101010101010101010101010101010101010101010101010101010101010101010101
+010101010101010101010101161616aaaaaaadadadacacaca5a5a59e9f9ef4f4f49a9b9adbdcdb
+e0e0e0cfcfcfcdcdcdcdcdcdcacaca5959590000000000000000005b5b5ba3a3a3bfbfbfbbbbbb
+c3c3c3bbbbbbb9b9b9b8b8b8b9b9b9b8b8b8b4b4b4b3b3b3b7b7b7b8b8b8b5b5b58080801b1b1b
+666666abababaeaeaeacacac999a99cccccccccdcca9aaa9f2f2f2d0d0d0cfcfcf7d7d7d2a2a2a
+c2c2c2010101010101010101292929cacacac4c4c4bcbcbcc1c1c1c1c1c1bdbdbdbabababababa
+bababab8b8b8b5b5b5b5b5b52424240101010101012e2e2e9f9f9f0000009d9d9dadadada6a6a6
+9a9b9af4f4f49e9f9ed7d8d7ebebebd0d0d0cdcdcdcdcdcdcacacac9c9c9cacacac8c8c8c3c3c3
+c8c8c8c7c7c7c1c1c1bcbcbcc4c4c4bfbfbfbbbbbbbababababababababab6b6b6b5b5b5b6b6b6
+bababab7b7b7b2b2b2afafafabababaaaaaaaeaeaeacacac9a9b9ac8c8c8d0d1d0949594e9e9e9
+dbdbdbcfcfcfcdcdcdcdcdcdc9c9c9cacacac9c9c9c6c6c6c4c4c4cacacac4c4c4bcbcbcbebebe
+c3c3c3bdbdbdbabababababa989898707070010101000000010101959595e0e0e0b1b1b1aeaeae
+abababacacacadadada7a7a7969796f4f4f4bfbfbfb7b8b7edededd0d0d0cdcdcdcdcdcdcccccc
+c9c9c9cacacac8c8c8c3c3c3c8c8c8c7c7c7c4c4c4bbbbbbc2c2c2c0c0c0bbbbbbbabababbbbbb
+8d8d8d333333000000000000111111ffffffbbbbbbb0b0b0adadadaaaaaaaeaeaeacacac9b9c9b
+a8a8a8f1f1f1949594e5e5e5dcdcdccfcfcfcdcdcdcdcdcdc9c9c9454545777700b3b300161616
+cacacac6c6c6bfbfbfbdbdbdb2b2b2010101010101010101010101010101010101010101010101
+0101010101010101010101010101015b5b5badadadacacac979897d5d6d5dfdfdfcacacacacaca
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b414141c8c8c8c9c9c9cacaca9fa09fb2b2b2
+eeeeeecccccccacacac9c9c9c9c9c9767676000000000000000000000000000000000000000000
+0000000000000000000000000000000000009e9e9ebbbbbbb6b6b6b2b2b2bcbcbcb8b8b8b6b6b6
+b3b3b3aaaaaab0b0b0acacac9c9d9cbebfbef4f4f4969796dfe0dfdbdbdbcbcbcbcacacabbbbbb
+0000000000000000002e2e2ec6c6c6c0c0c0c3c3c3c5c5c5c6c6c6c0c0c0bdbdbdbebebebdbdbd
+bcbcbcbbbbbbbdbdbdb8b8b8b4b4b45c5c5c0000000000000000006d6d6dadadadafafafa9a9a9
+949594d0d0d0c8c9c8adaeadf0f0f0cccccccacacacacacac9c9c9c8c8c8c5c5c5c9c9c9c7c7c7
+c3c3c3c0c0c0c4c4c4c7c7c7c3c3c3bebebebdbdbdbebebebdbdbdbbbbbbbcbcbcbbbbbbb7b7b7
+b2b2b2bcbcbcbababab7b7b7b4b4b4aaaaaab0b0b0adadada5a5a59e9f9ef4f4f49a9b9adbdcdb
+ddddddcbcbcbcacacacacacac9c9c9595959000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000
+666666acacacb1b1b1acacac999a99cccccccccdcca9aaa9f2f2f2cbcbcbcacaca7a7a7a2a2a2a
+c8c8c8c5c5c5c9c9c9c8c8c8c5c5c5c0c0c0c4c4c4c7c7c7c3c3c3bfbfbfbdbdbdbfbfbfbdbdbd
+bbbbbbbbbbbbbbbbbbb8b8b8b3b3b3b8b8b8bababab7b7b7a6a6a60000009f9f9faeaeaea6a6a6
+9a9b9af4f4f49e9f9ed7d8d7eaeaeacbcbcbcacacacacacac9c9c9c6c6c6c5c5c5cacacac7c7c7
+c2c2c2c1c1c1c5c5c5c7c7c7c1c1c1bebebebdbdbdbebebebcbcbcbbbbbbbcbcbcbababab6b6b6
+b3b3b3bdbdbdb8b8b8b6b6b6afafafaaaaaab1b1b1acacac9a9b9ac8c8c8d0d1d0949594e9e9e9
+d7d7d7cacacacacacac9c9c9c9c9c9c6c6c6c7c7c7c8c8c8c5c5c5c0c0c0c3c3c3c7c7c7c5c5c5
+c0c0c0bdbdbdbebebebdbdbdbbbbbbb3b3b3808080070707c2c2c2ffffffe2e2e2b7b7b7b6b6b6
+adadadadadadafafafa7a7a7969796f4f4f4bfbfbfb7b8b7ecececcbcbcbcacacacacacac9c9c9
+c8c8c8c5c5c5c9c9c9c7c7c7c2c2c2c1c1c1c4c4c4c7c7c7c2c2c2bebebebdbdbdbebebebdbdbd
+bbbbbb9797974a4a4a3d3d3dffffffffffffc0c0c0b7b7b7b3b3b3aaaaaab1b1b1acacac9b9c9b
+a8a8a8f1f1f1949594e5e5e5d9d9d9cbcbcbcacacacacacac9c9c9454545000000000000151515
+c0c0c0c2c2c2c5c5c5c6c6c6c0c0c0bdbdbdbebebebebebebcbcbcbbbbbbbdbdbdb8b8b8b3b3b3
+b7b7b7bcbcbcb8b8b8b6b6b6adadadadadadafafafacacac979897d5d6d5ddddddc8c8c8c8c8c8
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c9c9c9c9c9c99fa09fb2b2b2
+eeeeeecacacac6c6c6c7c7c7c5c5c5b5b5b59f9f9f9e9e9e9f9f9f9f9f9f9d9d9d9d9d9da2a2a2
+9c9c9c9b9b9b9c9c9c9b9b9b9a9a9a9c9c9cb5b5b5b7b7b7b0b0b0aeaeaeb6b6b6b4b4b4aeaeae
+abababa7a7a7acacacacacac9c9d9cbebfbef4f4f4969796dfe0dfdadadac8c8c8c7c7c7c5c5c5
+a1a1a1a0a0a09f9f9fa7a7a7c1c1c1c0c0c0bebebec0c0c0c2c2c2bcbcbcbdbdbdbdbdbdbbbbbb
+bbbbbbbcbcbcbababab2b2b2afafafa2a2a29595959393938d8d8d9d9d9daaaaaaacacaca9a9a9
+949594d0d0d0c8c9c8adaeadf0f0f0cacacac7c7c7c7c7c7c7c7c7c3c3c3c1c1c1c1c1c1c1c1c1
+c0c0c0bfbfbfbebebec3c3c3bebebebcbcbcbdbdbdbdbdbdbbbbbbbcbcbcbbbbbbb7b7b7b1b1b1
+aeaeaeb6b6b6b5b5b5b1b1b1abababa7a7a7acacacacacaca5a5a59e9f9ef4f4f49a9b9adbdcdb
+dcdcdcc8c8c8c6c6c6c8c8c8c5c5c5afafaf9f9f9f9e9e9e9f9f9f9e9e9e9d9d9d9e9e9ea0a0a0
+9a9a9a9c9c9c9c9c9c9b9b9b9a9a9a9c9c9c9999999393939090909090909595959494948e8e8e
+9c9c9ca9a9a9acacacacacac999a99cccccccccdcca9aaa9f2f2f2cacacac8c8c8787878070707
+222222222222222222222222222222212121212121222222212121212121202020212121202020
+2020202020202020201f1f1f1f1f1f2020202020201f1f1f1c1c1c0000009c9c9cacacaca6a6a6
+9a9b9af4f4f49e9f9ed7d8d7eaeaeacacacac6c6c6c8c8c8c5c5c5c2c2c2c1c1c1c1c1c1c1c1c1
+c0c0c0bfbfbfc0c0c0c3c3c3bcbcbcbcbcbcbdbdbdbcbcbcbbbbbbbcbcbcbababab6b6b6b0b0b0
+afafafb6b6b6b4b4b4aeaeaea9a9a9a7a7a7acacacacacac9a9b9ac8c8c8d0d1d0949594e9e9e9
+d6d6d6c8c8c8c7c7c7c7c7c7c4c4c4c2c2c2c1c1c1c1c1c1c1c1c1c0c0c0bebebec3c3c3c1c1c1
+bcbcbcbdbdbdbdbdbdbbbbbbbcbcbcbbbbbbafafafd0d0d0f4f4f4dbdbdbbcbcbcb2b2b2acacac
+a8a8a8aaaaaaacacaca7a7a7969796f4f4f4bfbfbfb7b8b7ecececcacacac6c6c6c8c8c8c6c6c6
+c3c3c3c1c1c1c1c1c1c1c1c1c0c0c0bfbfbfbebebec4c4c4bdbdbdbcbcbcbdbdbdbcbcbcbbbbbb
+bcbcbcb5b5b5bebebeddddddfbfbfbc1c1c1b5b5b5b0b0b0abababa7a7a7acacacacacac9b9c9b
+a8a8a8f1f1f1949594e5e5e5d8d8d8c8c8c8c6c6c6c8c8c8c4c4c4acacac9f9f9f9e9e9ea3a3a3
+c0c0c0bebebec1c1c1c2c2c2bcbcbcbdbdbdbdbdbdbcbcbcbbbbbbbcbcbcbababab2b2b2afafaf
+b2b2b2b6b6b6b3b3b3acacaca8a8a8aaaaaaacacacacacac979897d5d6d5dcdcdcc6c6c6c6c6c6
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c9c9c9c9c9c99fa09fb2b2b2
+eeeeeec9c9c9c8c8c8c7c7c7c4c4c4c2c2c2c2c2c2c0c0c0c1c1c1bfbfbfbebebebebebebdbdbd
+bfbfbfbbbbbbb8b8b8b8b8b8b5b5b5b5b5b5b4b4b4b2b2b2b0b0b0afafafb0b0b0b2b2b2adadad
+aaaaaaa7a7a7a9a9a9a7a7a79b9b9bbebfbef4f4f4969796dfe0dfd9d9d9c9c9c9c8c8c8c6c6c6
+c2c2c2c2c2c2c1c1c1c0c0c0c1c1c1bebebebebebebdbdbdbebebebfbfbfb7b7b7b9b9b9b7b7b7
+b5b5b5b5b5b5b4b4b4b1b1b1b0b0b0b0b0b0b1b1b1b2b2b2acacaca8a8a8a8a8a8a8a8a8a4a4a4
+949594d0d0d0c8c9c8adaeadf0f0f0c9c9c9c8c8c8c7c7c7c5c5c5c2c2c2c2c2c2c0c0c0c1c1c1
+bfbfbfbebebebebebebdbdbdbfbfbfbbbbbbb8b8b8b9b9b9b5b5b5b5b5b5b5b5b5b3b3b3b0b0b0
+afafafb0b0b0b1b1b1b0b0b0aaaaaaa7a7a7a9a9a9a7a7a7a1a1a19e9f9ef4f4f49a9b9adbdcdb
+dbdbdbc9c9c9c8c8c8c6c6c6c3c3c3c2c2c2c1c1c1c0c0c0c1c1c1bfbfbfbebebebebebebebebe
+c0c0c0b7b7b7b9b9b9b8b8b8b5b5b5b5b5b5b4b4b4b1b1b1b0b0b0b0b0b0b0b0b0b2b2b2acacac
+a9a9a9a8a8a8a9a9a9a6a6a6989998cccccccccdcca9aaa9f1f1f1c9c9c9c8c8c8acacac7f7f7f
+7d7d7d7d7d7d7c7c7c7c7c7c7c7c7c7a7a7a7b7b7b7878787b7b7b797979747474767676747474
+7373737272727272727272727272727171717171717171716e6e6e6c6c6ca3a3a3a8a8a8a2a2a2
+9a9b9af4f4f49e9f9ed7d8d7eaeaeac9c9c9c8c8c8c6c6c6c3c3c3c2c2c2c2c2c2c0c0c0c1c1c1
+bfbfbfbebebebebebebdbdbdc0c0c0bababab8b8b8b8b8b8b5b5b5b5b5b5b4b4b4b2b2b2b0b0b0
+afafafb0b0b0b2b2b2adadada9a9a9a7a7a7a9a9a9a7a7a7999999c8c8c8d0d1d0949594e9e9e9
+d5d5d5c8c8c8c7c7c7c5c5c5c2c2c2c2c2c2c1c1c1c0c0c0c1c1c1bebebebebebebdbdbdbebebe
+bfbfbfb7b7b7b9b9b9b6b6b6b5b5b5b5b5b5b4b4b4cbcbcbccccccbcbcbcb1b1b1b0b0b0ababab
+a8a8a8a8a8a8a8a8a8a2a3a2969796f4f4f4bfbfbfb7b8b7ebebebc9c9c9c8c8c8c7c7c7c5c5c5
+c2c2c2c2c2c2c0c0c0c1c1c1bfbfbfbebebebebebebdbdbdbfbfbfbbbbbbb8b8b8b8b8b8b5b5b5
+b5b5b5b4b4b4bebebecccccccacacab0b0b0b2b2b2afafafaaaaaaa7a7a7a9a9a9a7a7a7999a99
+a8a8a8f1f1f1949594e5e5e5d7d7d7c8c8c8c8c8c8c6c6c6c3c3c3c2c2c2c1c1c1c0c0c0c1c1c1
+bebebebebebebdbdbdbebebebfbfbfb7b7b7b9b9b9b8b8b8b5b5b5b5b5b5b4b4b4b1b1b1b0b0b0
+b0b0b0b0b0b0b2b2b2acacaca8a8a8a8a8a8a8a8a8a6a6a6969796d5d6d5ddddddc7c7c7c6c6c6
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c9c9c9c9c9c99fa09fb2b2b2
+eeeeeec8c8c8c8c8c8c6c6c6c4c4c4c3c3c3c3c3c3bfbfbfc0c0c0bebebebdbdbdbdbdbdbababa
+bfbfbfbababab6b6b6b7b7b7b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1b0b0b0adadadb0b0b0adadad
+aaaaaaa8a8a8a7a7a7a4a4a49a9a9abebfbef4f4f4969796dfe0dfd9d9d9c8c8c8c8c8c8c5c5c5
+c2c2c2c3c3c3c1c1c1bfbfbfc0c0c0bcbcbcbdbdbdbcbcbcbbbbbbc0c0c0b5b5b5b8b8b8b5b5b5
+b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1afafafaeaeaeb0b0b0acacaca9a9a9a7a7a7a6a6a6a2a2a2
+949594d0d0d0c8c9c8adaeadf0f0f0c8c8c8c8c8c8c6c6c6c5c5c5c2c2c2c3c3c3bfbfbfc0c0c0
+bebebebcbcbcbebebebabababfbfbfbbbbbbb6b6b6b8b8b8b3b3b3b2b2b2b2b2b2b1b1b1b1b1b1
+b0b0b0adadadafafafaeaeaeaaaaaaa8a8a8a7a7a7a5a5a59f9f9f9e9f9ef4f4f49a9b9adbdcdb
+dbdbdbc8c8c8c8c8c8c6c6c6c3c3c3c3c3c3c2c2c2bfbfbfc0c0c0bdbdbdbdbdbdbcbcbcbbbbbb
+c1c1c1b5b5b5b8b8b8b7b7b7b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1b0b0b0adadadb0b0b0acacac
+a9a9a9a7a7a7a7a7a7a4a4a4979897cccccccccdcca9aaa9f1f1f1c8c8c8c8c8c8c7c7c7c5c5c5
+c2c2c2c3c3c3c0c0c0bfbfbfbfbfbfbcbcbcbebebebabababebebebbbbbbb5b5b5b8b8b8b4b4b4
+b3b3b3b2b2b2b1b1b1b2b2b2b1b1b1aeaeaeaeaeaeafafafaaaaaaa8a8a8a7a7a7a5a5a5a0a0a0
+9a9b9af4f4f49e9f9ed7d8d7e9e9e9c8c8c8c8c8c8c6c6c6c3c3c3c3c3c3c3c3c3bfbfbfc0c0c0
+bdbdbdbdbdbdbcbcbcbbbbbbc1c1c1b9b9b9b7b7b7b7b7b7b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1
+b0b0b0adadadb0b0b0acacaca9a9a9a7a7a7a7a7a7a4a4a4989998c8c8c8d0d1d0949594e9e9e9
+d5d5d5c8c8c8c7c7c7c5c5c5c2c2c2c3c3c3c1c1c1bfbfbfbfbfbfbcbcbcbebebebabababcbcbc
+bfbfbfb5b5b5b8b8b8b4b4b4b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1aeaeaeaeaeaeafafafababab
+a8a8a8a7a7a7a6a6a6a0a1a0969796f4f4f4bfbfbfb7b8b7ebebebc8c8c8c8c8c8c6c6c6c4c4c4
+c2c2c2c3c3c3bfbfbfc0c0c0bdbdbdbdbdbdbebebebabababfbfbfb9b9b9b6b6b6b7b7b7b3b3b3
+b2b2b2b2b2b2b1b1b1b1b1b1b0b0b0adadadb0b0b0aeaeaeaaaaaaa7a7a7a7a7a7a4a4a4999999
+a8a8a8f1f1f1949594e5e5e5d7d7d7c8c8c8c8c8c8c6c6c6c3c3c3c3c3c3c1c1c1bfbfbfc0c0c0
+bcbcbcbdbdbdbcbcbcbcbcbcc0c0c0b5b5b5b8b8b8b6b6b6b3b3b3b2b2b2b1b1b1b1b1b1b1b1b1
+afafafadadadb0b0b0abababa8a8a8a7a7a7a6a6a6a4a4a4969796d5d6d5ddddddc8c8c8c7c7c7
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9c9c9c9cacaca9fa09fb2b2b2
+eeeeeec6c6c6c4c4c4c6c6c6c6c6c6c6c6c6c6c6c6bdbdbdbababab9b9b9b9b9b9babababababa
+b9b9b9b7b7b7b9b9b9bbbbbbb6b6b6b5b5b5b5b5b5b5b5b5b5b5b5afafafabababaaaaaaa8a8a8
+a7a7a7a8a8a8a3a3a3a5a5a59a9b9abebfbef4f4f4969796dfe0dfd8d8d8c5c5c5c4c4c4c6c6c6
+c6c6c6c6c6c6c2c2c2bcbcbcb9b9b9b9b9b9b9b9b9babababababab8b8b8b7b7b7bcbcbcb8b8b8
+b6b6b6b5b5b5b5b5b5b6b6b6b2b2b2aeaeaeabababaaaaaaa7a7a7a7a7a7a5a5a5a3a3a3a4a4a4
+949594d0d0d0c8c9c8adaeadefefefc7c7c7c4c4c4c6c6c6c6c6c6c6c6c6c6c6c6bdbdbdbababa
+b9b9b9b9b9b9babababababab9b9b9b7b7b7b9b9b9bcbcbcb7b7b7b6b6b6b5b5b5b5b5b5b6b6b6
+b0b0b0acacacabababa9a9a9a7a7a7a8a8a8a3a3a3a5a5a5a0a1a09e9f9ef4f4f49a9b9adbdcdb
+dadadac5c5c5c4c4c4c6c6c6c6c6c6c6c6c6c2c2c2bcbcbcbababab9b9b9b9b9b9babababababa
+b9b9b9b7b7b7bbbbbbbababab6b6b6b6b6b6b5b5b5b6b6b6b2b2b2afafafacacacaaaaaaa7a7a7
+a7a7a7a5a5a5a3a3a3a5a5a5989898cccccccccdcca9aaa9f1f1f1c6c6c6c5c5c5c5c5c5c6c6c6
+c6c6c6c6c6c6bebebebbbbbbb9b9b9b9b9b9babababababab9b9b9b8b8b8b7b7b7bdbdbdb8b8b8
+b6b6b6b5b5b5b5b5b5b7b7b7b1b1b1adadadabababa9a9a9a7a7a7a8a8a8a3a3a3a4a4a4a1a1a1
+9a9b9af4f4f49e9f9ed7d8d7e9e9e9c6c6c6c4c4c4c6c6c6c6c6c6c6c6c6c5c5c5bdbdbdbababa
+b9b9b9b9b9b9babababababab9b9b9b7b7b7b9b9b9bbbbbbb6b6b6b6b6b6b5b5b5b5b5b5b5b5b5
+afafafacacacaaaaaaa7a7a7a7a7a7a7a7a7a3a3a3a5a5a5989998c8c8c8d0d1d0949594e9e9e9
+d4d4d4c5c5c5c5c5c5c6c6c6c6c6c6c6c6c6c1c1c1bbbbbbb9b9b9b9b9b9b9b9b9babababababa
+b8b8b8b7b7b7bcbcbcb8b8b8b6b6b6b5b5b5b5b5b5b6b6b6b1b1b1adadadabababa9a9a9a7a7a7
+a7a7a7a5a5a5a4a4a4a2a2a2969796f4f4f4bfbfbfb7b8b7ebebebc6c6c6c4c4c4c6c6c6c6c6c6
+c6c6c6c6c6c6bdbdbdbababab9b9b9b9b9b9babababababab9b9b9b7b7b7b9b9b9bbbbbbb7b7b7
+b6b6b6b5b5b5b5b5b5b5b5b5afafafacacacaaaaaaa8a8a8a7a7a7a7a7a7a3a3a3a5a5a5999a99
+a8a8a8f1f1f1949594e5e5e5d5d5d5c5c5c5c4c4c4c7c7c7c6c6c6c6c6c6c1c1c1bcbcbcb9b9b9
+b9b9b9b9b9b9babababababab8b8b8b7b7b7bcbcbcbababab6b6b6b5b5b5b5b5b5b6b6b6b1b1b1
+adadadabababaaaaaaa7a7a7a7a7a7a5a5a5a4a4a4a6a6a6969796d5d6d5dfdfdfcacacac9c9c9
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cbcbcbcbcbcbcacaca9fa09f949594
+959695949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594bebfbef4f4f4969796949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594d0d0d0c8c9c8949594959695949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+9495949495949495949495949495949495949495949495949495949e9f9ef4f4f49a9b9a949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594cccccccccdcc949594959695949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+9a9b9af4f4f49e9f9e949594959595949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594c8c8c8d0d1d0949594959595
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594969796f4f4f4bfbfbf949594959695949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+a8a8a8f1f1f1949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594949594949594949594949594
+949594949594949594949594949594949594949594949594949594d5d6d5dfdfdfcbcbcbcbcbcb
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cbcbcbcbcbcbc9c9c9dcdcdce1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e9e9e9f4f4f4e2e2e2e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1edededebebebe1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e3e3e3f4f4f4e2e2e2e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1ecececececece1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e2e2e2f4f4f4e3e3e3e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1ebebebededede1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e2e1f4f4f4e9e9e9e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e5e5e5f3f3f3e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1
+e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1eeeeeedfdfdfcbcbcbcbcbcb
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cbcbcbcacacac9c9c9d3d3d3d6d6d6
+d5d5d5d5d5d5d7d7d7d7d7d7d8d8d8d8d8d8d7d7d7d7d7d7d6d6d6d6d6d6d7d7d7d9d9d9dadada
+d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d8d8d8d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d8d8d8d9d9d9
+dadadadadadad9d9d9d9d9d9dbdbdbdddddddedededcdcdcdadadad9d9d9d9d9d9d8d8d8d7d7d7
+d8d8d8d9d9d9dadadadadadadadadadadadadadadad9d9d9d8d8d8d8d8d8d9d9d9dadadadbdbdb
+dcdcdcdddddddddddddddddddddddddedededddddddcdcdcdbdbdbdadadadadadadadadadadada
+dadadad9d9d9d9d9d9d8d8d8d9d9d9d9d9d9dadadadbdbdbdcdcdcdddddddcdcdcdadadad9d9d9
+dadadadbdbdbdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdadadadadadad9d9d9dadadadadada
+dadadadadadadadadad9d9d9dadadadadadad9d9d9d9d9d9dadadadbdbdbdcdcdcdcdcdcdcdcdc
+dcdcdcdcdcdcdcdcdcdbdbdbdadadad7d7d7d6d6d6d5d5d5d7d7d7d8d8d8d9d9d9d9d9d9d9d9d9
+d9d9d9dadadad9d9d9d9d9d9d9d9d9d9d9d9dadadadadadadbdbdbdcdcdcdedededfdfdfdddddd
+dbdbdbdadadadadadadadadadbdbdbdcdcdcdbdbdbdadadadadadadadadadadadadadadadadada
+dadadadadadadadadadadadad9d9d9d9d9d9dadadadadadad9d9d9d9d9d9dadadad9d9d9d9d9d9
+d7d7d7d6d6d6d5d5d5d5d5d5d6d6d6d7d7d7d8d8d8d8d8d8d7d7d7d7d7d7d6d6d6d6d6d6d7d7d7
+d9d9d9dadadad9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d8d8d8d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7
+d8d8d8d9d9d9dadadadadadad9d9d9d9d9d9dadadadcdcdcdddddddcdcdcdadadadadadad9d9d9
+d8d8d8d7d7d7d8d8d8d9d9d9dadadadadadadadadadadadadadadad9d9d9d8d8d8d8d8d8d9d9d9
+d9d9d9dbdbdbdcdcdcdddddddddddddddddddddddddededededededcdcdcdadadadadadadadada
+dadadadadadadadadad9d9d9d8d8d8d8d8d8d9d9d9d9d9d9dadadadbdbdbdcdcdcdddddddcdcdc
+dadadad9d9d9dadadadbdbdbdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdadadad9d9d9d9d9d9
+d9d9d9dadadadadadadadadadadadad9d9d9dadadadadadad9d9d9d9d9d9dadadadbdbdbdcdcdc
+dcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdadadad9d9d9d8d8d8d6d6d6d5d5d5d7d7d7d8d8d8d9d9d9
+d9d9d9d9d9d9d9d9d9dadadad9d9d9d9d9d9d9d9d9d9d9d9dadadadadadadbdbdbdcdcdcdedede
+dfdfdfdedededcdcdcdadadadadadadadadadcdcdcdcdcdcdbdbdbdadadadadadadadadadadada
+dadadadadadadadadadadadadadadadadadad9d9d9d9d9d9dadadadadadad1d1d1cacacacbcbcb
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacac9c9c9c9c9c9c6c6c6c5c5c5
+c4c4c4c5c5c5c6c6c6c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c5c5c5c4c4c4c6c6c6c9c9c9cacaca
+cacacacacacacacacacacacac8c8c8c7c7c7c7c7c7c6c6c6c6c6c6c6c6c6c6c6c6c7c7c7c9c9c9
+cbcbcbcdcdcdcbcbcbcbcbcbcccccccecececfcfcfcdcdcdcacacac9c9c9c8c8c8c6c6c6c6c6c6
+c6c6c6c7c7c7c9c9c9cacacacacacacacacacacacac9c9c9c9c9c9c9c9c9cacacacccccccdcdcd
+cececececececececececececfcfcfcfcfcfcfcfcfcecececccccccbcbcbcbcbcbcbcbcbcacaca
+cacacac9c9c9c8c8c8c7c7c7c7c7c7c8c8c8cacacacccccccdcdcdcfcfcfcecececdcdcdcccccc
+cccccccdcdcdcecececececececececececececececdcdcdcbcbcbcacacacacacacacacacbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcccccccacacac9c9c9cacacacccccccecececececececece
+cecececececececececccccccbcbcbc8c8c8c5c5c5c4c4c4c6c6c6c7c7c7c9c9c9cacacacacaca
+cacacacacacacacacacacacacacacacacacacacacacbcbcbcccccccdcdcdd0d0d0d2d2d2d1d1d1
+cfcfcfcdcdcdcccccccccccccdcdcdcdcdcdcbcbcbc9c9c9c9c9c9cbcbcbcbcbcbcbcbcbcbcbcb
+cccccccbcbcbcbcbcbcacacac9c9c9c9c9c9cbcbcbcbcbcbc9c9c9c9c9c9cacacac9c9c9c9c9c9
+c7c7c7c5c5c5c4c4c4c5c5c5c6c6c6c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c4c4c4c4c4c4c5c5c5
+c8c8c8cbcbcbcacacacacacacacacacacacac9c9c9c8c8c8c7c7c7c6c6c6c6c6c6c6c6c6c6c6c6
+c7c7c7c9c9c9cbcbcbcdcdcdcbcbcbcbcbcbcbcbcbcdcdcdcfcfcfcdcdcdcacacac9c9c9c8c8c8
+c7c7c7c6c6c6c6c6c6c8c8c8c9c9c9cacacacacacacacacacacacac9c9c9c9c9c9c9c9c9cacaca
+cbcbcbcdcdcdcececececececececececececfcfcfcfcfcfcfcfcfcecececccccccbcbcbcbcbcb
+cbcbcbcbcbcbcacacac9c9c9c8c8c8c7c7c7c7c7c7c8c8c8cacacacccccccdcdcdcfcfcfcecece
+cdcdcdcccccccccccccececececececececececececececececececdcdcdcbcbcbcacacacacaca
+cacacacbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcacacac9c9c9cacacacccccccecece
+cececececececececececececdcdcdcccccccbcbcbc8c8c8c5c5c5c3c3c3c6c6c6c8c8c8c9c9c9
+cacacacacacacacacacacacacacacacacacacacacacacacacacacacbcbcbcccccccececed0d0d0
+d2d2d2d1d1d1cfcfcfcdcdcdcccccccccccccdcdcdcdcdcdccccccc9c9c9c9c9c9cbcbcbcbcbcb
+cbcbcbcbcbcbcccccccbcbcbcbcbcbcacacac9c9c9c9c9c9cacacacbcbcbc9c9c9c9c9c9cacaca
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c8c8c8c8c8c8c9c9c9c7c7c7c6c6c6
+c6c6c6c6c6c6c7c7c7c7c7c7c7c7c7c8c8c8c9c9c9c8c8c8c5c5c5c4c4c4c6c6c6c8c8c8cacaca
+cbcbcbcccccccbcbcbcacacac8c8c8c7c7c7c7c7c7c7c7c7c7c7c7c6c6c6c4c4c4c5c5c5c9c9c9
+cbcbcbd0d0d0cecececccccccccccccccccccecececcccccc9c9c9c8c8c8c7c7c7c5c5c5c5c5c5
+c5c5c5c6c6c6c7c7c7c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9cacacacccccccccccccdcdcdcecece
+cdcdcdcccccccccccccdcdcdcececececececececececececccccccccccccccccccbcbcbcacaca
+cacacac9c9c9c8c8c8c6c6c6c6c6c6c6c6c6c8c8c8cbcbcbcccccccececed0d0d0d0d0d0cecece
+cfcfcfd0d0d0d0d0d0cececececececececececececdcdcdcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcccccccccccccccccccccccccacacac9c9c9c9c9c9cccccccecececececececece
+cececececececececececececcccccc9c9c9c6c6c6c5c5c5c6c6c6c7c7c7c9c9c9cacacacbcbcb
+cbcbcbcbcbcbcbcbcbcccccccccccccccccccbcbcbcacacacbcbcbccccccd0d0d0d1d1d1d2d2d2
+d0d0d0cfcfcfcecececececececececccccccacacac8c8c8c8c8c8cacacacbcbcbcccccccccccc
+cdcdcdcccccccbcbcbcacacac9c9c9c9c9c9cbcbcbcbcbcbc9c9c9c8c8c8c9c9c9c9c9c9c9c9c9
+c7c7c7c6c6c6c6c6c6c7c7c7c7c7c7c8c8c8c8c8c8c8c8c8c9c9c9c9c9c9c5c5c5c5c5c5c5c5c5
+c8c8c8cbcbcbcbcbcbcccccccbcbcbcacacac9c9c9c8c8c8c8c8c8c8c8c8c7c7c7c7c7c7c5c5c5
+c5c5c5c8c8c8cccccccfcfcfcecececccccccccccccccccccdcdcdccccccc9c9c9c8c8c8c7c7c7
+c5c5c5c5c5c5c5c5c5c6c6c6c8c8c8c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9cacacacccccccccccc
+cdcdcdcecececdcdcdcccccccccccccdcdcdcececececececececececececccccccccccccccccc
+cbcbcbcbcbcbcacacac9c9c9c7c7c7c6c6c6c6c6c6c6c6c6c8c8c8cacacacccccccececed0d0d0
+d0d0d0cecececececed0d0d0cfcfcfcececececececececececececdcdcdcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcccccccccccccccccccccccccacacac9c9c9cacacacccccccecece
+cececececececececececececececececececcccccc9c9c9c6c6c6c5c5c5c6c6c6c7c7c7c9c9c9
+cacacacbcbcbcbcbcbcbcbcbcbcbcbcccccccccccccccccccbcbcbcacacacacacacdcdcdd0d0d0
+d2d2d2d2d2d2d1d1d1cfcfcfcdcdcdcececececececdcdcdcacacac8c8c8c8c8c8cacacacbcbcb
+cccccccccccccecececccccccbcbcbc9c9c9c9c9c9c9c9c9cacacacbcbcbc9c9c9c8c8c8c9c9c9
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c7c7c7c8c8c8c8c8c8c8c8c8
+c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c9c9c9c8c8c8c8c8c8c6c6c6c6c6c6c7c7c7c9c9c9
+cacacacbcbcbcbcbcbcacacac8c8c8c7c7c7c8c8c8c9c9c9c9c9c9c7c7c7c4c4c4c5c5c5c7c7c7
+cacacad0d0d0d3d3d3d2d2d2cecececccccccccccccbcbcbcacacac9c9c9c7c7c7c5c5c5c4c4c4
+c5c5c5c5c5c5c6c6c6c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8cacacacccccccdcdcdcccccccbcbcb
+cacacacacacacbcbcbcbcbcbcccccccccccccccccccccccccccccccbcbcbcacacac9c9c9c9c9c9
+c9c9c9c9c9c9c8c8c8c8c8c8c7c7c7c7c7c7c8c8c8cbcbcbcccccccececed0d0d0d2d2d2d3d3d3
+d3d3d3d3d3d3d1d1d1d1d1d1cecececccccccbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcacaca
+cacacacbcbcbcccccccecececfcfcfcdcdcdcbcbcbc8c8c8c8c8c8cacacacbcbcbcbcbcbcdcdcd
+cececececececfcfcfd0d0d0cecececbcbcbc8c8c8c6c6c6c6c6c6c7c7c7c8c8c8cacacacbcbcb
+cbcbcbcacacacacacacacacacacacacbcbcbcbcbcbcacacacbcbcbcccccccececed0d0d0d1d1d1
+d1d1d1d1d1d1cfcfcfcfcfcfcfcfcfcdcdcdcacacac7c7c7c7c7c7c8c8c8cacacacccccccecece
+cfcfcfcdcdcdcccccccbcbcbc9c9c9c9c9c9cacacacacacac8c8c8c7c7c7c7c7c7c7c7c7c7c7c7
+c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c9c9c9c9c9c9c8c8c8c7c7c7c6c6c6
+c7c7c7c9c9c9cacacacbcbcbcbcbcbcacacac9c9c9c8c8c8c8c8c8c9c9c9c9c9c9c7c7c7c5c5c5
+c5c5c5c7c7c7cbcbcbd0d0d0d3d3d3d2d2d2cfcfcfcccccccbcbcbcbcbcbcacacac9c9c9c8c8c8
+c5c5c5c4c4c4c4c4c4c5c5c5c7c7c7c8c8c8c8c8c8c8c8c8c8c8c8c9c9c9cacacacccccccdcdcd
+cccccccbcbcbcacacacacacacbcbcbcbcbcbcccccccccccccccccccccccccccccccbcbcbcacaca
+c9c9c9c9c9c9c9c9c9c9c9c9c8c8c8c8c8c8c7c7c7c7c7c7c8c8c8cacacacccccccececed0d0d0
+d2d2d2d3d3d3d3d3d3d3d3d3d1d1d1d0d0d0cfcfcfcdcdcdcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcacacacbcbcbcdcdcdcecececfcfcfcdcdcdcbcbcbc9c9c9c8c8c8cacacacbcbcb
+cbcbcbcdcdcdcececececececfcfcfd0d0d0cecececcccccc9c9c9c6c6c6c6c6c6c7c7c7c9c9c9
+cacacacbcbcbcbcbcbcacacacacacacacacacacacacbcbcbcbcbcbcacacacacacacccccccecece
+d1d1d1d1d1d1d1d1d1d1d1d1cfcfcfcfcfcfcfcfcfcecececacacac7c7c7c7c7c7c7c7c7cacaca
+cdcdcdcecececfcfcfcecececccccccacacac9c9c9c9c9c9c9c9c9cacacac9c9c9c7c7c7c7c7c7
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8c6c6c6c7c7c7c9c9c9d6d6d6
+f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f2f2f2f3f3f3f3f3f3
+f3f3f3f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f2f2f2f2f2f2f3f3f3
+f3f3f3f5f5f5f5f5f5f5f5f5f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f2f2f2f2f2f2
+f2f2f2f2f2f2f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4f4f4f4f4f4f4f4f4f4f4f4
+f3f3f3f3f3f3f3f3f3f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3
+f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4f4f4f4f4f4f5f5f5f4f4f0f4f5f1
+f3f5f3f3f5f5f3f4f4f3f5f5f3f4f4f3f3f2f2f2f1f1f1f0f2f2f0f1f1eff1f0eff1f0eeefefed
+efefeef0f0f0f1f0f1f2f1f2f2f1f2efefefefeff0f1efeff1eeeff1edeef2f0eef1f3eef2f4f1
+f2f4f3f2f4f4f2f5f5f4f5f4f4f4f1f4f2f0f3f1f1f2f1f2f2f1f2f2f2f2f2f2f2f2f3f3f2f3f3
+f1f3f3f2f2f2f2f2f2f2f2f2f2f2f2f3f2f3f3f2f3f2f2f2f3f2f3f3f2f3f3f3f3f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f3f2f3f2f1f2f2f1f2f2f1f2f3f2f3f3f2f3f3f3f3
+f4f3f4f3f3f3f4f2f4f3f2f3f2f2f2f2f1f2f2f2f2f2f2f2f2f2f2f2f2f2f1f2f2f1f2f2f1f2f2
+f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2f1f2f2
+f2f2f2f2f1f2f3f2f3f3f2f3f3f2f3f3f2f3f2f1f2f2f1f2f2f0f0f2eff0f2eff0edf0f2e9edf0
+edeff0f1f1eff2f1eff3f2f1f2f4f2f2f4f1f1f3eff1f3eef0f3eef0f2eef1f2eef1f2eef0f2ef
+eff1efeff0eff0f1eff1f0eff0f0eff0f0f0f0f0f1eff0f1eef0f1eff0f1f1f0f1f2f1f1f2f1ef
+f1f2eff0f3f0f1f2f2f2f0f2f1f0f0f2f1f0f1f2f0f0f1f0eff1f0f0f2f1eff2f1eff2f1eef2f1
+edf1f2ecf0f1eef0eff0f0eff2f1f0f2f2f0f1f2f1f1f1f2f2f2f2f3f3f3f4f4f4f4f4f4f5f5f5
+f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f4f4f4
+f4f4f4f3f3f3f3f3f3f4f4f4f4f4f4f4f4f4f5f5f5f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3
+f4f4f4f4f4f4f4f4f4f4f4f4f5f5f5f5f5f5f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3
+f3f3f3f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4f4f4f4f4f4
+f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3
+f4f4f4f4f4f4f5f5f5f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3d7d7d7cacacac9c9c9c8c8c8
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c8c8c8c7c7c7c7c7c7c9c9c9dadada
+fefefefefefefefefefffffffefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfefafdfefb
+fdfefdfdfefefdfefefdfefefefefdfefdfbfdfcf9fcfcf8fbfcf9f9fbf9f7fbfaf4fbfaf1fafa
+effafbebf7faecf6faedf6fbeef9fcf1fafcf2fafcf6fbfbf8fafbf9f9fafbfbfafcfefafdfefc
+fdfefefdfefffdfefffefefefffefbfffdfbfefdfcfefdfefefdfefefdfefefdfefdfefefdfefe
+fdfefefdfefefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefefe
+fefefefefefefefefefefefefefefefefdfefefdfefefdfefefdfefdfdfdfefdfefefdfefefdfe
+fefdfefefdfefefdfefefdfefefdfefefdfefefefefefefefefefefdfefefdfefefdfefefdfefe
+fdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefe
+fefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefdfbfcf9f8f9f5f5f8e9f3f8e6f5f9
+e9f7faebf8f9ecf7faeaf7fbeaf6fbedf4faeff4f9eff5f9f0f6f9f0f6faf0f5faf0f5faeff6fa
+edf8faebf9f9ecf8f9ecf8f9ecf8f8ebf7f8ecf7f9edf7f9f0f9faf6fbfbfbfbfcfdfbfbfdfcf9
+fafdf9fafefbfcfcfdfdfafdfdfafbfcfbfbf8fafaf2f8f9ecf5f7ebf8fae8f7fae7f7f9e7f6f8
+e8f5f7edf7f9f7fcfbfbfcfbfdfcfcfdfdfcfdfefdfbfdfdfdfdfefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcbcbcbcacacac8c8c8
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8c8c8c8c8c8c8c9c9c9dadada
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfefcfdfefd
+fefefdfefdfefefdfefefdfefefdfdfefdfafcfcf8fafcf8f6fcf9f0f9f9e0eff2cce2e7bad6dd
+abcbd5a6c6d2a6c6d2a7c5d1a8c6d0adc8d0b7d0d5c5d9ded2e1e6e7f1f4f5fcfcfbfdfdfdfdfe
+fdfdfefefdfefefdfefefdfdfefefbfefefbfefdfcfefdfefefdfefefdfefefdfefefefefefefe
+fefefefefefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefefefefefefefefefefe
+fefefefefefefefefefefefefefefefdfefefdfefefdfefefdfefefdfdfdfdfefefdfefefdfefe
+fdfdfefefdfefefdfefefdfefefdfefefefefefefefefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe
+fefdfefefdfefefdfefefdfefefdfefefdfefefdfefefdfefbfcfce7eff2d3e2e7a5c2cba1c6d0
+9fc6d09fc6cf9ec5cf9cc3d19cc2d2a1c1d1a4c2d2a6c3d3a7c4d4a7c4d4a6c3d3a5c3d2a4c3d2
+a1c5d29ec5d19ec5d19fc5cfa0c4cfa0c4cea3c4cda3c2cabacfd4e0eaecfafbfcfdfafbfdfcfa
+fbfdfafafefbfcfcfdfdfafdfdf9fafbfafbf3f8fadae8eeb5ccd5a8c5d0a2c4d09fc0cc9fbfc8
+a4c0c4bfd3d6f3fafbfbfbfdfdfcfdfdfcfdfdfefdf9fefdfcfefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcbcbcbcacacac8c8c8
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b414141c8c8c8c9c9c9c9c9c9c9c9c9dadada
+fffffffefefeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fefefefffffffffffffefefefffffffefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfefefdfefe
+fefdfefefdfefefcfefefcfefefcfdfdfcfbf8fbfaeff9f9deeef0cae0e6a9c9d486b0c06a9baf
+5289a14f879f50879e50859c4f8398518294618e9d769dab8aabb9adc8d3d0e3e9eef5f8f8fafd
+fcfbfefdfbfefefcfdfefcfcfefefcfdfefcfdfefcfefefdfefefdfefefdfefefdfefefdfefefd
+fefefdfefefdfdfefdfdfefdfdfefdfdfefdfdfefdfdfefdfdfefdfdfefdfefefefefefefefefe
+fefefefefefefefefefefefefefefefdfefdfdfefdfdfefdfdfefdfdfdfdfdfefdfdfefdfdfefd
+fdfefdfefefefefefefefefefefefefefefefefefefefefefefefefefefdfefefdfefefdfefefd
+fefefdfefefdfefefdfefefdfefefdfefefdfefefdfefdfdfefdfdfefefdfefefdfefefdfefefd
+fefefdfefefefefefefdfdfdfdfdfdfefefefefefefefefef8fdfdd3e7e9aecfd45589974b899d
+43879a4084963f82963d80993d7f9a41819a45839b47849d48859f49869f47859d46849c44829b
+42829c3d7e993f819c40819b42809b43809a49809845788c6f929ec0d0d5f8fbfcfdfafbfdfcfb
+fcfdfbfbfefcfcfcfdfdfafdfdf9f9f9fbfceaf6f9bbd3de7197ab57869b50829a47798f457485
+4d767e8ba9adedf7fafafafdfdfbfdfdfcfdfdfefdf8fefdfbfefdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcbcbcbcacacac8c8c8
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9c9c9c9c9c9c9c8c8c8d9d9d9
+fffffffefefefffffffefefefffffffefefefffffffefefefefefefefefefffffffefefeffffff
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfefefdfefe
+fefdfefefcfdfefafdfdfafdfdfcfdfafbfdedf7fad1e9f0a3c2cf7199ab4f829a447f9c4281a2
+3c80a33d83a43e82a33e81a13e81a03e809c3c79933c758e3d728b44728a7093a7c8d6ddeaf2f5
+f7f9fcfbfafcfefcfcfefcfcfdfdfcfdfefdfdfefcfdfefcfdfefcfdfefcfdfefcfdfdfcfdfdfc
+fefefcfefdfcfefdfcfefdfdfefdfcfefdfcfefdfcfefdfcfefdfcfefdfdfefdfefefdfefefefe
+fefefefefefefefefefefefefefefefefdfdfefdfcfefdfcfefdfcfdfdfcfefdfcfefdfcfefdfc
+fdfdfcfdfefefdfefefdfefefdfefefdfefefefefefefefefefefefdfefdfdfefcfdfdfcfdfdfc
+fdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfdfcfdfefcfdfefc
+fdfefdfdfefefdfefefdfdfdfdfdfdfdfefefefefefefefef7fdfed2e9eeaed3dd528da13e89a2
+35849e317f99307a962e7896307696337896357997377b99387c9a3a7d9a3a7e9c387b99367897
+3477972a709232789e327aa1347aa2367aa13d7c9e2f688552798bb1c3caf7fbfdfcfbfdfdfbfc
+fdfdfcfcfefdfcfcfefdfbfdfdfbfcf6fbfddef0f7a7cada5e90ac4b86a4427f9c356e872d6276
+36606a99b6bbeaf6f9f8f9fcfcfbfdfdfcfdfdfefdf8fefdfbfefdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcbcbcbcacacac9c9c9
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b414141c8c8c8c9c9c9c9c9c9c9c9c9d9d9d9
+fefefefffffffefefefffffffefefefffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfefefdfefe
+fefdfefefcfdfefafdfdfafdf8fafcedf6f9c5dbe581a8be5d8ca75185a4457e9f3d799b377397
+316a8e2e688830698a336d8e3571913575953a7a983b799a3b789a3b75963f6e8a607b8ac5d6dc
+eff6f9f9f9fafefcfbfefcfbfdfdfdfbfefefbfefefbfefefbfefefbfefefcfefefdfdfdfdfdfd
+fefefefefdfefefdfefefcfdfefcfdfefdfefefcfdfefcfdfefdfefefdfefefdfefefdfefefefe
+fefefefefefefefefefefefefefefefefdfefefcfdfefcfdfefcfdfdfbfdfefcfdfefcfdfefdfe
+fdfdfefdfefefcfefefcfefefdfefefdfefefefefefefefefefefefdfefefdfefefcfdfefcfdfe
+fbfcfcfbfcfcfcfcfdfcfcfdfcfcfdfdfdfdfdfdfdfcfdfdfcfdfdfcfdfdfcfdfdfdfefefdfefe
+fdfefefdfefefdfefefcfefdfcfefdfdfefefefefefefefefafcfed1dbe7a9bbd06186a6487ca0
+4077993f71913964852e5576335577345675355774355673355573375775355774345673345672
+36586f325768527f95497b9744779543759448748e34596d46616ea6b7bdf4fbfefbfbfefdfbfd
+fdfdfdfdfefdfdfdfefdfcfdfafbfdf0fafdbfd8e380aac2548cae4482a43b75902354672a5361
+5b787edff1f4f1f7faf8f9fbfcfbfdfdfcfdfdfefdfafefdfcfefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcbcbcbcacacac8c8c8
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8cacacacacacacacacadbdbdb
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfefefefefe
+fefdfdfefcfcfefbfdfbfcfdf4fbfcd5e5eaa2bfcd6b98b15687a54c7f9f4478983c6f8e346180
+345d773a5f773b60793c637e3d6a853e708c4279964078973f789840789940728f4e70809bb1b9
+d9e4e7f9fbfafefcfafefcf9fcfdfcfafdfdf9fefef9fefef9fefef9fefefafdfefafdfdfbfdfd
+fcfefefbfdfefbfdfefbfdfdfbfdfdfbfdfefbfdfdfbfdfdfbfdfefbfdfefdfdfefdfdfefefefe
+fefefefefefefefefefefefefefefefcfdfefcfdfdfbfdfdfbfdfdfbfdfdfbfdfdfbfdfdfbfdfe
+fbfdfefbfefefbfefefbfefefcfefefdfefefefefefdfdfdfdfdfdfcfdfefafdfefafdfefafcfe
+fafdfdfafdfdfafdfdfafdfdfafcfdfbfdfdfbfdfdfbfdfdfbfdfdfafdfdfbfdfdfbfefefbfefe
+fbfdfdfbfdfdfcfdfdfcfdfdfcfefdfdfefefdfefefdfefefafcfecad3e09cacc3688cad4e7fa2
+467797416a873f5e78465d76596e865970855972845a73855b74865b74875b76875a74865a7384
+5e7581627b827ea0b05b859c4b79944977944f78913c5d7049636fa6b8bdf3fbfef9fafefdfbfd
+fdfdfdfdfefdfdfdfefcfcfdf7fafde2eff4aac7d5729eb9578eb14680a23c6c832a4e5b44606a
+95a4aaecf5f8f4f8faf9fafbfcfcfcfdfdfdfdfefdfbfdfdfdfdfefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdcdcdccccccccacacac8c8c8
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacaccccccccccccccccccdcdcdc
+fffffffffffffffffffffffffffffffefefefffffffffffffffffffffffffefefeffffffffffff
+fffffffefefefffffffefefefffffffefefefffffffffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefdfefefd
+fefdfcfefcfbfcfcfcf6fcfde4f2f5b4cbd47ea2b45c8da84f819f497997416c883a5e77415b70
+6c7d8c85909d8591a08394a47e95a66a889c5179914c7994497997477a9a4679963e68786e8b94
+c1d0d3f7fbfafefdf9fefcf9f9fafaeff5f7e8f1f4e5f1f4e6f1f3e7f0f3e7f0f3edf4f7f7fcfd
+f7fdfdecf4f6e8f2f5e6f1f4e6f0f4e6f2f5e6f0f3e9f3f5eff8f9f3fbfcfafdfefcfdfcfdfdfc
+fdfdfcfdfdfcfcfdfdfbfdfdf8fcfcf2f8faecf5f8e8f3f6e5f0f3e6f1f3e6f0f3e6f0f3e6f1f4
+e7f3f5ecf5f6f0f7f8f3f9faf7fcfcfafcfdfcfdfdfcfcfcfcfbfbfafcfcf9fdfeecf1f4e4eaef
+e1e9ede1e9ede1e9eee1e9eee9f0f5f3f8fbf4fafceef4f7e5ecf0e2e9ede2eaeee2eaeee2eaee
+e3eaeeecf0f2f6f9fafbfcfdfcfdfdfdfefdfdfefdfcfefdf4f9fbbccbd88aa0b86993b24f84a2
+48799340677a4d66757f8c98a9b3bca8b5bca9b6bcaab8bdabb9beabb9beabbabfabb9beabb7bd
+abb8bbafbfc0a5c2ce6b93a852809b4f7e9b527e993f6379496674a5b9bff2fbfef9fafefdfbfd
+fefdfdfdfdfdfcfdfdfafcfdf1f9fcc7dae291b1c46794b1578cae4b7e9d395c6d455960747f84
+d6d7dcf9f8fcf9f9fbfbfcfbfcfdfcfcfefcfdfefefdfcfefdfdfefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdededececececbcbcbcacaca
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cdcdcdcdcdcdcdcdcdcdcdcddddddd
+fefefefefefefffffffffffffffffffefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffdfdfefb
+fefdfbfefdfbfafcfceffafbc7dde593b3c26b95aa5b8da54e7e974a73893f5f72475d6c77828c
+c7c8cdeae8eaeceaede9eaefdce2e8b2c0c9708b9c5e8197567e974e7d9a4e809c3d697a587881
+b1c2c4f3f8f6fefdf9fdfdfbf4f7f9d8e4e9bfd3dbb6cfd8b6cdd7b8cbd4b9c9d2c9d7dee6f1f5
+e8f3f5c5d6ddbad0dab6cedab4cddab6d0dbb3cbd7bacfd8c9dbe1d5e5e9edf5f7fafbfafcfbf7
+fdfcf8fafcfaf7fbfbedf6f8ddeaedcddfe5c0d7e0b9d1dcb5ccd6b8ced7b7cdd7b7ced7b7cfd8
+b8d2dac0d4dcc8d8ddcfdde2dce7eaebf1f3f9fbfcfbfbfafaf9f8fafafaf9fcfdd8e1e7b9c8d1
+afc1cdacc1ceabbfcfaabdccbbc9d6dae6eedfecf2cad8e1b4c5d0aec0cdaec1ceaec2ceadc1ce
+afc1ccbecbd1dae2e6f2f4f7fbfbfdfdfdfdfdfdfdfafdfde7f3f6acc6d37b9fb46597af50889f
+487b8d3f6672627b80c4d1d2f7fdfcf9fdfdf9fefdf9fefdfafefdfafefdf9fdfdfafdfdf9fdfc
+f6fcfceffdfeb3d2dc729db1578aa55387a552839f3f69814a697aa6bac1f2fbfdf9fbfdfdfbfb
+fdfcfafdfdfbfbfdfcf5fbfce2f1f6a9c0cd7fa1b66792ad5686a34e7790354c59778084acafb1
+faf6f9fefafefcfbfcfdfdfcfcfefcfcfefcfefefefefcfefefdfefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e0e0d0d0d0cecececdcdcd
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d1d1d1cecececccccccdcdcddddddd
+fefefefefefefefefefffffffefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfefefcfefc
+fdfefbfdfdfbf8fcfce2f2f5a6c3d17ca4ba6595ae588aa0568091436471475c64808c90e3e7e9
+f8f8f9fbfbfbfcfcfbfcfdfbfbfcfcf4fafcb1c1cd7692a561849b5783a05587a247718154707a
+a6b4b8ecf0effdfdfafbfdfcf2fafdbdd1d986a7b76e97ac6d92a66d8da06b8698889fabc0d4da
+c1d5db819dac7497ad7096ae6d96b06d95b0698ea868889c7792a28ca2afccd9def7f9f8fcf9f2
+fcfaf4f6faf7eef9f9c7e0e698b7bf7ca0af6e96ab6a92a96d92a77293a77395a87397a97098ab
+6c97ab6e93a57392a17c97a493a8b1c2ced4f5f9fbf9f9f8fbf9f7fbfaf9fbfafcd5e2ea8da8b7
+779cb1729bb46e95b16b8da9637d98a7bbcfb5cada8ca6b77897ac7397b07198b27198b27097b0
+6b91a66b86968e9faad9dde3f8f7fbfefafdfefcfef6fdfed9ecf1a2c0cf789eb46a93aa61889e
+5474874a626f79868eeaefeffefefcfdfefdfdfefefdfefefefefefefefefdfdfdfdfdfcfdfdfc
+fcfcfcf9fdfebed4db7d9fac638d9e5f8b9f5d86994a6b7e546b79adbbc1f6fcfdfbfcfcfdfcf9
+fdfaf6fbfbf7f8fcfbebf6f9c1d9e394b3c5769ab06990a65e85994364733c4d58b6bcc0e3e5e6
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe1e1e1d2d2d2d2d2d2d1d1d1
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d1d1d1d0d0d0cecececfcfcfdedede
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfefefdfefd
+fdfefbfcfdfbf5fcfdc3d5dc98b4c3779db2658fa75d849b486879415864829096d6dcdff8fafa
+f8f9fafafafafbfcfbfcfdfbfbfdfdf7fdffd0dee587a2b26d8ea3618ba55d8aa4517787536d76
+9fabaee6e9e8fdfdfafafdfdf4fdfec2d7e08dafc0729ab06c91a666869a5f7b8d7c94a4b3c9d4
+9db4bf87a4b57597ad6e93ac6a92ac6a93b0688ca768889e627e8f647c8a85949bf0f4f4fbfaf7
+fafbf8e9f0f3d4e3eb8faebb7ea2b0739bae6b93ab668ba56081985e7c915f7e93618297658a9f
+6c95a96e95a97192a46e8d9d69828f6a7a84ecf3f5f6f8f8f9f8f7f9f8f9f8fafcd5e3ea93afbd
+7a9fb46e97af668da664859f637b9589a0b58faabb7897a97094a96e95ac6a94ac6a94ac6995aa
+6790a560808e5b6e79b0b6bcdddde1fefafdfefcfef6fcfeccdde59dbacb80a5bc6c93aa6689a0
+587588566b778d989dfafdfcfefefcfefefdfdfefefdfefefefefefefefefefefdfefefcfdfdfb
+fcfcfafafdfec3d6dd85a5b26c94a56892a4688ca05472825b707cadb9c0f2f8faf9fbfafdfbf9
+fefcf9fbfdfbf2f9fbd9e7ee99b4c585a6ba7599af678ba0557588354e5b839096dfe2e4f9f9f9
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdededed1d1d1d2d2d2d1d1d1
+d0d0d0060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cdcdcdcecececfcfcfd1d1d1dfdfdf
+fefefefefefefffffffffffffefefefefefefffffffffffffefefefefefefefefefefefeffffff
+fffffffefefefefefefffffffffffffefefefefefefffffffefefefefefefefefeffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfdfdfdfd
+fcfefdfafdfdecf5f7acbfc790acbc799cb16a8ca4627e93485c6d667580b3babff3f5f6f7f7f7
+f7f8fafafcfcfbfdfcfdfdfbfafcfcf8fdffdbe8ef97afbd7c9bad6b94aa6490a7557888576f78
+a2aeb1e6eae8fafaf7fafdfeeff9fcbed2dc8dadbe779cb16c8ca16380945e788978909fa4bac8
+849aa76a859365839766859b688aa36a8ea9668aa36b8ca3617f915c77866a7a83dbdfe1f3f4f5
+f2f6f8d1dee6aabfce88a8bd7295aa6a8ca15e7e934e6a7f4a61745b6e7f5e7283617889647f91
+6a8a9b6c8ea27091a36c8b9c627d8b596f7ac0cbcfeff3f5fafafbf9fafcf8fbfdc6d5db97b1be
+7c9fb26f93aa6a8ba26c879d63798d687f8f677f8e5d788a5a798b67899f6c90a56e93a76e94a8
+6a91a4638391556973949ca2c1c3c8fefafdfefbfef3f8fdc3d1dc9ab5c886aac37195ad6c8ca2
+5e7a8c6b808ca4b0b6f5fafafcfefcfbfefdfafefefbfefefcfefefefefefdfdfcfdfdfcfdfdfb
+fcfcfaf9fcfdc9dadf8baab57398a87096a76f91a25b77855e717aabb6baeff4f4f9fbf8fcfbf7
+fdfcfaf8fcfce3eef4b9cddb8aa8bc7f9fb57595ab6483974d69784d606bd2d8dcf2f2f2fbfaf9
+fefefcfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcbcbcbcccccccdcdcd
+cecece060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacacbcbcbcdcdcdd0d0d0dfdfdf
+fefefefffffffefefefffffffefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfbfcfcfcfc
+fafdfdf7fbfce1ebf0a6bac38da9b87a9baf6c8ca161788b485867818c96cbd0d4fafbfbf9f9f9
+f8f9fbf9fcfcfbfdfcfcfdfcfbfdfdf8fdffdde9ee9bb1bf819eaf7097ab6992a85878885b717a
+a6b2b4e9edebfafaf7fafdfeebf5f9baced98dabbc7a9eb2708fa366819461798a7388978ea2b0
+758994667d8a657e8f65819468879e6d8fa86a8da56f8fa56582955e788963737dcfd4d7f0f2f4
+e9eff4c4d3df9eb5c883a3ba7193a9658397566f815266757382908d99a58e9ca78e9ea9899ca9
+7d95a26f8fa17191a36d8d9d637f8e566c78aebbc0ecf2f3fafbfcf9fbfcf7fcfdbecdd397b0bd
+7d9eb07193a86d8ba0688194596c7d657785718491738897718999708c9f728fa37392a57495a8
+7092a563828f54677098a0a5c6c8ccfdfbfdfcfafef2f6fcbeccd898b1c588aac27396ad708fa4
+657f91768b97abb8beedf3f5f1f6f7f1f6f8f1f7f8f3f7f8f3f6f7f4f6f6f5f5f5f5f5f4f7f7f5
+fbfbfaf9fcfdcddde291adb8789baa7498a87493a45f798661727aacb5b8eff3f2fafbf8fcfbf7
+fbfcfaf3f9fbd7e4eea8bfd188a7bb7f9eb37290a55e7a8c4e6573717f8ae7ebeef8f7f6fcfbf9
+fefefcfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdadadac8c8c8c8c8c8cacaca
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c7c7c7cacacacececededede
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfbfafbfcfb
+f8fbfcf3f9fbcdd9e0a1b6c18aa5b47d9bac708a9c5e6f7f4e5964abb1b8eaecedfefdfcfdfdfc
+fcfdfefafdfdfafdfcfcfefcfcfefef8fdffdbe6ea9fb2be87a0af7899ac7195a95c778761737b
+aeb7baeff2f1fbfcf9f8fdfde1eef2b4c8d38da9ba7c9eb07591a4688092677b8a74859183929d
+7d8c9481909a81939e7e929f7690a17391a67392a77593a86d889b677e8f616f7ac2c8ccecf1f3
+d8e2eab1c3d396aec57c9bb47290a55f7686586671767e85c6cbd1e3e5e8e2e5e7dde2e4cdd7da
+abb9bf7a96a37796a67391a1688493566c7b9baaafe9f0f1fafbfcf7fbfbf3fbfcb2c2c996aebb
+7f9dad7594a66e899a5d717f5967737e8994a1aab4b0b9c2afbac595a5b38093a47f94a67f97a9
+7a95a6627e8a53656da9b1b5d9dcddfdfbfdfafafeedf3f8b9c6d296adc089a7be7997ac7792a6
+728a9b8296a3a9b8bfd2dce1d2dce1d3dde3d5dfe4d7dfe3d7dee1d8dddfd8dadcd8d9dbe1e2e2
+fafafafbfefed5e2e89ab2bd809eac7a9baa7a97a6657c8865757baeb6b7f0f3f1fcfcf8fcfbf7
+f8fafae6f0f5c1d3e195afc488a7ba7e9cae6b8697576d7c5b6a75b4bbc3f7f7f9fcfbf9fdfcf9
+fefefcfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffd9d9d9c5c5c5c4c4c4c6c6c6
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b404040c4c4c4c4c4c4c6c6c6cacacadbdbdb
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefafcfdfc
+f8fdfdf2fafbb5c5cf9eb3bf8ba5b27f9aa97087955c6a77676f78c7cacef9f9f9fefdfafdfdfb
+fdfefdfbfefefbfefefbfefefcfefefbfeffd8e2e7a2b4be8da4b1819faf7998ab5f7484627179
+b5bcbff5f9f8fbfefbf7fdfcd7e3e9afc4d08eaaba7a98ab6d87985b6f7e68778298a2aad1d9de
+e6edeee7eeefe5edf0d5dfe4abbbc6788fa07994a87c94a8758c9e6f849465757ec0c8c9e6eef1
+c7d3dda3b7c797b1c67e9ab16e87975c6c76838a8fd3d5d8f7f6f7fbf8f8fbfaf8f9f9f7f4f7f5
+eaf2f391a9b287a4b07e9ba86d88955b728098a5aae8eef0fbfbfbf5f9f9edf7f8a7b9bf96aeba
+859faf7791a1677b894e5d65969fa4d9dce0f8f7fbf9f9fcf7fafce2e8ee9ca8b291a0ad899dac
+7e95a660768355646dbcc3c7eceff1fbfbfdf9fbfee6ebf2b6c2cf96aabd8ba4b9839bae8197a9
+8095a58194a2889aa695a6b097a7b099a9b49baab79caab69da9b2a4abb3969ca2979da1b3b8bb
+f7f8f8fbfdfed8e5e9a0b5c087a2b0829ead8099a66c7f896a787cb0b7b8f1f3f1fcfcfaf9f8f6
+f5f8f8d3dfe7aac0d190aec482a0b27a94a36479865b6b7580898feef1f5fcfbfcfbfaf8fbfbf8
+fefefdfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffd9d9d9c5c5c5c2c2c2c4c4c4
+c4c4c4060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c6c6c6c5c5c5c6c6c6c9c9c9dadada
+fffffffefefefffffffefefeffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fefefefefefefefefefefefefffffffefefefffffffffffffffffffffffffefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefcf8fbfcfb
+f3fafbe9f4f8aabdc99cb4c18fa7b4819aa66d838d61707898a1a5dadfe0fcfdfafffefafefefa
+fdfefdfbfefefbfefefbfefefbfefefbfeffd4dbe0a9b9c196abb5839fab7b97a7607282707d85
+c1c7cafafdfcfbfefcf3fcfcc9d7dfa9bdcb90abbb7c97a86c83925a6a75969fa5dee1e4f9f8f7
+f9f7f3f8f4f0f9f8f8f7f9fae6eef3aab9c58ca1b1879aab7c8e9e73859265757bbfc8c9d8e2e5
+bdccd59cb1c18ea7ba839baa6e7f89768388acb5b7edf2f4f5f7f8f9f8f8f9f9f9f7f9f9f4fbfa
+eef8f9a7bbc295aeb888a1ad7189965c707d9daab2ebeff0fbfaf9eff3f2e2ededa7b9bf98aeb9
+8ca4b17b909d65767f545f63c2c8c8f0f1edfcfbf5fbfbf6f6f8f3ebf2f1b2c0c4a3b6bd92aab5
+7d98a6617381707b85d0d6d9f8fbfcfafbfcf9fdfed5dfe6b2c1cd9eaebe96a7b98ea0b0889baa
+889baa859aa8839aa8859dac88a0ae8aa0b08ca1b28ea1b18fa0ad8e9ba66772797d888eb5bdc1
+fafdfefbfdfedde7eaa9bac590a6b589a1b1889faa788a92748084b3b8baeff0f0fcfcfafbfcfa
+e5eceebeced79ab5c689aac07f9baa6f848f596871767e86d1d4d8f9f8fbfbf9fbfcfbfbfcfcfb
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffddddddc9c9c9c6c6c6c6c6c6
+c6c6c6060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c5c5c5c6c6c6c7c7c7cacacadbdbdb
+fefefefffffffefefefffffffefefefefefefffffffffffffefefefefefefefefefefefeffffff
+fefefefffffffefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfbf7fafcfa
+ebf4f5dce9eeafc2cea1b6c496abb98397a26b7d8569767bc3cbcee8ececfcfcf9fefdf9fefefa
+fdfefdfbfefefafdfdfafdfcfcfdfef8fcfecad2d8adbbc49dafba89a1ae7f97a5657481848d95
+ccd1d3f9fdfbf8fcfaf1fafbc6d4dcaabecb95adbd7d96a6687a89606d78aab1b6f1f3f4faf9f7
+fcf8f4fdf8f2fbfaf6f9fbf9edf4f6becdd599acb991a2af8494a17c8a966e7a80cdd6d7cfd9dd
+b9c7d0a2b4c297abba899faa7d8c94929fa4bcc8cce2ebf0e6edefebeff3ecf0f3ebf1f4e8f2f4
+e3f1f3b5c9d1a0b7bf90a6ae7487915c6c75bbc3c7eff2f2fbfbfaecf0f1d9e4e7afc0c69cb1bb
+92a7b27d909a69787e6d7878d5dad8f7f8f3fcfcf5fbfcf5f8fbf4e3ece9b5c4c7a7bac096aeb7
+8299a662717b889299d8dde0f8fbfcfafbfcf9fdfdccd5ddb5c2cda6b3c19eabba94a3b08b9ca6
+7d8f9872848e6b7f8b697f8c677d8c687d8b697b89697a87697883636e795c6770848e95c6cdd2
+fafcfcfbfcfae0e9ebaebdc697abb893a8b591a5ad8090957b8688b1b5b6e8e9eafafbfaf6f9fa
+d5dee2b6c7d09db5c48aa6b78398a464737c69737aa3a9aef3f5f7fcfbfcfcfafbfcfcfbfdfdfc
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfcdcdcdc9c9c9c6c6c6
+c6c6c6060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b414141c8c8c8c7c7c7c7c7c7cacacadbdbdb
+fefefefefefefefefefefefefefefefefefefffffffefefefffffffefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcf7f9fcfb
+e6f0f3d5e3e9b4c6d3a5b8c69faebb8996a0737e857d878be0e6e8f2f5f4fcfdfafefdfafefefb
+fdfefefbfefdfbfdfcfcfdfcfbfcfdf4f9fcc7d0d7b2bfc9a5b4bf91a3b18497a368757d969ea2
+d7dbdbfbfdfcf9fdfcedf6f8c5d4daadc0cb9bafbf8396a6667582717d85bac1c5f7fafafcfbfa
+fdfaf9fdfaf6fafbf8f7fcf9ebf5f7c1d2d8a4b7c299a9b58b98a4828d98737b82dce3e4cbd4d9
+bac5cea9b6c29dacb89aaab5919ea898a5adadb8c0c0cad2c2cbd3c9d0d8cad2d9cbd5dbccd6dd
+cad6ddb7cad4a1b5bd8ea0a8717e8460686ddadddef5f6f6fbfbfce9edf0d5dde3b7c5cda5b5bd
+99aab2808f96707d80868f8fe0e5e3fbfbf8fdfdf9fcfdfaf9fbf9dee5e6bec9ceb1bec69eadb8
+8694a0646f75a9afb3e4e7e9fafbfcfcfcfdf9fbfdcdd2dabbc3ceacb6c2a2adba9aa7ae7f8e90
+6776766d7d7d7d8d917e8e96818e99838e97848e96858f97868f97858d94899196a9b0b5dae0e3
+fbfdfbfbfcf8e4ecedb6c4cba2b3bd9db0ba99abb189979a838c8db2b6b7e5e7e9f8fbfceaeff3
+ccd5dbb3bfc99fafbc8d9fac75838c5f6a708d9599d5d9dcfbfdfdfdfdfdfdfcfcfdfdfbfdfdfc
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe1e1e1d1d1d1cdcdcdc8c8c8
+c7c7c7060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacac8c8c8c8c8c8cacacadbdbdb
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcf8f9fcfb
+e5eef2d3e0e7b8c9d6a9b9c6a4b0bb8d969e788085899194edf2f4f7f9f8fdfdfafefefafefefb
+fefefdfcfefdfcfdfcfcfdfcfafcfdf1f6f9c7d0d8b5c1cba9b7c295a5b28594a06a747aa5aaae
+dfe1e2fbfdfdfbfdfdeaf2f5c6d4dab0c1cc9eb0be8696a565727d7e8a90c4cccef9fdfcfcfcfc
+fefbfcfcfaf9f8fcf9f5fcfae9f5f6c1d4d8abbdc69dadb88e9ba5858f99787d85e4e8ebcbd3d8
+bcc5cdaeb8c2a2adb7a2afb997a4ae96a2ab9faab2aab4bdacb5beb1bac2b2bcc4b4bfc7b7c1ca
+b6c1caadbec998aab387969d6e767b666b6de9eaebf8f9f9fafbfce7ebefd2d9e0bcc7d0abb8bf
+9caab0818f9376818399a0a0e6ebe8fcfdfafdfdfbfdfefcf9fbfbdce1e3c4ccd2b8c1c9a2adb8
+87919c686f74bdc1c4eceeeffbfcfdfcfcfdf7f9fbcfd2dabec4cfafb8c3a5afbb9aa6aa778483
+6b7874828f8b9ca9a9a0abafa4acb3a6adb2a8aeb2a9afb4aab0b5acb1b6b1b6b9c7cccfe7ecee
+fbfdfbfcfcf7e7eeefbdc9cea9b9c1a5b6be9fafb4909c9f889192b3b8b8e4e7e8f7fbfde2e8ed
+c8d1d8b2bcc69facb78d9aa56a757c676f75a7adb1ecf0f2fbfdfdfefefefefefcfefefbfefefb
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe2e2e2d3d3d3cfcfcfcacaca
+c8c8c8060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b444444cfcfcfcbcbcbc8c8c8c8c8c8d9d9d9
+fffffffefefefffffffffffffffffffffffffffffffefefefffffffefefefffffffefefeffffff
+fffffffffffffffffffffffffffffffefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfefbfafdfd
+e5ebf0d2dce3c1cdd7aebac4aab3ba90969d7c8186969c9df9fcfcfbfcfbfdfdfafefefafefefc
+fefefcfcfefcfcfdfdfdfdfefafbfcecf0f3cad2d8b7c3c9adbac19baab37d8992696e76c1c3c7
+efeff1fbfafbfafafbe5eaeec9d3dcb3c2cda0afbb8896a066717996a0a5d5dbddfbfdfdfdfcfd
+fefcfdfbfafaf8fbfbf5fbfbe8f3f5c4d4d8b3c4caa3b0b9909ca4869199858a90e9ecefd0d3d9
+c0c5ccb4bbc2acb2baa3acb68f9aa589949d8c959d9099a1919aa28d979e9099a1939ca4959fa6
+949ca5818d9775828a6c767d656a6f7b7d7ff5f6f6fafbfaf9fbfbe4e8ecced4dac1c9d1b1bbc2
+9da7ae828b8f838a8cbfc3c4f0f2f1fcfdfcfcfdfcfcfdfcf8fbfad7dcdccad1d4bec5cca6adb7
+848c95727679d5d6d9f4f5f6fcfcfdfcfcfdf2f2f6d1d2dac3c7d0b6bdc6adb5be91989c6e7777
+aab2afdee5e2f2f9f9f5fbfcf8fcfdfafdfdfbfdfdf9fcfdf8fbfcf8fbfcf6f9f9f7fafafafcfc
+fbfcfbfdfdfbebf1f2c7d1d5b5c2c9b1bec6a9b3bb9ca5ab91989cb5babce0e4e6f2f7f9d8dfe3
+c6ced4b3bcc49ea8b1838f985b646a8f9499ced2d5f6f9fbfbfcfdfefefefefefcfefefbfefefb
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e3d6d6d6d4d4d4cfcfcf
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b464646d5d5d5d0d0d0cbcbcbc9c9c9d9d9d9
+fefefefffffffffffffffffffefefefffffffffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefffffffefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcfafcfc
+e4e7e9d1d4d7c7cbceb3b6baacaeaf9495958e8e8fb6b8b8fbfcfbfbfbf9fcfcfafefefcfefefc
+fefefcfdfdfcfdfcfdfdfbfef9f7fae3e3e5d1d5d5b9c0c1aeb8b9a2aeb17d868a79797dd9d8da
+fbf9fafbf9fafaf8fadfe2e5ced2d9bac1c8a2abb18790966c7379b8bcc0eaedeefcfdfdfefdfd
+fefdfdfdfcfdfcfdfdf5f9f9e5ebecccd6d8bac5c8a5b0b3909b9e8993979ba1a4e6e5e7ceccce
+bfbdbfb5b3b7aeaeb2898f966f767f757c8480868c83888e868c918991958a90958a9095899095
+878f94858c9284888e878b8f94989ababcbbf9fbf9f9fbf9f4f6f3e0e3e3ced2d4bcc0c4b3b6ba
+999ba0828487939496ddddddf5f5f5fafbfcfafcfcfafcfaf6f7f4d3d4d0c8cac8bdbfc1a4a7ac
+828589858688e7e7e8f8f6f7faf8f9fbfbfbebebedd1d3d7c0c2c8b7b9beb3b5b98a8c8e7d7f7d
+cacbc9f4f6f4f9fbfafbfdfdfdfdfdfdfdfdfefefdfefefdfdfdfcfdfdfcfbfbfafbfbfafdfcfb
+fdfdfbfdfdfdeff1f4d1d5dcc0c5cdbabfc7b0b3bba7a7ad98989bb8babadddfdee6e9e9d2d6d8
+bfc3c8b0b5bb959aa26f757b6e7377c3c7c9f0f3f4f8fafbfafbfcfefefefefefcfefefcfefefc
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e3d7d7d7d7d7d7d5d5d5
+d1d1d1070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b474747d8d8d8d5d5d5cdcdcdcacacadadada
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfdfdfbfbfa
+e3e2dfcecbc9c3bfbbb1aca7a6a19b928e8894918dc0c0bcfbfbf8fbfbf8fcfcfafdfdfbfefefc
+fefefcfdfdfcfdfcfbfcf9f9f6f1efd9d5d0cccac4bbbcb7b0b3af9ca4a1767c7c949397e2dfe3
+fbf8f9faf7f7faf7f5d2cfcfc8c6c6bababaa2a3a4868788797b7cc9cacbf4f4f5fdfdfdfefdfd
+fefdfdfdfdfcfbfbf8f0f0eddadad7d0d1cfb7bab9a2a8a7899090888f8fc8caccdedbdbc7c0be
+bbb2aeaea5a1a29a97807f82a6aaafccd1d4dee3e5e0e4e5e0e4e5e1e5e6e1e5e6e0e4e6e0e4e6
+e0e3e6e1e3e5e2e2e4e3e3e5e6e7e7eef0effcfdfafcfdf9f0f0ebdbd9d5cbc8c4b6b1aeadaaa6
+918d8b827e7da19e9df2f0f1faf8faf9fbfdf8fcfcf8f8f5edece5cdc9c0bdb9b2b4b0ab9d9a99
+7b797a9a9899f0efeffbfaf9fbfaf9faf9f7dfdddad3ceccbdb9b6b0aca9aaa7a37c7a76898784
+d7d6d4fcfcfafdfdfbfdfdfdfdfdfdfdfdfdfefefdfefefdfdfdfcfdfdfcfdfdfcfdfdfcfdfdfb
+fcfcfcfdfdfdf3f5f7d5d6dac4c3c8bebcc0b3aeb0aba6a49e9994b6b3acd1d0c9d7d6d0c4c3c1
+b8b7b8a3a3a784858a727479b1b3b6e0e2e3f8f9f9fbfbfbfcfcfcfefefefefefcfefefcfefefc
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e3d7d7d7d7d7d7d8d8d8
+d5d5d5070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b474747d8d8d8d6d6d6d0d0d0ccccccdbdbdb
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfefcfaf9f4
+dfdad2c7bfb7bcb0a3ab9f91a1968891887b989288c6c3befcfbf6fcfbf8fcfcfafdfdfcfefefd
+fefefcfefdf9fefbf7f8f2ede6ddd5cdc1b7beb5a9b5b0a5a9a79d8d908a6c6e6cbbbabdece9eb
+faf7f6f7f3f0f2eee8c6beb7c0b7aeb5ada59c9790837f7b8c8986d8d6d5faf9f9fefefefdfefe
+fdfefefdfef8f6f4ebe8e3dacdc6bec6beb6b3aea89796927f827f858987ebebe9cfc9c5beb3aa
+b4a498a5968a988a8076706cc6c7c7edf0f1f8fafbf9fbfbf9fafbf9fbfbf9fbfbf9fafbf8fafb
+f8f9fbfbfaf9f9f8f7f8f8f7f8f9f7fbfcfafdfefbfdfcf7e8e5ded2cac1c5bbafafa196a59a8f
+897f7788807bb3aeacfaf8f8fbfbfcf8fbfdf8faf9f4f3ede3ddd1c7bcadb4a99baa9f94948a83
+77706db0b0aef3f3f2fbfaf8faf9f6f7f6f0d2ccc3cac0b5b6ad9fa69e8f9d9688766f669c9793
+e0dddcfdfdfcfdfefdfcfefdfdfefdfdfefdfefefdfefefdfefefdfefefdfefefdfefdfdfdfcfd
+fcfcfdfcfdfdf4f4f3d2d1cfc0bcb7bab4adaea69caa9f92a49889b0a696bfb5a7c3baaeb2aca4
+aaa4a1908c8e7b777c8a888de5e4e6f3f2f2fbfaf9fcfcfafcfcfbfefefcfefefcfefefcfefefc
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e3d7d7d7d7d7d7d8d8d8
+d6d6d6070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b474747d7d7d7d6d6d6d2d2d2cdcdcddcdcdc
+fffffffefefefffffffffffffefefefefefefffffffffffffffffffefefefefefefefefeffffff
+fffffffefefefefefefefefefffffffefefefefefefffffffefefefefefefefefeffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfcf9faf8f0
+dcd5c8c2b7a7baaa93ac9b84a2947d9488739c9283c9c4bdfdfbf5fcfbf8fcfcfbfcfdfdfdfefd
+fdfdfbfdfcf6fcf8f0f2e8ddd9cabcc4b3a2b2a290ada1929e95887b786f71706cdcdcddf4f2f2
+faf7f4f4efe9ebe3dac3b5a8baa999ae9e8d968b7d847c74a29c97e3dfddfdfbfbfdfdfefdfefe
+fcfefefdfdf4ede8d9ddd5c6c4b7a9b9aa9da79a9188807a85837e989894f9f8f3c9c1b7bcad9f
+b39f8da48f7c94827171685ed5d5d1f8faf9fcfdfdfcfdfefcfdfefcfdfdfdfdfdfdfdfdfcfdfd
+fcfcfcfefdf8faf9f6faf9f6fafbf8fcfcfafefdfafbf9f4dfd8d0cabdafc3b19dae9a86a08e7b
+84756691877dc5c0bcfafafafafcfdf8fbfcf9faf7f2eee5d8cdbdc5b49fb2a08da492828c7c70
+796d65c8c9c6f5f6f3f9faf5f8f8f2f2f1e7c8bfb0c0b39eb2a38ba3957c988d75796f60b1aba3
+e8e5e3fdfdfcfbfefdf9fefdfafefdfcfefdfdfefefdfefefdfefefdfefefdfdfefcfdfefcfcfe
+fcfcfdfcfcfcf5f4f0cfcbc2bcb4a7b6ac9cab9f8aab9c82ab9980b09d85b4a28db5a592aa9d90
+988e877c73727f797cb7b4b8fbf8f9fbf9f8fcfaf8fcfbf9fdfdfbfefefcfefefcfefefcfefefc
+fefefdfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe2e2e2d6d6d6d7d7d7d7d7d7
+d6d6d6070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b464646d6d6d6d4d4d4d2d2d2cfcfcfdedede
+fffffffefefefefefefffffffefefefefefefffffffffffffefefefefefefffffffefefefefefe
+fefefefefefefefefefffffffefefefefefefffffffefefefefefefffffffefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafbf6faf8ee
+ddd4c4c2b5a0c1ad90b4a082ad9a7c9f8e73a29480c6bdb4fbf7f0fbf9f5fbfbf9fcfcfbfdfdfc
+fdfcfafdf9f2f9f1e5ecddcbd4c2acc0ab93ad9983a2917f8f82736e665b908e89eef0eff9f9f7
+fbf9f3f2ece1e4d8cacbb8a3baa48baa957c938370887e73bbb4adeae5e3fdfafbfdfcfdfcfdfc
+fcfefcfbf8ebe4dac5d7c9b3c2b19cb29e8b958377786c649c9691c0beb9faf7f0ccc2b3c2b09c
+bca38bad937b9d866f796c5ddbd8d0fbfbf7fdfdfbfefdfcfefdfcfdfdfcfefdfbfefdfafdfcfa
+fdfcf9fcf9f2f6f4eef7f6f0fafbf7fbfbf8fdfcf7f9f5eddacec4c8b5a2c6af94b59b819e886f
+82705d9d9285d9d5cef9faf9f8fcfcf8fcfbfafaf4f1ebddd1c2acc9b397b8a086a48e77877462
+807267e1e1def6f8f5f9faf4f6f6edede9dac5baa3bdab8eb4a080a694759a8b6d7f725fc9c1b8
+f0edeafdfcfcfafdfdf7fdfcf8fefcfbfdfdfdfdfdfdfdfdfcfdfdfcfcfcfafbfcfafbfcfafbfc
+fcfdfdfcfcfaf6f4edcec8b9bbb19bb7aa8fad9e7db19f7bb49e7bb69d7eb49c81b19b83a89685
+86786e736a66979092e6e2e6fdfafbfdfbfafdfbf9fcfbfafdfdfcfefefcfefefcfefefcfdfdfc
+fdfdfdfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe1e1e1d5d5d5d6d6d6d6d6d6
+d4d4d4070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d4d4d4d3d3d3d3d3d3d2d2d2e0e0e0
+fefefefefefefefefefffffffefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafbf6faf8ee
+dfd6c5c8b9a3ccb698c0a887bda481b19a78aa967dbbada0f6eee5f9f4edfcf8f5fdfaf8fdfbf9
+fefcf9fef5ecf5e7d6e8d3b8d9c2a4c1a98ab49d8396846f8071606b6056c4bfbbf4f4f3fafbf9
+fcfaf4f0e8dbddcebad7c0a3c3aa88ae977894826a8c8073d5cec7f0ebeafcf8f9fcfafafcfcf9
+fdfef7f8f1dce1d1b4d6c2a5c6b095b29b848572636d6157b6b0abe9e6e2fbf7edd2c4b3cbb49d
+c6a98dbb9d7fb194778d7a66dbd4c7f9f7effefcf6fefcf7fefcf7fdfbf6fefbf6fefaf5fefaf5
+fdf9f4f7f1e7e9e5dbecebe2f6f9f1f7fbf3fdfcf3f7efe3dcc8b9ceb49cceb394bca2839d856b
+826f5cada195f1ece6fbfbf9f7fafafafbf9fcfaf1f2e9d6d3bfa2d3b693c0a584a78f7186745e
+8a7f71f6f6f3f7faf7fafbf5f7f6ebe7e0cbcaba9bc0ab86bba681ab99769b8d6d80725de1d8cf
+faf4f2fdf9fbfafbfdf8fdfcf9fefcfbfdfcfdfdfdfdfcfcfcfbfafafaf7f8f9f7f7f8f7f8f9f9
+fcfdfdfcfcfaf8f4ecd1c8b4c1b294beac88b6a276baa57abea67dbea47fbaa080b59d81a28f79
+76685a837974beb9bafcfafdfefdfdfefdfcfefdfcfdfdfcfdfdfcfefefdfefefdfefefdfdfdfd
+fdfdfdfefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e0e0d4d4d4d5d5d5d4d4d4
+d4d4d4070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d4d4d4d4d4d4d4d4d4d3d3d3e1e1e1
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9fbf7fafaf1
+e8e1d1d7cbb6c7b397c4aa88c4a782c0a37db99e7eb5a18bcbbca9e2d7c7f1eadcf7f1e4f7f1e5
+f1ebdcebdecae8d6bcdec6a5cdb491c1a886ad967a7d6c577466568d837bece7e5f8f8f8fafbf9
+faf9f1ede5d3dac9aed5bc97c6ac85b69e7aa69174a89783e5daccefe5ddf4ebe5f4ede5f4ede1
+f3ecd9e9dabbddc7a3d3bb97c0a888ab967b7566528c8479cfcdc8f6f5f2fcf8efd1c3afd0b89e
+cbab8cc4a280c09f7ead947bc4b3a0d8cebce5ddcbeae2d1eae3d2ece4d3ece5d4ebe3d1e7decd
+e5dacad2c5b7cfc8bcdedcd2f8fbf4f8fdf7fcfbeef5e9d6e3cbb3d4b697ccae8abca282967f67
+877565baaea5faf6f1fcfbf9fbfcfbfafaf7f9f5e9f0e3ccd9c09ed6b58bbda07aa68d6c8d7d64
+9b9485fafaf7fafdfbfbfcf5f7f4e7e5d9c0d2bc97c2ab81bda87db2a079a89974928369e8ddcd
+f8f0e6f8f2ebf8f3ebf7f4eaf7f5eaf8f5ebf9f4ebf8f3eaf8f3e9f6f1e7e7e4ddebeae4f5f5f1
+fcfdfbfdfcfcfaf4ecd7ccb6cbba95c9b588bea675c0a579c2a57ec1a680bda482b9a285907d68
+77695ca8a19be6e4e4fcfbfdfdfdfdfdfdfdfefdfdfdfdfdfdfdfdfefefdfefefdfefefdfdfdfd
+fdfdfdfefefefefefefefefefefefefdfdfdfdfdfdfdfdfdfefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd2d2d2d4d4d4d4d4d4
+d4d4d4070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b454545d4d4d4d5d5d5d7d7d7d6d6d6e3e3e3
+fffffffffffffffffffffffffffffffffffffffffffffffffefefefefefeffffffffffffffffff
+fefefefefefefffffffffffffefefefefefefefefefffffffffffffffffffefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7faf8fafaf4
+f0ebdee3d9c9bba990bba284bea27ec1a47ebfa37eb9a080c1ac8ecdba9ed6c7acdccfb3dfd3b6
+dccfb2d9c8aad5c0a0cab190baa281a79070836f556e614f84796ccbc5bff9f5f5f7f6f7f7f7f6
+f5f1e8e7dbc7d5bfa1ceb28bc3a980bca47db89f7cbaa284d1bda4d6c4afd9c8b5dbc9b5dccab1
+deccacd8c29ccbb38dc3ab87b49d7c8c7a5f736653b9b3a9e7e6e1f9f9f6fdfaf2d2c6b3cab49a
+c5a685c2a07cc2a17bbaa07fbca88dc7b79ad3c4a4d8c9a9d7c7a7d9c9a9d7c8a8d5c5a4cfc09f
+c6b697ac9c8bcec5bbe2ded6f9fbf7fafdfafdfbeeeddfc7e1c7a7d2b48ec6a981b49a798c765e
+928171c7beb4fcfaf4fafbf8f9fcfafafbf6f6f0e4eadbc4d9bf9dceae85b1956f9a83628a7b63
+b1aa9afcfcf8fbfcfafbfaf2f6f0e2e1d1b6d8bd97c5a97ebea67ab7a378b2a076a69371d2c2a6
+dccdb5ded0b9dfd2bae0d5b9e1d4b8e1d4bae0d1b9dcceb6d9cbb5d0c4b2c2bcafd9d6caf6f5ee
+f9faf7fcfcfbf9f3ebd9cdb7cdbb96cbb688c0a775c0a277bfa27bbea27eb69d7caa9377796956
+8e8378c9c4c0f5f5f6f9fbfdfcfdfefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfbfbfbfbfbfb
+fafafaf9f9f9fafafafbfbfbfcfcfcfcfcfcfdfdfdfdfdfdfefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d2d2d2d4d4d4
+d5d5d5070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d3d3d3d5d5d5d8d8d8d8d8d8e4e4e4
+fffffffefefefffffffffffffefefefffffffffffffefefefefefefffffffefefefffffffefefe
+fefefefefefefffffffefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fbfbfafbf9
+f6f4ecefe9ddb7a995a89379a89171b19774b89c78b99e78bda37fbfa884c3ae89c7b38dc9b68f
+c6b28bbca883b29d7ca38d6f947f62806e52766953948b7db4ada4eae7e4efedefe9e9ebeae8e7
+e6ded4dac9b4ceb595c5a780bda27bbba27cbca27dbda17ec1a787c2aa8cc3ac8fc4ad8ec5ac89
+c7ac83bfa67cb29b76a893719480626c5e45958b7bd9d6cdeff0ecf4f5f2f9f7f0d5cababeab93
+b79d7db89a74b99b73b9a07ab8a380bda985c3b089c7b28bc5b089c6b18ac3ae87c0aa83b8a27b
+9d88638b7a68d7cec5ece6e2f7f6f6f8f8f9faf7eae1d3b8d6bf99c8ad84bb9f78a48a6c7f6b53
+9f9181d5cec4f7f7f1f2f6f3f2f5f4f5f5f0ede6daddceb8d2bb9bbea17c9f85638b7557887a64
+cac3b5f6f4f0f7f6f2f6f1e8efe5d6d9c4a9d4b690c0a178b99f75b59e74b29d72af9873bea885
+c5b08ec9b492cbb893cdba91ceb890cfb791ccb390c6ae8dbba587a3907aaba192cdc6baf3f1ea
+f2f3f0f5f5f4f4eee7d3c7b4c7b694c5b186bca274b99b73b79a76b49978a48c6f8b765d716455
+b0a9a0e2dfddf6f7f9f7fafcfbfcfdfbfbfcfafafafafafaf9f9f9f7f7f7f4f4f4f2f2f2f1f1f1
+efefefedededefefeff2f2f2f5f5f5f7f7f7fbfbfbfdfdfdfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d1d1d1d3d3d3
+d5d5d5070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b444444d1d1d1d4d4d4d8d8d8d7d7d7e3e3e3
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9fdfefafdfd
+fbfbf7f8f6eecac0b2a1927d958369988366a18869a88d6bab906dad9571b09873b09a73ae9870
+a59068927e5b8874567a685074644d786c569c9585d0ccc1dfddd6e0dfded6d5d9cdcdcfcdc9c6
+cbc0b4c9b49cc7aa89bc9b74b49974ae9774ab9472ad9372b09877ac9675aa9574aa9371a9906b
+a68a629a825c927d5d86745672634b6f6551c7c2b4e1e0d8e7e8e4e7e8e5eae8e3d5cbc0b0a18d
+a38f72a38b67a48a64a9916ba8936fab9573ae9675ae9675ad9574ad9674a99170a58b6b9a8060
+755d3e7c6c5ddcd3cdede6e5ede9ecedebedefe9dfcdc1a6c0ad85af9a71a28b6789735871604a
+aba091ddd7cee9eae5e2e6e4e0e4e3e6e4dfdcd4c8c8baa6bea98da1896a8670527a674d887b68
+d9d2c6e3e0dde6e4e0e4ddd3ddd1c2c9b298bfa07cad8f6ba78e6ba38d69a18b67a28b69aa9170
+af9774b39b76b69e76b8a075b89d73b89a72b69772ae906f9c81657d6954a99c91c8c0b8e9e6e3
+e8e8e7e7e9e7eae6e0c9beaeb9a88cb4a07da99069a48968a287699d836888715a6a58447e7369
+c8c4bfebeaeaf1f3f4f4f7f9f6f8f9f5f6f6f3f3f3f0f0f0ededede9e9e9e3e3e3dfdfdfdedede
+dcdcdcdcdcdce0e0e0e6e6e6ecececefefeff7f7f7fbfbfbfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d1d1d1d1d1d1
+d4d4d4070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b444444d1d1d1d3d3d3d5d5d5d5d5d5e2e2e2
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfdfefbfdfe
+fcfdfbfbfbf8f1ede5c0b7a99c907d897a64827059836d54887359877156867053846e517e6a4e
+7662466c5b426c5d48726656877d6fb9b3a5dcdad0dcdbd5d6d5d2c7c7c7b6b6b8a4a4a2a19c95
+ac9d8dbaa084c19f7bb08e67a98f6c96826589775e89775f84735b7e6f557c6d527a6c4f78684b
+7763467060456b5c456a5d48746a59aca69adcd9d2d4d3cdcfcfcbcdcdcacac7c5ccc5bda79d8d
+8c7f67817153816e4f85735386735787725b89715e897060866d5d866f5d8069587861506f5846
+634d3c8f847ad2cbc7dad5d5d5d2d4d5d2d3d6d0c7b1a790998b6a857453796649695843695b4c
+afa69bd4cfcacececbc5c8c7c1c6c3c9c8c2c3bab1ada09196867177664d6858406a5b47847a6b
+c6c2b7c2bfbcc3c1bec3bcb4bfb3a6b09b84967a5d8a7157867058836e57816d55806b55836c56
+856f568670558871528a72518c7251886c4e85684c7d6149705643685549b1a5a1c7bfbfdbd8db
+dadbdcd8dbd8e0ddd9c0b7aba4967f948467816d4e7e69507b6550745e4b675443574a3c948f88
+c2c1c0dcdddee7e9eaebedede9eaebe7e7e8e2e2e2dcdcdcd7d7d7d3d3d3cbcbcbc8c8c8c7c7c7
+c7c7c7cbcbcbd4d4d4dddddde5e5e5eaeaeaf4f4f4fafafafcfcfcfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d1d1d1d1d1d1
+d3d3d3070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d2d2d2d3d3d3d4d4d4d4d4d4e2e2e2
+fffffffffffffffffffffffffffffffffffffffffffefefefffffffefefefffffffefefeffffff
+fefefefefefefefefefefefefffffffefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfcfefdfcfe
+fafafcf7f8f8eaeae8e0ded9c7c3b8a6a092888072766d5f74685975665776675877695a7a6d61
+85796e978d86aca49dc7c4bcd6d5cddde0d9d4d7d2cdcecbc3c4c2afaead9796957e7c787f786b
+9a8872b29475b9936ea5805c93795c74624f685a4e75695f72685b716a5b716d5c7370607a7567
+847b708b8378999388a9a39ac3bfb9cecec9c0bfbdb5b4b0a6a3a09f9a989f9999a7a09fa49d96
+9990828b806f827765776c5b73665973655c74656075666275666276676375666272635f756662
+897a77bbb5b3c3c0bec1bfbdb9b8b6b5b4afbab6ac9a92848a7f6d8175637d716074695c766d64
+98928daaa7a4a4a4a39d9f9d9da09da9a7a4a8a19c978d84857b6c796e5f736a5b726a5d787169
+908a8591918e9595949d98949f948d9886778a7462817163786c6273675e71645a6e62576e6257
+6c60546c5f526e605171635376665977675979685a79695d786a5f867975cec6c6d3ced1d3d2d6
+d2d5d5d7dad6e2e0dcc4bfb79d9586867b68756a54665a4a6152455d4d425b4f465d554e8d8c8a
+adaeafc5c7c7d2d3d1d3d2cfd0cecccbcbcac6c6c6c2c2c2bfbfbfbdbdbdbebebec0c0c0c2c2c2
+c6c6c6d1d1d1dcdcdce3e3e3eaeaeaefefeff9f9f9fcfcfcfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d1d1d1d2d2d2
+d3d3d3070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d2d2d2d2d2d2d4d4d4d5d5d5e2e2e2
+fefefefefefefefefefffffffffffffefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfbfdfdfbfd
+f9f9fcf5f6f9e2e5e6c9cac8b1b2ab99988e84827878776c7b776c878075958c81a1988faaa39d
+b5afacbfbcbbc7c5c3d0d1ced5d8d4d0d6d2cbd0cec3c6c4bbbcbbaaaaa896949084847b8a8274
+9e8973ac8b6cae85619e7957876b526d5b4c70635c8b807c857d76837f748685798f8e849d9b94
+aba7a5b7b4b0bebcb8c2c0bdc5c5c3c2c3c4b4b5b6a7a6a49f9a989c96949c9597a49d9fa29c98
+9d98909690868f8980877f78857b77867c798b827e928a85978f8b9e9691a19a95a39b97a39c97
+ada6a2bfbcbcc1c1bfc0c0bdbdbdb8bbbbb3bdbbb2b3aea4afa79fada49baba399a39a9399928d
+9c97969d9a9a9b9a9a9b9b9aa4a5a2b3b1afbcb7b4bbb5aeb5aea4aaa4979a9589938f858a8881
+8986838a8a8a939494a4a09eaca5a0b4a69bb4a397ada39da19b9897918c918a83817a737b746c
+7a746c7f7870888078928b839a938ca09991a69f96aaa29aaea69fb6b0abd2d0cdd5d5d4d7dada
+dbdfdce0e3dde7e6e3dbd7d2beb8aea9a2938881726c645b6257505f524c61575367615e89898b
+a5a7a8b9babac5c4c1c9c6c1c6c3c1c5c4c3c5c5c5c6c6c6c6c6c6c9c9c9cdcdcdd2d2d2d4d4d4
+dadadae1e1e1e9e9e9eeeeeef3f3f3f7f7f7fcfcfcfdfdfdfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d1d1d1d2d2d2
+d3d3d3070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b3b1af9999994b4b4b454545d3d3d3d3d3d3d4d4d4d5d5d5e2e2e2
+fefefefefefefefefefffffffefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefcfdfdfbfd
+f8f9fbf3f5f8dee2e4bfc2c3a9aca9989b968d8f8a888b8690928ba2a19bb5b3adc5c2bed1cfcd
+dad9dbdcdde0dbdcded8dbdbd4d8d8cacfcec6cbcac0c2c2bbbcbcb3b2b1a8a5a1a09d939f9484
+a38b73a38062a17755926d4f755a4669574c7a6f6b9a93939a95929b9994a1a29caeb0abbfc0bf
+cecdd0d8d7d9d8d8d9d3d4d5c9caccc0c1c4b4b5b7abaaaaaaa5a4aba5a3ada8aab3aeb2b0acac
+aba8a6a5a3a0a09e9c9a95969a95989e9a9ba6a4a1b0afaabab7b4c1bfbbc6c4c0c9c8c4c9c8c4
+cccbc7cacacac9cac9cacbc8ccccc7cbcdc5cdcdc7ceccc8cecbcacdc9c7ccc7c4bfbab8b1adad
+a6a3a4a19fa1a5a3a4adababbbbab9cac8c7d6d3d2dcd8d5d9d6d0cdcac3b8b7b0aeaea8a0a09c
+969693999a9ba8a9aabab9b7c4c1bed1c8c3d4cac5cac6c6bdbcbeb0afafa9a6a396949192908b
+93938e9c9b98aaa8a6b7b6b5c0c0bec6c6c4ccccc9d1d0ccd6d4d0d9d8d4dadbd7dcdfdbe1e4e2
+e6ebe6eef0ebefefeeeceae7d7d4cfc4c2baa09d96827f7c797371776f6e7b7574828080959699
+a8aaacb8b9b8c3c1bec9c6c1c9c7c5cbcbcad0d0d0d5d5d5d8d8d8dddddde3e3e3e7e7e7eaeaea
+eeeeeef2f2f2f6f6f6f9f9f9fbfbfbfcfcfcfefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd1d1d1d1d1d1d3d3d3
+d3d3d3070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b454545d4d4d4d3d3d3d5d5d5d6d6d6e2e2e2
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefdfefefdfe
+f9f9faf5f6f6e5e6e7ced0d0bdbfbeb1b2b1aaabaaaaacaab2b4b1bfc0bdcdcdcad7d7d5dedede
+e4e4e5e2e3e5e1e2e4dedfe0d8d9dad1d2d2cdcfcfcbcdcccbcccbcbcbcacac9c6cac4bbb2a392
+a2876f987558936c4d805e44654b386d5e538c8480a8a5a5b0afadb6b6b3bfbfbdc9cac9d4d6d6
+dddddfe0e0e2dddedfd9dadbd2d2d4cdcdcfc7c7c8c6c6c6c8c7c6cac8c7d1cecfd4d2d4cecdcd
+c2c2c2b9b9b9b5b5b5b1afb1b3b2b4b9b8b9c1c1c0cacac8d1d1cfd4d4d2d5d5d3d7d7d6d9d9d8
+dadbd8dbdbdbdcdddcdedfdee2e2e0e3e4e0e6e6e4e3e3e2dddcddd7d6d6d3d2d2c4c3c3bdbbbc
+bbb9babdbcbdc5c4c5d0cfcfdbdbdbe5e5e4ebeaeaebeae9e4e3e0d5d3d2c6c6c4bfbfbdb9b9b7
+babab8c0c1c1cfd0d0dad9d9dfdddde3e0dedddad9d1d0d1c6c6c8bdbcbdb7b6b6afafadb1b1af
+b5b6b5bfbfbfcacacad3d3d5d9dadadadbdbdddedddfdfdee0e0dee1e1dfe5e6e3e8e9e7eceeec
+f1f3f1f9f9f8f8f7f7f1f0efdfdfddd1d1cfb9b9b7a9a9a9a6a5a4a6a4a3a8a7a6acacacb6b6b7
+bfbfc0cacacad3d2d1d8d7d5dddddbe0e0e0e6e6e6ebebebeeeeeef0f0f0f5f5f5f7f7f7f8f8f8
+f9f9f9fafafafcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfd0d0d0d1d1d1d4d4d4
+d3d3d3070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b444444d1d1d1d2d2d2d5d5d5d6d6d6e3e3e3
+fefefefffffffffffffffffffffffffefefefffffffffffffefefefefefefffffffefefeffffff
+fffffffffffffffffffefefefffffffefefefefefefefefefefefefffffffefefeffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fbfbfbf9f9f9f2f2f2e7e7e7e0e0e0dbdbdbd8d8d8d8d8d8dbdbdbe0e0e0e6e6e6eaeaeaededed
+eeeeeeedededecececeaeaeae7e7e7e3e3e3e3e3e3e5e5e5e7e7e7e9e9e8e9e9e6ebe6dbb2a290
+937b6286674b7e5d3f6b4d3456422c7f7365b5afaacececed7d7d7dcdcdce1e1e1e6e6e6eaeaea
+ececececececeaeaeae8e8e8e4e4e4e2e2e2e2e2e2e4e4e4e7e7e7eaeaeaeeeeeeeeeeeee9e9e9
+e2e2e2dcdcdcdadadad8d8d8dbdbdbdededee2e2e2e6e6e6e9e9e9e9e9e9eaeaeaebebebebebeb
+ebebebebebebeeeeeef0f0f0f4f4f4f6f6f6f6f6f6f4f4f4efefefeaeaeae6e6e6dedededcdcdc
+dddddde2e2e2e8e8e8eeeeeef3f3f3f7f7f7f8f8f8f6f6f6f0f0f0e7e7e7e1e1e1dedededddddd
+e0e0e0e6e6e6edededf2f2f2f3f3f3f2f2f2ededede5e5e5e0e0e0dcdcdcd9d9d9d7d7d7dadada
+dddddde2e2e2e7e7e7e9e9e9eaeaeaecececedededededededededeeeeeef1f1f1f3f3f3f7f7f7
+fafafafdfdfdfdfdfdf8f8f8eeeeeee7e7e7ddddddd9d9d9d8d8d8d6d6d6d6d6d6d7d7d7dcdcdc
+e0e0e0e6e6e6ecececefefeff2f2f2f4f4f4f7f7f7f9f9f9fbfbfbfafafafcfcfcfcfcfcfdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdededecfcfcfd0d0d0d1d1d1
+d2d2d2070707646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b434343cecececfcfcfd2d2d2d4d4d4e2e2e2
+fefefefefefefefefefefefefffffffefefefffffffefefefefefefefefefffffffefefefefefe
+fefefefefefefffffffefefefefefefefefefefefefffffffefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fefefefdfdfdfcfcfcfafafaf9f9f9f7f7f7f7f7f7f7f7f7f8f8f8f8f8f8f9f9f9f9f9f9f9f9f9
+f9f9f9f9f9f9f9f9f9f9f9f9f8f8f8f7f7f7f7f7f7f8f8f8f9f9f9f9f9f9f9f9f6f4f1e79d9483
+6c5b44553f254a3317422e17574a36a59d90e5e3ddf5f5f5f6f6f6f7f7f7f9f9f9fafafafafafa
+fbfbfbfbfbfbfafafaf9f9f9f9f9f9f9f9f9f9f9f9fafafafbfbfbfcfcfcfdfdfdfcfcfcfafafa
+f8f8f8f7f7f7f7f7f7f7f7f7f7f7f7f8f8f8f8f8f8f9f9f9fbfbfbfafafafafafafafafafafafa
+fafafafbfbfbfbfbfbfcfcfcfefefefefefefefefefdfdfdfcfcfcfafafaf9f9f9f8f8f8f7f7f7
+f7f7f7f9f9f9fbfbfbfefefefefefefefefefdfdfdfdfdfdfdfdfdfafafaf8f8f8f7f7f7f7f7f7
+f9f9f9fafafafcfcfcfdfdfdfdfdfdfbfbfbf9f9f9f8f8f8f8f8f8f8f8f8f7f7f7f7f7f7f7f7f7
+f8f8f8f9f9f9fafafaf9f9f9fafafafafafaf9f9f9f9f9f9f9f9f9f9f9f9fafafafbfbfbfdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfbfbfbf9f9f9f8f8f8f7f7f7f7f7f7f6f6f6f6f6f6f7f7f7f8f8f8
+f9f9f9fafafafbfbfbfdfdfdfdfdfdfdfdfdfefefefefefefefefefefefefefefefefefefefefe
+fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbcccccccccccccecece
+d0d0d0060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9cbcbcbcececed0d0d0dfdfdf
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fefefefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfbfbfbfbfbfbfcfcfcfcfcfcfcfcfcfcfcfc
+fcfcfcfbfbfbfcfcfcfdfdfdfdfdfdfcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfbfcfbf9f9f4cdc8bf
+999083796d5d73665672685b827c70c4c1b9f7f6f3fcfcfcfcfcfcfcfcfcfcfcfcfdfdfdfdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd
+fdfdfdfcfcfcfcfcfcfbfbfbfbfbfbfcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd
+fdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd
+fdfdfdfcfcfcfcfcfcfdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfdfdfdfdfdfdfdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd
+fdfdfdfdfdfdfdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfbfbfbfcfcfcfdfdfd
+fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfbfbfbfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfd
+fdfdfdfdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfefefefefefefefefefefefefefefefefefefefefe
+fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffdadadac8c8c8c8c8c8c9c9c9
+cbcbcb060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c9c9c9cccccccececedddddd
+fefefefefefefefefefefefefefefefefefefffffffefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefefefe
+fefefefefefefefefefefefefefefefefefefdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd
+fdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfefefefefefefefefefefefefdfdfefcfcfbf1efed
+e2dedad8d3ced6d0cbd6d2cedbd8d5eeedeafdfcfbfdfdfdfdfdfdfdfdfdfdfdfdfefefefefefe
+fefefefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefefefefefefefefefefefefefefefefe
+fefefefdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfdfdfdfdfdfdfefefefdfdfdfefefefefefefdfdfd
+fdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfefefefefefefefefe
+fefefefcfcfcfbfbfbfdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfefefefdfdfd
+fdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfefefefefefefefefefefefe
+fefefefefefefdfdfdfdfdfdfefefefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfcfcfcfdfdfd
+fefefefefefefefefefefefefdfdfdfdfdfdfdfdfdfdfdfdfbfbfbfbfbfbfbfbfbfbfbfbfdfdfd
+fdfdfdfdfdfdfcfcfcfcfcfcfefefefefefefefefefefefefefefefefefefefefefefefefefefe
+fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefeffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffd8d8d8c7c7c7c7c7c7c7c7c7
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c7c7c7c9c9c9cbcbcbcdcdcddadada
+f4f4f4f5f5f5f5f5f5f5f5f5f5f5f5f4f4f4f4f4f4f3f3f3f3f3f3f2f2f2f2f2f2f2f2f2f2f2f2
+f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f3f3f3f3f3f3f2f2f2f2f2f2f1f1f1f1f1f1f1f1f1f2f2f2
+f2f2f2f3f3f3f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f2f2f2f2f2f2f3f3f3f3f3f3f3f3f3f4f4f4f4f4f4
+f5f5f5f6f6f6f6f6f6f6f6f6f6f6f6f5f5f5f5f5f5f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f4f4f4
+f4f4f4f5f5f5f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f2f2f2f2f2f2f2f2f2f3f3f3f3f3f3f4f4f4
+f3f3f3f3f3f3f3f3f3f3f3f3f2f2f2f2f2f2f3f3f3f3f3f3f3f3f3f3f3f3f2f2f3f2f2f3f4f3f3
+f4f3f3f4f2f1f4f2f1f4f3f3f5f4f4f5f4f4f4f4f4f3f3f3f2f2f2f2f2f2f1f1f1f1f1f1f1f1f1
+f1f1f1f0f0f0f1f1f1f2f2f2f2f2f2f3f3f3f3f3f3f2f2f2f2f2f2f1f1f1f1f1f1f1f1f1f1f1f1
+f1f1f1f1f1f1f1f1f1f1f1f1f0f0f0f1f1f1f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2
+f1f1f1f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f1f1f1f1f1f1f2f2f2f2f2f2f2f2f2
+f3f3f3f2f2f2f2f2f2f4f4f4f3f3f3f4f4f4f4f4f4f3f3f3f2f2f2f1f1f1f2f2f2f1f1f1f1f1f1
+f0f0f0f0f0f0f1f1f1f0f0f0f0f0f0f1f1f1f0f0f0f1f1f1f2f2f2f1f1f1f1f1f1f1f1f1f1f1f1
+f1f1f1f1f1f1f2f2f2f2f2f2f3f3f3f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2
+f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f2f2f2f1f1f1f0f0f0f0f0f0f1f1f1f2f2f2
+f3f3f3f3f3f3f3f3f3f2f2f2f3f3f3f3f3f3f3f3f3f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f3f3f3
+f3f3f3f4f4f4f4f4f4f5f5f5f5f5f5f5f5f5f5f5f5f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4
+f4f4f4f4f4f4f4f4f4f5f5f5f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f3f3f3f4f4f4f4f4f4
+f4f4f4f4f4f4f5f5f5f5f5f5f5f5f5f4f4f4f4f4f4f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3
+f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f5f5f5f5f5f5f5f5f5f4f4f4f3f3f3f2f2f2f2f2f2
+f2f2f2f2f2f2f2f2f2f2f2f2f3f3f3f3f3f3f4f4f4f4f4f4f4f4f4f3f3f3f3f3f3f2f2f2f2f2f2
+f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f3f3f3f2f2f2f2f2f2f3f3f3f3f3f3
+f3f3f3f3f3f3f3f3f3f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2d4d4d4c7c7c7c7c7c7c7c7c7
+c9c9c9060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b414141c8c8c8cacacacccccccdcdcdcecece
+d1d1d1d3d3d3d4d4d4d4d4d4d3d3d3d1d1d1cecececbcbcbc8c8c8c6c6c6c6c6c6c6c6c6c5c5c5
+c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c8c8c8c8c8c8c7c7c7c5c5c5c4c4c4c2c2c2c3c3c3c5c5c5
+c7c7c7c9c9c9cdcdcdcdcdcdcfcfcfcfcfcfcecececccccccccccccdcdcdcececececececfcfcf
+cfcfcfcfcfcfd0d0d0cfcfcfcdcdcdcccccccccccccccccccccccccdcdcdcdcdcdcfcfcfd0d0d0
+d0d0d0d0d0d0d0d0d0cccccccacacac9c9c9c8c8c8c8c8c8c9c9c9cbcbcbcccccccececed1d1d1
+d4d4d4d7d7d7d9d9d9d9d9d9d6d6d6d4d4d4d3d3d3d1d1d1d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0
+d1d1d1d2d2d2d1d1d1d0d0d0cecececececececececdcdcdcccccccdcdcdcfcfcfd0d0d0d1d1d1
+d3d3d3d3d3d3d2d2d2d0d0d0cdcdcdcccccccccccccccccccbcbcbcacacacbcbcbcdcdcdd0d0d0
+d0d0d0d0d0d0d0d0d0d1d1d1d1d1d1d2d2d2d1d1d1cfcfcfcbcbcbc7c7c7c5c5c5c5c5c5c4c4c4
+c4c4c4c5c5c5c9c9c9cbcbcbcecececfcfcfcecececacacac8c8c8c6c6c6c5c5c5c5c5c5c5c5c5
+c5c5c5c6c6c6c6c6c6c6c6c6c7c7c7c8c8c8c8c8c8c8c8c8c8c8c8cacacacbcbcbcbcbcbcbcbcb
+c9c9c9c8c8c8c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c8c8c8c8c8c8cacacacbcbcb
+cdcdcdcfcfcfd1d1d1d3d3d3d4d4d4d5d5d5d4d4d4d1d1d1cdcdcdcbcbcbc8c8c8c7c7c7c6c6c6
+c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c8c8c8c8c8c8c7c7c7c6c6c6c4c4c4c3c3c3
+c3c3c3c5c5c5c7c7c7cacacacdcdcdcdcdcdcecececfcfcfcdcdcdcccccccccccccccccccecece
+cecececfcfcfcfcfcfcfcfcfd0d0d0cfcfcfcecececccccccccccccccccccccccccdcdcdcdcdcd
+cfcfcfd0d0d0d0d0d0d0d0d0d0d0d0cdcdcdcbcbcbc9c9c9c8c8c8c8c8c8c9c9c9cbcbcbcccccc
+cececed0d0d0d4d4d4d8d8d8d9d9d9d9d9d9d7d7d7d4d4d4d3d3d3d1d1d1d0d0d0d0d0d0d0d0d0
+d0d0d0d0d0d0d1d1d1d2d2d2d1d1d1d0d0d0cfcfcfcecececececececececccccccdcdcdcfcfcf
+d0d0d0d1d1d1d3d3d3d3d3d3d1d1d1cfcfcfcecececccccccccccccccccccbcbcbcacacacbcbcb
+cdcdcdcfcfcfd0d0d0d0d0d0d0d0d0d1d1d1d1d1d1d2d2d2d2d2d2d0d0d0cbcbcbc6c6c6c5c5c5
+c5c5c5c4c4c4c4c4c4c5c5c5c8c8c8cbcbcbcdcdcdcfcfcfcecececbcbcbc8c8c8c6c6c6c5c5c5
+c5c5c5c5c5c5c5c5c5c6c6c6c6c6c6c7c7c7c7c7c7c8c8c8c8c8c8c8c8c8c8c8c8cacacacbcbcb
+cbcbcbcbcbcbcacacac8c8c8c7c7c7c7c7c7c7c7c7c8c8c8c7c7c7c7c7c7c7c7c7c8c8c8c8c8c8
+cacaca060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242c9c9c9cbcbcbcdcdcdcdcdcdcecece
+d1d1d1d2d2d2d4d4d4d4d4d4d5d5d5d5d5d5d4d4d4d0d0d0ccccccc9c9c9c8c8c8c6c6c6c5c5c5
+c6c6c6c5c5c5c6c6c6c6c6c6c7c7c7c7c7c7c8c8c8c9c9c9c8c8c8c5c5c5c2c2c2c3c3c3c5c5c5
+c6c6c6c9c9c9cbcbcbcdcdcdcecececececececececccccccdcdcdcececececececececececece
+cecececfcfcfd0d0d0cfcfcfcececececececccccccccccccbcbcbcacacacacacacbcbcbcdcdcd
+cecececececed0d0d0d0d0d0cfcfcfcccccccacacac9c9c9c9c9c9cacacacbcbcbcccccccecece
+d2d2d2d5d5d5d9d9d9dcdcdcdbdbdbdadadad7d7d7d5d5d5d4d4d4d3d3d3d3d3d3d3d3d3d5d5d5
+d7d7d7d7d7d7d7d7d7d7d7d7d6d6d6d5d5d5d4d4d4d4d4d4d4d4d4d5d5d5d6d6d6d7d7d7d7d7d7
+d7d7d7d7d7d7d7d7d7d4d4d4d1d1d1d1d1d1d1d1d1d0d0d0cecececccccccececed0d0d0d2d2d2
+d3d3d3d4d4d4d5d5d5d4d4d4d1d1d1cecececbcbcbc9c9c9c8c8c8c7c7c7c6c6c6c5c5c5c4c4c4
+c4c4c4c4c4c4c5c5c5c7c7c7cacacacecececfcfcfcecececccccccacacac7c7c7c5c5c5c5c5c5
+c6c6c6c6c6c6c6c6c6c7c7c7c8c8c8c8c8c8c8c8c8c8c8c8c9c9c9cacacacbcbcbcbcbcbcacaca
+c9c9c9c9c9c9c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c8c8c8c9c9c9c9c9c9cccccccdcdcd
+cecececfcfcfd1d1d1d3d3d3d4d4d4d5d5d5d5d5d5d5d5d5d3d3d3d0d0d0cbcbcbcacacac8c8c8
+c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c7c7c7c7c7c7c7c7c7c8c8c8c9c9c9c8c8c8c6c6c6c3c3c3
+c3c3c3c4c4c4c6c6c6c9c9c9cccccccdcdcdcececececececdcdcdcccccccdcdcdcececececece
+cecececececececececfcfcfd0d0d0cfcfcfcececececececccccccccccccbcbcbcbcbcbcacaca
+cbcbcbcdcdcdcecececececed0d0d0d0d0d0cfcfcfcccccccacacac9c9c9c9c9c9cacacacbcbcb
+cccccccececed1d1d1d6d6d6dadadadcdcdcdcdcdcdadadad7d7d7d5d5d5d4d4d4d3d3d3d3d3d3
+d3d3d3d5d5d5d6d6d6d7d7d7d7d7d7d7d7d7d6d6d6d5d5d5d4d4d4d4d4d4d4d4d4d5d5d5d6d6d6
+d7d7d7d7d7d7d7d7d7d7d7d7d6d6d6d4d4d4d2d2d2d1d1d1d1d1d1d0d0d0cecececccccccecece
+d0d0d0d2d2d2d3d3d3d4d4d4d4d4d4d4d4d4d1d1d1cecececbcbcbc9c9c9c8c8c8c7c7c7c6c6c6
+c5c5c5c4c4c4c4c4c4c4c4c4c5c5c5c7c7c7cacacacecececfcfcfcecececcccccc9c9c9c6c6c6
+c5c5c5c5c5c5c6c6c6c6c6c6c6c6c6c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c9c9c9cacacacbcbcb
+cbcbcbcacacacacacac9c9c9c7c7c7c7c7c7c7c7c7c8c8c8c7c7c7c7c7c7c8c8c8c9c9c9c9c9c9
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b424242cacacacbcbcbcccccccccccccccccc
+cececed0d0d0d2d2d2d3d3d3d5d5d5d5d5d5d5d5d5d3d3d3cfcfcfcccccccbcbcbcacacac8c8c8
+c7c7c7c6c6c6c7c7c7c7c7c7c7c7c7c9c9c9cbcbcbcbcbcbcacacac8c8c8c6c6c6c5c5c5c5c5c5
+c6c6c6c9c9c9cbcbcbcdcdcdcecececececececececdcdcdcdcdcdcececececececececececece
+cecececfcfcfd0d0d0cfcfcfcfcfcfcecececdcdcdcdcdcdcbcbcbcacacac9c9c9c9c9c9cacaca
+cacacacbcbcbcececed1d1d1d3d3d3d2d2d2d1d1d1cfcfcfcdcdcdcccccccbcbcbcccccccccccc
+cececed2d2d2d7d7d7dadadadcdcdcdcdcdcdadadad7d7d7d6d6d6d4d4d4d3d3d3d4d4d4d5d5d5
+d6d6d6d6d6d6d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d5d5d5d8d8d8d9d9d9d8d8d8
+d7d7d7d7d7d7d7d7d7d6d6d6d4d4d4d4d4d4d5d5d5d3d3d3d2d2d2cfcfcfcececececececdcdcd
+cdcdcdcececed0d0d0d0d0d0cecececcccccc9c9c9c7c7c7c8c8c8c9c9c9cacacac9c9c9c6c6c6
+c4c4c4c4c4c4c4c4c4c5c5c5c8c8c8cbcbcbcececed1d1d1d1d1d1d0d0d0cbcbcbc7c7c7c7c7c7
+c6c6c6c6c6c6c6c6c6c7c7c7c8c8c8c8c8c8c8c8c8c9c9c9c9c9c9cacacacbcbcbcbcbcbcacaca
+cacacac9c9c9c8c8c8c7c7c7c7c7c7c7c7c7c7c7c7c8c8c8c8c8c8c9c9c9c9c9c9cccccccccccc
+cccccccdcdcdcececed0d0d0d2d2d2d3d3d3d4d4d4d5d5d5d5d5d5d3d3d3cecececccccccbcbcb
+cacacac8c8c8c7c7c7c6c6c6c7c7c7c7c7c7c7c7c7c9c9c9cbcbcbcbcbcbcacacac8c8c8c6c6c6
+c5c5c5c5c5c5c6c6c6c9c9c9cccccccdcdcdcececececececdcdcdcdcdcdcdcdcdcececececece
+cecececececececececfcfcfd0d0d0cfcfcfcfcfcfcecececdcdcdcccccccbcbcbcbcbcbc9c9c9
+c9c9c9cacacacacacacbcbcbcececed0d0d0d2d2d2d3d3d3d1d1d1cfcfcfcdcdcdcccccccbcbcb
+cccccccccccccececed3d3d3d8d8d8dadadadcdcdcdcdcdcdadadad8d8d8d5d5d5d4d4d4d3d3d3
+d4d4d4d5d5d5d6d6d6d5d5d5d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d6d6d6d7d7d7
+d9d9d9d8d8d8d7d7d7d7d7d7d6d6d6d5d5d5d4d4d4d4d4d4d5d5d5d4d4d4d1d1d1cfcfcfcecece
+cecececdcdcdcdcdcdcdcdcdd0d0d0d0d0d0cecececdcdcdcacacac7c7c7c8c8c8cacacacacaca
+c9c9c9c6c6c6c4c4c4c4c4c4c4c4c4c5c5c5c7c7c7cbcbcbcdcdcdd0d0d0d1d1d1cfcfcfcacaca
+c7c7c7c7c7c7c6c6c6c6c6c6c6c6c6c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c9c9c9cacacacbcbcb
+cbcbcbcacacacacacac9c9c9c8c8c8c7c7c7c7c7c7c8c8c8c7c7c7c8c8c8c8c8c8c9c9c9cacaca
+cccccc060606646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1af9999994b4b4b151515414141414141414141414141424242
+424242434343434343444444444444454545454545444444424242424242414141414141404040
+4040404040404040404040404040404141414141414141414141414040404040403f3f3f3f3f3f
+404040404040414141424242424242424242424242424242424242424242424242424242424242
+424242424242434343434343434343424242424242424242414141414141404040404040414141
+414141414141424242434343444444444444434343434343424242424242414141414141414141
+424242434343454545464646474747474747464646454545454545444444444444444444444444
+454545454545444444444444444444444444444444444444444444454545454545464646464646
+454545454545454545454545444444444444454545444444444444434343424242424242424242
+414141424242434343434343424242414141414141404040404040414141414141414141404040
+3f3f3f3f3f3f3f3f3f3f3f3f404040414141424242434343434343434343414141404040404040
+404040404040404040404040404040404040404040404040414141414141414141414141414141
+414141414141404040404040404040404040404040404040404040414141414141414141414141
+414141424242424242434343434343444444444444454545454545444444424242424242414141
+414141404040404040404040404040404040404040414141414141414141414141404040404040
+3f3f3f3f3f3f404040414141414141424242424242424242424242424242424242424242424242
+424242424242424242424242434343434343434343424242424242424242414141414141404040
+404040414141414141414141424242434343444444444444434343434343424242414141414141
+414141414141424242444444454545464646474747474747464646454545454545444444444444
+444444444444454545444444444444444444444444444444444444444444444444454545454545
+464646464646454545454545454545454545444444444444454545444444434343424242424242
+424242424242414141424242434343434343424242424242414141404040404040414141414141
+4141414040403f3f3f3f3f3f3f3f3f3f3f3f404040414141424242434343434343434343414141
+404040404040404040404040404040404040404040404040404040404040414141414141414141
+414141414141414141414141404040404040404040404040404040404040404040404040414141
+414141020202646464c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afa6a5a4848484656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565656565656565656565656565656565656565656565656565656565656565656565
+656565656565979797c4c3c3b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0bfbebdcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb
+cbcbcbcbcbcbcbcbcbc3c3c2b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585edededc7c7c7b2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb3b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb2b1afb2b1af
+b2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb2b1afb3b1afb2b1afb2b1afb3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b858585e6e5e5b6b5b3b3b1afb3b2b0b3b1afb3b1afb2b1afb2b1afb3b2b0b2b1afb3b1af
+b2b1afb2b1afb2b1afb3b1afb2b1afb2b1afb3b2b0b2b1afb2b1afb2b1afb2b1afb3b1afb2b1af
+b2b1afb2b1afb2b1afb3b1afb2b1afb3b1afb2b1afb2b1afb2b1afb3b1afb3b1afb2b1afb3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b2b1afb3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0
+b3b2b0b3b2b0b3b2b0b3b2b0b3b2b0b3b2b09a9a9a2323234b4b4b
+4b4b4b6b6b6abebdbca1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0
+a1a1a0a1a1a0a1a1a0a1a1a0a1a1a0a1a1a09999992323234b4b4b
+4b4b4b313030595858505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+505050505050505050505050505050505050505050505050505050505050505050505050505050
+5050505050505050505050505050505050505050501212124b4b4b
+8080804b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b
+4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b808080
+showpage
+%%Trailer
+end
+%%EOF

Added: packages/openev/branches/upstream/current/gextra.c
===================================================================
--- packages/openev/branches/upstream/current/gextra.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gextra.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,76 @@
+/******************************************************************************
+ * $Id: gextra.c,v 1.4 2000/06/20 13:26:54 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Support functions that should have been in Glib.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gextra.c,v $
+ * Revision 1.4  2000/06/20 13:26:54  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include "gextra.h"
+
+gint
+g_compare_gint(gconstpointer a, gconstpointer b)
+{
+    return *(gint*)a - *(gint*)b;
+}
+
+void
+g_sort(gpointer mem, guint nmemb, gsize size, GCompareValFunc compare)
+{
+    qsort(mem, nmemb, size, compare);
+}
+
+void
+g_ptr_array_insert_fast(GPtrArray *array, guint index, gpointer data)
+{
+    g_return_if_fail(array);
+    g_return_if_fail(index <= array->len);
+    
+    if (index == array->len)
+    {
+	g_ptr_array_add(array, data);
+    }
+    else
+    {
+	gpointer moved = g_ptr_array_index(array, index);
+	g_ptr_array_add(array, moved);
+	g_ptr_array_index(array, index) = data;
+    }
+}
+
+double g_get_current_time_as_double()
+
+{
+    GTimeVal      cur_time;
+
+    g_get_current_time( &cur_time );
+    
+    return cur_time.tv_sec + cur_time.tv_usec / 1000000.0;
+}
+

Added: packages/openev/branches/upstream/current/gextra.h
===================================================================
--- packages/openev/branches/upstream/current/gextra.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gextra.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * $Id: gextra.h,v 1.4 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Support functions that should have been in Glib.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gextra.h,v $
+ * Revision 1.4  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __G_EXTRA_H__
+#define __G_EXTRA_H__
+
+#include <glib.h>
+
+/* Duplicates a memory buffer containing elements of homogeneous type */
+#define g_memdup_type(mem,type,count)  \
+    ((type*)g_memdup((gconstpointer)mem, (unsigned)sizeof(type) * (count)))
+
+/* Sorts a buffer containing elements of homogeneous type */
+#define g_sort_type(mem,type,count) \
+    (g_sort((gpointer)mem, count, (gsize)sizeof(type), g_compare_##type))
+
+/* Comparison function for sorting (see qsort man page) */
+typedef gint (*GCompareValFunc) (gconstpointer a, gconstpointer b);
+
+/* General in-place sorting (uses qsort) */
+void g_sort(gpointer mem, guint nmemb, gsize size, GCompareValFunc compare);
+
+/* gint comparison for g_sort */
+gint g_compare_gint(gconstpointer a, gconstpointer b);
+
+void g_ptr_array_insert_fast(GPtrArray *array, guint index, gpointer data);
+
+#define g_list_push(list,data)  g_list_prepend(list, data)
+#define g_list_pop(list)        g_list_remove_link(list, list)
+
+double g_get_current_time_as_double();
+
+#endif /* __G_EXTRA_H__ */

Added: packages/openev/branches/upstream/current/gtkcolorwell.c
===================================================================
--- packages/openev/branches/upstream/current/gtkcolorwell.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gtkcolorwell.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,700 @@
+/******************************************************************************
+ * $Id: gtkcolorwell.c,v 1.7 2001/09/27 00:39:14 pgs Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Color well button for GTK+
+ * Author: Federico Mena <federico at nuclecu.unam.mx>
+ *         Paul J.Y. Lahaie <pjlahaie at atlsci.com>
+ *
+ ******************************************************************************
+ * Copyright (C) 1998 Red Hat Software, Inc.
+ * Copyright (C) 2000 Atlantis Scientific, Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gtkcolorwell.c,v $
+ * Revision 1.7  2001/09/27 00:39:14  pgs
+ * fixed initialization problem with resizing preview widget
+ *
+ * Revision 1.6  2001/09/26 20:28:52  pgs
+ * fixed bug in set functions that called gtk_widget_draw( cp->da ) - da is never
+ * initialized to anything and isn't used.
+ *
+ * Revision 1.5  2001/09/21 20:21:54  pgs
+ * tried to make it resizable but it didn't work.
+ *
+ * Revision 1.4  2001/09/17 03:38:06  pgs
+ * updated render to honour use_alpha setting and modified the set_ routines
+ * to only render/draw if the widget is realized (prevent Gtk-CRITICAL errors)
+ *
+ * Revision 1.3  2001/09/16 03:27:32  pgs
+ * Modified render code to render transparency.  TODO: make it render not
+ * using transparency if this option is not set.
+ *
+ * Revision 1.2  2000/06/20 13:26:54  warmerda
+ * added standard headers
+ *
+ */
+
+#include <gtk/gtkalignment.h>
+#include <gtk/gtkcolorsel.h>
+#include <gtk/gtkdrawingarea.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkcompat.h>
+#include "gtkcolorwell.h"
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkbutton.h>
+
+/* These are the dimensions of the color sample in the color picker */
+#define COLOR_WELL_WIDTH  24
+#define COLOR_WELL_HEIGHT 16
+#define COLOR_WELL_PAD    1
+
+/* Size of checks and gray levels for alpha compositing checkerboard*/
+#define CHECK_SIZE  4
+#define CHECK_DARK  (1.0 / 3.0)
+#define CHECK_LIGHT (2.0 / 3.0)
+
+enum {
+    COLOR_SET,
+    LAST_SIGNAL
+};
+
+static void gtk_color_well_class_init (GtkColorWellClass *class);
+static void gtk_color_well_init       (GtkColorWell      *cw);
+static void gtk_color_well_destroy    (GtkObject             *object);
+static void gtk_color_well_clicked    (GtkButton             *button);
+static void gtk_color_well_state_changed (GtkWidget *widget, GtkStateType previous_state);
+static void gtk_color_well_realize (GtkWidget *widget);
+static void gtk_color_well_style_set (GtkWidget *widget, GtkStyle *previous_style);
+
+
+static guint color_well_signals[LAST_SIGNAL] = { 0 };
+
+static GtkButtonClass *parent_class;
+
+
+GtkType
+gtk_color_well_get_type (void)
+{
+    static GtkType cp_type = 0;
+
+    if (!cp_type) {
+        GtkTypeInfo cp_info = {
+            "GtkColorWell",
+            sizeof (GtkColorWell),
+            sizeof (GtkColorWellClass),
+            (GtkClassInitFunc) gtk_color_well_class_init,
+            (GtkObjectInitFunc) gtk_color_well_init,
+            NULL, /* reserved_1 */
+            NULL, /* reserved_2 */
+            (GtkClassInitFunc) NULL
+        };
+
+        cp_type = gtk_type_unique (gtk_button_get_type (), &cp_info);
+    }
+    return cp_type;
+}
+
+static void
+render(GtkColorWell *cw)
+{
+    int e;
+    guchar row[COLOR_WELL_WIDTH*3];
+    guchar red, green, blue, alpha;
+
+    gint x, y, f, n, width, height;
+    guchar c[3], cc[3 * 2], *cp = c;
+    gdouble o; //opacity ( 0 - 1 )
+
+    g_return_if_fail (cw != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cw));
+
+	//width = GTK_WIDGET(cw)->allocation.width;
+	//height = GTK_WIDGET(cw)->allocation.height;
+
+	width = COLOR_WELL_WIDTH;
+	height = COLOR_WELL_HEIGHT;
+
+    gtk_color_well_get_i8( cw, &red, &green, &blue, &alpha );
+
+    //g_message( "red=%d, green=%d, blue=%d, alpha=%d", red, green, blue, alpha);
+    c[0] = red;
+    c[1] = green;
+    c[2] = blue;
+    o = (gdouble)(alpha / 255.0);
+
+    //for alpha blending to create the gray checkerboard.  The colors here are
+    //for either the dark or light areas of the checkerboard.  A nifty
+    //calc below chooses between them.
+    if (cw->use_alpha)
+    {
+		for ( n=0; n < 3; n++ )
+		{
+			cc[n] = (guchar)((1.0 - o) * 255 + ( o * c[n] ));
+			cc[n+3] = (guchar)((1.0 - o) * 192 + ( o * c[n] ));
+		}
+		cp = cc;
+	}
+
+    for( y = 0; y < height; y++ )
+    {
+
+        for( x = 0, e = 0; x < width; x++ )
+        {
+            //this determines the size of the checkerboard.  The result is
+            //either 0 or 3.
+            if (cw->use_alpha)
+            	f = 3 * (((x % 12) < 6) ^ ((y % 12) < 6 ));
+            else
+            	f = 0;
+
+            for ( n=0; n < 3; n++ )
+                row[e++] = cp[n+f];
+        }
+        gtk_preview_draw_row( GTK_PREVIEW( cw->preview ), row, 0, y, width );
+    }
+
+    gtk_widget_queue_draw( cw->preview );
+}
+
+static void
+gtk_color_well_class_init (GtkColorWellClass *class)
+{
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+    GtkButtonClass *button_class;
+
+
+    object_class = (GtkObjectClass *) class;
+    button_class = (GtkButtonClass *) class;
+    widget_class = (GtkWidgetClass *) class;
+    parent_class = gtk_type_class (gtk_button_get_type ());
+
+    color_well_signals[COLOR_SET] =
+        gtk_signal_new ("color_set",
+                GTK_RUN_FIRST,
+                object_class->type,
+                GTK_SIGNAL_OFFSET (GtkColorWellClass, color_set),
+                gtk_marshal_NONE__POINTER,
+                GTK_TYPE_NONE, 1,
+                GTK_TYPE_POINTER);
+
+    gtk_object_class_add_signals (object_class, color_well_signals, LAST_SIGNAL);
+
+    object_class->destroy = gtk_color_well_destroy;
+    widget_class->state_changed = gtk_color_well_state_changed;
+    widget_class->realize = gtk_color_well_realize;
+    widget_class->style_set = gtk_color_well_style_set;
+    button_class->clicked = gtk_color_well_clicked;
+
+
+    class->color_set = NULL;
+
+}
+
+static void
+gtk_color_well_realize (GtkWidget *widget)
+{
+    if (GTK_WIDGET_CLASS(parent_class)->realize)
+        GTK_WIDGET_CLASS (parent_class)->realize (widget);
+    render (GTK_COLOR_WELL (widget));
+}
+static void
+gtk_color_well_style_set (GtkWidget *widget, GtkStyle *previous_style)
+{
+    if (GTK_WIDGET_CLASS(parent_class)->style_set)
+        GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
+    if (GTK_WIDGET_REALIZED (widget))
+        render (GTK_COLOR_WELL (widget));
+}
+
+static void
+gtk_color_well_state_changed (GtkWidget *widget, GtkStateType previous_state)
+{
+    if (widget->state == GTK_STATE_INSENSITIVE || previous_state == GTK_STATE_INSENSITIVE)
+        render (GTK_COLOR_WELL (widget));
+}
+static void
+gtk_color_well_init (GtkColorWell *cw)
+{
+    cw->continuous = 0;
+}
+
+GtkWidget *
+gtk_color_well_new(const gchar *title)
+{
+    GtkWidget *alignment;
+    GtkWidget *frame;
+    GtkColorWell *color_well;
+
+    color_well = GTK_COLOR_WELL(gtk_type_new (gtk_color_well_get_type () ));
+
+    /* Check to make sure we can init a color_well */
+
+    if( ( color_well->preview = gtk_preview_new( GTK_PREVIEW_COLOR ) ) == NULL )
+    {
+    /* We failed */
+    	g_error( "failed to create preview in gtk_color_well_new" );
+
+    }
+
+    alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0 );
+    gtk_container_set_border_width( GTK_CONTAINER(alignment), COLOR_WELL_PAD);
+    gtk_container_add (GTK_CONTAINER (color_well), alignment );
+    gtk_widget_show (alignment);
+
+    frame = gtk_frame_new (NULL);
+    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT );
+    gtk_container_add (GTK_CONTAINER (alignment), frame );
+    gtk_widget_show (frame);
+
+    gtk_preview_size( GTK_PREVIEW( color_well->preview ), COLOR_WELL_WIDTH, COLOR_WELL_HEIGHT );
+    gtk_container_add (GTK_CONTAINER(frame), color_well->preview );
+    gtk_widget_show(color_well->preview);
+
+    return GTK_WIDGET( color_well );
+
+
+}
+
+static void
+gtk_color_well_destroy (GtkObject *object)
+{
+    GtkColorWell *cw;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (object));
+
+    cw = GTK_COLOR_WELL (object);
+
+    if( cw->color_dialog )
+        gtk_widget_destroy( cw->color_dialog );
+
+    if( cw->preview )
+        gtk_widget_destroy( cw->preview );
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+/* Callback used when the color selection dialog is destroyed */
+static gboolean
+color_wheel_destroy(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+    GtkColorSelectionDialog *cs = GTK_COLOR_SELECTION_DIALOG (data);
+
+    gtk_widget_hide( GTK_WIDGET( cs ) );
+
+    return TRUE;
+}
+
+static void
+cs_cancel_clicked (GtkWidget *widget, gpointer data)
+{
+    GtkColorSelectionDialog *cs = GTK_COLOR_SELECTION_DIALOG (data);
+
+    gtk_widget_hide( GTK_WIDGET( cs ) );
+}
+
+/* Callback for when the OK button in the color selection dialog is clicked */
+static void
+cs_ok_clicked (GtkWidget *widget, gpointer data)
+{
+    GtkColorWell *cp;
+    gdouble color[4];
+
+    cp = GTK_COLOR_WELL (data);
+
+    gtk_color_selection_get_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (cp->color_dialog)->colorsel),
+                       color);
+    gtk_widget_hide(cp->color_dialog);
+
+    cp->r = color[0];
+    cp->g = color[1];
+    cp->b = color[2];
+    cp->a = cp->use_alpha ? color[3] : 1.0;
+
+    render (cp);
+
+    gtk_signal_emit (GTK_OBJECT (cp), color_well_signals[COLOR_SET] );
+}
+
+static void
+color_continuous_update( GtkWidget *widget, gpointer data )
+{
+    GtkColorWell *cp;
+    gdouble color[4];
+
+    cp = GTK_COLOR_WELL (data);
+
+    gtk_color_selection_get_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (cp->color_dialog)->colorsel),
+                       color);
+
+    cp->r = color[0];
+    cp->g = color[1];
+    cp->b = color[2];
+    cp->a = cp->use_alpha ? color[3] : 1.0;
+
+    render (cp);
+
+    /* Notify the world that the color was set */
+
+    gtk_signal_emit (GTK_OBJECT (cp), color_well_signals[COLOR_SET] );
+
+}
+
+
+void
+gtk_color_well_set_continuous( GtkColorWell *cw, gboolean continuous )
+{
+    g_return_if_fail( cw != NULL );
+    g_return_if_fail( GTK_IS_COLOR_WELL( cw ) );
+
+    if( continuous == cw->continuous )
+    return;
+
+    if( continuous )
+    {
+    if( cw->color_dialog )
+        cw->cont_sig_id = gtk_signal_connect( GTK_OBJECT( GTK_COLOR_SELECTION_DIALOG( cw->color_dialog )->colorsel ),
+                          "color-changed",
+                          (GtkSignalFunc) color_continuous_update,
+                          cw );
+    cw->continuous = 1;
+    } else {
+    if( cw->color_dialog )
+        gtk_signal_disconnect( GTK_OBJECT( GTK_COLOR_SELECTION_DIALOG( cw->color_dialog )->colorsel ), cw->cont_sig_id );
+
+    cw->continuous = 0;
+    }
+}
+
+
+static void
+gtk_color_well_clicked (GtkButton *button)
+{
+    GtkColorWell *cw;
+    GtkColorSelectionDialog *csd;
+    gdouble color[4];
+
+    g_return_if_fail (button != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (button));
+
+    cw = GTK_COLOR_WELL (button);
+
+    /*if dialog already exists, make sure it's shown and raised*/
+    if(cw->color_dialog) {
+        csd = GTK_COLOR_SELECTION_DIALOG (cw->color_dialog);
+        gtk_widget_show(cw->color_dialog);
+        if(cw->color_dialog->window)
+            gdk_window_raise(cw->color_dialog->window);
+    } else {
+        /* Create the dialog and connects its buttons */
+
+        cw->color_dialog = gtk_color_selection_dialog_new (cw->title);
+        csd = GTK_COLOR_SELECTION_DIALOG (cw->color_dialog);
+
+        gtk_signal_connect (GTK_OBJECT (cw->color_dialog), "delete-event",
+                    (GtkSignalFunc) color_wheel_destroy,
+                    cw->color_dialog );
+
+        gtk_signal_connect (GTK_OBJECT (csd->ok_button), "clicked",
+                    (GtkSignalFunc) cs_ok_clicked,
+                    cw);
+
+        gtk_signal_connect (GTK_OBJECT (csd->cancel_button), "clicked",
+                    (GtkSignalFunc) cs_cancel_clicked,
+                    cw->color_dialog);
+
+        if( cw->continuous )
+            cw->cont_sig_id = gtk_signal_connect (GTK_OBJECT (csd->colorsel), "color-changed",
+                              (GtkSignalFunc) color_continuous_update,
+                              cw);
+
+        /* FIXME: do something about the help button */
+
+        gtk_widget_hide( csd->help_button );
+
+        gtk_window_set_position (GTK_WINDOW (cw->color_dialog), GTK_WIN_POS_MOUSE);
+
+        /* If there is a grabed window, set new dialog as modal */
+        if (gtk_grab_get_current())
+            gtk_window_set_modal(GTK_WINDOW(cw->color_dialog),TRUE);
+    }
+    gtk_color_selection_set_opacity (GTK_COLOR_SELECTION (csd->colorsel), cw->use_alpha);
+
+    color[0] = cw->r;
+    color[1] = cw->g;
+    color[2] = cw->b;
+    color[3] = cw->use_alpha ? cw->a : 1.0;
+
+    /* Hack: we set the color twice so that GtkColorSelection will remember its history */
+    gtk_color_selection_set_color (GTK_COLOR_SELECTION (csd->colorsel), color);
+    gtk_color_selection_set_color (GTK_COLOR_SELECTION (csd->colorsel), color);
+
+    gtk_widget_show (cw->color_dialog);
+}
+
+
+/**
+ * gtk_color_well_set_d
+ * @cp: Pointer to GNOME color picker widget.
+ * @r: Red color component, values are in [0.0, 1.0]
+ * @g: Green color component, values are in [0.0, 1.0]
+ * @b: Blue color component, values are in [0.0, 1.0]
+ * @a: Alpha component, values are in [0.0, 1.0]
+ *
+ * Description:
+ * Set color shown in the color picker widget using floating point values.
+ */
+
+void
+gtk_color_well_set_d (GtkColorWell *cw, gdouble r, gdouble g, gdouble b, gdouble a)
+{
+    g_return_if_fail (cw != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cw));
+    g_return_if_fail ((r >= 0.0) && (r <= 1.0));
+    g_return_if_fail ((g >= 0.0) && (g <= 1.0));
+    g_return_if_fail ((b >= 0.0) && (b <= 1.0));
+    g_return_if_fail ((a >= 0.0) && (a <= 1.0));
+
+    cw->r = r;
+    cw->g = g;
+    cw->b = b;
+    cw->a = a;
+
+	if ( !GTK_WIDGET_REALIZED( cw ) ) return;
+
+    render( cw );
+}
+
+
+/**
+ * gtk_color_well_get_d
+ * @cp: Pointer to GNOME color picker widget.
+ * @r: Output location of red color component, values are in [0.0, 1.0]
+ * @g: Output location of green color component, values are in [0.0, 1.0]
+ * @b: Output location of blue color component, values are in [0.0, 1.0]
+ * @a: Output location of alpha color component, values are in [0.0, 1.0]
+ *
+ * Description:
+ * Retrieve color currently selected in the color picker widget in the form of floating point values.
+ */
+
+void
+gtk_color_well_get_d (GtkColorWell *cp, gdouble *r, gdouble *g, gdouble *b, gdouble *a)
+{
+    g_return_if_fail (cp != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cp));
+
+    if (r)
+        *r = cp->r;
+
+    if (g)
+        *g = cp->g;
+
+    if (b)
+        *b = cp->b;
+
+    if (a)
+        *a = cp->a;
+}
+
+
+/**
+ * gtk_color_well_set_i8
+ * @cp: Pointer to GNOME color picker widget.
+ * @r: Red color component, values are in [0, 255]
+ * @g: Green color component, values are in [0, 255]
+ * @b: Blue color component, values are in [0, 255]
+ * @a: Alpha component, values are in [0, 255]
+ *
+ * Description:
+ * Set color shown in the color picker widget using 8-bit integer values.
+ */
+
+void
+gtk_color_well_set_i8 (GtkColorWell *cp, guint8 r, guint8 g, guint8 b, guint8 a)
+{
+    g_return_if_fail (cp != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cp));
+    /* Don't check range of r,g,b,a since it's a 8 bit unsigned type. */
+
+    cp->r = r / 255.0;
+    cp->g = g / 255.0;
+    cp->b = b / 255.0;
+    cp->a = a / 255.0;
+
+	if ( !GTK_WIDGET_REALIZED( cp ) ) return;
+
+	render( cp );
+}
+
+
+/**
+ * gtk_color_well_get_i8
+ * @cp: Pointer to GNOME color picker widget.
+ * @r: Output location of red color component, values are in [0, 255]
+ * @g: Output location of green color component, values are in [0, 255]
+ * @b: Output location of blue color component, values are in [0, 255]
+ * @a: Output location of alpha color component, values are in [0, 255]
+ *
+ * Description:
+ * Retrieve color currently selected in the color picker widget in the form of 8-bit integer values.
+ */
+
+void
+gtk_color_well_get_i8 (GtkColorWell *cp, guint8 *r, guint8 *g, guint8 *b, guint8 *a)
+{
+    g_return_if_fail (cp != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cp));
+
+    if (r)
+        *r = (guint8) (cp->r * 255.0 + 0.5);
+
+    if (g)
+        *g = (guint8) (cp->g * 255.0 + 0.5);
+
+    if (b)
+        *b = (guint8) (cp->b * 255.0 + 0.5);
+
+    if (a)
+        *a = (guint8) (cp->a * 255.0 + 0.5);
+}
+
+
+/**
+ * gtk_color_well_set_i16
+ * @cp: Pointer to GNOME color picker widget.
+ * @r: Red color component, values are in [0, 65535]
+ * @g: Green color component, values are in [0, 65535]
+ * @b: Blue color component, values are in [0, 65535]
+ * @a: Alpha component, values are in [0, 65535]
+ *
+ * Description:
+ * Set color shown in the color picker widget using 16-bit integer values.
+ */
+
+void
+gtk_color_well_set_i16 (GtkColorWell *cp, gushort r, gushort g, gushort b, gushort a)
+{
+    g_return_if_fail (cp != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cp));
+    /* Don't check range of r,g,b,a since it's a 16 bit unsigned type. */
+
+    cp->r = r / 65535.0;
+    cp->g = g / 65535.0;
+    cp->b = b / 65535.0;
+    cp->a = a / 65535.0;
+
+	if ( !GTK_WIDGET_REALIZED( cp ) ) return;
+
+	render( cp );
+}
+
+
+/**
+ * gtk_color_well_get_i16
+ * @cp: Pointer to GNOME color picker widget.
+ * @r: Output location of red color component, values are in [0, 65535]
+ * @g: Output location of green color component, values are in [0, 65535]
+ * @b: Output location of blue color component, values are in [0, 65535]
+ * @a: Output location of alpha color component, values are in [0, 65535]
+ *
+ * Description:
+ * Retrieve color currently selected in the color picker widget in the form of 16-bit integer values.
+ */
+
+void
+gtk_color_well_get_i16 (GtkColorWell *cp, gushort *r, gushort *g, gushort *b, gushort *a)
+{
+    g_return_if_fail (cp != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cp));
+
+    if (r)
+        *r = (gushort) (cp->r * 65535.0 + 0.5);
+
+    if (g)
+        *g = (gushort) (cp->g * 65535.0 + 0.5);
+
+    if (b)
+        *b = (gushort) (cp->b * 65535.0 + 0.5);
+
+    if (a)
+        *a = (gushort) (cp->a * 65535.0 + 0.5);
+}
+
+
+/**
+ * gtk_color_well_set_dither
+ * @cp: Pointer to GNOME color picker widget.
+ * @dither: %TRUE if color sample should be dithered, %FALSE if not.
+ *
+ * Description:
+ * Sets whether the picker should dither the color sample or just paint
+ * a solid rectangle.
+ */
+
+/**
+ * gtk_color_well_set_use_alpha
+ * @cp: Pointer to GNOME color picker widget.
+ * @use_alpha: %TRUE if color sample should use alpha channel, %FALSE if not.
+ *
+ * Description:
+ * Sets whether or not the picker should use the alpha channel.
+ */
+
+void
+gtk_color_well_set_use_alpha (GtkColorWell *cp, gboolean use_alpha)
+{
+    g_return_if_fail (cp != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cp));
+
+    cp->use_alpha = use_alpha ? TRUE : FALSE;
+
+	if ( !GTK_WIDGET_REALIZED( cp ) ) return;
+
+	render( cp );
+}
+
+
+/**
+ * gtk_color_well_set_title
+ * @cp: Pointer to GNOME color picker widget.
+ * @title: String containing new window title.
+ *
+ * Description:
+ * Sets the title for the color selection dialog.
+ */
+
+void
+gtk_color_well_set_title (GtkColorWell *cw, const gchar *title)
+{
+    g_return_if_fail (cw != NULL);
+    g_return_if_fail (GTK_IS_COLOR_WELL (cw));
+
+    if (cw->title)
+        g_free (cw->title);
+
+    cw->title = g_strdup (title);
+
+    if (cw->color_dialog)
+        gtk_window_set_title (GTK_WINDOW (cw->color_dialog), cw->title);
+}

Added: packages/openev/branches/upstream/current/gtkcolorwell.h
===================================================================
--- packages/openev/branches/upstream/current/gtkcolorwell.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gtkcolorwell.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,109 @@
+/******************************************************************************
+ * $Id: gtkcolorwell.h,v 1.3 2001/09/26 20:29:35 pgs Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Color well button for GTK+
+ * Author: Federico Mena <federico at nuclecu.unam.mx>
+ *         Paul J.Y. Lahaie <pjlahaie at atlsci.com>
+ *
+ ******************************************************************************
+ * Copyright (C) 1998 Red Hat Software, Inc.
+ * Copyright (C) 2000 Atlantis Scientific, Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gtkcolorwell.h,v $
+ * Revision 1.3  2001/09/26 20:29:35  pgs
+ * removed extraneous variable da (unused)
+ *
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef GTK_COLOR_WELL_H
+#define GTK_COLOR_WELL_H
+
+#include <gtk/gtkbutton.h>
+
+#define GTK_TYPE_COLOR_WELL            (gtk_color_well_get_type ())
+#define GTK_COLOR_WELL(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_COLOR_WELL, GtkColorWell))
+#define GTK_COLOR_WELL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_COLOR_WELL, GtkColorWellClass))
+#define GTK_IS_COLOR_WELL(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_COLOR_WELL))
+#define GTK_IS_COLOR_WELL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_COLOR_WELL))
+
+
+typedef struct _GtkColorWell GtkColorWell;
+typedef struct _GtkColorWellClass GtkColorWellClass;
+
+struct _GtkColorWell {
+    GtkButton button;
+
+    gdouble r, g, b, a;	/* Red, green, blue, and alpha values */
+
+    GtkWidget *preview;	/* Pixmap with the sample contents */
+
+    GtkWidget *color_dialog;	/* Color selection dialog */
+
+    gchar *title;		/* Title for the color selection window */
+    guint cont_sig_id;
+    guint use_alpha : 1;	/* Use alpha or not */
+    guint continuous : 1;
+
+};
+
+struct _GtkColorWellClass {
+	GtkButtonClass parent_class;
+
+	/* Signal that is emitted when the color is set.  The rgba values are in the [0, 65535]
+	 * range.  If you need a different color format, use the provided functions to get the
+	 * values from the color picker.
+	 */
+        /*  (should be gushort, but Gtk can't marshal that.) */
+	void (* color_set) (GtkColorWell *cw, guint r, guint g, guint b, guint a);
+};
+
+
+/* Standard Gtk function */
+GtkType gtk_color_well_get_type (void);
+
+/* Creates a new color picker widget */
+GtkWidget *gtk_color_well_new (const gchar *title);
+
+/* Set/get the color in the picker.  Values are in [0.0, 1.0] */
+void gtk_color_well_set_d (GtkColorWell *cp, gdouble r, gdouble g, gdouble b, gdouble a);
+void gtk_color_well_get_d (GtkColorWell *cp, gdouble *r, gdouble *g, gdouble *b, gdouble *a);
+
+/* Set/get the color in the picker.  Values are in [0, 255] */
+void gtk_color_well_set_i8 (GtkColorWell *cp, guint8 r, guint8 g, guint8 b, guint8 a);
+void gtk_color_well_get_i8 (GtkColorWell *cp, guint8 *r, guint8 *g, guint8 *b, guint8 *a);
+
+/* Set/get the color in the picker.  Values are in [0, 65535] */
+void gtk_color_well_set_i16 (GtkColorWell *cp, gushort r, gushort g, gushort b, gushort a);
+void gtk_color_well_get_i16 (GtkColorWell *cp, gushort *r, gushort *g, gushort *b, gushort *a);
+
+/* Sets whether the picker should use the alpha channel or not */
+void gtk_color_well_set_use_alpha (GtkColorWell *cp, gboolean use_alpha);
+
+/* Sets whether the picker should update the colors continuously */
+void gtk_color_well_set_continuous(GtkColorWell *cw, gboolean update_continuous );
+
+/* Sets the title for the color selection dialog */
+void gtk_color_well_set_title (GtkColorWell *cp, const gchar *title);
+
+
+#endif

Added: packages/openev/branches/upstream/current/gv_config.h.in
===================================================================
--- packages/openev/branches/upstream/current/gv_config.h.in	                        (rev 0)
+++ packages/openev/branches/upstream/current/gv_config.h.in	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,25 @@
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+#undef HAVE_LIBDL 
+
+#undef HAVE_DLFCN_H
+#undef WORDS_BIGENDIAN
+
+#define HAVE_BROKEN_GL_POINTS 1
+
+#undef SHOW_TESS_LINES
+
+#undef HAVE_OGR

Added: packages/openev/branches/upstream/current/gv_config.h_win32
===================================================================
--- packages/openev/branches/upstream/current/gv_config.h_win32	                        (rev 0)
+++ packages/openev/branches/upstream/current/gv_config.h_win32	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+#undef HAVE_LIBDL 
+
+#undef HAVE_DLFCN_H
+#undef WORDS_BIGENDIAN
+
+#define HAVE_BROKEN_GL_POINTS 1
+
+#undef SHOW_TESS_LINES

Added: packages/openev/branches/upstream/current/gvarealayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvarealayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvarealayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,556 @@
+/******************************************************************************
+ * $Id: gvarealayer.c,v 1.18 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer of GvAreas.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvarealayer.c,v $
+ * Revision 1.18  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.17  2000/06/20 13:26:54  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvarealayer.h"
+#include <gtk/gtksignal.h>
+#include <GL/gl.h>
+
+static void gv_area_layer_class_init(GvAreaLayerClass *klass);
+static void gv_area_layer_init(GvAreaLayer *layer);
+static void gv_area_layer_draw(GvAreaLayer *layer, GvViewArea *view);
+static void gv_area_layer_extents(GvAreaLayer *layer, GvRect *rect);
+static void gv_area_layer_data_change(GvAreaLayer *layer, gpointer change_info);
+static void gv_area_layer_draw_selected(GvAreaLayer *layer, GvViewArea *view);
+static void gv_area_layer_delete_selected(GvAreaLayer *layer);
+static void gv_area_layer_translate_selected(GvAreaLayer *layer, GvVertex *delta);
+static void gv_area_layer_pick_shape(GvAreaLayer *layer);
+static void gv_area_layer_pick_node(GvAreaLayer *layer);
+static void gv_area_layer_get_node(GvAreaLayer *layer, GvNodeInfo *info);
+static void gv_area_layer_move_node(GvAreaLayer *layer, GvNodeInfo *info);
+static void gv_area_layer_insert_node(GvAreaLayer *layer, GvNodeInfo *info);
+static void gv_area_layer_delete_node(GvAreaLayer *layer, GvNodeInfo *info);
+static void gv_area_layer_node_motion(GvAreaLayer *layer, gint area_id);
+
+GtkType
+gv_area_layer_get_type(void)
+{
+    static GtkType area_layer_type = 0;
+
+    if (!area_layer_type)
+    {
+	static const GtkTypeInfo area_layer_info =
+	{
+	    "GvAreaLayer",
+	    sizeof(GvAreaLayer),
+	    sizeof(GvAreaLayerClass),
+	    (GtkClassInitFunc) gv_area_layer_class_init,
+	    (GtkObjectInitFunc) gv_area_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	area_layer_type = gtk_type_unique(gv_shape_layer_get_type(),
+					  &area_layer_info);
+    }
+    return area_layer_type;
+}
+
+static void
+gv_area_layer_class_init(GvAreaLayerClass *klass)
+{
+    GvDataClass *data_class;
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    data_class = (GvDataClass*) klass;
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    data_class->changed = (void (*)(GvData*,gpointer))
+        gv_area_layer_data_change;
+    
+    layer_class->draw = (void (*)(GvLayer*,GvViewArea*)) gv_area_layer_draw;
+    layer_class->extents_request = (void (*)(GvLayer*,GvRect*))
+        gv_area_layer_extents;
+
+    shape_layer_class->draw_selected = (void (*)(GvShapeLayer*,GvViewArea*))
+        gv_area_layer_draw_selected;
+    shape_layer_class->delete_selected = (void (*)(GvShapeLayer*))
+        gv_area_layer_delete_selected;
+    shape_layer_class->translate_selected = (void (*)(GvShapeLayer*,GvVertex*))
+        gv_area_layer_translate_selected;
+    shape_layer_class->pick_shape = (void (*)(GvShapeLayer*))
+        gv_area_layer_pick_shape;
+    shape_layer_class->pick_node = (void (*)(GvShapeLayer*))
+        gv_area_layer_pick_node;
+    shape_layer_class->get_node = (void (*)(GvShapeLayer*,GvNodeInfo*))
+        gv_area_layer_get_node;
+    shape_layer_class->move_node = (void (*)(GvShapeLayer*,GvNodeInfo*))
+        gv_area_layer_move_node;
+    shape_layer_class->insert_node =  (void (*)(GvShapeLayer*,GvNodeInfo*))
+        gv_area_layer_insert_node;
+    shape_layer_class->delete_node =  (void (*)(GvShapeLayer*,GvNodeInfo*))
+        gv_area_layer_delete_node;
+    shape_layer_class->node_motion =  (void (*)(GvShapeLayer*,gint))
+        gv_area_layer_node_motion;
+}
+
+static void
+gv_area_layer_init(GvAreaLayer *layer)
+{
+    GvColor default_area_color = {0.0, 1.0, 0.0, 0.3};  /* green */
+    
+    layer->data = NULL;
+    layer->edit_ring = -1;
+    gv_color_copy(GV_SHAPE_LAYER(layer)->color, default_area_color);
+}
+
+GtkObject *
+gv_area_layer_new(GvAreas *data)
+{
+    GvAreaLayer *layer = GV_AREA_LAYER(gtk_type_new(gv_area_layer_get_type()));
+    
+    if (data)
+    {
+	layer->data = data;
+    }
+    else
+    {
+	layer->data = GV_AREAS(gv_areas_new());
+    }
+
+    /* In case areas exist in data before layer is creatd */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_areas_num_areas(layer->data));
+
+    gv_data_set_parent(GV_DATA(layer), GV_DATA(layer->data));
+
+    
+    
+    return GTK_OBJECT(layer);
+}
+
+gint
+gv_area_layer_select_new_area(GvAreaLayer *layer)
+{
+    gint area_id;
+    
+    layer->edit_ring = 0;
+    area_id = gv_areas_new_area(layer->data);
+    
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_areas_num_areas(layer->data));
+    gv_shape_layer_select_shape(GV_SHAPE_LAYER(layer), area_id);
+
+    return area_id;
+}
+
+gint
+gv_area_layer_select_new_ring(GvAreaLayer *layer, gint area_id)
+{
+    gint ring_id;
+    
+    ring_id = gv_areas_new_ring(layer->data, area_id);
+    g_return_val_if_fail(ring_id > 0, 0);
+    layer->edit_ring = ring_id;
+
+    gv_shape_layer_clear_selection(GV_SHAPE_LAYER(layer));
+    gv_shape_layer_select_shape(GV_SHAPE_LAYER(layer), area_id);
+
+    return ring_id;
+}
+
+void
+gv_area_layer_edit_done(GvAreaLayer *layer)
+{
+    layer->edit_ring = -1;
+}
+
+/*******************************************************/
+
+static void
+gv_area_layer_draw(GvAreaLayer *layer, GvViewArea *view)
+{
+    gint i, r, areas, rings;
+    gint *selected, presentation;
+    gint hit_selected = FALSE;
+    GvArea *area;
+    GArray *ring;
+
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    areas = gv_areas_num_areas(layer->data);
+
+    glColor4fv(GV_SHAPE_LAYER(layer)->color);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_BLEND);
+    for (i=0; i < areas; ++i)
+    {
+        int     fill_object;
+        
+	if (selected[i] && !presentation)
+	{
+	    continue;
+	}
+
+	area = gv_areas_get_area(layer->data, i);
+
+        if( area->fill != NULL )
+            glVertexPointer(2, GL_GEOCOORD, 0, area->fill->data );
+        for( fill_object = 0; fill_object < area->fill_objects; fill_object++ )
+        {
+            int f_offset=g_array_index(area->mode_offset,gint,fill_object*2+1);
+            int f_mode = g_array_index(area->mode_offset,gint,fill_object*2);
+            int f_len;
+
+            if( fill_object == area->fill_objects-1 )
+              f_len = area->fill->len - f_offset;
+            else
+              f_len = g_array_index(area->mode_offset,gint,fill_object*2+3)
+                - f_offset;
+            
+	    glDrawArrays(f_mode, f_offset, f_len);
+	}
+    }
+    glDisable(GL_BLEND);
+
+    for (i=0; i < areas; ++i)
+    {
+	if (selected[i] && !presentation)
+	{
+	    hit_selected = 1;
+	    continue;
+	}
+
+	area = gv_areas_get_area(layer->data, i);
+	rings = gv_areas_num_rings(area);
+
+	for (r=0; r < rings; ++r)
+	{
+	    ring = gv_areas_get_ring(area, r);
+	    
+	    glVertexPointer(2, GL_GEOCOORD, 0, ring->data);
+	    glDrawArrays(GL_LINE_LOOP, 0, ring->len);
+	}	
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);
+
+    if (hit_selected && ! GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+	gv_area_layer_draw_selected(layer, view);
+    }    
+}
+
+static void
+gv_area_layer_draw_selected(GvAreaLayer *layer, GvViewArea *view)
+{
+    gint i, r, areas, rings;
+    gint *selected;
+    GvArea *area;
+    GArray *ring;
+    GLenum mode;
+
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    areas = gv_areas_num_areas(layer->data);
+
+    glColor4fv(GV_SHAPE_LAYER(layer)->color);
+    
+    glEnableClientState(GL_VERTEX_ARRAY);
+
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_BLEND);
+    for (i=0; i < areas; ++i)
+    {
+	if (selected[i])
+	{
+            int   fill_object;
+            
+	    area = gv_areas_get_area(layer->data, i);
+            if( area->fill != NULL )
+              glVertexPointer(2, GL_GEOCOORD, 0, area->fill->data );
+            
+            for( fill_object = 0;
+                 fill_object < area->fill_objects;
+                 fill_object++ )
+            {
+                int f_offset =
+                  g_array_index(area->mode_offset,gint,fill_object*2+1);
+                int f_mode =
+                  g_array_index(area->mode_offset,gint,fill_object*2);
+                int f_len;
+                
+                if( fill_object == area->fill_objects-1 )
+                  f_len = area->fill->len - f_offset;
+                else
+                  f_len = g_array_index(area->mode_offset,gint,fill_object*2+3)
+                    - f_offset;
+                
+                glDrawArrays(f_mode, f_offset, f_len);
+		
+#ifdef SHOW_TESS_LINES		    
+                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                glColor4f(1.0,1.0,1.0,1.0);
+                glDrawArrays(f_mode, f_offset, f_len);
+                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                glColor4f(GV_SHAPE_LAYER(layer)->color);
+#endif /* SHOW_TESS_LINES */		    
+	    }
+	}
+    }
+    glDisable(GL_BLEND);
+    
+    glPointSize(3.0);
+    for (i=0; i < areas; ++i)
+    {
+	if (selected[i])
+	{
+	    area = gv_areas_get_area(layer->data, i);
+	    rings = gv_areas_num_rings(area);
+	    
+	    for (r=0; r < rings; ++r)
+	    {
+		if (r == layer->edit_ring)
+		{
+		    mode = GL_LINE_STRIP;
+		}
+		else
+		{
+		    mode = GL_LINE_LOOP;
+		}
+		
+		ring = gv_areas_get_ring(area, r);
+		
+		glVertexPointer(2, GL_GEOCOORD, 0, ring->data);
+		glDrawArrays(mode, 0, ring->len);
+
+#ifdef HAVE_BROKEN_GL_POINTS
+		{
+		    int j;
+		    for (j=0; j < ring->len; ++j)
+		    {
+			glBegin(GL_POINTS);
+			glArrayElement(j);
+			glEnd();
+		    }
+		}
+#else
+		glDrawArrays(GL_POINTS, 0, ring->len);
+#endif		
+	    }
+	}
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+static void
+gv_area_layer_pick_shape(GvAreaLayer *layer)
+{
+    gint i, areas;
+    GvArea *area;
+
+    if (!gv_layer_is_visible(GV_LAYER(layer))) return;
+
+    areas = gv_areas_num_areas(layer->data);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    for (i=0; i < areas; ++i)
+    {
+        int    fill_object;
+        
+	glLoadName(i);
+	area = gv_areas_get_area(layer->data, i);
+
+        if( area->fill != NULL )
+          glVertexPointer(2, GL_GEOCOORD, 0, area->fill->data );
+        for( fill_object = 0; fill_object < area->fill_objects; fill_object++ )
+        {
+            int f_offset=g_array_index(area->mode_offset,gint,fill_object*2+1);
+            int f_mode = g_array_index(area->mode_offset,gint,fill_object*2);
+            int f_len;
+
+            if( fill_object == area->fill_objects-1 )
+              f_len = area->fill->len - f_offset;
+            else
+              f_len = g_array_index(area->mode_offset,gint,fill_object*2+3)
+                - f_offset;
+            
+	    glDrawArrays(f_mode, f_offset, f_len);
+	}
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+static void
+gv_area_layer_pick_node(GvAreaLayer *layer)
+{
+    GvArea *area;
+    GArray *ring;
+    gint sel, rings, r, i;
+
+    if (!gv_layer_is_visible(GV_LAYER(layer))) return;
+
+    if (!gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_FIRST, &sel))
+    {
+	return;
+    }
+    area = gv_areas_get_area(layer->data, sel);
+    rings = gv_areas_num_rings(area);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    
+    /* Nodes first */
+    glLoadName(0);
+    glPushName(-1);
+    glPointSize(1.0);
+    for (r=0; r < rings; ++r)
+    {
+	glLoadName(r);
+	glPushName(-1);
+	
+	ring = gv_areas_get_ring(area, r);
+	glVertexPointer(2, GL_GEOCOORD, 0, ring->data);
+	for (i=0; i < ring->len; ++i)
+	{
+	    glLoadName(i);
+	    glBegin(GL_POINTS);
+	    glArrayElement(i);
+	    glEnd();
+	}
+	glPopName(); /* node id */
+    }
+    glPopName(); /* ring id */
+
+    /* Segments next */
+    glLoadName(1);
+    glPushName(-1);
+    for (r=0; r < rings; ++r)
+    {
+	glLoadName(r);
+	
+	ring = gv_areas_get_ring(area, r);
+	glVertexPointer(2, GL_GEOCOORD, 0, ring->data);
+
+	/* First segment connects last vertex to first vertex:
+	   "before" vertex zero */
+	glPushName(0);
+	glBegin(GL_LINES);
+	glArrayElement(ring->len - 1);
+	glArrayElement(0);
+	glEnd();	
+	for (i=1; i < ring->len; ++i)
+	{
+	    glLoadName(i);
+	    glBegin(GL_LINES);
+	    glArrayElement(i-1);
+	    glArrayElement(i);
+	    glEnd();
+	}
+	glPopName(); /* node id */
+    }
+    glPopName(); /* ring id */
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+static void
+gv_area_layer_delete_selected(GvAreaLayer *layer)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+	/* This will force a selection clear */
+	gv_areas_delete_areas(layer->data, sel->len, (gint*)sel->data);
+    }
+    g_array_free(sel, TRUE);
+}
+
+static void
+gv_area_layer_translate_selected(GvAreaLayer *layer, GvVertex *delta)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+	/* This will force a selection clear */
+	gv_areas_translate_areas(layer->data, sel->len, (gint*)sel->data,
+				 delta->x, delta->y);
+    }
+    g_array_free(sel, TRUE);
+}
+
+static void
+gv_area_layer_get_node(GvAreaLayer *layer, GvNodeInfo *info)
+{
+    info->vertex = gv_areas_get_node(layer->data, info->shape_id,
+				     info->ring_id, info->node_id);
+}
+
+static void
+gv_area_layer_move_node(GvAreaLayer *layer, GvNodeInfo *info)
+{
+    gv_areas_move_node(layer->data, info->shape_id, info->ring_id,
+		       info->node_id, info->vertex);
+}
+
+static void
+gv_area_layer_insert_node(GvAreaLayer *layer, GvNodeInfo *info)
+{
+    gv_areas_insert_nodes(layer->data, info->shape_id, info->ring_id,
+			  info->node_id, 1, info->vertex);
+}
+
+static void
+gv_area_layer_delete_node(GvAreaLayer *layer, GvNodeInfo *info)
+{
+    gv_areas_clear_fill(layer->data, info->shape_id);
+    gv_areas_delete_nodes(layer->data, info->shape_id, info->ring_id,
+			  1, &info->node_id);
+}
+
+static void
+gv_area_layer_node_motion(GvAreaLayer *layer, gint area_id)
+{
+    gv_areas_clear_fill(layer->data, area_id);
+}
+
+static void
+gv_area_layer_extents(GvAreaLayer *layer, GvRect *rect)
+{
+    gv_areas_get_extents(layer->data, rect);
+}
+
+static void
+gv_area_layer_data_change(GvAreaLayer *layer, gpointer change_info)
+{
+    /* Reset the selected array to reflect the data length */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_areas_num_areas(layer->data));
+}

Added: packages/openev/branches/upstream/current/gvarealayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvarealayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvarealayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * $Id: gvarealayer.h,v 1.4 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer of GvAreas.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvarealayer.h,v $
+ * Revision 1.4  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_AREA_LAYER_H__
+#define __GV_AREA_LAYER_H__
+
+#include "gvshapelayer.h"
+#include "gvareas.h"
+
+#define GV_TYPE_AREA_LAYER            (gv_area_layer_get_type ())
+#define GV_AREA_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_AREA_LAYER, GvAreaLayer))
+#define GV_AREA_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_AREA_LAYER, GvAreaLayerClass))
+#define GV_IS_AREA_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_AREA_LAYER))
+#define GV_IS_AREA_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_AREA_LAYER))
+
+typedef struct _GvAreaLayer       GvAreaLayer;
+typedef struct _GvAreaLayerClass  GvAreaLayerClass;
+
+struct _GvAreaLayer
+{
+    GvShapeLayer shape_layer;
+
+    GvAreas *data;
+    gint edit_ring;
+};
+
+struct _GvAreaLayerClass
+{
+    GvShapeLayerClass parent_class;
+};
+
+GtkType gv_area_layer_get_type(void);
+GtkObject* gv_area_layer_new(GvAreas *data);
+
+gint gv_area_layer_select_new_area(GvAreaLayer *layer);
+gint gv_area_layer_select_new_ring(GvAreaLayer *layer, gint area_id);
+void gv_area_layer_edit_done(GvAreaLayer *layer);
+
+#endif /* __GV_AREA_LAYER_H__ */

Added: packages/openev/branches/upstream/current/gvareas.c
===================================================================
--- packages/openev/branches/upstream/current/gvareas.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvareas.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,744 @@
+/******************************************************************************
+ * $Id: gvareas.c,v 1.15 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Data container of areas (superceeded by GvShapes-will be removed)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvareas.c,v $
+ * Revision 1.15  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.14  2000/06/20 13:26:54  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gextra.h"
+#include "gvareas.h"
+#include "gvtess.h"
+
+typedef struct _GvAreasMemento GvAreasMemento;
+
+struct _GvAreasMemento
+{
+    GvDataMemento base;
+    GArray *area_ids;
+    GPtrArray *areas;
+};
+
+static void gv_areas_class_init(GvAreasClass *klass);
+static void gv_areas_init(GvAreas *pgdata);
+static void gv_areas_replace_areas(GvAreas *areas, gint num_areas, gint *area_id, GvArea **area);
+static void gv_areas_insert_areas(GvAreas *areas, gint num_areas, gint *area_ids, GvArea **area);
+static void gv_areas_get_memento(GvData *data, gpointer info, GvDataMemento **memento);
+static void gv_areas_set_memento(GvData *data, GvDataMemento *memento);
+static void gv_areas_del_memento(GvData *data, GvDataMemento *memento);
+static void gv_areas_changed(GvData *data, gpointer change_info);
+static void gv_areas_finalize(GtkObject *object);
+
+GtkType
+gv_areas_get_type(void)
+{
+    static GtkType areas_type = 0;
+
+    if (!areas_type)
+    {
+	static const GtkTypeInfo areas_info =
+	{
+	    "GvAreas",
+	    sizeof(GvAreas),
+	    sizeof(GvAreasClass),
+	    (GtkClassInitFunc) gv_areas_class_init,
+	    (GtkObjectInitFunc) gv_areas_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	areas_type = gtk_type_unique(gv_data_get_type(), &areas_info);
+    }
+    return areas_type;
+}
+
+static void
+gv_areas_init(GvAreas *areas)
+{
+    areas->areas = g_ptr_array_new();
+    areas->extents_valid = FALSE;
+}
+
+static void
+gv_areas_class_init(GvAreasClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    object_class = (GtkObjectClass*) klass;
+    data_class = (GvDataClass*) klass;
+
+    object_class->finalize = gv_areas_finalize;
+    
+    data_class->changed = gv_areas_changed;
+    data_class->get_memento = gv_areas_get_memento;
+    data_class->set_memento = gv_areas_set_memento;
+    data_class->del_memento = gv_areas_del_memento;
+}
+
+GvData *
+gv_areas_new(void)
+{
+    return GV_DATA(gtk_type_new(gv_areas_get_type()));
+}
+
+gint
+gv_areas_new_area(GvAreas *areas)
+{
+    return gv_areas_new_area_with_data(areas, NULL);
+}
+
+gint
+gv_areas_new_area_with_data(GvAreas *areas, GvArea *area_data)
+{
+    GvArea *area;
+    int area_id;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    area_id = areas->areas->len;
+    if (area_data)
+    {
+	area = gv_area_copy(area_data);
+    }
+    else
+    {
+	area = gv_area_new(TRUE);
+    }
+    g_return_val_if_fail(area, 0);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    g_ptr_array_add(areas->areas, area);
+    
+    gv_data_changed(GV_DATA(areas), &change_info);
+    
+    /* Generate tesselation if necessary */
+    if (area_data)
+    {
+	gv_areas_tessellate_areas(areas, 1, &area_id);
+    }
+
+    return area_id;
+}
+
+void
+gv_areas_delete_areas(GvAreas *areas, gint num_areas, gint *area_id)
+{
+    GvArea *area;
+    GvShapeChangeInfo change_info = {GV_CHANGE_DELETE, 0, NULL};
+
+    change_info.num_shapes = num_areas;
+    change_info.shape_id = area_id;
+    
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    if (num_areas == 1)
+    {
+	area = (GvArea*)g_ptr_array_remove_index_fast(areas->areas, *area_id);
+	if (area) gv_area_delete(area);
+    }
+    else
+    {
+	/* Strategy: sort the area_id buffer and delete lines in descending
+	   order, so that indicies remain valid */
+	gint *id, i;
+
+	id = g_memdup_type(area_id, gint, num_areas);
+	g_sort_type(id, gint, num_areas);
+
+	for (i=num_areas-1; i >= 0; --i)
+	{
+	    area = (GvArea*)g_ptr_array_remove_index_fast(areas->areas, id[i]);
+	    if (area) gv_area_delete(area);
+	}
+	g_free(id);
+    }
+    gv_data_changed(GV_DATA(areas), &change_info);
+}
+
+void
+gv_areas_translate_areas(GvAreas *areas, gint num_areas, gint *area_id,
+						 gvgeocoord dx, gvgeocoord dy)
+{
+    int i, j, k;
+    GArray *ring;
+    GvArea *area;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_areas;
+    change_info.shape_id = area_id;
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+
+    for (k=0; k < num_areas; ++k)
+    {
+	g_return_if_fail(area_id[k] >= 0 && area_id[k] < areas->areas->len);
+	area = gv_areas_get_area(areas, area_id[k]);
+	
+	for (i=0; i < area->rings->len; ++i)
+	{
+	    ring = gv_areas_get_ring(area, i);
+	    for (j=0; j < ring->len; ++j)
+	    {
+		g_array_index(ring, GvVertex, j).x += dx;
+		g_array_index(ring, GvVertex, j).y += dy;
+	    }
+	}
+	
+	if (area->fill_objects > 0)
+	{
+	    for (i=0; i < area->fill->len; ++i)
+	    {
+		g_array_index(area->fill, GvVertex, i).x += dx;
+		g_array_index(area->fill, GvVertex, i).y += dy;
+	    }
+	}
+    }
+    
+    gv_data_changed(GV_DATA(areas), &change_info);
+}
+
+gint
+gv_areas_new_ring(GvAreas *areas, gint area_id)
+{
+    GArray *ring;
+    GvArea *area;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    g_return_val_if_fail(area_id >= 0 && area_id < areas->areas->len, 0);
+    area = gv_areas_get_area(areas, area_id);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    ring = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+    g_ptr_array_add(area->rings, ring);
+
+    gv_areas_clear_fill(areas, area_id);
+    area->fill_objects = -1;
+    
+    gv_data_changed(GV_DATA(areas), &change_info);
+    
+    return area->rings->len - 1;
+}
+
+void
+gv_areas_delete_ring(GvAreas *areas, gint area_id, gint ring_id)
+{
+    GvArea *area;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    /* Can't delete ring 0 (outer ring) */
+    g_return_if_fail(ring_id > 0);
+    
+    g_return_if_fail(area_id >= 0 && area_id < areas->areas->len);
+    area = gv_areas_get_area(areas, area_id);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    g_ptr_array_remove_index(area->rings, ring_id);
+
+    gv_data_changed(GV_DATA(areas), &change_info);    
+}
+
+void
+gv_areas_append_nodes(GvAreas *areas, gint area_id, gint ring_id,
+		      gint num_nodes, GvVertex *vertex)
+{
+    GvArea *area;
+    GArray *ring;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    g_return_if_fail(area_id >= 0 && area_id < areas->areas->len);
+    area = gv_areas_get_area(areas, area_id);
+    g_return_if_fail(ring_id >= 0 && ring_id < area->rings->len);
+    ring = gv_areas_get_ring(area, ring_id);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    g_array_append_vals(ring, vertex, num_nodes);
+
+    gv_data_changed(GV_DATA(areas), &change_info);    
+}
+
+GvVertex *
+gv_areas_get_node(GvAreas *areas, gint area_id, gint ring_id, gint node_id)
+{
+    GvArea *area;
+    GArray *ring;
+
+    g_return_val_if_fail(area_id >= 0 && area_id < areas->areas->len, NULL);
+    area = gv_areas_get_area(areas, area_id);
+    g_return_val_if_fail(ring_id >= 0 && ring_id < area->rings->len, NULL);
+    ring = gv_areas_get_ring(area, ring_id);
+    g_return_val_if_fail(node_id >= 0 && node_id < ring->len, NULL);
+    
+    return &g_array_index(ring, GvVertex, node_id);
+}
+
+void
+gv_areas_move_node(GvAreas *areas, gint area_id, gint ring_id, gint node_id,
+		   GvVertex *vertex)
+{
+    GvArea *area;
+    GArray *ring;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    g_return_if_fail(area_id >= 0 && area_id < areas->areas->len);
+    area = gv_areas_get_area(areas, area_id);
+    g_return_if_fail(ring_id >= 0 && ring_id < area->rings->len);
+    ring = gv_areas_get_ring(area, ring_id);
+    g_return_if_fail(node_id >= 0 && node_id < ring->len);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    g_array_index(ring, GvVertex, node_id).x = vertex->x;
+    g_array_index(ring, GvVertex, node_id).y = vertex->y;
+
+    gv_data_changed(GV_DATA(areas), &change_info);
+}
+
+void
+gv_areas_insert_nodes(GvAreas *areas, gint area_id, gint ring_id,
+		      gint node_id, gint num_nodes, GvVertex *vertex)
+{
+    GvArea *area;
+    GArray *ring;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    g_return_if_fail(area_id >= 0 && area_id < areas->areas->len);
+    area = gv_areas_get_area(areas, area_id);
+    g_return_if_fail(ring_id >= 0 && ring_id < area->rings->len);
+    ring = gv_areas_get_ring(area, ring_id);
+    g_return_if_fail(node_id >= 0 && node_id < ring->len);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    g_array_insert_vals(ring, node_id, vertex, num_nodes);
+
+    gv_data_changed(GV_DATA(areas), &change_info);
+}
+
+void
+gv_areas_delete_nodes(GvAreas *areas, gint area_id, gint ring_id,
+		      gint num_nodes, gint *node_id)
+{
+    GvArea *area;
+    GArray *ring;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &area_id;
+
+    g_return_if_fail(area_id >= 0 && area_id < areas->areas->len);
+    area = gv_areas_get_area(areas, area_id);
+    g_return_if_fail(ring_id >= 0 && ring_id < area->rings->len);
+    ring = gv_areas_get_ring(area, ring_id);
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    if (num_nodes == 1)
+    {
+	/* Need to preserve node order, so we can't use *_remove_fast */
+	g_array_remove_index(ring, *node_id);
+    }
+    else
+    {
+	/* Strategy: sort the node_id buffer and delete nodes in desending
+	   order, so that indicies remain valid */
+	gint *id, i;
+
+	id = g_memdup_type(node_id, gint, num_nodes);
+	g_sort_type(id, gint, num_nodes);
+
+	for (i=num_nodes-1; i >= 0; --i)
+	{
+	    g_array_remove_index(ring, id[i]);
+	}
+	g_free(id);
+    }
+    gv_data_changed(GV_DATA(areas), &change_info);
+}
+
+gint
+gv_areas_tessellate_areas(GvAreas *areas, gint num_areas, gint *area_id)
+{
+    gint i, hit, ok;
+    GvArea *area;
+
+    hit = FALSE;
+    ok = TRUE;
+
+    for (i=0; i < num_areas; ++i)
+    {
+	area = gv_areas_get_area(areas, area_id[i]);
+	if (area->fill_objects == 0)
+	{
+	    if (!gv_area_tessellate(area)) ok = FALSE;
+	    hit = TRUE;	    
+	}
+    }
+
+    if (hit)
+    {
+	/* FIXME: need a change_info param which describes a
+	   tesselation change.  For now use NULL */
+	gv_data_changed(GV_DATA(areas), NULL);
+    }
+
+    return ok;
+}
+
+void
+gv_areas_clear_fill(GvAreas *areas, gint area_id)
+{
+    GvArea *area;
+
+    g_return_if_fail(area_id >= 0 && area_id < areas->areas->len);
+    area = gv_areas_get_area(areas, area_id);
+    area->fill_objects = 0;
+    if (area->mode_offset) g_array_set_size(area->mode_offset, 0);
+    if (area->fill) g_array_set_size(area->fill, 0);
+}
+
+void
+gv_areas_get_extents(GvAreas *areas, GvRect *rect)
+{
+    if (!areas->extents_valid)
+    {
+	gint i, j, num_areas;
+	GvArea *area;
+	GArray *ring = NULL;
+	GvVertex vmax, vmin, *v;
+
+	vmin.x = vmin.y = GV_MAXFLOAT;
+	vmax.x = vmax.y = -GV_MAXFLOAT;
+	num_areas = gv_areas_num_areas(areas);
+	for (i=0; i < num_areas; ++i)
+	{
+	    area = gv_areas_get_area(areas, i);
+	    ring = gv_areas_get_ring(area, 0);
+	    for (j = 0; j < ring->len; ++j)
+	    {
+		v = &g_array_index(ring, GvVertex, j);
+		if (v->x < vmin.x) vmin.x = v->x;
+		if (v->x > vmax.x) vmax.x = v->x;
+		if (v->y < vmin.y) vmin.y = v->y;
+		if (v->y > vmax.y) vmax.y = v->y;
+	    }
+	}
+	if (num_areas == 0 || (num_areas == 1 && ring->len == 0))
+	{
+	    areas->extents.x = 0;
+	    areas->extents.y = 0;
+	    areas->extents.width = 0;
+	    areas->extents.height = 0;
+	}
+	else
+	{
+	    areas->extents.x = vmin.x;
+	    areas->extents.y = vmin.y;
+	    areas->extents.width = vmax.x - vmin.x;
+	    areas->extents.height = vmax.y - vmin.y;
+	}	    
+	areas->extents_valid = TRUE;
+    }
+    rect->x = areas->extents.x;
+    rect->y = areas->extents.y;
+    rect->width = areas->extents.width;
+    rect->height = areas->extents.height;
+}
+
+/***************************************************/
+
+GvArea *
+gv_area_new(gint alloc_outer_ring)
+{
+    GvArea *area;    
+
+    area = g_new(GvArea, 1);
+    g_return_val_if_fail(area, NULL);
+    
+    area->rings = g_ptr_array_new();
+    g_return_val_if_fail(area->rings, NULL);
+
+    if (alloc_outer_ring)
+    {
+	/* New area is created with ring 0 (outer ring) allocated, but empty */
+	GArray *ring = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+	g_return_val_if_fail(ring, NULL);
+	g_ptr_array_add(area->rings, ring);
+    }
+    
+    area->fill_objects = -1;
+    area->mode_offset = NULL;
+    area->fill = NULL;
+
+    return area;
+}    
+
+GvArea *
+gv_area_copy(GvArea *area)
+{
+    GvArea *copyarea;
+    GArray *ring, *copyring;
+    gint rings, i;
+
+    copyarea = g_new(GvArea, 1);
+    g_return_val_if_fail(copyarea, NULL);
+    
+    copyarea->rings = g_ptr_array_new();
+    g_return_val_if_fail(copyarea->rings, NULL);
+
+    rings = gv_areas_num_rings(area);
+    for (i=0; i < rings; ++i)
+    {
+	copyring = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+	g_ptr_array_add(copyarea->rings, copyring);
+
+	ring = gv_areas_get_ring(area, i);
+	g_array_append_vals(copyring, ring->data, ring->len);
+    }
+
+    /* Don't bother to copy tessellation info, as this can be regenerated */
+    copyarea->fill_objects = 0;
+    copyarea->mode_offset = NULL;
+    copyarea->fill = NULL;
+
+    return copyarea;
+}
+
+void
+gv_area_delete(GvArea *area)
+{
+    int i;
+
+    for (i=0; i < area->rings->len; ++i)
+    {
+	g_array_free(gv_areas_get_ring(area, i), TRUE);
+    }
+    g_ptr_array_free(area->rings, TRUE);
+
+    if (area->fill)
+    {
+	g_array_free(area->fill, TRUE);
+    }
+
+    if (area->mode_offset)
+    {
+	g_array_free(area->mode_offset, TRUE);
+    }
+
+    g_free(area);
+}
+
+/***************************************************/
+
+static void
+gv_areas_replace_areas(GvAreas *areas, gint num_areas, gint *area_id,
+		       GvArea **area)
+{
+    int i;
+    
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_areas;
+    change_info.shape_id = area_id;
+    
+    gv_data_changing(GV_DATA(areas), &change_info);
+
+    for (i=0; i < num_areas; ++i)
+    {
+	gv_area_delete(gv_areas_get_area(areas, area_id[i]));
+        g_ptr_array_index(areas->areas, area_id[i]) = area[i];
+    }
+    
+    gv_data_changed(GV_DATA(areas), &change_info);    
+}
+
+static void
+gv_areas_insert_areas(GvAreas *areas, gint num_areas, gint *area_ids,
+		      GvArea **area)
+{
+    /* The area_id array must be in ascending order! */
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 0, NULL};
+
+    change_info.num_shapes = num_areas;
+    change_info.shape_id = area_ids;
+
+    gv_data_changing(GV_DATA(areas), &change_info);
+    
+    for (i=0; i < num_areas; ++i)
+    {
+	g_ptr_array_insert_fast(areas->areas, area_ids[i], area[i]);
+    }
+
+    gv_data_changed(GV_DATA(areas), &change_info);    
+}
+
+static void
+gv_areas_get_memento(GvData *data, gpointer change_info,
+		     GvDataMemento **memento)
+{
+    GvAreas *areas = GV_AREAS(data);
+    GvShapeChangeInfo *info = (GvShapeChangeInfo *) change_info;
+    GvAreasMemento *mem;
+    int i;
+
+    mem = g_new(GvAreasMemento, 1);
+    mem->base.data = data;
+    mem->base.type = info->change_type;
+
+    mem->area_ids = g_array_new(FALSE, FALSE, sizeof(gint));
+    g_array_append_vals(mem->area_ids, info->shape_id,	info->num_shapes);
+    
+    /* Grab areas in ascending order */
+    if (info->num_shapes > 1)
+    {
+	g_sort_type(mem->area_ids->data, gint, mem->area_ids->len);
+    }
+
+    if (info->change_type == GV_CHANGE_ADD)
+    {
+	mem->areas = NULL;
+    }
+    else
+    {
+	mem->areas = g_ptr_array_new();
+	for (i=0; i < info->num_shapes; ++i)
+	{
+	    GvArea *area = gv_area_copy(gv_areas_get_area(areas,
+							  info->shape_id[i]));
+	    g_ptr_array_add(mem->areas, area);
+	}
+    }
+    
+    *memento = (GvDataMemento*)mem;
+}
+
+static void
+gv_areas_set_memento(GvData *data, GvDataMemento *data_memento)
+{
+    GvAreasMemento *memento = (GvAreasMemento *) data_memento;
+    GvAreas *areas = GV_AREAS(data);
+
+    switch (memento->base.type)
+    {
+	case GV_CHANGE_ADD:
+	    gv_areas_delete_areas(areas, memento->area_ids->len,
+				  (gint*)memento->area_ids->data);
+	    break;
+
+	case GV_CHANGE_REPLACE:
+	    gv_areas_replace_areas(areas, memento->area_ids->len,
+				  (gint*)memento->area_ids->data,
+				  (GvArea**)memento->areas->pdata);
+	    break;
+		
+	case GV_CHANGE_DELETE:
+	    gv_areas_insert_areas(areas, memento->area_ids->len,
+				  (gint*)memento->area_ids->data,
+				  (GvArea**)memento->areas->pdata);
+	    break;
+    }
+
+    if (memento->areas)
+    {
+	g_ptr_array_free(memento->areas, TRUE);
+	memento->areas = NULL;
+    }
+    gv_areas_del_memento(GV_DATA(areas), (GvDataMemento *) memento);
+}
+
+static void
+gv_areas_del_memento(GvData *data, GvDataMemento *data_memento)
+{
+    GvAreasMemento *memento = (GvAreasMemento *) data_memento;
+    int i;
+    
+    if (memento->areas)
+    {
+	for (i=0; i < memento->areas->len; ++i)
+	{
+	    gv_area_delete(g_ptr_array_index(memento->areas, i));
+	}
+	g_ptr_array_free(memento->areas, TRUE);
+    }
+    g_array_free(memento->area_ids, TRUE);
+    g_free(memento);    
+}
+
+static void
+gv_areas_changed(GvData *data, gpointer change_info)
+{
+    GvAreas *areas = GV_AREAS(data);
+    GvShapeChangeInfo *info = (GvShapeChangeInfo *) change_info;
+
+    /* NULL info indicates change due to tesselation: ignore */
+    if (info && info->change_type == GV_CHANGE_REPLACE)
+    {
+	/* Retesselate changed areas if necessary */
+	gv_areas_tessellate_areas(areas, info->num_shapes, info->shape_id);
+    }
+    areas->extents_valid = FALSE;
+}
+
+static void
+gv_areas_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvAreas *areas;
+    int i;
+
+    areas = GV_AREAS(object);
+    
+    for (i=0; i < gv_areas_num_areas(areas); i++)
+    {
+	gv_area_delete(gv_areas_get_area(areas, i));
+    }
+    g_ptr_array_free(areas->areas, TRUE);
+    
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}
+

Added: packages/openev/branches/upstream/current/gvareas.h
===================================================================
--- packages/openev/branches/upstream/current/gvareas.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvareas.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,107 @@
+/******************************************************************************
+ * $Id: gvareas.h,v 1.11 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Data container of areas (superceeded by GvShapes-will be removed)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvareas.h,v $
+ * Revision 1.11  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.10  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_AREAS_H__
+#define __GV_AREAS_H__
+
+#include "gvdata.h"
+
+#define GV_TYPE_AREAS            (gv_areas_get_type ())
+#define GV_AREAS(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_AREAS, GvAreas))
+#define GV_AREAS_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_AREAS, GvAreasClass))
+#define GV_IS_AREAS(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_AREAS))
+#define GV_IS_AREAS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_AREAS))
+
+typedef struct _GvArea        GvArea;
+typedef struct _GvAreas       GvAreas;
+typedef struct _GvAreasClass  GvAreasClass;
+
+struct _GvArea
+{
+    GPtrArray *rings;
+    gint      fill_objects;
+    GArray    *mode_offset;
+    GArray    *fill;
+};
+
+struct _GvAreas
+{
+    GvData data;
+
+    GPtrArray *areas;
+    GvRect extents;
+    guint extents_valid : 1;
+};
+
+struct _GvAreasClass
+{
+    GvDataClass parent_class;
+};
+
+GvArea* gv_area_new(gint alloc_outer_ring);
+GvArea* gv_area_copy(GvArea *area);
+void gv_area_delete(GvArea *area);
+
+GtkType    gv_areas_get_type (void);
+
+GvData* gv_areas_new(void);
+gint gv_areas_new_area(GvAreas *areas);
+gint gv_areas_new_area_with_data(GvAreas *areas, GvArea *area_data);
+
+void gv_areas_delete_areas(GvAreas *areas, gint num_areas, gint *area_id);
+void gv_areas_translate_areas(GvAreas *areas, gint num_areas, gint *area_id, gvgeocoord dx, gvgeocoord dy);
+
+gint gv_areas_new_ring(GvAreas *areas, gint area_id);
+void gv_areas_delete_ring(GvAreas *areas, gint area_id, gint ring_id);
+void gv_areas_append_nodes(GvAreas *areas, gint area_id, gint ring_id, gint num_nodes, GvVertex *vertex);
+GvVertex* gv_areas_get_node(GvAreas *areas, gint area_id, gint ring_id, gint node_id);
+void gv_areas_move_node(GvAreas *areas, gint area_id, gint ring_id, gint node_id, GvVertex *vertex);
+void gv_areas_insert_nodes(GvAreas *areas, gint area_id, gint ring_id, gint node_id, gint num_nodes, GvVertex *vertex);
+void gv_areas_delete_nodes(GvAreas *areas, gint area_id, gint ring_id, gint num_nodes, gint *node_id);
+
+gint gv_areas_tessellate_areas(GvAreas *areas, gint num_areas, gint *area_id);
+void gv_areas_clear_fill(GvAreas *areas, gint area_id);
+void gv_areas_get_extents(GvAreas *areas, GvRect *rect);
+
+#define gv_areas_num_areas(adata) \
+     (adata->areas->len)
+#define gv_areas_get_area(adata,id) \
+     ((GvArea*)g_ptr_array_index(adata->areas, id))
+#define gv_areas_num_rings(area) \
+     (area->rings->len)
+#define gv_areas_get_ring(area,id) \
+     ((GArray*)g_ptr_array_index(area->rings, id))
+
+#endif /*__GV_AREAS_H__ */

Added: packages/openev/branches/upstream/current/gvareatool.c
===================================================================
--- packages/openev/branches/upstream/current/gvareatool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvareatool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,649 @@
+/******************************************************************************
+ * $Id: gvareatool.c,v 1.25 2004/10/28 21:59:52 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Area (Polygon) editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvareatool.c,v $
+ * Revision 1.25  2004/10/28 21:59:52  gmwalter
+ * Send a changed signal when editing finishes.
+ *
+ * Revision 1.24  2004/01/27 19:51:34  gmwalter
+ * Make sure memento pointers are set to NULL
+ * after being freed.
+ *
+ * Revision 1.23  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.22  2002/10/01 15:06:38  warmerda
+ * ensure old selection clear when selecting new object
+ *
+ * Revision 1.21  2002/09/30 13:24:54  warmerda
+ * ensure areas are closed properly
+ *
+ * Revision 1.20  2001/08/08 17:44:12  warmerda
+ * use gv_shape_type() macro
+ *
+ * Revision 1.19  2001/04/09 18:13:51  warmerda
+ * improved warning
+ *
+ * Revision 1.18  2000/08/10 15:47:35  warmerda
+ * fixed one-off undo quirk when adding rings to existing area
+ *
+ * Revision 1.17  2000/08/08 20:58:47  warmerda
+ * recover from layer destruction
+ *
+ * Revision 1.16  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.15  2000/07/24 14:00:24  warmerda
+ * set_layer with NULL should be allowed
+ *
+ * Revision 1.14  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.13  2000/07/17 20:20:32  warmerda
+ * enable delete-last-vertex
+ *
+ * Revision 1.12  2000/06/20 13:26:54  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtksignal.h>
+#include "gvareatool.h"
+#include "gvundo.h"
+#include "gvshapeslayer.h"
+#include <GL/gl.h>
+
+static void gv_area_tool_class_init(GvAreaToolClass *klass);
+static void gv_area_tool_init(GvAreaTool *tool);
+static void gv_area_tool_draw(GvAreaTool *tool);
+static void gv_area_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_area_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_area_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_area_tool_deactivate(GvTool *tool, GvViewArea *view);
+static void gv_area_tool_stop_drawing(GvAreaTool *tool);
+static gint gv_area_tool_configure(GvAreaTool *tool);
+
+GtkType
+gv_area_tool_get_type(void)
+{
+    static GtkType area_tool_type = 0;
+
+    if (!area_tool_type)
+    {
+	static const GtkTypeInfo area_tool_info =
+	{
+	    "GvAreaTool",
+	    sizeof(GvAreaTool),
+	    sizeof(GvAreaToolClass),
+	    (GtkClassInitFunc) gv_area_tool_class_init,
+	    (GtkObjectInitFunc) gv_area_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	area_tool_type = gtk_type_unique(gv_tool_get_type(),
+					 &area_tool_info);
+    }
+    return area_tool_type;
+}
+
+static void
+gv_area_tool_class_init(GvAreaToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_area_tool_deactivate;
+    tool_class->button_press = gv_area_tool_button_press;
+    tool_class->motion_notify = gv_area_tool_motion_notify;
+    tool_class->key_press = gv_area_tool_key_press;
+}
+
+static void
+gv_area_tool_init(GvAreaTool *tool)
+{
+    GV_TOOL(tool)->cursor = gdk_cursor_new(GDK_TCROSS);
+    tool->layer = NULL;
+    tool->named_layer = NULL;
+    tool->drawing = FALSE;
+    tool->ring_id = 0;
+    tool->memento = NULL;
+}
+
+GvTool *
+gv_area_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_AREA_TOOL));
+}
+
+static gint gv_area_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvAreaTool *tool = (GvAreaTool *) data;
+
+    if( tool->layer == GV_SHAPE_LAYER(layer) )
+        gv_area_tool_set_layer( tool, NULL );
+    
+    return 0;
+}
+
+void
+gv_area_tool_set_layer(GvAreaTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	g_warning("gv_area_tool_set_layer(): inactive tool");
+	return;
+    }
+
+    if( layer != NULL && gv_data_is_read_only( GV_DATA(layer) ) )
+    {
+        g_warning( "gv_area_tool_set_layer(): layer is read-only" );
+        return;
+    }
+
+    /* Disconnect from the previous layer (for draw) */
+    if (tool->layer)
+    {
+	if (tool->drawing)
+	{
+	    gv_area_tool_stop_drawing(tool);
+	}	
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gtk_signal_disconnect_by_data(GTK_OBJECT(tool->layer), (gpointer)tool);
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+    
+    tool->layer = layer;
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+	
+        /* Redraw when the layer draws */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "draw",
+					GTK_SIGNAL_FUNC(gv_area_tool_draw),
+					GTK_OBJECT(tool));
+
+        /* recover if layer destroyed */
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy", 
+            GTK_SIGNAL_FUNC(gv_area_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+void
+gv_area_tool_set_named_layer(GvAreaTool *tool, gchar *name)
+{
+    if (tool->named_layer)
+    {
+	g_free(tool->named_layer);
+	tool->named_layer = NULL;
+    }
+    if (name)
+    {
+	tool->named_layer = g_strdup(name);	
+    }
+    /* Tool layer will be updated next time it is configured */
+}
+
+static void
+gv_area_tool_draw(GvAreaTool *tool)
+{
+    if (tool->drawing)
+    {
+	/* Color is set when the layer is drawn,
+	   so we don't need to repeat it here */
+
+	glBegin(GL_LINES);
+	glVertex2v((GLgeocoord*)&tool->v_head);
+	glVertex2v((GLgeocoord*)&tool->v_tail);
+	glEnd();
+    }
+}
+
+static void
+gv_area_tool_button_press(GvTool *rtool, GdkEventButton *event)
+{
+    GvAreaTool *tool = GV_AREA_TOOL(rtool);
+
+    /* ignore control corded buttons -- these are for zooming and panning */
+    if( event->state & GDK_CONTROL_MASK )
+        return;
+
+    if (event->button == 1)
+    {
+	gint area_id = -1;
+	
+	if (!gv_area_tool_configure(tool)) return;
+
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+	
+	if (tool->drawing)
+	{
+            gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                                     &(tool->v_tail.x), &(tool->v_tail.y) );
+
+	    /* Filter out duplicate verticies */
+	    if (tool->v_head.x == tool->v_tail.x &&
+		tool->v_head.y == tool->v_tail.y)
+	    {
+		return;
+	    }
+	    
+	    /* Add a new vertex to the area */
+	    gv_shape_layer_selected(GV_SHAPE_LAYER(tool->layer), GV_FIRST,
+				    &area_id);
+            if( area_id == -1 )
+            {
+                g_warning( "gv_area_tool_button_press: get selection failed.");
+                return;
+            }
+	}
+	else
+	{
+            if( !gv_tool_check_bounds( GV_TOOL(tool), 
+                                       tool->v_tail.x, tool->v_tail.y ) )
+                return;
+            
+	    /* Start a new ring */
+	    tool->drawing = TRUE;
+	    
+	    /* Close down undo.  We don't want undos pushed for each vertex */
+	    gv_undo_close();
+	    gv_undo_disable();
+
+	    /* If the first click is inside an existing area,
+	       start a hole ring */
+	    if (gv_shape_layer_pick_shape(GV_SHAPE_LAYER(tool->layer),
+					  GV_TOOL(tool)->view,
+					  event->x, event->y, &area_id))
+	    {
+                if( GV_IS_AREA_LAYER(tool->layer) )
+                    tool->ring_id = 
+                        gv_area_layer_select_new_ring(
+                            GV_AREA_LAYER(tool->layer), area_id);
+                else
+                {
+                    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE,1,NULL};
+
+                    GvShape     *area;
+                    GvShapes    *shapes;
+
+                    gv_shape_layer_clear_selection(
+                        GV_SHAPE_LAYER(tool->layer));
+                    gv_shape_layer_select_shape(GV_SHAPE_LAYER(tool->layer),
+                                                area_id);
+
+                    shapes = GV_SHAPES_LAYER(tool->layer)->data;
+                    area = gv_shapes_get_shape( shapes, area_id );
+                    tool->ring_id = gv_shape_get_rings( area );
+
+                    /* force a changing() signal for the shape */
+                    area = gv_shape_copy(area);
+                    gv_shapes_replace_shapes( shapes, 1, &area_id, &area, 
+                                              FALSE );
+
+                    change_info.shape_id = &area_id;
+                    if( tool->memento != NULL )
+                        gv_data_del_memento( GV_DATA(shapes), tool->memento );
+                    tool->memento = gv_data_get_memento( GV_DATA(shapes), 
+                                                         &change_info );
+                }
+	    }
+	    else
+	    {
+                /* Start a new area */
+                if( GV_IS_AREA_LAYER(tool->layer) )
+                {
+                    area_id = gv_area_layer_select_new_area(
+                        GV_AREA_LAYER(tool->layer));
+                    tool->ring_id = 0;
+                }
+                else
+                {
+                    GvShape *area = gv_shape_new( GVSHAPE_AREA );
+
+                    area_id = gv_shapes_layer_select_new_shape( 
+                        GV_SHAPES_LAYER(tool->layer), area );
+                    tool->ring_id = 0;
+                }
+	    }
+	}
+
+	tool->v_head = tool->v_tail;
+        if( GV_IS_AREA_LAYER(tool->layer) )
+            gv_areas_append_nodes(GV_AREA_LAYER(tool->layer)->data, area_id, 
+                                  tool->ring_id, 1, &tool->v_tail);
+        else
+        {
+            GvShapes *shapes = GV_SHAPES_LAYER(tool->layer)->data;
+            GvShape *area = gv_shapes_get_shape(shapes, area_id );
+
+            if( gv_shape_type(area) == GVSHAPE_AREA )
+            {
+                gv_shape_add_node(area, tool->ring_id, 
+                                  tool->v_tail.x, tool->v_tail.y, 0);
+                ((GvAreaShape *) area)->fill_objects = -2;
+            }
+            else
+                g_warning( "selected object not area in gvareatool mode!\n" );
+        }
+    }
+    else if (event->button == 3 && tool->drawing)
+    {
+	gv_area_tool_stop_drawing(tool);
+    }	
+}
+
+static void
+gv_area_tool_motion_notify(GvTool *rtool, GdkEventMotion *event)
+{
+    GvAreaTool *tool = GV_AREA_TOOL(rtool);
+
+    if (tool->drawing)
+    {
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+	
+        gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                                 &(tool->v_tail.x), &(tool->v_tail.y) );
+
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+}
+
+static void
+gv_area_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvAreaTool *tool = GV_AREA_TOOL(rtool);
+
+    if (!gv_area_tool_configure(tool)) return;
+    
+    switch (event->keyval)
+    {
+	case GDK_Delete:
+	case GDK_BackSpace:
+          if( tool->drawing && !GV_IS_AREA_LAYER(tool->layer) )
+          {
+              gint line_id;
+              GvNodeInfo  node_info;
+              GvShape     *shape;
+              GvShapes *shapes = GV_SHAPES_LAYER(tool->layer)->data;
+
+              gv_shape_layer_selected(GV_SHAPE_LAYER(tool->layer), GV_FIRST,
+                                      &line_id);
+
+              shape = gv_shapes_get_shape( shapes, line_id );
+
+              node_info.shape_id = line_id;
+              node_info.ring_id = MAX(0,gv_shape_get_rings(shape) - 1);
+              node_info.node_id = gv_shape_get_nodes( shape, 
+                                                      node_info.ring_id) - 1;
+
+              if( gv_shape_get_nodes( shape, node_info.ring_id ) > 1 )
+              {
+                  tool->v_head.x = gv_shape_get_x( shape, 
+                                                   node_info.ring_id, 
+                                                   node_info.node_id-1);
+                  tool->v_head.y = gv_shape_get_y( shape, 
+                                                   node_info.ring_id, 
+                                                   node_info.node_id-1);
+              }
+              else
+              {
+                  tool->drawing = FALSE;
+              }
+
+              gv_shape_layer_delete_node(tool->layer, &node_info );
+
+
+              if( !tool->drawing )
+                  gv_area_tool_stop_drawing( tool );
+              else
+              {
+                  shape = gv_shapes_get_shape( shapes, line_id );
+                  if( shape != NULL && gv_shape_type(shape) == GVSHAPE_AREA )
+                      ((GvAreaShape *) shape)->fill_objects = -2;
+              }
+          }
+          break;
+    }
+}
+
+static void
+gv_area_tool_deactivate(GvTool *rtool, GvViewArea *view)
+{
+    GvAreaTool *tool = GV_AREA_TOOL(rtool);
+
+    /* Disconnect from layer */
+    if (tool->layer)
+    {
+	gv_area_tool_set_layer(tool, NULL);
+    }
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+}
+
+static void
+gv_area_tool_stop_drawing(GvAreaTool *tool)
+{
+    gint sel, push_undo = TRUE;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    if( GV_IS_AREA_LAYER(tool->layer) )
+    {
+        GvAreaLayer *alayer = GV_AREA_LAYER(tool->layer);
+
+        change_info.shape_id = &sel;
+
+        tool->drawing = FALSE;
+        gv_area_layer_edit_done(alayer);
+
+        gv_shape_layer_selected(GV_SHAPE_LAYER(alayer), GV_FIRST, &sel);
+        gv_areas_clear_fill(alayer->data, sel);
+        if (!gv_areas_tessellate_areas(alayer->data, 1, &sel))
+        {
+            g_warning("Invalid area drawn");
+            if(tool->ring_id != 0 )
+            {
+                gv_areas_delete_ring(alayer->data,sel,tool->ring_id);
+                gv_areas_clear_fill(alayer->data, sel);
+                gv_areas_tessellate_areas(alayer->data, 1, &sel);
+            }
+            else
+            {
+                gv_shape_layer_delete_selected(GV_SHAPE_LAYER(alayer));
+            }
+            push_undo = FALSE;
+        }
+
+        /* Reopen undo.  Push a memento describing the ring addition */
+        gv_undo_enable();
+        gv_undo_open();
+        if (push_undo)
+        {
+            if (tool->ring_id > 0)
+            {
+                if( tool->memento != NULL )
+                {
+                    gv_undo_push(tool->memento);
+                    tool->memento = NULL;
+                }
+            }
+            else
+            {
+                gv_undo_push(gv_data_get_memento(GV_DATA(alayer->data),
+                                                 &change_info));
+            }
+        }
+
+        if( tool->memento != NULL )
+        {
+            gv_data_del_memento( GV_DATA(alayer->data), tool->memento );
+            tool->memento = NULL;
+        }
+    }
+    else /* GV_IS_SHAPES_LAYER(tool->layer) */
+    {
+        GvShapesLayer *slayer = GV_SHAPES_LAYER(tool->layer);
+        GvShapes      *shapes = slayer->data;
+        GvAreaShape   *area;
+        GvShapeChangeInfo sent_change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+        change_info.shape_id = &sel;
+
+        tool->drawing = FALSE;
+
+        if( gv_shape_layer_selected(GV_SHAPE_LAYER(slayer), GV_FIRST, &sel) )
+            area = (GvAreaShape *) gv_shapes_get_shape(shapes, sel);
+        else
+            area = NULL;
+
+        if( area != NULL && gv_shape_type(area) == GVSHAPE_AREA )
+        {
+            GvShape *shape = (GvShape *) area;
+            int   vert_count = gv_shape_get_nodes( shape, tool->ring_id );
+            /* If the ring isn't properly closed, do now. */
+            if( gv_shape_get_x( shape, tool->ring_id, 0 )
+                    != gv_shape_get_x( shape, tool->ring_id, vert_count-1 )
+                || gv_shape_get_y( shape, tool->ring_id, 0 )
+                    != gv_shape_get_y( shape, tool->ring_id, vert_count-1 )
+                || gv_shape_get_z( shape, tool->ring_id, 0 )
+                    != gv_shape_get_z( shape, tool->ring_id, vert_count-1 ) )
+            {
+                gv_shape_add_node(shape, tool->ring_id, 
+                                  gv_shape_get_x( shape, tool->ring_id, 0 ),
+                                  gv_shape_get_y( shape, tool->ring_id, 0 ),
+                                  gv_shape_get_z( shape, tool->ring_id, 0 ) );
+            }
+
+            area->fill_objects = -1;
+
+            if (!gv_area_shape_tessellate(area))
+            {
+                g_warning("Invalid area drawn");
+                if(tool->ring_id != 0 )
+                {
+                    gv_shape_delete_ring((GvShape *) area,tool->ring_id);
+                    gv_area_shape_tessellate(area);
+                }
+                else
+                {
+                    gv_shape_layer_delete_selected(GV_SHAPE_LAYER(slayer));
+                }
+                push_undo = FALSE;
+            }
+            
+            /* Reopen undo.  Push a memento describing the ring addition */
+            gv_undo_enable();
+            gv_undo_open();
+            if (push_undo)
+            {
+                if (tool->ring_id > 0)
+                {
+                    if( tool->memento != NULL )
+                    {
+                        gv_undo_push(tool->memento);
+                        tool->memento = NULL;
+                    }
+                }
+                else
+                {
+                    gv_undo_push(gv_data_get_memento(GV_DATA(shapes),
+                                                     &change_info));
+                }
+
+                /* Force a changed() signal for the finalized shape,
+                   but send it as a "replace" signal, since "add"
+                   was already sent if necessary when first node 
+                   was created. */
+                sent_change_info.shape_id = &sel;
+                gv_data_changed(GV_DATA(shapes), &sent_change_info);
+            }
+        }
+        else
+        {
+            gv_undo_enable();
+            gv_undo_open();
+        }
+
+        if( tool->memento != NULL )
+        {
+            gv_data_del_memento( GV_DATA(shapes), tool->memento );
+            tool->memento = NULL;
+        }
+
+    }
+
+    gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+}
+
+static gint
+gv_area_tool_configure(GvAreaTool *tool)
+{
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	if (tool->named_layer)
+	{
+	    /* Look for named layer if given */
+	    layer = gv_view_area_get_named_layer(GV_TOOL(tool)->view,
+						 tool->named_layer);
+	}
+	else
+	{
+	    /* Attempt to find a area layer to edit */
+	    layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+						   GV_TYPE_AREA_LAYER,
+                                                   FALSE);
+            if( layer == NULL )
+                layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+                                                       GV_TYPE_SHAPES_LAYER,
+                                                       FALSE);
+	}
+	if (!layer)
+	{
+	    g_warning("gv_area_tool_configure(): no editable area layer in view");
+	    return FALSE;
+	}
+
+	gv_area_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+
+    return tool->layer != NULL;
+}

Added: packages/openev/branches/upstream/current/gvareatool.h
===================================================================
--- packages/openev/branches/upstream/current/gvareatool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvareatool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,73 @@
+/******************************************************************************
+ * $Id: gvareatool.h,v 1.7 2000/08/10 15:56:53 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Area (Polygon) editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvareatool.h,v $
+ * Revision 1.7  2000/08/10 15:56:53  warmerda
+ * added memento
+ *
+ * Revision 1.6  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_AREA_TOOL_H__
+#define __GV_AREA_TOOL_H__
+
+#include "gvtool.h"
+#include "gvarealayer.h"
+
+#define GV_TYPE_AREA_TOOL            (gv_area_tool_get_type ())
+#define GV_AREA_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_AREA_TOOL, GvAreaTool))
+#define GV_AREA_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_AREA_TOOL, GvAreaToolClass))
+#define GV_IS_AREA_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_AREA_TOOL))
+#define GV_IS_AREA_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_AREA_TOOL))
+
+typedef struct _GvAreaTool       GvAreaTool;
+typedef struct _GvAreaToolClass  GvAreaToolClass;
+
+struct _GvAreaTool
+{
+    GvTool tool;
+
+    GvShapeLayer *layer;
+    gchar *named_layer;
+    guint drawing : 1;
+    gint ring_id;
+    GvVertex v_head, v_tail;
+    GvDataMemento   *memento;
+};
+
+struct _GvAreaToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_area_tool_get_type(void);
+GvTool* gv_area_tool_new(void);
+void gv_area_tool_set_layer(GvAreaTool *tool, GvShapeLayer *layer);
+void gv_area_tool_set_named_layer(GvAreaTool *tool, gchar *name);
+
+#endif /* __GV_AREA_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvautopan.c
===================================================================
--- packages/openev/branches/upstream/current/gvautopan.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvautopan.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2416 @@
+/******************************************************************************
+ * $Id: gvautopan.c,v 1.3 2005/09/12 15:33:10 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Auto-panning tool.  Scans systematically over an area or along
+ *           a preset path.
+ * Author:   Gillian Walter
+ *
+ * Developed by Atlantis Scientific Inc. (www.atlantis-scientific.com) for
+ * DRDC Ottawa.
+ *
+ ******************************************************************************
+ * Copyright (c) Her majesty the Queen in right of Canada as represented
+ * by the Minister of National Defence, 2004. 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvautopan.c,v $
+ * Revision 1.3  2005/09/12 15:33:10  gmwalter
+ * Update autopan tool for line paths.
+ *
+ * Revision 1.2  2005/02/22 13:22:27  gmwalter
+ * Add autopan tool.
+ *
+ * Revision 1.1.2.9  2005/02/04 19:18:45  gmwalter
+ * Updated rectangle merging.
+ *
+ * Revision 1.1.2.8  2005/01/31 21:17:16  gmwalter
+ * First pass at merging rectangles.
+ *
+ * Revision 1.1.2.7  2005/01/29 04:01:11  gmwalter
+ * Initial support for autopan trail.
+ *
+ * Revision 1.1.2.6  2005/01/21 15:38:53  gmwalter
+ * Fix some problems with speed.
+ *
+ * Revision 1.1.2.5  2005/01/20 15:20:07  gmwalter
+ * Add ability to change standard path.
+ *
+ * Revision 1.1.2.4  2005/01/17 21:27:19  gmwalter
+ * Add ability to cope with raw/georef
+ * mismatch beteen main and secondary
+ * view.
+ *
+ * Revision 1.1.2.3  2005/01/05 21:22:22  gmwalter
+ * Updated autopan tool to add more functions.
+ *
+ * Revision 1.1.2.2  2004/12/21 15:10:26  gmwalter
+ * Add ability to relocate zoomed region by
+ * dragging in secondary view.
+ *
+ * Revision 1.1.2.1  2004/12/09 16:58:28  gmwalter
+ * Add initial autopan support.
+ *
+ *
+ *
+ */
+
+/* TODO:
+ * - If more than one overview is ever used and cache becomes a problem, may want
+ *   to consider context-sharing textures instead of different textures for each
+ *   secondary view.  Might also want to consider resident textures, though not
+ *   sure what this would do to the performance of the rest of the application...
+ * - need to test with main view in georef mode and overview window in pixel/line
+ *   mode, and vice versa.
+ * - extension: reset high-res window zoom level from overview window (resize)
+ */
+
+/* Notes:
+ * - It should be possible to have more than one overview window, with trails, but
+ *   this has never been tested, so likely bugs would need to be worked out if 
+ *   anyone actually tried this...
+ * - When overlap and boundaries are set, the tool will not follow them exactly
+ *   because in order to have constant speed, it must step over regions of
+ *   constant size.  Boundaries and overlap are rounded to the nearest allowable
+ *   increment.  Likewise, with repositioning, zoom area will snap to the closest
+ *   valid location.
+ *
+ * - Currently all translated locations (centers) are pre-calculated and stored
+ *   in the "centers" array.  This might occupy too much memory for large panning
+ *   areas at high resolution.  If this turns out to be the case, the 
+ *   compute_locations_and_zoom function should be replaced with a new function
+ *   that gets called at every zoom iteration and avoids storage.
+ *   
+ */
+
+#include "gvautopan.h"
+#include <gtk/gtksignal.h>
+#include <gtk/gtkmain.h>
+#include <stdio.h>
+#include <math.h>
+#include "gvutils.h"
+
+#define LOG2(x)         (log(x) / 0.69314718056)
+
+#define PICK_SIZE 6.0
+
+#ifndef MAX
+#define MAX(a,b) ((a>b) ? a:b)
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a<b) ? a:b)
+#endif
+
+enum
+{
+    PICK_NONE = 0,
+    PICK_TOP,
+    PICK_RIGHT,
+    PICK_BOTTOM,
+    PICK_LEFT,
+    PICK_CENTER
+};
+
+/* Ideas for future:
+ *
+ * - Ability to step between frames instead of panning
+ *
+ */
+
+
+/* Signals */
+enum
+{
+    ZOOMEXTENTS_CHANGED,  /* for when extents are reset by secondary views */
+    ZOOMEXTENTS_CHANGING,
+    LAST_SIGNAL
+};
+
+static void gv_autopan_tool_class_init(GvAutopanToolClass *klass);
+static void gv_autopan_tool_init(GvAutopanTool *tool);
+
+static void gv_autopan_tool_activate(GvTool *tool, GvViewArea *view);
+static void gv_autopan_tool_deactivate(GvTool *tool, GvViewArea *view);
+static void gv_autopan_tool_destroy(GtkObject *object);
+
+static void gv_autopan_tool_sv_draw(GvTool *tool, GvViewArea *view);
+
+static void gv_autopan_tool_sv_button_press(GtkWidget *view, 
+                                            GdkEventButton *event, 
+                                            gpointer *data_tool);
+
+static void gv_autopan_tool_sv_button_release(GtkWidget *view, 
+                                            GdkEventButton *event, 
+                                            gpointer *data_tool);
+
+static void gv_autopan_tool_sv_motion_notify(GtkWidget *view, 
+                                            GdkEventMotion *event, 
+                                            gpointer *data_tool);
+static gint
+gv_autopan_tool_pick(GvAutopanTool *tool, GvViewArea *view,
+                     gvgeocoord x, gvgeocoord y);
+
+static gint gv_autopan_tool_quit(gpointer data);
+
+static void compute_locations_and_zoom(GvAutopanTool *tool);
+
+static gint map_view_to_view_xy(GvViewArea *view1, GvViewArea *view2, gvgeocoord *x, gvgeocoord *y);
+
+static void new_trail_tile( GvAutopanTool *tool, gint xindex, gint yindex);
+static void update_trail( GvAutopanTool *tool, gvgeocoord xmin, gvgeocoord ymin,
+                          gvgeocoord width, gvgeocoord height );
+
+static void create_trail_textures( GvAutopanTool *tool, GvAutopanViewItem *item );
+
+
+static guint autopantool_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_autopan_tool_get_type(void)
+{
+    static GtkType autopan_tool_type = 0;
+
+    if (!autopan_tool_type)
+    {
+	static const GtkTypeInfo autopan_tool_info =
+	{
+	    "GvAutopanTool",
+	    sizeof(GvAutopanTool),
+	    sizeof(GvAutopanToolClass),
+	    (GtkClassInitFunc) gv_autopan_tool_class_init,
+	    (GtkObjectInitFunc) gv_autopan_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	autopan_tool_type = gtk_type_unique(gv_tool_get_type(),
+					&autopan_tool_info);
+    }
+    return autopan_tool_type;
+}
+
+static void
+gv_autopan_tool_class_init(GvAutopanToolClass *klass)
+{
+    GvToolClass *tool_class;
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+    tool_class = (GvToolClass*)klass;
+
+    autopantool_signals[ZOOMEXTENTS_CHANGED] =
+	gtk_signal_new ("zoomextents_changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvAutopanToolClass,
+                                           zoomextents_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    autopantool_signals[ZOOMEXTENTS_CHANGING] =
+	gtk_signal_new ("zoomextents_changing",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvAutopanToolClass,
+                                           zoomextents_changing),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    gtk_object_class_add_signals(object_class, 
+                         autopantool_signals, LAST_SIGNAL);
+    
+    object_class->destroy = gv_autopan_tool_destroy;
+
+    klass->zoomextents_changed = NULL;
+    klass->zoomextents_changing = NULL;
+
+    tool_class->activate = gv_autopan_tool_activate;
+    tool_class->deactivate = gv_autopan_tool_deactivate;
+
+    /* NOTE: Currently, no gtk events are connected to, so
+     *       the parent gvtool deactivation function is not
+     *       called in gv_autopan_tool_deactivate.  If any
+     *       gtk events do end up being connected to, then
+     *       gv_autopan_tool_deactivate should be updated to
+     *       uncomment this call (it was commented out because
+     *       gtk gives a warning message if you disconnect
+     *       and there were no handlers connected)
+     */
+}
+
+static void
+gv_autopan_tool_init(GvAutopanTool *tool)
+{
+    int i;
+
+    tool->path_type = TL_R_D_L_D;
+
+    tool->nonstandard_path_shapes = NULL;
+
+    tool->play_flag = 0;
+
+    tool->win_width = 0;
+    tool->win_height = 0;
+    tool->win_zoom = 0; 
+
+    tool->pan_region.x = 0;
+    tool->pan_region.y = 0;
+    tool->pan_region.width = -1;
+    tool->pan_region.height = -1;
+
+    tool->overlap = 0.1;
+    tool->speed = 0.01;
+
+    tool->block_size_mode = 0;
+    tool->block_x_size = 0;
+    tool->resolution = 0;
+
+    tool->centers = NULL;
+    tool->view_items = NULL;
+    tool->num_views = 0;
+
+    tool->trail_overview_region.x = 0;
+    tool->trail_overview_region.y = 0;
+    tool->trail_overview_region.width = -1;
+    tool->trail_overview_region.height = -1;
+
+    tool->trail_overview_width_pixels = 256;
+    tool->trail_tile_pixels = 256;
+    tool->trail_tile_lines = 256;
+
+    tool->trail_x0 = 0.0;
+    tool->trail_y0 = 0.0;
+    tool->trail_tile_xsize = 0.0;
+    tool->trail_tile_ysize = 0.0;
+
+    tool->trail = NULL;
+    tool->num_trail_tiles = 0;
+    tool->trail_mode = 0;
+    tool->trail_block = g_malloc(
+                    tool->trail_tile_pixels*tool->trail_tile_lines*2);
+
+    for (i=0; i<tool->trail_tile_pixels*tool->trail_tile_lines*2; i++)
+        ((unsigned char *) tool->trail_block)[i] = 255;
+
+    /* make sure panning stops when application is signalled to quit- */
+    /* gtk won't quit until play function exits */
+    tool->quit_handler_id = gtk_quit_add(0, gv_autopan_tool_quit, 
+                                         (gpointer) tool);
+}
+
+GvTool *
+gv_autopan_tool_new(void)
+{
+    GvTool *tool;
+
+    tool = GV_TOOL(gtk_type_new(GV_TYPE_AUTOPAN_TOOL));
+
+    return tool;    
+}
+
+/**************************************************************/
+
+
+static void
+gv_autopan_tool_activate(GvTool *rtool, GvViewArea *view)
+{
+    GvAutopanTool *tool = GV_AUTOPAN_TOOL(rtool);
+
+    /* Call the parent class func */
+    GV_TOOL_ACTIVATE(tool, view);
+
+    /* TODO: 
+     * - get current view extents
+     * - store window size parameters
+     * - calculate locations for default speed, block size,
+     *   overlap.
+     * - calculate view zoom level
+     * - connect view window's resize event to resize function
+     * - activate secondary view signals, if there are any
+     */
+    gv_view_area_get_extents(view, 
+                             &tool->pan_region.x, 
+                             &tool->pan_region.y,
+                             &tool->pan_region.width, 
+                             &tool->pan_region.height);
+    tool->pan_region.width -= tool->pan_region.x;
+    tool->pan_region.height -= tool->pan_region.y;
+
+    tool->win_width = view->state.shape_x;
+    tool->win_height = view->state.shape_y;
+
+    /* default: use first standard path, 10% overlap at each
+     * edge between successive passes, zoomed in at 8 times
+     * the regular extents.
+     */
+    tool->block_size_mode = 0;
+    tool->block_x_size = 1.0/8.0;
+
+    tool->overlap = 0.1;
+    tool->speed = 0.01;
+
+    tool->current_index = 0;
+}
+
+
+
+static void
+gv_autopan_tool_deactivate(GvTool *rtool, GvViewArea *view)
+{
+    GvAutopanTool *tool = GV_AUTOPAN_TOOL(rtool);
+    GvAutopanViewItem *sv;
+    int sv_idx;
+
+    /* Call the parent class func- uncomment the line below
+     * and remove the 3 lines following it if any gtk events 
+     * are connected to in future.
+     */
+    /*GV_TOOL_DEACTIVATE(tool, view); */
+    g_return_if_fail(GV_TOOL(tool)->view == view);
+    GV_TOOL(tool)->view = NULL;
+    gtk_object_unref(GTK_OBJECT(view));
+
+
+    gv_autopan_tool_stop(tool);
+    if (tool->centers != NULL)
+    {
+        g_array_free(tool->centers, TRUE);
+        tool->centers = NULL;
+    }
+
+    /* TODO:
+     * - clear extent settings, zoom, block size
+     * - disconnect secondary views
+     */
+
+    gv_view_area_queue_draw(view);
+    if (tool->num_views > 0)
+    {
+        for (sv_idx = tool->num_views-1; sv_idx >= 0; sv_idx--)
+        {
+            sv = &(g_array_index(tool->view_items, GvAutopanViewItem, 
+                                 sv_idx));
+            gv_autopan_tool_remove_view( tool, sv->view);
+        }
+    }
+}
+
+static void gv_autopan_tool_destroy(GtkObject *object)
+{
+    gv_autopan_tool_clear_trail(GV_AUTOPAN_TOOL(object));
+    g_free(GV_AUTOPAN_TOOL(object)->trail_block);
+    gtk_quit_remove(GV_AUTOPAN_TOOL(object)->quit_handler_id);
+}
+
+/* Start panning */
+gint gv_autopan_tool_play(GvAutopanTool *tool)
+{
+    /* TODO:
+     * - Set zoom level and initial translation
+     * - While tool is not paused:
+     *    - translate to next position in list.
+     *    - If secondary views are present, trigger
+     *      a redraw (maybe not every time- if not
+     *      then make sure one is at least triggered
+     *      at the end
+     *    - do a while loop of gtk_main_iterations
+     *      on gtk_events_pending, limited by a timer.
+     *      If panning has reached the very end, maybe
+     *      allow a little more time?
+     *      Is there any way to set a priority on
+     *      an event (eg. if left click used to pause)?
+     */
+    GvVertex3d *nloc;
+    GTimer *timer;
+    int sv_idx;
+    GvAutopanViewItem *sv;
+    gvgeocoord xmin, ymin, xmax, ymax;
+
+    /* Avoid recursive plays */
+    if (tool->play_flag == 1)
+        return TRUE;
+
+    /* If tool is not just paused, compute zoom locations */
+    if (tool->play_flag == 0)
+    {
+        compute_locations_and_zoom(tool);
+    
+        if (tool->num_centers < 1)
+            return FALSE;
+
+        gv_view_area_zoom(GV_TOOL(tool)->view,
+                      -1*gv_view_area_get_zoom(GV_TOOL(tool)->view));
+        gv_view_area_zoom(GV_TOOL(tool)->view, tool->win_zoom);
+    }
+    else if (tool->num_centers < 1)
+        return FALSE;
+
+    tool->play_flag = 1;
+    while (tool->play_flag == 1)
+    {
+        /* check if user has resized window; update if they have */
+        if ((tool->win_width != GV_TOOL(tool)->view->state.shape_x) ||
+            (tool->win_height != GV_TOOL(tool)->view->state.shape_y))
+            compute_locations_and_zoom(tool);
+
+        /* check if user has zoomed; reset if they have */
+        if (gv_view_area_get_zoom(GV_TOOL(tool)->view) != tool->win_zoom)
+        {
+            gv_view_area_zoom(GV_TOOL(tool)->view,
+                  -1*gv_view_area_get_zoom(GV_TOOL(tool)->view));
+            gv_view_area_zoom(GV_TOOL(tool)->view,tool->win_zoom);
+        }
+
+        nloc = &( g_array_index( tool->centers, GvVertex3d, 
+                            tool->current_index) );
+
+        gv_view_area_set_translation(GV_TOOL(tool)->view, nloc->x, nloc->y);
+
+        if ( tool->trail_mode > 0 )
+        {
+            gv_view_area_get_extents( GV_TOOL(tool)->view, &xmin, &ymin, 
+                                      &xmax, &ymax );
+            update_trail( tool, xmin, ymin, xmax-xmin, ymax-ymin );
+        }
+
+        if (tool->speed > 0)
+        {
+            tool->current_index++;
+            if (tool->current_index >= tool->num_centers)
+                tool->current_index = 0;
+        }
+        else
+        {
+            tool->current_index--;
+            if (tool->current_index < 0)
+                tool->current_index = tool->num_centers;
+        
+        }
+        
+        if (tool->num_views > 0)
+        {
+            for (sv_idx = 0; sv_idx < tool->num_views; sv_idx++)
+            {
+                sv = &(g_array_index(tool->view_items, GvAutopanViewItem, 
+                                       sv_idx));                 
+                gv_view_area_queue_draw(sv->view);
+            }
+        }
+        /* continue to be interactive */
+        timer = g_timer_new();
+        g_timer_start(timer);
+        while(gtk_events_pending() && 
+              (g_timer_elapsed(timer,NULL) < 2.0))
+            gtk_main_iteration();
+        g_timer_destroy(timer);
+    }
+
+    return TRUE;
+}
+
+/* Pause the tool, but maintain current position, resolution, and
+ * speed settings.
+ */
+
+static gint gv_autopan_tool_quit(gpointer data)
+{
+    gv_autopan_tool_stop(GV_AUTOPAN_TOOL(data));
+    return 0;
+}
+
+gint gv_autopan_tool_pause(GvAutopanTool *tool)
+{
+
+    if (tool->play_flag == 0)
+        compute_locations_and_zoom(tool);
+
+    tool->play_flag = 2;
+
+    return TRUE;
+}
+
+gint gv_autopan_tool_stop(GvAutopanTool *tool)
+{
+    int sv_idx;
+    GvAutopanViewItem *sv;
+
+    if (tool->play_flag == 0)
+        return TRUE;
+
+    /* Same as pause, except that secondary views will be
+     * refreshed without the current region drawn.
+     */
+    if (tool->num_views > 0)
+    {
+        /* If tool is not playing, force redraw of secondary views */
+        for (sv_idx = 0; sv_idx < tool->num_views; sv_idx++)
+        {
+            sv = &(g_array_index(tool->view_items, GvAutopanViewItem, 
+                                   sv_idx));
+            gv_view_area_queue_draw(sv->view);
+        }
+    }
+
+    if (tool->centers != NULL)
+    {
+        g_array_free(tool->centers, TRUE);
+        tool->centers = NULL;
+        tool->current_index = 0;
+    }
+    tool->play_flag = 0;
+
+    return TRUE;
+}
+
+/* Speed is adjusted by placing successive locations closer or
+ * further apart in the direction of panning.  Function returns
+ * the final speed setting (speed is forced to be between
+ * -1.0 and 1.0, with an absolute value of at least 0.000001).
+ */
+double gv_autopan_tool_set_speed(GvAutopanTool *tool, gvgeocoord speed)
+{
+    /* TODO:
+     * - if playing, set pause flag
+     * - recalculate locations and zoom, and reset current position
+     *   to equivalent point in new list
+     * - if tool was playing, restart
+     * - warn if speed is out of bounds
+     */
+    if (tool->speed == speed)
+        return tool->speed;
+
+    if (tool->speed == -1*speed)
+    {
+        tool->speed = speed;
+        return tool->speed;
+    }
+
+    if (speed > 0)
+    {
+        if (speed < 0.000001)
+            tool->speed = 0.000001;
+        else if (speed > 1.0)
+            tool->speed = 1.0;
+        else
+            tool->speed = speed;
+    }
+    else
+    {
+        if (speed < -1.0)
+            tool->speed = -1.0;
+        else if (speed > -0.000001)
+            tool->speed = -0.000001;
+        else
+            tool->speed = speed;
+    }
+
+    /* If tool is stopped, don't recompute locations just yet in
+     * case other things (eg. extents) also need to be reset.
+     */
+    if (tool->play_flag != 0)
+        compute_locations_and_zoom(tool);
+
+    return (double) tool->speed;
+}
+
+double gv_autopan_tool_get_speed(GvAutopanTool *tool)
+{
+    return (double) tool->speed;
+}
+
+gint gv_autopan_tool_set_standard_path(GvAutopanTool *tool, gint path_type)
+{
+    gint playing;
+
+    if ( ( path_type < 0 ) || ( path_type >= STANDARD_PATHS ) )
+    {
+        g_error("GvAutopan: invalid value for standard path setting");
+        return FALSE;
+    }
+
+    playing = tool->play_flag;
+    if ( playing == 1 )
+        gv_autopan_tool_pause(tool);
+
+    tool->path_type = path_type;
+
+    if ( playing != 0 )
+        compute_locations_and_zoom(tool);
+
+    if ( playing == 1 )
+        gv_autopan_tool_play(tool);
+
+    return TRUE;
+}
+
+gint gv_autopan_tool_set_lines_path( GvAutopanTool *tool, GvShapes *lines)
+{
+    int idx;
+    GvShape *shp;
+
+    if (tool->nonstandard_path_shapes != NULL)
+    {
+      /* clear old shapes; free space associated with them */
+      gtk_object_unref( GTK_OBJECT(tool->nonstandard_path_shapes) );
+      tool->nonstandard_path_shapes = NULL;
+    }
+    tool->nonstandard_path_shapes = (GvShapes *) gv_shapes_new();
+
+    for (idx = 0; idx<lines->shapes->len; idx++)
+    {
+        shp = gv_shapes_get_shape(lines, idx);
+	if ( ( shp != NULL ) && 
+             ( gv_shape_type( shp ) == GVSHAPE_LINE ) )
+	{
+	    gv_shapes_add_shape(tool->nonstandard_path_shapes, gv_shape_copy(shp));
+        }
+    }
+    tool->path_type = LINES_PATH;
+    compute_locations_and_zoom(tool);
+
+    return TRUE;
+}
+
+/* Set the extents of the full panning region  */
+/* Returns TRUE for success; FALSE for failure */
+/* (eg. if panning extents parameter doesn't   */
+/* apply for the current path type)            */
+gint gv_autopan_tool_new_rect(GvAutopanTool *tool, GvRect *rect)
+{
+
+
+    tool->pan_region.x = rect->x;
+    tool->pan_region.y = rect->y;
+    tool->pan_region.width = rect->width;
+    tool->pan_region.height = rect->height;
+ 
+    if ( ( tool->trail_overview_region.width <= 0 ) && (tool->num_trail_tiles < 1) )
+    {
+        gv_autopan_tool_set_trail_parameters( tool, rect, tool->trail_overview_width_pixels );
+        tool->trail_tile_ysize = tool->trail_tile_xsize;
+    }
+
+    /* If path is non-standard, region can be set, but won't be used */
+    if ( tool->path_type < 0 )
+      return TRUE;
+
+    if (tool->centers != NULL)
+    {
+        g_array_free(tool->centers, TRUE);
+        tool->centers = NULL;
+    }
+
+    if (tool->play_flag != 0)
+        compute_locations_and_zoom(tool);
+
+    return TRUE;
+}
+gint gv_autopan_tool_get_rect(GvAutopanTool *tool, GvRect *rect)
+{
+    if ( tool->path_type < 0 )
+      return FALSE;
+
+    rect->x = tool->pan_region.x;
+    rect->y = tool->pan_region.y;
+    rect->width = tool->pan_region.width;
+    rect->height = tool->pan_region.height;
+
+    return TRUE;
+}
+
+/* Reset the current position of the primary view window.  Snaps
+ * to the closest listed location.
+ */
+gint gv_autopan_tool_set_location(GvAutopanTool *tool, gvgeocoord x,
+                                  gvgeocoord y, gvgeocoord z)
+{
+    /* TODO:
+     * - if playing, set pause flag
+     * - find closest position in list and set new current position to it
+     * - if tool was playing, restart
+     */
+    gvgeocoord mindiff, cdiff, cx, cy, cz;
+    gint cidx=0, i;
+    GvVertex3d *loc;
+
+    if (GV_TOOL(tool)->view == NULL)
+        return FALSE;
+
+    if (tool->centers == NULL)
+        compute_locations_and_zoom(tool);
+
+    if (tool->num_centers < 1)
+        return FALSE;
+
+    loc = &(g_array_index( tool->centers, GvVertex3d, 0) ); 
+    cx = -1*loc->x;
+    cy = -1*loc->y;
+    cz = -1*loc->z;
+    cdiff = sqrt((cx-x)*(cx-x)+(cy-y)*(cy-y)+(cz-z)*(cz-z));
+    mindiff = cdiff;
+    for (i=1; i < tool->num_centers; i++)
+    {
+        loc = &(g_array_index( tool->centers, GvVertex3d, i) );  
+        cx = -1*loc->x;
+        cy = -1*loc->y;
+        cz = -1*loc->z;
+        cdiff = sqrt((cx-x)*(cx-x)+(cy-y)*(cy-y)+(cz-z)*(cz-z));
+        if (cdiff < mindiff)
+        {
+            mindiff = cdiff;
+            cidx = i;
+        }
+    }
+
+    tool->current_index = cidx;
+
+    return TRUE;
+}
+
+/*
+ * Get the current position.  Returns FALSE if there is no
+ * current position; TRUE if the position is valid.
+ */
+gint gv_autopan_tool_get_location(GvAutopanTool *tool, gvgeocoord *x,
+                                  gvgeocoord *y, gvgeocoord *z)
+{
+    GvVertex3d *oloc;
+
+    /* If tool is deactivated or stopped, return FALSE because
+     * there is no "current location".
+     */
+
+    *x = 0;
+    *y = 0;
+    *z = 0;
+
+    if (GV_TOOL(tool)->view == NULL)
+        return FALSE;
+
+    if (tool->play_flag == 0)
+        return FALSE;
+
+    if (tool->centers == NULL)
+        return FALSE;
+
+    if (tool->current_index < tool->num_centers)
+    {
+        oloc = &(g_array_index(tool->centers, GvVertex3d, 
+                               tool->current_index));
+        *x = -1*oloc->x;
+        *y = -1*oloc->y;
+    }
+    else
+        return FALSE;
+
+
+    return TRUE;
+}
+
+/* Overlap is only relevant for standard paths covering the whole
+ * panning region.  It determines the overlap between successive
+ * passes perpendicular to the direction of panning.
+ */
+
+gint gv_autopan_tool_set_overlap(GvAutopanTool *tool, gvgeocoord overlap)
+{
+
+    tool->overlap = overlap;
+    if ( (tool->play_flag != 0) && (tool->path_type >= 0) )
+        compute_locations_and_zoom(tool);
+
+    return TRUE;
+
+}
+
+double gv_autopan_tool_get_overlap(GvAutopanTool *tool)
+{
+    return (double) tool->overlap;
+}
+
+/* Either the size of the block in the x dimension OR the x resolution of
+ * the view in terms of the pixels of a reference raster may be used to
+ * determine the panning zoom level.  
+ */
+
+gint gv_autopan_tool_set_block_x_size(GvAutopanTool *tool,
+                                      gvgeocoord block_x_size, 
+                                      gint mode)
+{
+    /* mode 0: block size is a fraction of panning extents
+     *         - block size will change depending on panning extents
+     *         - zoom level will change when window is resized
+     *      1: block size is in view coordinates
+     *         - block size will not change with different panning extents
+     *         - zoom level will change when window is resized
+     */
+    if ((mode != 0) && (mode != 1))
+    {
+        g_error("mode must be 0 or 1");
+        return FALSE;
+    }
+    tool->block_size_mode = mode;
+    tool->block_x_size = block_x_size;
+    if (tool->play_flag != 0)
+        compute_locations_and_zoom(tool);
+
+    return TRUE;
+
+}
+
+gint gv_autopan_tool_set_x_resolution(GvAutopanTool *tool, 
+                                    gvgeocoord resolution)
+{
+    /* mode 2: block size is set so that the current layer resolution
+     *         will remain constant.  
+     *         - block size will change when window is resized, and
+     *           has no dependence on panning extents
+     *         - zoom level will not change when window is resized
+     */
+    tool->block_size_mode = 2;
+    tool->resolution = resolution;
+    if (tool->play_flag != 0)
+        compute_locations_and_zoom(tool);
+
+    return TRUE;
+}
+
+/* Get state information about the tool */
+void gv_autopan_tool_get_state(GvAutopanTool *tool,
+                               gint *play_flag,
+                               gint *path_type,
+                               gint *block_size_mode,
+                               gvgeocoord *block_x_size,
+                               gvgeocoord *x_resolution,
+                               gint *num_views)
+{
+    *play_flag = tool->play_flag;
+    *path_type = tool->path_type;
+    *block_size_mode = tool->block_size_mode;
+    *block_x_size = tool->block_x_size;
+    *x_resolution = tool->resolution;
+    *num_views = tool->num_views;
+}
+
+void gv_autopan_tool_clear_trail( GvAutopanTool *tool )
+{
+    int i, j, tnum;
+    GvAutopanViewItem *item;
+    GvAutopanTrailTile *tile;
+    GLuint *texName;
+
+    /* Note below: should context be set before texture deleted? */
+    if ( tool->trail != NULL )
+    {
+        tnum = tool->num_trail_tiles;
+
+        while ( tool->num_trail_tiles > 0 )
+        {
+            tile = &(g_array_index(tool->trail, GvAutopanTrailTile, tool->num_trail_tiles - 1 ));
+            g_free(tile->mask);
+            g_array_remove_index(tool->trail, tool->num_trail_tiles-1);
+            tool->num_trail_tiles--;
+        }
+    
+        g_array_free(tool->trail, TRUE);
+        tool->trail = NULL;
+
+        for( i=0; i<tool->num_views; i++)
+        {
+            item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+            if ( item->trail_mode > 0)
+            {
+                for ( j = tnum-1; j >= 0; j-- )
+                {
+                    texName = &(g_array_index(item->trail_textures, GLuint, j));
+                    glDeleteTextures(1,texName);
+                }
+                g_array_free(item->trail_textures,TRUE);
+                item->trail_textures = NULL;
+                gv_view_area_queue_draw(item->view);
+            }
+        }
+        /* Force tool to reset panning region next time trail comes on */
+        tool->trail_overview_region.width = -1;
+    }
+
+}
+
+gint gv_autopan_tool_set_trail_color(GvAutopanTool *tool, GvViewArea *view,
+                                     float red, float green, float blue,
+                                     float alpha)
+{
+    int i, viewidx;
+    GvAutopanViewItem *item;
+
+    viewidx = -1;
+    for (i=0; i<tool->num_views; i++)
+    {
+        item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+        if (item->view == view)
+            viewidx = i;
+    }
+    if (viewidx < 0)
+    {
+        g_error("View not registered with autopan tool!");
+        return FALSE;
+    }
+
+    item = &(g_array_index(tool->view_items, GvAutopanViewItem, viewidx));
+    item->trail_color[0] = red;
+    item->trail_color[1] = green;
+    item->trail_color[2] = blue;
+    item->trail_color[3] = alpha;
+    if ( tool->num_trail_tiles > 0 )
+        gv_view_area_queue_draw( view );
+
+    return TRUE;
+
+}
+
+gint gv_autopan_tool_set_trail_mode( GvAutopanTool *tool, GvViewArea *view,
+                                     gint trail_mode )
+{
+    int i, j, viewidx, omode;
+    GvAutopanViewItem *item;
+    GLuint *texName;
+
+    viewidx = -1;
+    for (i=0; i<tool->num_views; i++)
+    {
+        item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+        if (item->view == view)
+            viewidx = i;
+    }
+    if (viewidx < 0)
+    {
+        g_error("View not registered with autopan tool!");
+        return FALSE;
+    }
+
+    if ( (trail_mode != 0) && (trail_mode != 1) )
+    {
+        g_warning("gvautopan: trail_mode must be 0 or 1- leaving alone");
+        return FALSE;
+    }
+
+    item = &(g_array_index(tool->view_items, GvAutopanViewItem, viewidx));
+    tool->trail_mode = tool->trail_mode - item->trail_mode + trail_mode;
+    omode = item->trail_mode;
+    item->trail_mode = trail_mode;
+
+    /* Make sure trail is properly initialized if mode is turning on: set to pan_region region */
+    if (( tool->trail_mode > 0 ) && ( tool->trail_overview_region.width < 0 ) && ( tool->pan_region.width > 0 ) )
+        gv_autopan_tool_set_trail_parameters( tool, &tool->pan_region, tool->trail_overview_width_pixels );
+
+    if ( ( omode == 1 ) && ( trail_mode == 0 ) && ( item->trail_textures != NULL ) )
+    {
+        for ( j = tool->num_trail_tiles-1; j >= 0; j-- )
+        {
+            texName = &(g_array_index(item->trail_textures, GLuint, j));
+            glDeleteTextures(1,texName);
+        }
+        g_array_free(item->trail_textures,TRUE);
+        item->trail_textures = NULL; 
+    }
+    else if ( ( omode == 0 ) && ( trail_mode == 1 ) )
+        create_trail_textures( tool, item );
+
+    if ( tool->num_trail_tiles > 0 )
+        gv_view_area_queue_draw( view );
+
+    return TRUE;
+}
+
+
+/* Compute the zoom level and populate the list of locations to translate to */
+
+static void compute_locations_and_zoom(GvAutopanTool *tool)
+{
+    gvgeocoord cx=0, cy=0, mindiff=-1, cdiff;
+    gint reset_position=0, cidx=0;
+    GvVertex3d nloc;
+    GvVertex3d *oloc;
+
+    tool->win_width = GV_TOOL(tool)->view->state.shape_x;
+    tool->win_height = GV_TOOL(tool)->view->state.shape_y;
+
+    if (tool->centers != NULL)
+    {
+        if (tool->current_index < tool->num_centers)
+        {
+            oloc = &(g_array_index(tool->centers, GvVertex3d, 
+                                   tool->current_index));
+            cx = oloc->x;
+            cy = oloc->y;
+            reset_position=1;
+        }
+        g_array_free(tool->centers, TRUE);
+    }
+
+    if ((tool->play_flag == 0) ||
+        (( tool->path_type > -1 ) &&
+	 ((-1*cx < tool->pan_region.x) ||
+          (-1*cy < tool->pan_region.y) ||
+          (-1*cx > tool->pan_region.x + tool->pan_region.width) ||
+	  (-1*cy > tool->pan_region.y + tool->pan_region.height))))
+        reset_position = 0;
+
+    tool->centers = g_array_new(FALSE, FALSE, sizeof(GvVertex3d));
+
+    nloc.z = 0.0;
+
+    /* TODO: see comment above */
+    if (tool->path_type < LINES_PATH)
+    {
+        g_error("non-standard paths other than lines not implemented");
+    }
+    else if ( tool->path_type == LINES_PATH )
+    {
+        GvShape *line;
+        gvgeocoord bsx, delta, x1, y1, x2, y2, dist, dx, dy, doff;
+        gint i, j, k, nnodes, nstop;
+
+        if (tool->block_size_mode == 2)
+            tool->block_x_size = tool->resolution * tool->win_width;
+
+        bsx = tool->block_x_size;
+        /* For now, ignore case where block size mode is 0; eventually
+         * might make it related to total line length, or something
+         * like that.  Here, block size modes 0 and 1 will both behave
+         * the same way as block size mode 1 does in the standard path case.  
+         */
+
+        delta = fabs(tool->speed*bsx);
+
+        tool->num_centers = 0;
+
+        for ( i = 0; i < tool->nonstandard_path_shapes->shapes->len; i++ )
+	{
+  	    line = gv_shapes_get_shape(tool->nonstandard_path_shapes, i);
+
+            if (( line != NULL ) && ( gv_shape_get_nodes( line, 0 ) > 1 ))
+	    {
+	        nnodes = gv_shape_get_nodes( line, 0 );
+	        for ( j = 0 ; j < nnodes - 1; j++ )
+	        {
+		    x1 = gv_shape_get_x( line, 0, j );
+		    y1 = gv_shape_get_y( line, 0, j );
+		    x2 = gv_shape_get_x( line, 0, j+1 );
+		    y2 = gv_shape_get_y( line, 0, j+1 ); 
+		    
+                    dx = x2-x1;
+                    dy = y2-y1;
+                    dist = sqrt(dx*dx + dy*dy);
+                    nstop = (int) ceil(dist/delta) + 1;
+                    /* offset ensures that centers calculated in one direction
+                     * are same as the centers calculated if the speed is reversed.
+                     */
+                    doff = ( ceil(dist/delta) - dist/delta )/2;
+                    if (nstop > 1)
+		    {
+                        dx = dx/(nstop-1);
+                        dy = dy/(nstop-1);
+                    }
+                    else
+ 		        doff = 0.0;
+
+                    for ( k = 0; k < nstop; k++ )
+		    {
+  	  	        nloc.x = -1*(dx*(k-doff) + x1);
+		        nloc.y = -1*(dy*(k-doff) + y1);
+                        g_array_append_val(tool->centers, nloc);
+                        if (reset_position == 1)
+                        {
+                            cdiff = sqrt((nloc.x-cx)*(nloc.x-cx) +
+                                         (nloc.y-cy)*(nloc.y-cy));
+                            if ((mindiff == -1) || (cdiff < mindiff))
+                            {
+                                cidx = tool->num_centers;
+                                mindiff = cdiff;
+                            }
+                        }
+                        tool->num_centers += 1;
+                    }
+                }
+            }
+            else if (( line != NULL ) && ( gv_shape_get_nodes( line, 0 ) == 1 ))
+	    {
+	        /* line only has a single node- set path so that node
+                 * gets visited once.
+                 */
+  	        nloc.x = -1*gv_shape_get_x(line,0,0);
+                nloc.y = -1*gv_shape_get_y(line,0,0);
+                g_array_append_val(tool->centers, nloc);
+                if (reset_position == 1)
+                {
+                    cdiff = sqrt((nloc.x-cx)*(nloc.x-cx) +
+                                 (nloc.y-cy)*(nloc.y-cy));
+                    if ((mindiff == -1) || (cdiff < mindiff))
+                    {
+                        cidx = tool->num_centers;
+                        mindiff = cdiff;
+                    }
+                }
+                tool->num_centers += 1;
+	    }
+        }
+
+        tool->win_zoom = LOG2(tool->win_width/tool->block_x_size);
+    
+    }
+    else if ((tool->path_type == TL_R_D_L_D) || 
+             (tool->path_type == BL_R_U_L_U) ||
+             (tool->path_type == TR_L_D_R_D) || 
+             (tool->path_type == BR_L_U_R_U))
+    {
+        /* Note: currently overlap is dealt with by showing extra
+           along the last (bottom) pass if the blocks don't fit
+           evenly into the panning extents.  May want to do this
+           a bit differently later.
+        */
+
+        gvgeocoord bsx, bsy, delta, xoff, yoff;
+        gint nxloc, npasses, nvert, i, j;
+
+        if (tool->block_size_mode == 2)
+            tool->block_x_size = tool->resolution * tool->win_width;
+
+        bsx = tool->block_x_size;
+        if (tool->block_size_mode == 0)
+            bsx = bsx * tool->pan_region.width;
+
+        bsy = bsx * tool->win_height/tool->win_width;
+
+        npasses = (int) ceil(
+               (tool->pan_region.height - (tool->overlap*bsy))/
+                           ((1.0 - tool->overlap)*bsy));
+
+        if (tool->speed < 0)
+            delta = -1*tool->speed*bsx;
+        else
+            delta = tool->speed*bsx;
+
+        nxloc = (int) ceil((tool->pan_region.width - bsx)/delta) + 1;
+        nvert = (int) ceil(bsy*(1.0 - tool->overlap)/delta);
+
+        /* NOTE: in order to keep speed constant for different
+         *       sizes of panning region, the panning region is
+         *       rounded to the nearest multiple of delta
+         *       (otherwise delta would have to be modified
+         *       to fit evenly into the panning region, which
+         *       results in noticeable speed changes between
+         *       small and large regions.  Likewise, the
+         *       overlap is also rounded.
+         */
+
+        if (((tool->path_type == TL_R_D_L_D) &&
+             (GV_TOOL(tool)->view->state.flip_y > 0)) ||
+            ((tool->path_type == BL_R_U_L_U) &&
+             (GV_TOOL(tool)->view->state.flip_y < 0)) ||
+            ((tool->path_type == TR_L_D_R_D) &&
+             (GV_TOOL(tool)->view->state.flip_y > 0)) ||
+            ((tool->path_type == BR_L_U_R_U) &&
+             (GV_TOOL(tool)->view->state.flip_y < 0)))
+            bsy=-1*bsy;
+
+        if ((tool->path_type == TR_L_D_R_D) || 
+            (tool->path_type == BR_L_U_R_U))
+            xoff = tool->pan_region.x + tool->pan_region.width - bsx/2.0;
+        else
+            xoff = tool->pan_region.x + bsx/2.0;
+
+        if ( tool->pan_region.width < bsx )
+        {
+            /* Very narrow panning region- only one x location */
+            xoff = tool->pan_region.x + tool->pan_region.width/2.0;
+            nxloc = 1;
+        }
+
+        if (bsy > 0)
+            yoff = tool->pan_region.y + bsy/2.0;
+        else
+            yoff = tool->pan_region.y + tool->pan_region.height + bsy/2.0;
+
+        if ((tool->path_type == TR_L_D_R_D) || 
+            (tool->path_type == BR_L_U_R_U))
+            delta = -1.0*delta;
+
+        tool->num_centers = 0;
+        for ( i = 0; i < npasses; i++ )
+        {
+            for ( j = 0; j < nxloc; j++ )
+            {
+                if (nxloc > 1)
+                {
+                    if ( (i % 2) == 0)
+                        nloc.x = -1*(xoff + j*delta);
+                    else
+                        nloc.x = -1*(xoff + (nxloc-1-j)*delta);
+                }
+                else
+                    nloc.x = -1*xoff;
+
+                nloc.y = -1*(yoff + i*bsy*(1.0 - tool->overlap));
+
+                g_array_append_val(tool->centers, nloc);
+                if (reset_position == 1)
+                {
+                    cdiff = sqrt((nloc.x-cx)*(nloc.x-cx) +
+                                 (nloc.y-cy)*(nloc.y-cy));
+                    if ((mindiff == -1) || (cdiff < mindiff))
+                    {
+                        cidx = tool->num_centers;
+                        mindiff = cdiff;
+                    }
+                }
+                tool->num_centers += 1;
+            }
+           
+            if (nxloc > 1)
+            {
+                nloc.x = -1*xoff;
+                if ( (i % 2) == 0)
+                    nloc.x -= (nxloc-1)*delta;
+            }
+
+            if ( i != npasses - 1)
+            {
+                for ( j = 1; j < nvert; j++ )
+                {
+                    nloc.y = -1*(yoff+(i+j*1.0/nvert)*bsy*(1.0-tool->overlap));
+                    g_array_append_val(tool->centers, nloc);
+                    if (reset_position == 1)
+                    {
+                        cdiff = sqrt((nloc.x-cx)*(nloc.x-cx) +
+                                     (nloc.y-cy)*(nloc.y-cy));
+                        if ((mindiff == -1) || (cdiff < mindiff))
+                        {
+                            cidx = tool->num_centers;
+                            mindiff = cdiff;
+                        }
+                    }
+                    tool->num_centers += 1;
+                }
+            }    
+        }
+        if (tool->block_size_mode == 0)
+        {
+            tool->win_zoom = LOG2(tool->win_width/
+                      (tool->block_x_size*tool->pan_region.width));
+        }
+        else
+            tool->win_zoom = LOG2(tool->win_width/tool->block_x_size);
+
+    }
+    else if (tool->path_type == TL_R_D_R_D)
+    {        
+        gvgeocoord bsx, bsy, delta, xoff, yoff;
+        gint nxloc, npasses, i, j;
+
+        if (tool->block_size_mode == 2)
+            tool->block_x_size = tool->resolution * tool->win_width;
+
+        bsx = tool->block_x_size;
+        if (tool->block_size_mode == 0)
+            bsx = bsx * tool->pan_region.width;
+
+        bsy = bsx * tool->win_height/tool->win_width;
+
+        npasses = (int) ceil(
+               (tool->pan_region.height - (tool->overlap*bsy))/
+                           ((1.0 - tool->overlap)*bsy));
+
+        if (tool->speed < 0)
+            delta = -1*tool->speed*bsx;
+        else
+            delta = tool->speed*bsx;
+
+        nxloc = (int) ceil((tool->pan_region.width - bsx)/delta) + 1;
+
+        if (GV_TOOL(tool)->view->state.flip_y > 0)
+            bsy=-1*bsy;
+
+
+        xoff = tool->pan_region.x + bsx/2.0;
+
+        if ( tool->pan_region.width < bsx )
+        {
+            xoff = tool->pan_region.x + tool->pan_region.width/2.0;
+            nxloc = 1;
+        }
+
+        if (bsy > 0)
+            yoff = tool->pan_region.y + bsy/2.0;
+        else
+            yoff = tool->pan_region.y + tool->pan_region.height + bsy/2.0;
+
+
+        tool->num_centers = 0;
+        for ( i = 0; i < npasses; i++ )
+        {
+            for ( j = 0; j < nxloc; j++ )
+            {
+                if (nxloc > 1)
+                    nloc.x = -1*(xoff + j*delta);
+                else
+                    nloc.x = -1*xoff;
+
+                nloc.y = -1*(yoff + i*bsy*(1.0 - tool->overlap));
+
+                g_array_append_val(tool->centers, nloc);
+                if (reset_position == 1)
+                {
+                    cdiff = sqrt((nloc.x-cx)*(nloc.x-cx) +
+                                 (nloc.y-cy)*(nloc.y-cy));
+                    if ((mindiff == -1) || (cdiff < mindiff))
+                    {
+                        cidx = tool->num_centers;
+                        mindiff = cdiff;
+                    }
+                }
+                tool->num_centers += 1;
+            }
+        }
+        if (tool->block_size_mode == 0)
+        {
+            tool->win_zoom = LOG2(tool->win_width/
+                      (tool->block_x_size*tool->pan_region.width));
+        }
+        else
+            tool->win_zoom = LOG2(tool->win_width/tool->block_x_size);
+
+    }
+    else
+    {
+        g_error("path not implemented");
+    }
+
+    tool->current_index = cidx;
+}
+
+
+/* When the tool is panning, optional secondary views can be associated
+ * with the primary view.  In these views, the area currently
+ * covered by the primary view is drawn as a blue-green rectangle
+ * in each secondary view.  The secondary views can be configured
+ * so that the primary view resolution and location can be updated
+ * by dragging the rectangle in the secondary view.
+ *
+ * can_resize: 0 if resetting the resolution is disabled; 1 if it
+ *             is enabled.  If resizing is on, corners can be 
+ *             selected and dragged.  The shape of the rectangle
+ *             must always be dictated by the aspect ratio of the
+ *             primary view though, so dragging will snap to
+ *             a rectangle of the appropriate shape at the end.
+ *
+ * can_reposition: 0 if resetting the position is disabled; 1 if it
+ *               is enabled.  If repositioning is on, borders
+ *               can be selected and dragged (whole box will move).
+ *
+ * trail_mode: 0 if secondary view should not indicate where tool
+ *             has already travelled; 1 if it should display the trail.
+ *
+ */
+
+gint gv_autopan_tool_register_view(GvAutopanTool *tool, GvViewArea *view,
+                                   gint can_resize, gint can_reposition,
+                                   gint trail_mode)
+{
+    GvAutopanViewItem newview;
+
+    /* TODO:
+     * - if playing, pause
+     * - add view to list of secondary views
+     * - connect the view's gldraw event to gv_autopan_tool_sv_draw
+     * - if can_resize or can_reposition is 1, connect to button-press,
+     *   button-release, and motion-notify events for that view.
+     * Note: if things are too slow or flashy using the gldraw event,
+     *       could do something similar to what was done for the
+     *       ghost cursor.  Probably could avoid changes to gvviewarea
+     *       by storing more parameters in the autopan tool and just
+     *       calling gv_manager_set_busy, gtk_widget_queue draw, then
+     *       connecting the view to GLCURSOR instead of GLDRAW.
+     */
+
+    /* First, make sure this is not the main panning view */
+    if (view == GV_TOOL(tool)->view)
+    {
+        g_error("Autopan: Cannot register main view as a secondary view!");
+        return FALSE;
+    }
+
+    if (can_resize != 0)
+        g_warning("gvautopan: Secondary view resize not implemented yet!");
+
+    if ( (trail_mode != 0) && (trail_mode != 1) )
+    {
+        g_warning("gvautopan: trail_mode must be 0 or 1");
+        return FALSE;
+    }
+
+    newview.can_resize = 0;
+    newview.can_reposition = can_reposition;
+    newview.trail_mode = trail_mode;
+
+    tool->trail_mode = tool->trail_mode + trail_mode;
+
+    newview.trail_color[0] = 1.0;
+    newview.trail_color[1] = 0.75;
+    newview.trail_color[2] = 0.0;
+    newview.trail_color[3] = 0.5;
+
+    newview.banding = 0;
+    newview.translating = 0;
+    newview.play_flag = 0;
+
+    /* Add a reference to this view */
+    gtk_object_ref(GTK_OBJECT(view));
+
+    newview.view = view;
+    newview.tool = tool;
+    newview.trail_textures = NULL;
+
+    if (tool->view_items == NULL)
+        tool->view_items = g_array_new(FALSE, FALSE, sizeof(GvAutopanViewItem));
+
+    /* Connect to view area delete event */
+    gtk_signal_connect_object(GTK_OBJECT(view), "destroy",
+			      GTK_SIGNAL_FUNC(gv_autopan_tool_remove_view),
+			      GTK_OBJECT(tool));
+
+    /* Connect to view area gldraw signal so that current region
+     * can be drawn in the view whenever the view is refreshed.
+     */
+    gtk_signal_connect_object(GTK_OBJECT(view), "gldraw",
+			      GTK_SIGNAL_FUNC(gv_autopan_tool_sv_draw),
+			      GTK_OBJECT(tool));
+
+    /* Connect to button-press and button-release signals so that
+     * translating/resizing can be detected.
+     */
+    newview.press_id = \
+           gtk_signal_connect(GTK_OBJECT(view), "button-press-event",
+		       GTK_SIGNAL_FUNC(gv_autopan_tool_sv_button_press),
+                              (gpointer *) tool);
+
+    newview.release_id = \
+           gtk_signal_connect(GTK_OBJECT(view), "button-release-event",
+		       GTK_SIGNAL_FUNC(gv_autopan_tool_sv_button_release),
+                              (gpointer *) tool);
+
+    newview.motion_id = \
+           gtk_signal_connect(GTK_OBJECT(view), "motion-notify-event",
+		       GTK_SIGNAL_FUNC(gv_autopan_tool_sv_motion_notify),
+                              (gpointer *) tool);
+
+
+    g_array_append_val(tool->view_items, newview);
+
+
+    tool->num_views = tool->num_views + 1;
+
+    if ( newview.trail_mode > 0 )
+    {
+        create_trail_textures( tool, &newview );
+        gv_view_area_queue_draw( newview.view );
+    }
+
+    return TRUE;
+}
+
+gint gv_autopan_tool_remove_view(GvAutopanTool *tool, GvViewArea *view)
+{
+    int i, j, viewidx;
+    GvAutopanViewItem *item;
+    GLuint *texName;
+
+   /* TODO:
+     * - disconnect from view's gldraw event
+     * - disconnect view from button-press/button-release/motion-notify
+     *   events if necessary
+     * - remove view from list
+     * - redraw the view to get rid of trail artifact
+     */
+
+    viewidx = -1;
+    for (i=0; i<tool->num_views; i++)
+    {
+        item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+        if (item->view == view)
+            viewidx = i;
+    }
+    if (viewidx < 0)
+    {
+        g_error("View not registered for removal!");
+        return FALSE;
+    }
+
+    item = &(g_array_index(tool->view_items, GvAutopanViewItem, viewidx));
+
+    tool->trail_mode = tool->trail_mode - item->trail_mode;
+
+    if ( item->trail_textures != NULL )
+    {
+        for ( j = tool->num_trail_tiles-1; j >= 0; j-- )
+        {
+            texName = &(g_array_index(item->trail_textures, GLuint, j));
+            glDeleteTextures(1,texName);
+        }
+        g_array_free(item->trail_textures,TRUE);
+        item->trail_textures = NULL; 
+    }
+
+    gtk_signal_disconnect(GTK_OBJECT(view), item->press_id);
+    gtk_signal_disconnect(GTK_OBJECT(view), item->release_id);
+    gtk_signal_disconnect(GTK_OBJECT(view), item->motion_id);
+
+    g_array_remove_index(tool->view_items, viewidx);
+
+    gtk_signal_disconnect_by_data(GTK_OBJECT(view), GTK_OBJECT(tool));
+    gtk_object_unref(GTK_OBJECT(view));
+    tool->num_views = tool->num_views - 1;
+    if (tool->num_views == 0)
+    {
+        g_array_free(tool->view_items, TRUE);
+        tool->view_items = NULL;
+    }
+    return TRUE;
+}
+
+
+static void gv_autopan_tool_sv_draw(GvTool *tool, GvViewArea *view)
+{
+    int i;
+    GvAutopanViewItem *item;
+    GvAutopanTrailTile *tile;
+    GLuint texName;
+    gvgeocoord xmin, xmax, ymin, ymax;
+    gvgeocoord x1, y1, x2, y2, x3, y3, x4, y4;
+    gvgeocoord dx=6.0, dy=0.0, xc=0.0, yc=0.0;
+
+    if (GV_AUTOPAN_TOOL(tool)->play_flag == 0)
+        return;
+
+    item = &(g_array_index(GV_AUTOPAN_TOOL(tool)->view_items, 
+                           GvAutopanViewItem, 0)); 
+    for (i=0; i<GV_AUTOPAN_TOOL(tool)->num_views; i++)
+    {
+        item = &(g_array_index(GV_AUTOPAN_TOOL(tool)->view_items, 
+                               GvAutopanViewItem, i));
+        if (item->view == view)
+            break;
+    }
+
+    if (( item->trail_mode > 0 ) && 
+        (GV_AUTOPAN_TOOL(tool)->num_trail_tiles > 0))
+    {
+        glColor4fv( item->trail_color );
+
+	glEnable( GL_BLEND );
+	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+        glEnable( GL_TEXTURE_2D );
+
+        // May need to uncomment the next four lines at some point-
+        // gvrasterlayer.c does in its draw function...not sure why...
+        //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+        //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+        //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+        //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 
+        
+        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
+
+        for (i=0; i<GV_AUTOPAN_TOOL(tool)->num_trail_tiles; i++)
+        {
+            tile = (GvAutopanTrailTile *) &g_array_index(
+                                        GV_AUTOPAN_TOOL(tool)->trail,
+                                        GvAutopanTrailTile, i);
+            texName = g_array_index(item->trail_textures, GLuint, i);
+            glBindTexture(GL_TEXTURE_2D, texName);
+ 
+            xmin = tile->x0;
+            ymin = tile->y0;
+            xmax = tile->xf;
+            ymax = tile->yf;  
+
+            x1 = x4 = xmin;
+            y3 = y4 = ymin;
+            x2 = x3 = xmax;
+            y1 = y2 = ymax; 
+  
+            if ( map_view_to_view_xy(tool->view, view, &x1, &y1) &&
+                 map_view_to_view_xy(tool->view, view, &x2, &y2) &&
+                 map_view_to_view_xy(tool->view, view, &x3, &y3) &&
+                 map_view_to_view_xy(tool->view, view, &x4, &y4) )
+            {
+                glBegin(GL_QUADS);
+                glTexCoord2f(0.0,1.0); glVertex2f(x1,y1);
+                glTexCoord2f(1.0,1.0); glVertex2f(x2,y2);
+                glTexCoord2f(1.0,0.0); glVertex2f(x3,y3);
+                glTexCoord2f(0.0,0.0); glVertex2f(x4,y4);
+                glEnd();
+            }
+        }
+        glDisable( GL_TEXTURE_2D );
+        glDisable( GL_BLEND );
+    }
+    
+    gv_view_area_get_extents(tool->view, &xmin, &ymin, &xmax, &ymax);
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);
+
+    x1 = xmin;
+    y1 = ymin;
+    x2 = xmin;
+    y2 = ymax;
+    x3 = xmax;
+    y3 = ymax;
+    x4 = xmax;
+    y4 = ymin;
+
+    xc = (xmin + xmax)/2.0;
+    yc = (ymin + ymax)/2.0;
+
+    if ( !map_view_to_view_xy(tool->view, view, &x1, &y1) ||
+         !map_view_to_view_xy(tool->view, view, &x2, &y2) ||
+         !map_view_to_view_xy(tool->view, view, &x3, &y3) ||
+         !map_view_to_view_xy(tool->view, view, &x4, &y4) ||
+         !map_view_to_view_xy(tool->view, view, &xc, &yc) )
+    {      
+        CPLDebug( "GvAutopan", "gv_reproject_points(%s,%s) failed.", 
+                  gv_view_area_get_projection(tool->view), 
+                  gv_view_area_get_projection(view) );
+        return;
+    }
+
+    
+    glColor3f(0.0,0.5,1.0);
+    glBegin(GL_LINE_LOOP);
+    glVertex2(x1, y1);
+    glVertex2(x2, y2);
+    glVertex2(x3, y3);
+    glVertex2(x4, y4);
+    glEnd();
+
+    glBegin(GL_LINES);
+    glVertex3(xc-dx, yc,0.0);
+    glVertex3( xc+dx, yc,0.0);
+    glVertex3( xc, yc-dx,0.0);
+    glVertex3( xc, yc+dx,0.0);
+    glEnd();
+
+}
+
+static void gv_autopan_tool_sv_button_press(GtkWidget *view, 
+                                            GdkEventButton *event, 
+                                            gpointer *data_tool)
+{
+    int i, viewidx;
+    GvAutopanViewItem *item;
+    GvAutopanTool *tool;
+    gint pick;
+
+    /* TODO:
+     * - try to select a border (can_resize=1) and/or
+     *   the center (can_reposition=1).  If neither is
+     *   selected, return.
+     * - record whether or not tool is playing
+     * - if it is playing, pause
+     * - send a zoomextents-changing signal
+     */
+
+    if ( (event->type != GDK_BUTTON_PRESS) ||
+         (event->button != 1) )
+        return;
+
+
+    tool = (GvAutopanTool *) data_tool;
+
+    viewidx = -1;
+    for (i=0; i<tool->num_views; i++)
+    {
+        item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+        if ( item->view == (GvViewArea *) view)
+            viewidx = i;
+    }
+
+    if (viewidx < 0)
+    {
+        g_error("View not found in autopan tool list!");
+        return;
+    }
+
+    item = &(g_array_index(tool->view_items, GvAutopanViewItem, viewidx));
+
+    if ((item->can_resize == 0) && (item->can_reposition == 0))
+        return;
+
+    pick = gv_autopan_tool_pick(tool, (GvViewArea *) view, event->x, event->y);
+    if (( pick == PICK_CENTER ) && ( item->can_reposition == 1 ) )
+    {
+        item->translating = 1;
+        item->play_flag = tool->play_flag;
+        gv_autopan_tool_pause(tool);
+    }
+}
+
+static void gv_autopan_tool_sv_button_release(GtkWidget *view, 
+                                            GdkEventButton *event, 
+                                            gpointer *data_tool)
+{
+    int i, viewidx;
+    GvAutopanViewItem *item;
+    GvAutopanTool *tool;
+    gvgeocoord gx=0.0, gy=0.0;
+
+    /* TODO:
+     * - reset extents
+     * - send a zoomextents-changed signal
+     * - if tool was playing, restart
+     */
+
+    tool = (GvAutopanTool *) data_tool;
+
+    viewidx = -1;
+    for (i=0; i<tool->num_views; i++)
+    {
+        item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+        if (item->view == (GvViewArea *) view)
+            viewidx = i;
+    }
+
+    if (viewidx < 0)
+    {
+        g_error("View not found in autopan tool list!");
+        return;
+    }
+
+    item = &(g_array_index(tool->view_items, GvAutopanViewItem, viewidx));
+
+    if ((item->can_resize == 0) && (item->can_reposition == 0))
+        return;
+
+    if (item->translating == 1)
+    {
+        gv_view_area_map_pointer(item->view, event->x, event->y, &gx, &gy);
+        map_view_to_view_xy(item->view, ((GvTool *) tool)->view, &gx, &gy);
+        gv_autopan_tool_set_location(tool, gx, gy, 0);
+
+        if ( item->play_flag == 1 )
+        {
+            item->play_flag = 0;
+            item->translating = 0;
+            gv_autopan_tool_play(tool);
+        }
+        else
+        {
+            item->play_flag = 0;
+            item->translating = 0;
+        }
+    }
+
+}
+
+static void gv_autopan_tool_sv_motion_notify(GtkWidget *view, 
+                                            GdkEventMotion *event, 
+                                            gpointer *data_tool)
+{
+    int i, viewidx, sv_idx;
+    GvVertex3d *nloc;
+    GvAutopanViewItem *item;
+    GvAutopanViewItem *sv;
+    GvAutopanTool *tool;
+    gvgeocoord gx=0.0, gy=0.0;
+
+    /* TODO:
+     * - redraw the box in the view and in other secondary views,
+     *   making sure that box is snapped to correct aspect ratio.
+     */
+
+    tool = (GvAutopanTool *) data_tool;
+
+    viewidx = -1;
+    for (i=0; i<tool->num_views; i++)
+    {
+        item = &(g_array_index(tool->view_items, GvAutopanViewItem, i));
+        if (item->view == (GvViewArea *) view)
+            viewidx = i;
+    }
+
+    if (viewidx < 0)
+    {
+        g_error("View not found in autopan tool list!");
+        return;
+    }
+
+    item = &(g_array_index(tool->view_items, GvAutopanViewItem, viewidx));
+
+    if ((item->can_resize == 0) && (item->can_reposition == 0))
+        return;
+
+    if ( item->translating == 1 )
+    {
+        gv_view_area_map_pointer(item->view, event->x, event->y, &gx, &gy);
+        map_view_to_view_xy(item->view, ((GvTool *) tool)->view, &gx, &gy);
+        gv_autopan_tool_set_location(tool, gx, gy, 0);
+        nloc = &( g_array_index( tool->centers, GvVertex3d, 
+                            tool->current_index) );
+
+        gv_view_area_set_translation(GV_TOOL(tool)->view, nloc->x, nloc->y);
+        for (sv_idx = 0; sv_idx < tool->num_views; sv_idx++)
+        {
+            sv = &(g_array_index(tool->view_items, GvAutopanViewItem, 
+                                   sv_idx));
+            gv_view_area_queue_draw(sv->view);
+        }
+    }
+
+}
+
+static gint
+gv_autopan_tool_pick(GvAutopanTool *tool, GvViewArea *view,
+                     gvgeocoord x, gvgeocoord y)
+{
+    gvgeocoord xmin, xmax, ymin, ymax;
+    gvgeocoord dx=6.0, dy=0.0, xc=0.0, yc=0.0;
+    gvgeocoord x1, y1, x2, y2, x3, y3, x4, y4;
+
+    GLuint buf[20];
+    GLint hits;
+    GLint vp[4];    
+
+    gv_view_area_get_extents(((GvTool *) tool)->view, &xmin, &ymin, &xmax, &ymax);
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);
+
+    x1 = xmin;
+    y1 = ymin;
+    x2 = xmin;
+    y2 = ymax;
+    x3 = xmax;
+    y3 = ymax;
+    x4 = xmax;
+    y4 = ymin;
+
+    xc = (xmin + xmax)/2.0;
+    yc = (ymin + ymax)/2.0;
+
+    if ( !map_view_to_view_xy(((GvTool *) tool)->view, view, &x1, &y1) ||
+         !map_view_to_view_xy(((GvTool *) tool)->view, view, &x2, &y2) ||
+         !map_view_to_view_xy(((GvTool *) tool)->view, view, &x3, &y3) ||
+         !map_view_to_view_xy(((GvTool *) tool)->view, view, &x4, &y4) ||
+         !map_view_to_view_xy(((GvTool *) tool)->view, view, &xc, &yc) )
+    {      
+        CPLDebug( "GvAutopan", "gv_reproject_points(%s,%s) failed.", 
+                  gv_view_area_get_projection(((GvTool *) tool)->view), 
+                  gv_view_area_get_projection(view) );
+    }
+
+    vp[0] = vp[1]  = 0;
+    vp[2] = (GLint)view->state.shape_x;
+    vp[3] = (GLint)view->state.shape_y;
+    
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    gluPickMatrix(x, vp[3]-y, PICK_SIZE, PICK_SIZE, vp);
+    gluOrtho2D(-vp[2]/2.0, vp[2]/2.0, -vp[3]/2.0, vp[3]/2.0);
+
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    glRotate(view->state.rot, 0.0, 0.0, 1.0);
+    glScale(view->state.linear_zoom * view->state.flip_x,
+	     view->state.linear_zoom * view->state.flip_y, 1.0);
+    glTranslate(view->state.tx, view->state.ty, 0.0);
+
+    glSelectBuffer(20, buf);
+    glRenderMode(GL_SELECT);
+
+    glInitNames();
+    glPushName(-1);
+
+    /* Center */
+    glLoadName(4);
+    glBegin(GL_LINES);
+    glVertex2(xc-dx, yc);
+    glVertex2(xc+dx, yc);
+    glVertex2(xc, yc-dx);
+    glVertex2(xc, yc+dx);
+    glEnd();
+
+    /* Top */
+    glLoadName(0);
+    glBegin(GL_LINES);
+    glVertex2(x1, y1);
+    glVertex2(x4, y4);
+    glEnd();
+
+    /* Right */
+    glLoadName(1);
+    glBegin(GL_LINES);
+    glVertex2(x4, y4);
+    glVertex2(x3, y3);
+    glEnd();
+
+    /* Bottom */
+    glLoadName(2);
+    glBegin(GL_LINES);
+    glVertex2(x2, y2);
+    glVertex2(x3, y3);
+    glEnd();
+
+    /* Left */
+    glLoadName(3);
+    glBegin(GL_LINES);
+    glVertex2(x1, y1);
+    glVertex2(x2, y2);
+    glEnd();
+
+
+    hits = glRenderMode(GL_RENDER);
+    
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+
+    /* We're only concerned with the first hit */
+    if (hits >= 1)
+        return buf[3]+1;
+
+    /* No hits */
+    return PICK_NONE;
+}
+
+/* Map xy in view1 to xy in view2.  Returns TRUE if
+ * successful; FALSE otherwise.
+ */
+
+static gint map_view_to_view_xy(GvViewArea *view1, GvViewArea *view2, 
+                                gvgeocoord *x, gvgeocoord *y)
+{
+    GvRasterLayer *rst1, *rst2;
+    double xd, yd;
+
+    xd = (double) *x;
+    yd = (double) *y;
+
+    if (gv_view_area_get_primary_raster(view1) != NULL)
+    {
+        rst1 = GV_RASTER_LAYER(gv_view_area_get_primary_raster(view1));
+
+	if( gv_view_area_get_raw(view1,NULL) )
+            gv_raster_pixel_to_georef( rst1->prototype_data, &xd, &yd, NULL );
+    }
+ 
+    if( gv_view_area_get_projection(view1) != NULL
+        && gv_view_area_get_projection(view2) != NULL
+        && !EQUAL(gv_view_area_get_projection(view1),
+      	    gv_view_area_get_projection(view2)) 
+        && !EQUAL(gv_view_area_get_projection(view1),"PIXEL")
+        && !EQUAL(gv_view_area_get_projection(view2),"PIXEL") )
+    {
+        if( !gv_reproject_points( gv_view_area_get_projection(view1),
+      			    gv_view_area_get_projection(view2),
+      			    1, &xd, &yd, NULL ) 
+          )
+            return FALSE;
+    }
+
+    if (gv_view_area_get_primary_raster(view2) != NULL)
+    {
+        rst2 = GV_RASTER_LAYER(gv_view_area_get_primary_raster(view2));
+
+	if( gv_view_area_get_raw(view2,NULL) )
+            gv_raster_georef_to_pixel( rst2->prototype_data, &xd, &yd, NULL );
+    }
+
+    *x = (gvgeocoord) xd;
+    *y = (gvgeocoord) yd;
+
+    return TRUE;
+    
+}
+
+/* Trail-related functions */
+gint gv_autopan_tool_set_trail_parameters(GvAutopanTool *tool, 
+                                          GvRect *overview_extents,
+                                          int overview_width_pixels)
+{
+    gint i,j, xnum, ynum;
+
+    gv_autopan_tool_clear_trail( tool );
+
+    /* set extents */
+    tool->trail_overview_region.x = overview_extents->x;
+    tool->trail_overview_region.y = overview_extents->y;
+    tool->trail_overview_region.width = overview_extents->width;
+    tool->trail_overview_region.height = overview_extents->height;
+    tool->trail_overview_width_pixels = overview_width_pixels;
+
+    tool->trail_x0 = overview_extents->x;
+    tool->trail_y0 = overview_extents->y;
+    tool->trail_tile_xsize = overview_extents->width*tool->trail_tile_pixels/overview_width_pixels;
+    tool->trail_tile_ysize = tool->trail_tile_xsize;
+
+    /* initialize new tiles */
+    xnum = (int) ceil(overview_width_pixels/tool->trail_tile_pixels);
+    ynum = (int) ceil((overview_width_pixels/tool->trail_tile_lines)*
+                      (overview_extents->height/overview_extents->width));
+
+    for ( i = 0; i < xnum; i++ )
+    {
+        for ( j = 0; j < ynum; j++ )
+            new_trail_tile( tool, i, j );
+    }
+
+    return TRUE;
+}
+
+void gv_autopan_tool_get_trail_parameters(GvAutopanTool *tool, 
+                                          GvRect *overview_extents,
+                                          int *overview_width_pixels,
+                                          int *num_trail_tiles)
+{
+    overview_extents->x = tool->trail_overview_region.x;
+    overview_extents->y = tool->trail_overview_region.y;
+    overview_extents->width = tool->trail_overview_region.width;
+    overview_extents->height = tool->trail_overview_region.height;
+    *overview_width_pixels = tool->trail_overview_width_pixels;
+    *num_trail_tiles = tool->num_trail_tiles;
+}
+
+gint gv_autopan_tool_save_trail_tiles(GvAutopanTool *tool,
+                                      const char *basename)
+{
+    GvAutopanTrailTile *tile;
+    GDALDriverH   driver;
+    GDALDatasetH  dataset;
+    GDALRasterBandH band;
+    double gt[6];
+
+    int i, blen;
+    char *name_buf;
+    char xindex[25];
+    char yindex[25];
+
+    if (tool->num_trail_tiles < 1)
+        return 0;
+
+    driver = GDALGetDriverByName( "GTiff" );
+    if( driver == NULL )
+        return -1;
+
+    blen = strlen(basename);
+    name_buf = (char *) malloc( sizeof(char)*(blen+10) );
+
+    for ( i = 0; i < tool->num_trail_tiles; i++ )
+    {
+        tile = &(g_array_index(tool->trail, GvAutopanTrailTile, i ));
+        sprintf(name_buf,"%s%d",basename,i);
+        sprintf(xindex,"%d",tile->xindex);
+        sprintf(yindex,"%d",tile->yindex);
+        //dataset = GDALCreate( driver, name_buf, 
+        //                      tile->pixels,
+        dataset = GDALCreate( driver, name_buf, 
+                              2*tile->pixels, tile->lines, 1, GDT_Byte, 
+                              NULL );
+        GDALSetMetadataItem( dataset, "xindex", xindex, NULL );
+        GDALSetMetadataItem( dataset, "yindex", yindex, NULL );
+        gt[0] = tile->x0;
+        gt[1] = (tile->xf - tile->x0)/tile->pixels;
+        gt[2] = 0;
+        gt[3] = tile->y0;
+        gt[4] = 0;
+        gt[5] = (tile->yf - tile->y0)/tile->lines;
+        GDALSetGeoTransform( dataset, gt );
+        band = GDALGetRasterBand( dataset, 1 );
+        //GDALRasterIO( band, GF_Write, 0, 0, tile->pixels, tile->lines,
+        //              tile->mask, tile->pixels, tile->lines, GDT_Byte,
+        //              2, 2*tile->pixels );
+        GDALRasterIO( band, GF_Write, 0, 0, 2*tile->pixels, tile->lines,
+                      tile->mask, 2*tile->pixels, tile->lines, GDT_Byte,
+                      1, 2*tile->pixels );
+
+        GDALClose( dataset );
+    }
+
+    free(name_buf);
+
+    return tool->num_trail_tiles;
+
+}
+
+gint gv_autopan_tool_load_trail_tiles(GvAutopanTool *tool,
+                                      const char *basename,
+                                      int num_trail_tiles)
+{
+  /* 
+   * Note: trail parameters (overview region, overview width in pixels)
+   * must be set properly before this function is called.
+   */
+
+    GvAutopanTrailTile *tile;
+    GvAutopanViewItem *item;
+    GLuint texName;
+    GDALDatasetH  dataset;
+    GDALRasterBandH band;
+    int i, blen, xindex, yindex, idx;
+    char *name_buf;
+
+    gv_autopan_tool_clear_trail( tool );
+
+    if (num_trail_tiles < 1)
+        return 0;
+
+    blen = strlen(basename);
+    name_buf = (char *) malloc( sizeof(char)*(blen+10) );
+
+    for ( i = 0; i < num_trail_tiles; i++ )
+    {
+        sprintf(name_buf,"%s%d",basename,i);
+        dataset = GDALOpen( name_buf, GA_ReadOnly);
+        xindex = atoi(GDALGetMetadataItem( dataset, "xindex", NULL ));
+        yindex = atoi(GDALGetMetadataItem( dataset, "yindex", NULL ));
+        new_trail_tile(tool,xindex,yindex);
+        tile = &(g_array_index(tool->trail, GvAutopanTrailTile, tool->num_trail_tiles - 1 ));
+        band = GDALGetRasterBand( dataset, 1 );
+        //GDALRasterIO( band, GF_Read, 0, 0, tile->pixels, tile->lines,
+        //              tile->mask, 2*tile->pixels, tile->lines, GDT_Byte,
+        //              1, tile->pixels );
+        GDALRasterIO( band, GF_Read, 0, 0, 2*tile->pixels, tile->lines,
+                      tile->mask, 2*tile->pixels, tile->lines, GDT_Byte,
+                      1, 2*tile->pixels );
+        GDALClose( dataset );
+
+        // update textures for views
+        for ( idx = 0; idx < tool->num_views; idx++ )
+        {    
+            item = &(g_array_index(tool->view_items, 
+                           GvAutopanViewItem, idx));
+
+            if ( item->trail_mode < 1 )
+                continue;
+
+            /* Appropriate view must be active in order to update texture; */
+            /* otherwise changes won't show up.                            */
+            if (!gtk_gl_area_make_current( GTK_GL_AREA(item->view) ))
+                g_warning("gv_autopan_tool_load_trail_tiles: Unable to make view current, trail may not update properly!");
+
+            texName = g_array_index( item->trail_textures, GLuint, tool->num_trail_tiles - 1 );
+            glBindTexture(GL_TEXTURE_2D,texName);
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tile->pixels, tile->lines,
+                     GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tile->mask);
+	}
+    }
+
+    free(name_buf);
+
+    return num_trail_tiles;
+}
+
+static void new_trail_tile( GvAutopanTool *tool, gint xindex, gint yindex)
+{
+    GvAutopanTrailTile newtile;
+    int idx;
+    int pixel_count = tool->trail_tile_pixels*tool->trail_tile_lines;
+    GvAutopanViewItem *item;
+    GLuint texName;
+
+
+    newtile.xindex = xindex;
+    newtile.yindex = yindex;
+    newtile.pixels = tool->trail_tile_pixels;
+    newtile.lines = tool->trail_tile_lines;
+    newtile.x0 = tool->trail_x0 + tool->trail_tile_xsize*xindex;
+    newtile.y0 = tool->trail_y0 + tool->trail_tile_ysize*yindex;
+    newtile.xf = tool->trail_x0 + tool->trail_tile_xsize*(xindex + 1);
+    newtile.yf = tool->trail_y0 + tool->trail_tile_ysize*(yindex + 1);
+
+    newtile.mask = (unsigned char *) g_malloc( pixel_count*2 );
+    memset( newtile.mask, 0, pixel_count*2 ); 
+
+    if ( tool->trail == NULL )
+        tool->trail = g_array_new(FALSE,FALSE, sizeof(GvAutopanTrailTile));
+
+    g_array_append_val( tool->trail, newtile);
+    tool->num_trail_tiles = tool->num_trail_tiles + 1;
+
+    for (idx = 0; idx < tool->num_views; idx++)
+    { 
+        item = &(g_array_index(tool->view_items, 
+                               GvAutopanViewItem, 0));
+
+        if ( item->trail_mode < 1 )
+            continue;
+
+        if (item->trail_textures == NULL)
+        {
+            create_trail_textures( tool, item );
+        }
+        else
+        {
+            /* View that texture is going to be displayed in must be the  */
+            /* current context when that texture is generated or updated  */
+            /* There is supposedly a way to share contexts between views, */
+            /* but that hasn't been used here...                          */
+            if (!gtk_gl_area_make_current( GTK_GL_AREA(item->view) ))
+                g_warning("new_trail_tile: Unable to make view current, trail may not update properly!");
+
+            glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+            glGenTextures(1,&texName);
+            glBindTexture(GL_TEXTURE_2D, texName);
+            /* The next parameters need to be set whenever a texture is
+             * created (tried just putting them in draw function;
+             * things didn't work- entire rectangle goes solid-coloured)
+             */
+            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+            glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE_ALPHA,newtile.pixels,
+                newtile.lines, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, newtile.mask);
+            g_array_append_val( item->trail_textures, texName);
+        }
+
+    }
+}
+
+
+static void update_trail( GvAutopanTool *tool, gvgeocoord xmin, gvgeocoord ymin,
+                          gvgeocoord width, gvgeocoord height )
+{
+    int xs,xe,ys,ye, i, j, k, p1, p2, l1, l2, offset, pd, tidx;
+    GvAutopanTrailTile *tile;
+    GvAutopanViewItem *item;
+    GLuint texName;
+
+
+    xs = (int) floor((xmin - tool->trail_x0)/tool->trail_tile_xsize);
+    xe = (int) floor((xmin + width - tool->trail_x0)/tool->trail_tile_xsize);
+    ys = (int) floor((ymin - tool->trail_y0)/tool->trail_tile_ysize);
+    ye = (int) floor((ymin + height - tool->trail_y0)/tool->trail_tile_ysize);
+
+    /* Note: tiles currently aren't indexed/sorted because it is assumed there
+      won't be very many, so the search time shouldn't significantly
+      slow things down. 
+    */
+    for ( i = xs; i <= xe; i++ )
+    {
+        for ( j = ys; j <= ye; j++ )
+        {
+            tile = NULL;
+            for (k = 0; k < tool->num_trail_tiles; k++)
+            {
+                tile = &(g_array_index(tool->trail, GvAutopanTrailTile, k));
+                if ((i == tile->xindex) && (j == tile->yindex))
+                    break;
+                tile = NULL;
+            }
+            tidx = k;
+            if (tile == NULL)
+            {
+                new_trail_tile( tool, i, j );
+                tile = &(g_array_index(tool->trail, GvAutopanTrailTile, 
+                                       tool->num_trail_tiles - 1 ));
+                tidx = tool->num_trail_tiles - 1;
+            }
+            p1 = (int) floor(((xmin - tile->x0)*
+                       tool->trail_tile_pixels/tool->trail_tile_xsize) + 0.5);
+            p2 = (int) floor(((xmin + width - tile->x0)*
+                       tool->trail_tile_pixels/tool->trail_tile_xsize) + 0.5);
+            l1 = (int) floor(((ymin - tile->y0)*
+                       tool->trail_tile_lines/tool->trail_tile_ysize) + 0.5);
+            l2 = (int) floor(((ymin + height - tile->y0)*
+                       tool->trail_tile_lines/tool->trail_tile_ysize) + 0.5);
+ 
+            p1 = MIN(tool->trail_tile_pixels-1,MAX(p1,0));
+            p2 = MIN(tool->trail_tile_pixels-1,MAX(p2,0));
+            l1 = MIN(tool->trail_tile_lines-1,MAX(l1,0));
+            l2 = MIN(tool->trail_tile_lines-1,MAX(l2,0));
+
+            if ((l1 >= l2) || (p1 >= p2))
+                continue;
+            pd = (p2-p1+1)*2;
+            /* If saving wasn't needed, could use mask only for initializing
+             * and work with textures only after that point, I think.
+             */
+            for (k = l1; k<=l2; k++)
+            {
+                offset = p1*2 + k*tool->trail_tile_pixels*2;
+                memset(tile->mask + offset, 255, pd);
+            }
+
+            for ( k = 0; k < tool->num_views; k++ )
+            {    
+                item = &(g_array_index(tool->view_items, 
+                               GvAutopanViewItem, k));
+                
+                if ( item->trail_mode < 1 )
+                    continue;
+
+                /* Appropriate view must be active in order to update texture; */
+                /* otherwise changes won't show up.                            */
+                if (!gtk_gl_area_make_current( GTK_GL_AREA(item->view) ))
+                    g_warning("update_trail: Unable to make view current, trail may not update properly!");
+
+                texName = g_array_index( item->trail_textures, GLuint, tidx );
+                glBindTexture(GL_TEXTURE_2D,texName);
+                glTexSubImage2D(GL_TEXTURE_2D, 0, p1, l1, p2-p1+1, l2-l1+1,
+                         GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tool->trail_block);
+            }
+        }
+    }
+}
+
+static void create_trail_textures(GvAutopanTool *tool, GvAutopanViewItem *item)
+{
+    int i;
+    GvAutopanTrailTile *tile; 
+    GLuint texName; 
+
+    if ( item->trail_textures != NULL )
+        g_warning("create_trail_textures: textures unexpectedly not null!");
+
+    item->trail_textures = g_array_new(FALSE,FALSE,sizeof(GLuint));
+
+    if (!gtk_gl_area_make_current( GTK_GL_AREA(item->view) ))
+        g_warning("new_trail_tile: Unable to make view current, trail may not update properly!");
+
+    for ( i = 0; i < tool->num_trail_tiles; i++ )
+    {
+        tile = &(g_array_index(tool->trail, 
+                               GvAutopanTrailTile, i));
+
+        glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+        glGenTextures(1,&texName);
+        glBindTexture(GL_TEXTURE_2D, texName);
+        /* The next parameters need to be set whenever a texture is
+         * created (tried just putting them in draw function;
+         * things didn't work- entire rectangle goes solid-coloured)
+         */
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+        glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE_ALPHA,tile->pixels,
+            tile->lines, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tile->mask);
+        g_array_append_val( item->trail_textures, texName);
+    }
+}

Added: packages/openev/branches/upstream/current/gvautopan.h
===================================================================
--- packages/openev/branches/upstream/current/gvautopan.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvautopan.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,335 @@
+/******************************************************************************
+ * $Id: gvautopan.h,v 1.3 2005/09/12 15:33:10 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Auto-panning tool.  Scans systematically over an area or along
+ *           a preset path.
+ * Author:   Gillian Walter
+ *
+ * Developed by Atlantis Scientific Inc. (www.atlantis-scientific.com) for
+ * DRDC Ottawa.
+ *
+ ******************************************************************************
+ * Copyright (c) Her majesty the Queen in right of Canada as represented
+ * by the Minister of National Defence, 2004. 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvautopan.h,v $
+ * Revision 1.3  2005/09/12 15:33:10  gmwalter
+ * Update autopan tool for line paths.
+ *
+ * Revision 1.2  2005/02/22 13:22:36  gmwalter
+ * Add autopan tool.
+ *
+ * Revision 1.1.2.5  2005/01/29 04:01:11  gmwalter
+ * Initial support for autopan trail.
+ *
+ * Revision 1.1.2.4  2005/01/20 15:20:08  gmwalter
+ * Add ability to change standard path.
+ *
+ * Revision 1.1.2.3  2005/01/05 21:22:23  gmwalter
+ * Updated autopan tool to add more functions.
+ *
+ * Revision 1.1.2.2  2004/12/21 15:10:26  gmwalter
+ * Add ability to relocate zoomed region by
+ * dragging in secondary view.
+ *
+ * Revision 1.1.2.1  2004/12/09 16:58:28  gmwalter
+ * Add initial autopan support.
+ *
+ *
+ *
+ */
+
+
+#ifndef __GV_AUTOPAN_TOOL_H__
+#define __GV_AUTOPAN_TOOL_H__
+
+#include "gvtypes.h"
+#include "gvtool.h"
+#include "gvrasterlayer.h"
+#include "gvshapes.h"
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#define GV_TYPE_AUTOPAN_TOOL            (gv_autopan_tool_get_type ())
+#define GV_AUTOPAN_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_AUTOPAN_TOOL, GvAutopanTool))
+#define GV_AUTOPAN_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_AUTOPAN_TOOL, GvAutopanToolClass))
+#define GV_IS_AUTOPAN_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_AUTOPAN_TOOL))
+#define GV_IS_AUTOPAN_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_AUTOPAN_TOOL))
+
+typedef struct _GvAutopanTool       GvAutopanTool;
+typedef struct _GvAutopanToolClass  GvAutopanToolClass;
+typedef struct _GvAutopanViewItem      GvAutopanViewItem;
+typedef struct _GvAutopanTrailTile      GvAutopanTrailTile;
+
+
+
+enum
+{
+    TL_R_D_L_D = 0, /* Start top left, go right, down, left, down, right... */
+    BL_R_U_L_U,     /* Start bottom left, go right, up, left, up, right... */
+    TR_L_D_R_D,
+    BR_L_U_R_U,
+    TL_R_D_R_D,    /* Start top left, go right, jump down and to left edge, go right... */
+    /* TL_D_R_D_R,     Start top left, go down, jump right and to top edge, go down... */
+    STANDARD_PATHS
+};
+
+#define LINES_PATH -1
+
+struct _GvAutopanTool
+{
+    GvTool tool;
+
+    guint quit_handler_id;
+
+    /* play_flag: 0- not playing, 1- playing, 2- paused */
+    gint play_flag;
+
+    /* Route to follow in panning: -1 for non-standard, 0...STANDARD_PATHS-1
+     * for preset path coverage. non-standard is not implemented yet.
+     *
+     * standard paths cover the tool's current extents completely in a 
+     * regular fashion.
+     *
+     * non-standard will allow the user to provide a set of points that
+     * define a path to follow: the locations will be interpolated from
+     * this path (the number of locations between points will depend on
+     * the current speed settings). 
+     */
+
+    gint path_type;
+
+    GvShapes *nonstandard_path_shapes;
+
+    /* Track changes that user makes to window size and zoom level,
+     * and reset accordingly.
+     */
+
+    gvgeocoord win_width, win_height, win_zoom;
+
+    /* Default settings:
+     * - pan_region: extents (can't just use tool boundaries
+     *   because locations have to be recalculated when extents are
+     *   reset).  Only used for standard paths.
+     * - overlap (perpendicular to path direction, as a fraction
+     *   of the extents in that direction)
+     * - speed (panning speed- appropriate values will be machine 
+     *   dependent- set negative to go in reverse direction)
+     */
+
+    GvRect pan_region;
+    gvgeocoord overlap; /* only used for standard paths */
+    gvgeocoord speed;
+
+
+    /* Block size mode: user has different options in how block
+     * size is set.
+     *
+     * block_size_mode = 0:
+     *     - user sets block_x_size to a float between 0 and 1,
+     *       corresponding to the block size as a fraction of the
+     *       total x extents (block_y_size will be determined by
+     *       block_x_size and the window's aspect ratio).  resolution
+     *       is ignored in this mode.
+     *
+     * block_size_mode = 1:
+     *     - same as mode 0, except that block_x_size is in view
+     *       coordinates rather than a fraction of total x extents
+     *       to be panned.
+     *
+     * block_size_mode = 2:
+     *     - block size is set so that the size of pixels in the 
+     *       view will be constant at "resolution".
+     */
+
+    gint block_size_mode;
+
+    gvgeocoord block_x_size;
+
+    gvgeocoord resolution;
+
+    gint current_index;
+
+    /* list of sequential translates for panning */
+    GArray *centers;
+    gint num_centers;
+
+    /* trail info */
+    /* Predicted region that trail will cover: can go outside; just  */
+    /* used in resolution calculations.                              */
+    /* Overview_width_pixels should correspond to maximum resoluiont */
+    /* required by all overview windows.                             */
+    GvRect trail_overview_region; 
+    gint trail_overview_width_pixels;  
+
+    gint trail_tile_pixels;
+    gint trail_tile_lines;
+
+    /* Next four parameters are calculated based on overview info */
+    gvgeocoord trail_x0;
+    gvgeocoord trail_y0;
+    gvgeocoord trail_tile_xsize;
+    gvgeocoord trail_tile_ysize;
+
+    GArray *trail;
+    gint num_trail_tiles;
+    gint trail_mode; /* Number of views showing a trail */
+    GLuint *trail_block; /* Used to selectively replace parts of a texture */
+
+
+
+    /* optional secondary views in which to draw a box 
+       indicating current location. 
+       view- gvviewarea
+       can_resize- whether or not the current panning
+                        resolution can be reset by dragging
+                        a corner of the box in the view.
+       can_relocate- whether or not the current position
+                        can be reset by selecting a boundary
+                        of the box and dragging the box in
+                        the view.
+       trail_mode- whether or not the view should display a
+                   trail where user has already panned.
+     */
+    GArray *view_items;
+
+    gint num_views;
+
+};
+
+
+struct _GvAutopanTrailTile
+{
+    gint xindex;
+    gint yindex;
+    gint pixels;
+    gint lines;
+
+    gvgeocoord x0;
+    gvgeocoord y0;
+    gvgeocoord xf;
+    gvgeocoord yf;
+
+    unsigned char *mask;
+};
+
+struct _GvAutopanViewItem
+{
+    GvViewArea *view;
+    GvAutopanTool *tool;
+
+    gint can_resize;
+    gint can_reposition;
+    gint trail_mode;
+
+    gint banding;
+    gint translating;
+    gint play_flag;
+    GvColor trail_color;
+    GArray *trail_textures;
+
+    /* Store connections for later disconnect */
+    gint press_id;
+    gint release_id;
+    gint motion_id;
+};
+
+struct _GvAutopanToolClass
+{
+    GvToolClass parent_class;
+
+    void (* zoomextents_changed)(GvAutopanTool *tool);
+    void (* zoomextents_changing)(GvAutopanTool *tool);
+};
+
+
+GtkType gv_autopan_tool_get_type(void);
+GvTool* gv_autopan_tool_new(void);
+
+gint gv_autopan_tool_play(GvAutopanTool *tool);
+gint gv_autopan_tool_pause(GvAutopanTool *tool);
+gint gv_autopan_tool_stop(GvAutopanTool *tool);
+
+double gv_autopan_tool_set_speed(GvAutopanTool *tool, gvgeocoord speed);
+double gv_autopan_tool_get_speed(GvAutopanTool *tool);
+
+gint gv_autopan_tool_new_rect(GvAutopanTool *tool, GvRect *rect);
+gint gv_autopan_tool_get_rect(GvAutopanTool *tool, GvRect *rect);
+
+gint gv_autopan_tool_set_location(GvAutopanTool *tool, gvgeocoord x,
+                                  gvgeocoord y, gvgeocoord z);
+gint gv_autopan_tool_get_location(GvAutopanTool *tool, gvgeocoord *x,
+                                  gvgeocoord *y, gvgeocoord *z);
+
+gint gv_autopan_tool_set_overlap(GvAutopanTool *tool, gvgeocoord overlap);
+double gv_autopan_tool_get_overlap(GvAutopanTool *tool);
+
+gint gv_autopan_tool_set_block_x_size(GvAutopanTool *tool, 
+                                      gvgeocoord block_x_size,
+                                      gint mode);
+gint gv_autopan_tool_set_x_resolution(GvAutopanTool *tool, 
+                                    gvgeocoord resolution);
+
+gint gv_autopan_tool_set_standard_path(GvAutopanTool *tool, gint path_type);
+
+void gv_autopan_tool_get_state(GvAutopanTool *tool,
+                               gint *play_flag,
+                               gint *path_type,
+                               gint *block_size_mode,
+                               gvgeocoord *block_x_size,
+                               gvgeocoord *x_resolution,
+                               gint *num_views);
+
+void gv_autopan_tool_clear_trail(GvAutopanTool *tool);
+
+gint gv_autopan_tool_set_trail_color(GvAutopanTool *tool, GvViewArea *view,
+                                     float red, float green, float blue,
+                                     float alpha);
+
+gint gv_autopan_tool_set_trail_mode( GvAutopanTool *tool, GvViewArea *view,
+                                     gint trail_mode);
+
+gint gv_autopan_tool_register_view(GvAutopanTool *tool, GvViewArea *view,
+                                   gint can_resize, gint can_reposition,
+                                   gint trail_mode);
+
+gint gv_autopan_tool_remove_view(GvAutopanTool *tool, GvViewArea *view);
+
+gint gv_autopan_tool_set_lines_path(GvAutopanTool *tool, GvShapes *lines);
+
+gint gv_autopan_tool_set_trail_parameters(GvAutopanTool *tool, 
+                                          GvRect *overview_extents,
+                                          int overview_width_pixels);
+
+void gv_autopan_tool_get_trail_parameters(GvAutopanTool *tool, 
+                                          GvRect *overview_extents,
+                                          int *overview_width_pixels,
+                                          int *num_trail_tiles);
+
+gint gv_autopan_tool_save_trail_tiles(GvAutopanTool *tool,
+                                      const char *basename);
+
+gint gv_autopan_tool_load_trail_tiles(GvAutopanTool *tool,
+                                      const char *basename,
+                                      int num_trail_tiles);
+
+
+
+#endif /* __GV_AUTOPAN_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvdata.c
===================================================================
--- packages/openev/branches/upstream/current/gvdata.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvdata.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,457 @@
+/******************************************************************************
+ * $Id: gvdata.c,v 1.15 2003/02/07 20:06:49 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for raster, vector and layer data containers.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvdata.c,v $
+ * Revision 1.15  2003/02/07 20:06:49  andrey_kiselev
+ * Memory leaks fixed.
+ *
+ * Revision 1.14  2001/08/08 02:57:03  warmerda
+ * implemented support for gv_data_registry_dump()
+ *
+ * Revision 1.13  2001/06/20 14:02:45  warmerda
+ * avoid emitting signal in gv_data_set_name if object is destroyed
+ *
+ * Revision 1.12  2001/05/15 16:22:13  pgs
+ * added meta-changed signal for notification of\nchanges to meta data
+ *
+ * Revision 1.11  2000/08/08 20:09:06  warmerda
+ * cleanup properties and parents
+ *
+ * Revision 1.10  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.9  2000/06/20 13:26:54  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvdata.h"
+#include <gtk/gtksignal.h>
+#include <stdio.h>
+
+enum
+{
+    CHANGING,
+    CHANGED,
+    META_CHANGED,
+    LAST_SIGNAL
+};
+
+static void gv_data_class_init(GvDataClass *klass);
+static void gv_data_init(GvData *data);
+static void gv_data_parent_changed(GvData *data, gpointer change_info);
+static void gv_data_child_changed(GvData *data, GvData *child, gpointer change_info);
+static void gv_data_destroy(GtkObject *object);
+static void gv_data_finalize(GtkObject *object);
+
+static guint data_signals[LAST_SIGNAL] = { 0 };
+
+static GPtrArray *live_datasets = NULL;
+
+GtkType
+gv_data_get_type(void)
+{
+    static GtkType data_type = 0;
+
+    if (!data_type)
+    {
+	static const GtkTypeInfo data_info =
+	{
+	    "GvData",
+	    sizeof(GvData),
+	    sizeof(GvDataClass),
+	    (GtkClassInitFunc) gv_data_class_init,
+	    (GtkObjectInitFunc) gv_data_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	data_type = gtk_type_unique(gtk_data_get_type(), &data_info);
+    }
+    return data_type;
+}
+
+static void
+gv_data_class_init(GvDataClass *klass)
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    data_signals[CHANGING] =
+	gtk_signal_new ("changing",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvDataClass, changing),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    data_signals[CHANGED] =
+	gtk_signal_new ("changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvDataClass, changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+	data_signals[META_CHANGED] =
+	gtk_signal_new ("meta-changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvDataClass, meta_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0);
+
+    gtk_object_class_add_signals(object_class, data_signals, LAST_SIGNAL);
+
+    object_class->destroy = gv_data_destroy;
+    object_class->finalize = gv_data_finalize;
+
+    klass->changing = NULL;
+    klass->changed = NULL;
+    klass->meta_changed = NULL;
+    klass->child_changed = NULL;
+    klass->get_memento = NULL;
+    klass->set_memento = NULL;
+    klass->del_memento = NULL;
+}
+
+void gv_data_registry_dump()
+
+{
+    int		i;
+
+    printf( "gv_data registry dump\n" );
+    printf( "=====================\n" );
+
+    if( live_datasets == NULL )
+    {
+        printf( "no GvDatas created yet\n" );
+        return;
+    }
+
+    for( i = 0; i < live_datasets->len; i++ )
+    {
+        GvData	*data = GV_DATA(g_ptr_array_index(live_datasets,i));
+
+        printf( "  %-24s %p/%s\n", 
+                gtk_type_name(GTK_OBJECT_TYPE( GTK_OBJECT(data) )),
+                data, 
+                gv_data_get_name( data ) );
+    }
+
+    printf( "\n" );
+}
+
+static void
+gv_data_init(GvData *data)
+{
+    data->parent = NULL;
+    data->name = NULL;
+    data->frozen = FALSE;
+    data->changed_while_frozen = FALSE;
+    data->read_only = FALSE;
+    data->projection = NULL;
+    data->properties = NULL;
+
+
+    if( live_datasets == NULL )
+        live_datasets = g_ptr_array_new();
+
+    g_ptr_array_add( live_datasets, data );
+}
+
+GvData *
+gv_data_get_parent(GvData *data)
+{
+    return data->parent;
+}
+
+void
+gv_data_set_parent(GvData *data, GvData *parent)
+{
+    if (data->parent)
+    {
+	/* Remove reference to parent */
+	gtk_signal_disconnect_by_data(GTK_OBJECT(data->parent),
+				      GTK_OBJECT(data));
+	gtk_object_unref(GTK_OBJECT(data->parent));
+    }
+
+    data->parent = parent;
+
+    if (parent)
+    {
+	/* Each GvData maintains a reference to its parent */
+	gtk_object_ref(GTK_OBJECT(parent));
+	gtk_object_sink(GTK_OBJECT(parent));
+
+	gtk_signal_connect_object(GTK_OBJECT(parent), "changed",
+				  GTK_SIGNAL_FUNC(gv_data_parent_changed),
+				  GTK_OBJECT(data));
+	if (!data->name)
+	{
+	    gv_data_set_name(data, parent->name);
+	}
+    }
+}
+
+void
+gv_data_set_name(GvData *data, const gchar *name)
+{
+    if (data->name)
+    {
+	g_free(data->name);
+    }
+    if (name)
+    {
+	data->name = g_strdup(name);
+    }
+    else
+    {
+	data->name = NULL;
+    }
+
+    if( !GTK_OBJECT_DESTROYED(data) )
+        gv_data_meta_changed(data);
+}
+
+const gchar *
+gv_data_get_name(GvData *data)
+{
+    return (const gchar *)data->name;
+}
+
+const char *
+gv_data_get_property(GvData *data, const char *name)
+{
+    return gv_properties_get( &(data->properties), name );
+}
+
+void
+gv_data_set_property(GvData *data, const char *name, const char *value)
+{
+    gv_properties_set( &(data->properties), name, value );
+}
+
+GvProperties *
+gv_data_get_properties(GvData *data)
+{
+    return &(data->properties);
+}
+
+void
+gv_data_changing(GvData *data, gpointer change_info)
+{
+    if (!data->frozen)
+    {
+	if (data->parent)
+	{
+	    gv_data_changing(data->parent, change_info);
+	}
+	else
+	{
+	    gtk_signal_emit(GTK_OBJECT(data), data_signals[CHANGING],
+			    change_info);
+	}
+    }
+}
+
+void
+gv_data_changed(GvData *data, gpointer change_info)
+{
+    if (data->frozen)
+    {
+	data->changed_while_frozen = TRUE;
+    }
+    else
+    {
+	if (data->parent)
+	{
+	    gv_data_child_changed(data->parent, data, change_info);
+	}
+	else
+	{
+	    gtk_signal_emit(GTK_OBJECT(data), data_signals[CHANGED],
+			    change_info);
+	}
+    }
+}
+
+void
+gv_data_meta_changed(GvData *data)
+{
+	//do we need to any tests at this point?
+
+	gtk_signal_emit(GTK_OBJECT(data), data_signals[META_CHANGED]);
+}
+
+void
+gv_data_freeze(GvData *data)
+{
+    data->frozen = TRUE;
+    data->changed_while_frozen = FALSE;
+}
+
+void
+gv_data_thaw(GvData *data)
+{
+    if (data->frozen)
+    {
+	data->frozen = FALSE;
+	if (data->changed_while_frozen)
+	{
+	    gv_data_changed(data, NULL);
+	}
+    }
+}
+
+GvDataMemento *
+gv_data_get_memento(GvData *data, gpointer change_info)
+{
+    GvDataClass *klass = GV_DATA_CLASS(((GtkObject*)data)->klass);
+    GvDataMemento *memento = NULL;
+
+    if (klass->get_memento)
+    {
+	klass->get_memento(data, change_info, &memento);
+    }
+    return memento;
+}
+
+void
+gv_data_set_memento(GvData *data, GvDataMemento *memento)
+{
+    GvDataClass *klass = GV_DATA_CLASS(((GtkObject*)data)->klass);
+    g_return_if_fail(memento);
+
+    if (klass->set_memento)
+    {
+	klass->set_memento(data, memento);
+    }
+}
+
+void
+gv_data_del_memento(GvData *data, GvDataMemento *memento)
+{
+    GvDataClass *klass = GV_DATA_CLASS(((GtkObject*)data)->klass);
+    g_return_if_fail(memento);
+
+    if (klass->del_memento)
+    {
+	klass->del_memento(data, memento);
+    }
+}
+
+static void
+gv_data_parent_changed(GvData *data, gpointer change_info)
+{
+    if (data->frozen)
+    {
+	data->changed_while_frozen = TRUE;
+    }
+    else
+    {
+	gtk_signal_emit(GTK_OBJECT(data), data_signals[CHANGED],
+			change_info);
+    }
+}
+
+static void
+gv_data_child_changed(GvData *data, GvData *child, gpointer change_info)
+{
+    GvDataClass *klass = GV_DATA_CLASS(((GtkObject*)data)->klass);
+    if (klass->child_changed)
+    {
+	klass->child_changed(data, child, change_info);
+    }
+
+    gv_data_changed(data, change_info);
+}
+
+static void
+gv_data_destroy(GtkObject *object)
+{
+    GtkDataClass *parent_class;
+
+    /* Remove reference to parent */
+    gv_data_set_parent(GV_DATA(object), NULL);
+
+    parent_class = gtk_type_class(gtk_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(object);
+}
+
+static void
+gv_data_finalize(GtkObject *object)
+{
+    GtkDataClass *parent_class;
+
+    gv_data_set_name(GV_DATA(object), NULL);
+    gv_data_set_projection(GV_DATA(object), NULL);
+    gv_properties_destroy(&(GV_DATA(object)->properties));
+    gv_data_set_parent(GV_DATA(object), NULL);
+
+    parent_class = gtk_type_class(gtk_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);
+
+    g_ptr_array_remove( live_datasets, object );
+}
+
+const char *
+gv_data_get_projection(GvData *data)
+{
+    return data->projection;
+}
+
+void
+gv_data_set_projection(GvData *data, const char *projection)
+
+{
+    if( data->projection != NULL )
+    {
+        g_free( data->projection );
+        data->projection = NULL;
+    }
+
+    if( projection != NULL )
+        data->projection = g_strdup(projection);
+}
+
+gint
+gv_data_is_read_only(GvData *data)
+{
+    return data->read_only;
+}
+
+void
+gv_data_set_read_only(GvData *data, int read_only)
+{
+    data->read_only = read_only;
+}
+

Added: packages/openev/branches/upstream/current/gvdata.h
===================================================================
--- packages/openev/branches/upstream/current/gvdata.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvdata.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,127 @@
+/******************************************************************************
+ * $Id: gvdata.h,v 1.13 2003/02/07 20:06:49 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for raster, vector and layer data containers.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvdata.h,v $
+ * Revision 1.13  2003/02/07 20:06:49  andrey_kiselev
+ * Memory leaks fixed.
+ *
+ * Revision 1.12  2001/08/08 02:57:03  warmerda
+ * implemented support for gv_data_registry_dump()
+ *
+ * Revision 1.11  2001/05/15 16:22:13  pgs
+ * added meta-changed signal for notification of\nchanges to meta data
+ *
+ * Revision 1.10  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.9  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_DATA_H__
+#define __GV_DATA_H__
+
+#include <gtk/gtkdata.h>
+#include "gvtypes.h"
+#include "gvproperties.h"
+
+#define GV_TYPE_DATA            (gv_data_get_type ())
+#define GV_DATA(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_DATA, GvData))
+#define GV_DATA_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_DATA, GvDataClass))
+#define GV_IS_DATA(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_DATA))
+#define GV_IS_DATA_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_DATA))
+
+typedef struct _GvData       GvData;
+typedef struct _GvDataClass  GvDataClass;
+typedef struct _GvDataMemento GvDataMemento;
+
+struct _GvData
+{
+    GtkData data;
+
+    GvData *parent;
+    gchar *name;
+
+    guint frozen : 1;
+    guint changed_while_frozen : 1;
+    gint  read_only;
+
+    char *projection;
+
+    GvProperties properties;
+};
+
+struct _GvDataClass
+{
+    GtkDataClass parent_class;
+
+    void (* changing) (GvData *data, gpointer change_info);
+    void (* changed) (GvData *data, gpointer change_info);
+    void (* meta_changed) (GvData *data);
+    void (* child_changed) (GvData *data, GvData *child, gpointer change_info);
+    void (* get_memento) (GvData *data, gpointer change_info,
+			  GvDataMemento **memento);
+    void (* set_memento) (GvData *data, GvDataMemento *memento);
+    void (* del_memento) (GvData *data, GvDataMemento *memento);
+};
+
+struct _GvDataMemento
+{
+    GvData *data;
+    gint type;
+    gint group;
+};
+
+GtkType    gv_data_get_type (void);
+
+void gv_data_set_parent(GvData *data, GvData *parent);
+GvData *gv_data_get_parent(GvData *data);
+void gv_data_set_name(GvData *data, const gchar *name);
+const gchar* gv_data_get_name(GvData *data);
+
+void gv_data_changing(GvData *data, gpointer change_info);
+void gv_data_changed(GvData *data, gpointer change_info);
+void gv_data_meta_changed(GvData *data);
+void gv_data_freeze(GvData *data);
+void gv_data_thaw(GvData *data);
+GvDataMemento* gv_data_get_memento(GvData *data, gpointer change_info);
+void gv_data_set_memento(GvData *data, GvDataMemento *memento);
+void gv_data_del_memento(GvData *data, GvDataMemento *memento);
+const char *gv_data_get_projection(GvData *data);
+void gv_data_set_projection(GvData *data, const char *projection);
+void gv_data_set_read_only(GvData *data, gint read_only );
+gint gv_data_is_read_only(GvData *data );
+
+void   gv_data_set_property(GvData *data, const char *name, const char *value);
+const char *gv_data_get_property(GvData *data, const char *name);
+GvProperties *gv_data_get_properties(GvData *data);
+
+void gv_data_registry_dump( void );
+
+#endif /*__GV_DATA_H__ */
+
+

Added: packages/openev/branches/upstream/current/gview.h
===================================================================
--- packages/openev/branches/upstream/current/gview.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gview.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,93 @@
+/******************************************************************************
+ * $Id: gview.h,v 1.16 2005/02/22 13:22:36 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Primary OpenEV include file.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gview.h,v $
+ * Revision 1.16  2005/02/22 13:22:36  gmwalter
+ * Add autopan tool.
+ *
+ * Revision 1.15  2003/06/25 16:43:24  warmerda
+ * added gvrotatetool.h
+ *
+ * Revision 1.14  2003/05/23 16:18:17  warmerda
+ * added GvRecords for CIETMap
+ *
+ * Revision 1.13  2003/02/28 16:47:48  warmerda
+ * added gvsymbolmanager.h
+ *
+ * Revision 1.12  2002/02/28 18:52:22  gmwalter
+ * Added a point-of-interest tool similar to the region-of-interest
+ * tool (allows a user to select a temporary point without having to add a
+ * new layer).  Added a mechanism to allow some customization of openev
+ * via a textfile defining external modules.
+ *
+ * Revision 1.11  2000/08/25 20:05:34  warmerda
+ * added appcurlayer
+ *
+ * Revision 1.10  2000/07/25 23:33:04  warmerda
+ * added rectangle tool
+ *
+ * Revision 1.9  2000/07/14 18:25:53  warmerda
+ * added ipgcplayer
+ *
+ * Revision 1.8  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GVIEW_H__
+#define __GVIEW_H__
+
+#include "gvtypes.h"
+#include "gvmanager.h"
+#include "gvdata.h"
+#include "gvundo.h"
+#include "gvviewarea.h"
+#include "gvviewlink.h"
+#include "gvpointlayer.h"
+#include "gvlinelayer.h"
+#include "gvarealayer.h"
+#include "gvpquerylayer.h"
+#include "ipgcplayer.h"
+#include "appcurlayer.h"
+#include "gvrecords.h"
+#include "gvrasterlayer.h"
+#include "gvrasterlut.h"
+#include "gvselecttool.h"
+#include "gvpointtool.h"
+#include "gvlinetool.h"
+#include "gvrecttool.h"
+#include "gvrotatetool.h"
+#include "gvareatool.h"
+#include "gvnodetool.h"
+#include "gvroitool.h"
+#include "gvpoitool.h"
+#include "gvtracktool.h"
+#include "gvzoompantool.h"
+#include "gvtoolbox.h"
+#include "gvsymbolmanager.h"
+#include "gvautopan.h"
+
+#endif /*__GVIEW_H__*/

Added: packages/openev/branches/upstream/current/gvlayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvlayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvlayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,263 @@
+/******************************************************************************
+ * $Id: gvlayer.c,v 1.13 2001/10/12 17:44:18 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for all display layers.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvlayer.c,v $
+ * Revision 1.13  2001/10/12 17:44:18  warmerda
+ * avoid extra redraws when many raster layers displayed
+ *
+ * Revision 1.12  2001/04/09 18:14:49  warmerda
+ * added view field to GvLayer
+ *
+ * Revision 1.11  2001/03/28 15:13:59  warmerda
+ * added view to GvLayer
+ *
+ * Revision 1.10  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvlayer.h"
+#include <gtk/gtksignal.h>
+
+/** FIXME: we are currently leaking the projection string.  Should
+    add a finalize function. **/
+
+enum
+{
+    SETUP,
+    TEARDOWN,
+    DRAW,
+    EXTENTS_REQUEST,
+    DISPLAY_CHANGE,
+    LAST_SIGNAL
+};
+
+static void gv_layer_class_init(GvLayerClass *klass);
+static void gv_layer_init(GvLayer *layer);
+
+static guint layer_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_layer_get_type(void)
+{
+    static GtkType layer_type = 0;
+
+    if (!layer_type)
+    {
+	static const GtkTypeInfo layer_info =
+	{
+	    "GvLayer",
+	    sizeof(GvLayer),
+	    sizeof(GvLayerClass),
+	    (GtkClassInitFunc) gv_layer_class_init,
+	    (GtkObjectInitFunc) gv_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	layer_type = gtk_type_unique(gv_data_get_type(), &layer_info);
+    }
+    return layer_type;
+}
+
+static void
+gv_layer_class_init(GvLayerClass *klass)
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    layer_signals[SETUP] =
+	gtk_signal_new ("setup",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvLayerClass, setup),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    layer_signals[TEARDOWN] =
+	gtk_signal_new ("teardown",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvLayerClass, teardown),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+    
+    layer_signals[DRAW] =
+	gtk_signal_new ("draw",
+			GTK_RUN_FIRST | GTK_RUN_NO_RECURSE,
+                        object_class->type,
+			GTK_SIGNAL_OFFSET (GvLayerClass, draw),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    layer_signals[EXTENTS_REQUEST] =
+	gtk_signal_new ("get-extents",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvLayerClass, extents_request),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    layer_signals[DISPLAY_CHANGE] =
+	gtk_signal_new ("display-change",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvLayerClass, display_change),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    gtk_object_class_add_signals(object_class, layer_signals, LAST_SIGNAL);
+
+    klass->setup = NULL;
+    klass->teardown = NULL;
+    klass->draw = NULL;
+    klass->extents_request = NULL;
+    klass->reproject = NULL;
+    klass->display_change = NULL;
+}
+
+void
+gv_layer_init(GvLayer *layer)
+{
+    layer->setup_count = 0;
+    layer->invisible = FALSE;
+    layer->presentation = FALSE;
+    layer->projection = NULL;
+    layer->view = NULL;
+    layer->pending_idle = FALSE;
+}
+
+void
+gv_layer_setup(GvLayer *layer, GvViewArea *view)
+{
+    if (++layer->setup_count == 1)
+    {
+	gtk_signal_emit(GTK_OBJECT(layer), layer_signals[SETUP], view);
+    }
+}
+
+void
+gv_layer_teardown(GvLayer *layer, GvViewArea *view)
+{
+    if (--layer->setup_count < 1)
+    {
+	gtk_signal_emit(GTK_OBJECT(layer), layer_signals[TEARDOWN], view);
+    }
+}
+
+void
+gv_layer_draw(GvLayer *layer, GvViewArea *view)
+{
+    g_assert( view == layer->view );
+
+    if (!layer->invisible)
+    {
+	if (layer->presentation)
+	{
+	    /* Avoid triggering tool drawing by not emitting a signal */
+	    GvLayerClass *klass = GV_LAYER_CLASS(GTK_OBJECT(layer)->klass);
+	    if (klass->draw)
+	    {
+		klass->draw(layer, view);
+	    }
+	}
+	else
+	{
+	    gtk_signal_emit(GTK_OBJECT(layer), layer_signals[DRAW], view);
+	}
+    }
+}
+
+
+void
+gv_layer_extents(GvLayer *layer, GvRect *rect)
+{
+    rect->x = rect->y = rect->width = rect->height = 0.0;
+    gtk_signal_emit(GTK_OBJECT(layer), layer_signals[EXTENTS_REQUEST], rect);
+}
+
+void
+gv_layer_display_change(GvLayer *layer, gpointer change_info)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), layer_signals[DISPLAY_CHANGE], 
+                    change_info);
+}
+
+gint
+gv_layer_is_visible(GvLayer *layer)
+{
+    return (layer->invisible == FALSE);
+}
+
+void
+gv_layer_set_visible(GvLayer *layer, gint visible)
+{
+    gint invisible = !visible;
+    if (invisible != layer->invisible)
+    {
+	layer->invisible = invisible;
+        gv_layer_display_change(layer, NULL);
+    }    
+}
+
+gint
+gv_layer_set_visible_temp(GvLayer *layer, gint visible)
+{
+    gint old_visible = !layer->invisible;
+    layer->invisible = !visible;
+    return old_visible;
+}
+
+void
+gv_layer_set_presentation(GvLayer *layer, gint presentation)
+{
+    layer->presentation = presentation;
+}
+
+gint 
+gv_layer_reproject(GvLayer *layer, const char *projection)
+{
+    GvLayerClass *klass = GV_LAYER_CLASS(((GtkObject*)layer)->klass);
+
+    if (klass->reproject)
+	return klass->reproject(layer, projection);
+    else
+        return FALSE;
+}
+
+GvViewArea *
+gv_layer_get_view(GvLayer *layer)
+{
+    return GV_LAYER(layer)->view;
+}
+

Added: packages/openev/branches/upstream/current/gvlayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvlayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvlayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,103 @@
+/******************************************************************************
+ * $Id: gvlayer.h,v 1.13 2001/10/12 17:44:18 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for all display layers.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvlayer.h,v $
+ * Revision 1.13  2001/10/12 17:44:18  warmerda
+ * avoid extra redraws when many raster layers displayed
+ *
+ * Revision 1.12  2001/04/09 18:14:49  warmerda
+ * added view field to GvLayer
+ *
+ * Revision 1.11  2001/03/28 15:13:59  warmerda
+ * added view to GvLayer
+ *
+ * Revision 1.10  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_LAYER_H__
+#define __GV_LAYER_H__
+
+#include <gdk/gdk.h>
+#include "gvdata.h"
+#include "gvviewarea.h"
+
+#define GV_TYPE_LAYER            (gv_layer_get_type ())
+#define GV_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_LAYER, GvLayer))
+#define GV_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_LAYER, GvLayerClass))
+#define GV_IS_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_LAYER))
+#define GV_IS_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_LAYER))
+
+typedef struct _GvLayer       GvLayer;
+typedef struct _GvLayerClass  GvLayerClass;
+
+struct _GvLayer
+{
+    GvData data;
+
+    gint setup_count;        /* Prevents multiple setup/teardown events */
+    guint invisible : 1;
+    guint presentation : 1;  /* Presentation mode drawing (e.g. printing) */
+
+    char *projection;        /* in WKT format */
+
+    GvViewArea	*view;
+
+    int		pending_idle;/* Is there pending idle work for this layer? */
+};
+
+struct _GvLayerClass
+{
+    GvDataClass parent_class;
+
+    /* the following are signals */
+    void (* setup)           (GvLayer *layer, GvViewArea *view);
+    void (* teardown)        (GvLayer *layer, GvViewArea *view);
+    void (* draw)            (GvLayer *layer, GvViewArea *view);
+    void (* extents_request) (GvLayer *layer, GvRect *rect);
+    void (* display_change)  (GvLayer *layer, gpointer change_info);
+
+    /* this is just a hook function, not a signal */
+    gint (* reproject)       (GvLayer *layer, const char *);
+};
+
+GtkType    gv_layer_get_type (void);
+
+void gv_layer_setup(GvLayer *layer, GvViewArea *view);
+void gv_layer_teardown(GvLayer *layer, GvViewArea *view);
+void gv_layer_draw(GvLayer *layer, GvViewArea *view);
+void gv_layer_extents(GvLayer *layer, GvRect *rect);
+void gv_layer_display_change(GvLayer *layer, gpointer change_info);
+gint gv_layer_is_visible(GvLayer *layer);
+void gv_layer_set_visible(GvLayer *layer, gint visible);
+gint gv_layer_set_visible_temp(GvLayer *layer, gint visible);
+void gv_layer_set_presentation(GvLayer *layer, gint presentation);
+GvViewArea *gv_layer_get_view(GvLayer *layer);
+gint gv_layer_reproject(GvLayer *layer, const char *projection);
+
+#endif /*__GV_LAYER_H__ */
+

Added: packages/openev/branches/upstream/current/gvlinelayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvlinelayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvlinelayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,375 @@
+/******************************************************************************
+ * $Id: gvlinelayer.c,v 1.12 2002/11/05 18:56:24 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer for GvLines.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvlinelayer.c,v $
+ * Revision 1.12  2002/11/05 18:56:24  sduclos
+ * fix gcc warning
+ *
+ * Revision 1.11  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.10  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvlinelayer.h"
+#include <gtk/gtksignal.h>
+#include <GL/gl.h>
+
+static void gv_line_layer_class_init(GvLineLayerClass *klass);
+static void gv_line_layer_init(GvLineLayer *layer);
+static void gv_line_layer_draw(GvLineLayer *layer, GvViewArea *view);
+static void gv_line_layer_extents(GvLineLayer *layer, GvRect *rect);
+static void gv_line_layer_data_change(GvLineLayer *layer, gpointer change_info);
+static void gv_line_layer_draw_selected(GvLineLayer *layer, GvViewArea *view);
+static void gv_line_layer_delete_selected(GvLineLayer *layer);
+static void gv_line_layer_translate_selected(GvLineLayer *layer, GvVertex *delta);
+static void gv_line_layer_pick_shape(GvLineLayer *layer);
+static void gv_line_layer_pick_node(GvLineLayer *layer);
+static void gv_line_layer_get_node(GvLineLayer *layer, GvNodeInfo *info);
+static void gv_line_layer_move_node(GvLineLayer *layer, GvNodeInfo *info);
+static void gv_line_layer_insert_node(GvLineLayer *layer, GvNodeInfo *info);
+static void gv_line_layer_delete_node(GvLineLayer *layer, GvNodeInfo *info);
+
+GtkType
+gv_line_layer_get_type(void)
+{
+    static GtkType line_layer_type = 0;
+
+    if (!line_layer_type)
+    {
+	static const GtkTypeInfo line_layer_info =
+	{
+	    "GvLineLayer",
+	    sizeof(GvLineLayer),
+	    sizeof(GvLineLayerClass),
+	    (GtkClassInitFunc) gv_line_layer_class_init,
+	    (GtkObjectInitFunc) gv_line_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	line_layer_type = gtk_type_unique(gv_shape_layer_get_type(),
+					  &line_layer_info);
+    }
+    return line_layer_type;
+}
+
+static void
+gv_line_layer_class_init(GvLineLayerClass *klass)
+{
+    typedef void (*f)();
+    GvDataClass *data_class;
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    data_class = (GvDataClass*) klass;
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    data_class->changed = (f) gv_line_layer_data_change;
+    
+    layer_class->draw            = (f) gv_line_layer_draw;
+    layer_class->extents_request = (f) gv_line_layer_extents;
+
+    shape_layer_class->draw_selected      = (f) gv_line_layer_draw_selected;
+    shape_layer_class->delete_selected    = (f) gv_line_layer_delete_selected;
+    shape_layer_class->translate_selected = (f) gv_line_layer_translate_selected;
+    shape_layer_class->pick_shape         = (f) gv_line_layer_pick_shape;
+    shape_layer_class->pick_node          = (f) gv_line_layer_pick_node;
+    shape_layer_class->get_node           = (f) gv_line_layer_get_node;
+    shape_layer_class->move_node          = (f) gv_line_layer_move_node;
+    shape_layer_class->insert_node        = (f) gv_line_layer_insert_node;
+    shape_layer_class->delete_node        = (f) gv_line_layer_delete_node;
+}
+
+static void
+gv_line_layer_init(GvLineLayer *layer)
+{
+    GvColor default_line_color = {1.0, 1.0, 0.0, 1.0};  /* yellow */
+    
+    layer->data = NULL;
+    gv_color_copy(GV_SHAPE_LAYER(layer)->color, default_line_color);
+
+
+}
+
+GtkObject *
+gv_line_layer_new(GvPolylines *data)
+{
+    GvLineLayer *layer = GV_LINE_LAYER(gtk_type_new(gv_line_layer_get_type()));
+    
+    if (data)
+    {
+	layer->data = data;
+    }
+    else
+    {
+	layer->data = GV_POLYLINES(gv_polylines_new());
+    }
+
+    /* Set the number of shapes - case where data exists before layer was created */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_polylines_num_lines(layer->data));
+
+    gv_data_set_parent(GV_DATA(layer), GV_DATA(layer->data));
+    
+    return GTK_OBJECT(layer);
+}
+
+gint
+gv_line_layer_select_new_line(GvLineLayer *layer)
+{
+    gint line_id;
+    
+    line_id = gv_polylines_new_line(layer->data);
+    
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_polylines_num_lines(layer->data));
+    gv_shape_layer_select_shape(GV_SHAPE_LAYER(layer), line_id);
+
+    return line_id;
+}
+
+/*******************************************************/
+
+static void
+gv_line_layer_draw(GvLineLayer *layer, GvViewArea *view)
+{
+    gint i, lines;
+    GArray *line;
+    gint *selected, presentation;
+    gint hit_selected = FALSE;
+
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    lines = gv_polylines_num_lines(layer->data);
+    
+    glColor4fv(GV_SHAPE_LAYER(layer)->color);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    for (i=0; i < lines; ++i)
+    {
+	if (selected[i] && !presentation)
+	{
+	    hit_selected = 1;
+	    continue;
+	}
+
+	line = gv_polylines_get_line(layer->data, i);
+
+	glVertexPointer(2, GL_GEOCOORD, 0, line->data);
+	glDrawArrays(GL_LINE_STRIP, 0, line->len);
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);
+    
+    if (hit_selected && ! GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+	gv_line_layer_draw_selected(layer, view);
+    }    
+}
+
+static void
+gv_line_layer_draw_selected(GvLineLayer *layer, GvViewArea *view)
+{
+    gint i, lines;
+    GArray *line;
+    gint *selected;
+
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    lines = gv_polylines_num_lines(layer->data);
+
+    glColor4fv(GV_SHAPE_LAYER(layer)->color);
+    glPointSize(3.0);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    for (i=0; i < lines; ++i)
+    {
+	if (selected[i])
+	{
+	    line = gv_polylines_get_line(layer->data, i);
+	    
+	    glVertexPointer(2, GL_GEOCOORD, 0, line->data);
+	    glDrawArrays(GL_LINE_STRIP, 0, line->len);
+	    
+#ifdef HAVE_BROKEN_GL_POINTS
+	    {
+		int j;
+		for (j=0; j < line->len; ++j)
+		{
+		    glBegin(GL_POINTS);
+		    glArrayElement(j);
+		    glEnd();
+		}
+	    }
+#else	    
+	    glDrawArrays(GL_POINTS, 0, line->len);
+#endif /* HAVE_BROKEN_GL_POINTS */	    
+	}
+    }
+    glDisableClientState(GL_VERTEX_ARRAY); 
+}
+
+static void
+gv_line_layer_pick_shape(GvLineLayer *layer)
+{
+    gint i, lines;
+    GArray *line;
+
+    if (!gv_layer_is_visible(GV_LAYER(layer))) return;
+
+    lines = gv_polylines_num_lines(layer->data);
+    
+    glEnableClientState(GL_VERTEX_ARRAY);
+    for (i=0; i < lines; ++i)
+    {
+	line = gv_polylines_get_line(layer->data, i);
+
+	glLoadName(i);
+	glVertexPointer(2, GL_GEOCOORD, 0, line->data);
+	glDrawArrays(GL_LINE_STRIP, 0, line->len);
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);     
+}
+
+static void
+gv_line_layer_pick_node(GvLineLayer *layer)
+{
+    GArray *line;
+    gint sel, i;
+    
+    if (!gv_layer_is_visible(GV_LAYER(layer))) return;
+
+    if (!gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_FIRST, &sel))
+    {
+	return;
+    }
+    line = gv_polylines_get_line(layer->data, sel);
+    
+    glEnableClientState(GL_VERTEX_ARRAY);     
+    glVertexPointer(2, GL_GEOCOORD, 0, line->data);
+
+    /* Nodes first */
+    glLoadName(0);
+    glPushName(0); /* Ring id is always zero */
+    glPushName(-1);
+    glPointSize(1.0);
+    for (i=0; i < line->len; ++i)
+    {
+	glLoadName(i);
+	glBegin(GL_POINTS);
+	glArrayElement(i);
+	glEnd();
+    }
+    glPopName(); /* node id */
+    glPopName(); /* ring id */
+    
+    /* Segments next */
+    glLoadName(1);
+    glPushName(0); /* Ring id is always zero */
+    glPushName(-1);
+    for (i=1; i < line->len; ++i)
+    {
+	glLoadName(i);
+	glBegin(GL_LINES);
+	glArrayElement(i-1);
+	glArrayElement(i);
+	glEnd();
+    }
+    glPopName(); /* node id */
+    glPopName(); /* ring id */
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+static void
+gv_line_layer_delete_selected(GvLineLayer *layer)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+	/* This will force a selection clear */
+	gv_polylines_delete_lines(layer->data, sel->len, (gint*)sel->data);
+    }
+    g_array_free(sel, TRUE);
+}
+
+static void
+gv_line_layer_translate_selected(GvLineLayer *layer, GvVertex *delta)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+	gv_polylines_translate_lines(layer->data, sel->len, (gint*)sel->data,
+				     delta->x, delta->y);
+    }
+    g_array_free(sel, TRUE);
+}
+
+static void
+gv_line_layer_get_node(GvLineLayer *layer, GvNodeInfo *info)
+{
+    info->vertex = gv_polylines_get_node(layer->data, info->shape_id,
+					 info->node_id);
+}
+
+static void
+gv_line_layer_move_node(GvLineLayer *layer, GvNodeInfo *info)
+{
+    gv_polylines_move_node(layer->data, info->shape_id, info->node_id,
+			   info->vertex);
+}
+
+static void
+gv_line_layer_insert_node(GvLineLayer *layer, GvNodeInfo *info)
+{
+    gv_polylines_insert_nodes(layer->data, info->shape_id, info->node_id,
+			      1, info->vertex);
+}
+
+static void
+gv_line_layer_delete_node(GvLineLayer *layer, GvNodeInfo *info)
+{
+    gv_polylines_delete_nodes(layer->data, info->shape_id, 1, &info->node_id);
+}
+
+static void
+gv_line_layer_extents(GvLineLayer *layer, GvRect *rect)
+{
+    gv_polylines_get_extents(layer->data, rect);
+}
+
+static void
+gv_line_layer_data_change(GvLineLayer *layer, gpointer change_info)
+{
+    /* Reset the selected array to reflect the data length */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_polylines_num_lines(layer->data));
+}
+

Added: packages/openev/branches/upstream/current/gvlinelayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvlinelayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvlinelayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * $Id: gvlinelayer.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer for GvLines.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvlinelayer.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_LINE_LAYER_H__
+#define __GV_LINE_LAYER_H__
+
+#include "gvshapelayer.h"
+#include "gvpolylines.h"
+
+#define GV_TYPE_LINE_LAYER            (gv_line_layer_get_type ())
+#define GV_LINE_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_LINE_LAYER, GvLineLayer))
+#define GV_LINE_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_LINE_LAYER, GvLineLayerClass))
+#define GV_IS_LINE_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_LINE_LAYER))
+#define GV_IS_LINE_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_LINE_LAYER))
+
+typedef struct _GvLineLayer       GvLineLayer;
+typedef struct _GvLineLayerClass  GvLineLayerClass;
+
+struct _GvLineLayer
+{
+    GvShapeLayer shape_layer;
+
+    GvPolylines *data;
+};
+
+struct _GvLineLayerClass
+{
+    GvShapeLayerClass parent_class;
+};
+
+GtkType gv_line_layer_get_type(void);
+GtkObject* gv_line_layer_new(GvPolylines *data);
+
+gint gv_line_layer_select_new_line(GvLineLayer *layer);
+
+#endif /* __GV_LINE_LAYER_H__ */

Added: packages/openev/branches/upstream/current/gvlinetool.c
===================================================================
--- packages/openev/branches/upstream/current/gvlinetool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvlinetool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,469 @@
+/******************************************************************************
+ * $Id: gvlinetool.c,v 1.19 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Polyline editing mode. 
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvlinetool.c,v $
+ * Revision 1.19  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.18  2001/08/08 17:44:12  warmerda
+ * use gv_shape_type() macro
+ *
+ * Revision 1.17  2001/04/09 18:15:20  warmerda
+ * improved warning
+ *
+ * Revision 1.16  2000/08/09 14:40:16  warmerda
+ * escape also termintes digitization mode
+ *
+ * Revision 1.15  2000/08/08 20:58:47  warmerda
+ * recover from layer destruction
+ *
+ * Revision 1.14  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.13  2000/07/24 14:00:24  warmerda
+ * set_layer with NULL should be allowed
+ *
+ * Revision 1.12  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.11  2000/07/17 19:20:21  warmerda
+ * avoid putting gl.h ahead of openev include files: windows problem
+ *
+ * Revision 1.10  2000/07/17 17:11:09  warmerda
+ * added delete key support
+ *
+ * Revision 1.9  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include "gvlinetool.h"
+#include "gvundo.h"
+#include <GL/gl.h>
+
+static void gv_line_tool_class_init(GvLineToolClass *klass);
+static void gv_line_tool_init(GvLineTool *tool);
+static void gv_line_tool_draw(GvLineTool *tool);
+static void gv_line_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_line_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_line_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_line_tool_deactivate(GvTool *tool, GvViewArea *view);
+static void gv_line_tool_stop_drawing(GvLineTool *tool);
+static gint gv_line_tool_configure(GvLineTool *tool);
+
+GtkType
+gv_line_tool_get_type(void)
+{
+    static GtkType line_tool_type = 0;
+
+    if (!line_tool_type)
+    {
+	static const GtkTypeInfo line_tool_info =
+	{
+	    "GvLineTool",
+	    sizeof(GvLineTool),
+	    sizeof(GvLineToolClass),
+	    (GtkClassInitFunc) gv_line_tool_class_init,
+	    (GtkObjectInitFunc) gv_line_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	line_tool_type = gtk_type_unique(gv_tool_get_type(),
+					 &line_tool_info);
+    }
+    return line_tool_type;
+}
+
+static void
+gv_line_tool_class_init(GvLineToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_line_tool_deactivate;
+    tool_class->button_press = gv_line_tool_button_press;
+    tool_class->motion_notify = gv_line_tool_motion_notify;
+    tool_class->key_press = gv_line_tool_key_press;
+}
+
+static void
+gv_line_tool_init(GvLineTool *tool)
+{
+    GV_TOOL(tool)->cursor = gdk_cursor_new(GDK_TCROSS);
+    tool->layer = NULL;
+    tool->named_layer = NULL;
+    tool->drawing = FALSE;
+}
+
+GvTool *
+gv_line_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_LINE_TOOL));
+}
+
+static gint gv_line_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvLineTool *tool = (GvLineTool *) data;
+
+    if( tool->layer == GV_SHAPE_LAYER(layer) )
+        gv_line_tool_set_layer( tool, NULL );
+    
+    return 0;
+}
+
+void
+gv_line_tool_set_layer(GvLineTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	g_warning("gv_line_tool_set_layer(): inactive tool");
+	return;
+    }
+
+    if( layer != NULL && gv_data_is_read_only( GV_DATA(layer) ) )
+    {
+        g_warning( "gv_line_tool_set_layer(): layer is read-only" );
+        return;
+    }
+
+    /* Disconnect from the previous layer (for draw) */
+    if (tool->layer)
+    {
+	if (tool->drawing)
+	{
+	    gv_line_tool_stop_drawing(tool);
+	}
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gtk_signal_disconnect_by_data(GTK_OBJECT(tool->layer), (gpointer)tool);
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+    
+    tool->layer = layer;
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+	
+        /* Redraw when the layer draws */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "draw",
+					GTK_SIGNAL_FUNC(gv_line_tool_draw),
+					GTK_OBJECT(tool));
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy", 
+            GTK_SIGNAL_FUNC(gv_line_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+void
+gv_line_tool_set_named_layer(GvLineTool *tool, gchar *name)
+{
+    if (tool->named_layer)
+    {
+	g_free(tool->named_layer);
+	tool->named_layer = NULL;
+    }
+    if (name)
+    {
+	tool->named_layer = g_strdup(name);	
+    }
+    /* Tool layer will be updated next time it is configured */
+}
+
+/********************************************************/
+
+static void
+gv_line_tool_draw(GvLineTool *tool)
+{
+    if (tool->drawing)
+    {
+	/* Color is set when the layer is drawn,
+	   so we don't need to repeat it here */
+
+	glBegin(GL_LINES);
+	glVertex2v((GLgeocoord*)&tool->v_head);
+	glVertex2v((GLgeocoord*)&tool->v_tail);
+	glEnd();
+    }
+}
+
+static void
+gv_line_tool_button_press(GvTool *r_tool, GdkEventButton *event)
+{
+    GvLineTool *tool = GV_LINE_TOOL(r_tool);
+
+    if (event->button == 1)
+    {
+	gint line_id;
+	
+	if (!gv_line_tool_configure(tool)) return;
+
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+	
+	if (tool->drawing)
+	{
+            gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                                     &(tool->v_tail.x), &(tool->v_tail.y) );
+
+	    /* Filter out duplicate verticies */
+	    if (tool->v_head.x == tool->v_tail.x &&
+		tool->v_head.y == tool->v_tail.y)
+	    {
+		return;
+	    }
+
+	    /* Add a new vertex to the line */
+	    gv_shape_layer_selected(GV_SHAPE_LAYER(tool->layer), GV_FIRST,
+				    &line_id);
+	}
+	else
+	{
+            if( !gv_tool_check_bounds( GV_TOOL(tool), 
+                                       tool->v_tail.x, tool->v_tail.y ) )
+                return;
+
+	    /* Start a new line */
+	    tool->drawing = TRUE;
+	    
+	    /* Close down undo.  A single operation describing the new
+	       line will be pushed to undo when drawing stops. */
+	    gv_undo_close();
+	    gv_undo_disable();
+
+	    if( GV_IS_LINE_LAYER(tool->layer) )
+            {
+                line_id = gv_line_layer_select_new_line(
+                    GV_LINE_LAYER(tool->layer));
+            }
+            else
+            {
+                GvShape *line = gv_shape_new( GVSHAPE_LINE );
+
+                line_id = gv_shapes_layer_select_new_shape( 
+                    GV_SHAPES_LAYER(tool->layer), line );
+            }
+	}
+
+	tool->v_head = tool->v_tail;
+
+        if( GV_IS_LINE_LAYER(tool->layer) )
+            gv_polylines_append_nodes(GV_LINE_LAYER(tool->layer)->data, 
+                                      line_id, 1, &tool->v_tail);
+        else
+        {
+            GvShapes *shapes = GV_SHAPES_LAYER(tool->layer)->data;
+            GvShape *line = gv_shapes_get_shape(shapes, line_id );
+
+            if( gv_shape_type(line) == GVSHAPE_LINE )
+                gv_shape_add_node(line, 0, tool->v_tail.x, tool->v_tail.y, 0);
+            else
+                g_warning( "selected object not line in gvlinetool mode!\n" );
+        }
+    }
+    else if (event->button == 3 && tool->drawing)
+    {
+	gv_line_tool_stop_drawing(tool);
+    }	
+}
+
+static void
+gv_line_tool_motion_notify(GvTool *r_tool, GdkEventMotion *event)
+{
+    GvLineTool *tool = GV_LINE_TOOL(r_tool);
+
+    if (tool->drawing)
+    {
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+	
+        gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                                 &(tool->v_tail.x), &(tool->v_tail.y) );
+
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+}
+
+static void
+gv_line_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvLineTool *tool = GV_LINE_TOOL(rtool);
+
+    if (!gv_line_tool_configure(tool)) return;
+    
+    switch (event->keyval)
+    {
+        case GDK_Escape:
+          gv_line_tool_stop_drawing(tool);
+          break;
+
+	case GDK_Delete:
+	case GDK_BackSpace:
+          if( tool->drawing && !GV_IS_LINE_LAYER(tool->layer) )
+          {
+              gint line_id;
+              GvNodeInfo  node_info;
+              GvShape     *shape;
+              GvShapes *shapes = GV_SHAPES_LAYER(tool->layer)->data;
+
+              gv_shape_layer_selected(GV_SHAPE_LAYER(tool->layer), GV_FIRST,
+                                      &line_id);
+
+              shape = gv_shapes_get_shape( shapes, line_id );
+
+              node_info.shape_id = line_id;
+              node_info.ring_id = 0;
+              node_info.node_id = gv_shape_get_nodes( shape, 0 ) - 1;
+
+              if( gv_shape_get_nodes( shape, 0 ) > 1 )
+              {
+                  tool->v_head.x = gv_shape_get_x( shape, 0, 
+                                                   node_info.node_id-1);
+                  tool->v_head.y = gv_shape_get_y( shape, 0, 
+                                                   node_info.node_id-1);
+              }
+              else
+              {
+                  tool->drawing = FALSE;
+              }
+
+              gv_shape_layer_delete_node(tool->layer, &node_info );
+
+              if( !tool->drawing )
+                  gv_line_tool_stop_drawing( tool );
+          }
+          break;
+    }
+}
+
+static void
+gv_line_tool_deactivate(GvTool *r_tool, GvViewArea *view)
+{
+    GvLineTool *tool = GV_LINE_TOOL(r_tool);
+
+    /* Disconnect from layer */
+    if (tool->layer)
+    {
+	gv_line_tool_set_layer(tool, NULL);
+    }
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+}
+
+static void
+gv_line_tool_stop_drawing(GvLineTool *tool)
+{
+    gint sel, push_undo = TRUE;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    change_info.shape_id = &sel;
+    
+    tool->drawing = FALSE;
+
+    /* Reject lines with only one node */
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(tool->layer), GV_FIRST, &sel))
+    { 
+        if( GV_IS_LINE_LAYER(tool->layer) )
+        {
+            if (gv_polylines_get_line(GV_LINE_LAYER(tool->layer)->data, 
+                                      sel)->len < 2)
+            {
+                gv_shape_layer_delete_selected(GV_SHAPE_LAYER(tool->layer));
+                push_undo = FALSE;
+            }
+        }
+        else
+        {
+            GvShapes   *shapes = GV_SHAPES_LAYER(tool->layer)->data;
+            GvShape    *shape = gv_shapes_get_shape(shapes, sel);
+
+            if( shape != NULL && gv_shape_type(shape) == GVSHAPE_LINE 
+                && gv_shape_get_nodes(shape,0) < 2 )
+            {
+                gv_shape_layer_delete_selected(GV_SHAPE_LAYER(tool->layer));
+                push_undo = FALSE;
+            }
+        }
+    }
+
+    /* Reopen undo.  Push a memento describing the line addition. */
+    gv_undo_enable();
+    gv_undo_open();
+    if (push_undo)
+    {
+        gv_undo_push(
+            gv_data_get_memento(gv_data_get_parent(GV_DATA(tool->layer)),
+                                &change_info));
+    }
+    
+    gv_view_area_queue_draw(GV_TOOL(tool)->view);    
+}
+
+static gint
+gv_line_tool_configure(GvLineTool *tool)
+{
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	if (tool->named_layer)
+	{
+	    /* Look for named layer if given */
+	    layer = gv_view_area_get_named_layer(GV_TOOL(tool)->view,
+						 tool->named_layer);
+	}
+	else
+	{
+	    /* Attempt to find a line layer to edit */
+	    layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+						   GV_TYPE_LINE_LAYER, 
+                                                   FALSE);
+            if( layer == NULL )
+                layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+                                                       GV_TYPE_SHAPES_LAYER,
+                                                       FALSE);
+	}
+	if (!layer)
+	{
+	    g_warning("gv_line_tool_configure(): no editable line layer in view");
+	    return FALSE;
+	}
+
+	gv_line_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+    return tool->layer != NULL;
+}

Added: packages/openev/branches/upstream/current/gvlinetool.h
===================================================================
--- packages/openev/branches/upstream/current/gvlinetool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvlinetool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,69 @@
+/******************************************************************************
+ * $Id: gvlinetool.h,v 1.6 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Polyline editing mode. 
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvlinetool.h,v $
+ * Revision 1.6  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_LINE_TOOL_H__
+#define __GV_LINE_TOOL_H__
+
+#include "gvtool.h"
+#include "gvlinelayer.h"
+#include "gvshapeslayer.h"
+
+#define GV_TYPE_LINE_TOOL            (gv_line_tool_get_type ())
+#define GV_LINE_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_LINE_TOOL, GvLineTool))
+#define GV_LINE_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_LINE_TOOL, GvLineToolClass))
+#define GV_IS_LINE_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_LINE_TOOL))
+#define GV_IS_LINE_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_LINE_TOOL))
+
+typedef struct _GvLineTool       GvLineTool;
+typedef struct _GvLineToolClass  GvLineToolClass;
+
+struct _GvLineTool
+{
+    GvTool tool;
+
+    GvShapeLayer *layer;
+    gchar *named_layer;
+    guint drawing : 1;
+    GvVertex v_head, v_tail;
+};
+
+struct _GvLineToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_line_tool_get_type(void);
+GvTool* gv_line_tool_new(void);
+void gv_line_tool_set_layer(GvLineTool *tool, GvShapeLayer *layer);
+void gv_line_tool_set_named_layer(GvLineTool *tool, gchar *name);
+
+#endif /* __GV_LINE_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvmanager.c
===================================================================
--- packages/openev/branches/upstream/current/gvmanager.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvmanager.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,571 @@
+/******************************************************************************
+ * $Id: gvmanager.c,v 1.14 2004/02/10 15:38:30 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Preferences (and other?) manager.  Singleton object.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvmanager.c,v $
+ * Revision 1.14  2004/02/10 15:38:30  andrey_kiselev
+ * Added gv_manager_add_dataset() function.
+ *
+ * Revision 1.13  2001/07/24 02:21:54  warmerda
+ * added 8bit phase averaging
+ *
+ * Revision 1.12  2001/01/30 14:29:33  warmerda
+ * added gv_manager_dump
+ *
+ * Revision 1.11  2000/09/29 20:32:27  warmerda
+ * made decimation the default instead of averaging
+ *
+ * Revision 1.10  2000/09/27 19:17:13  warmerda
+ * use prefs for GvRaster sample method
+ *
+ * Revision 1.9  2000/08/23 14:16:06  warmerda
+ * avoid error message opening for update
+ *
+ * Revision 1.8  2000/08/09 17:36:50  warmerda
+ * update lists when GvRaster destroyed
+ *
+ * Revision 1.7  2000/07/25 14:18:19  warmerda
+ * added task dequeuing support
+ *
+ * Revision 1.6  2000/06/29 19:50:17  warmerda
+ * Initialize busy_changed signal handler.
+ *
+ * Revision 1.5  2000/06/29 14:38:22  warmerda
+ * added busy status, and idle task list
+ *
+ * Revision 1.4  2000/06/26 15:12:00  warmerda
+ * Added dataset/gvraster management
+ *
+ * Revision 1.3  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvmanager.h"
+#include <gtk/gtksignal.h>
+#include <stdio.h>
+#include <string.h>
+
+
+enum
+{
+    PREFERENCES_CHANGED,
+    BUSY_CHANGED,
+    LAST_SIGNAL
+};
+
+static void gv_manager_class_init(GvManagerClass *klass);
+
+static guint manager_signals[LAST_SIGNAL] = { 0 };
+static void gv_manager_init( GvManager *manager );
+static gint gv_manager_idle_handler( gpointer );
+
+GtkType
+gv_manager_get_type(void)
+{
+    static GtkType manager_type = 0;
+
+    if (!manager_type)
+    {
+	static const GtkTypeInfo manager_info =
+	{
+	    "GvManager",
+	    sizeof(GvManager),
+	    sizeof(GvManagerClass),
+	    (GtkClassInitFunc) gv_manager_class_init,
+	    (GtkObjectInitFunc) gv_manager_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	manager_type = gtk_type_unique(gtk_object_get_type(),
+				    &manager_info);
+    }
+    return manager_type;
+}
+
+static void
+gv_manager_class_init(GvManagerClass *klass)
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    manager_signals[PREFERENCES_CHANGED] =
+	gtk_signal_new ("preferences-changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvManagerClass,preferences_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    manager_signals[BUSY_CHANGED] =
+	gtk_signal_new ("busy-changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvManagerClass,busy_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    gtk_object_class_add_signals(object_class, manager_signals, LAST_SIGNAL);
+    
+    klass->preferences_changed = NULL;
+    klass->busy_changed = NULL;
+}
+
+static void 
+gv_manager_init( GvManager *manager )
+
+{
+    manager->preferences = NULL;
+    manager->datasets = g_ptr_array_new();
+    manager->idle_tasks = NULL;
+    manager->busy_flag = FALSE;
+    GDALAllRegister();
+}
+
+GvManager *gv_manager_new()
+{
+    return GV_MANAGER(gtk_type_new(GV_TYPE_MANAGER));
+}
+
+GvManager *gv_get_manager()
+
+{
+    static GvManager *main_manager = NULL;
+
+    if( main_manager == NULL )
+        main_manager = gv_manager_new();
+
+    return main_manager;
+}
+
+void gv_manager_dump( GvManager *manager )
+{
+    FILE	*fp = stderr;
+    int		i;
+    GvIdleTask	*task;
+
+    fprintf( fp, "GvManager Status Report\n" );
+    fprintf( fp, "=======================\n" );
+
+    fprintf( fp, "\n" );
+    fprintf( fp, "Preferences:\n" );
+    for( i = 0; i < gv_properties_count( &(manager->preferences) ); i++ )
+    {
+        fprintf( fp, "  %s=%s\n", 
+                 gv_properties_get_name_by_index( &(manager->preferences), i),
+                 gv_properties_get_value_by_index( &(manager->preferences),i));
+    }
+    
+    fprintf( fp, "\n" );
+    fprintf( fp, "Datasets:\n" );
+    for( i = 0; i < manager->datasets->len; i++ )
+    {
+        GvDataset *ds = (GvDataset *) g_ptr_array_index(manager->datasets,i);
+        int band;
+
+        fprintf( fp, "  %s:", GDALGetDescription( ds->dataset ) );
+        
+        for( band = 0; band < GDALGetRasterCount(ds->dataset); band++ )
+        {
+            if( ds->rasters[band] != NULL )
+                fprintf( fp, "R" );
+            else
+                fprintf( fp, "_" );
+        }
+        fprintf( fp, "\n" );
+    }
+    
+    fprintf( fp, "\n" );
+    fprintf( fp, "Idle Tasks:\n" );
+    
+    for( task = manager->idle_tasks; task != NULL; task = task->next )
+    {
+        fprintf( fp, "  %s: priority=%d, cb=%p, cb_data=%p\n",
+                 task->name, task->priority, task->callback, task->task_info );
+    }
+}
+
+GvProperties *gv_manager_get_preferences(GvManager *manager)
+
+{
+    return &(manager->preferences);
+}
+
+const char *gv_manager_get_preference(GvManager *manager, const char *name)
+
+{
+    return gv_properties_get(&(manager->preferences),name);
+}
+
+void gv_manager_set_preference(GvManager *manager, 
+                               const char *name, const char *value )
+
+{
+    /* don't do anything if it is already set */
+
+    if( gv_properties_get( &(manager->preferences), name ) != NULL 
+        && strcmp(gv_properties_get(&(manager->preferences),name),value) == 0 )
+        return;
+
+    gv_properties_set( &(manager->preferences), name, value );
+
+    gtk_signal_emit(GTK_OBJECT(manager), 
+                    manager_signals[PREFERENCES_CHANGED]);
+}
+
+/************************************************************************/
+/*                        gv_manager_add_dataset()                      */
+/************************************************************************/
+
+/**
+ * Adds GDALDatasetH object instance to the list of managed datasets.
+ *
+ * This method adds given GDALDatasetH object instance to the list of
+ * managed datasets. Does nothing if this dataset already listed.
+ *
+ * @param manager Pointer to GvManager object.
+ * @param dataset GDALDatasetH dataset handler.
+ *
+ * @return Handler for added GDALDatasetH object (the same as supplied in
+ * dataset parameter) or NULL if parameters are wrong.
+ */
+
+GDALDatasetH gv_manager_add_dataset( GvManager *manager, GDALDatasetH dataset )
+
+{
+    int       i;
+    GvDataset *ds;
+
+    if( dataset == NULL || manager == NULL )
+        return NULL;
+
+    /*
+     * Check for dataset in existing list of open files.  Note that our
+     * filename check does not account for different possible names for
+     * one dataset.
+     */
+    for( i = 0; i < manager->datasets->len; i++ )
+    {
+        ds = (GvDataset *) g_ptr_array_index(manager->datasets, i);
+
+        if( EQUAL(GDALGetDescription(ds->dataset),GDALGetDescription(dataset)) )
+        {
+            return ds->dataset;
+        }
+    }
+
+    /* 
+     * Add the dataset to the list of managed datasets. 
+     */
+    GDALReferenceDataset(dataset);
+    ds = g_new(GvDataset,1);
+    ds->dataset = dataset;
+    ds->rasters = g_new0(GvRaster *, GDALGetRasterCount(dataset));
+    
+    g_ptr_array_add( manager->datasets, ds );
+
+    return dataset;
+}
+
+GDALDatasetH gv_manager_get_dataset( GvManager *manager, const char * filename)
+
+{
+    int       i;
+    GvDataset *ds;
+    GDALDatasetH dataset;
+
+    /*
+     * Check for dataset in existing list of open files.  Note that our
+     * filename check does not account for different possible names for
+     * one dataset.
+     */
+    for( i = 0; i < manager->datasets->len; i++ )
+    {
+        ds = (GvDataset *) g_ptr_array_index(manager->datasets, i);
+
+        if( EQUAL(GDALGetDescription(ds->dataset),filename) )
+        {
+            return ds->dataset;
+        }
+    }
+
+    /*
+     * Try to open the dataset, preferably with update access.  We don't
+     * want to report update access errors so we supress error reporting
+     * temporarily. 
+     */
+    
+    CPLErrorReset();
+    CPLPushErrorHandler( CPLQuietErrorHandler );
+    dataset = GDALOpen( filename, GA_Update );
+    CPLPopErrorHandler();
+
+    if( dataset == NULL )
+    {
+        dataset = GDALOpen( filename, GA_ReadOnly );
+    }
+
+    if( dataset == NULL )
+        return NULL;
+
+    /* 
+     * Add the dataset to the list of managed datasets. 
+     */
+    ds = g_new(GvDataset,1);
+    ds->dataset = dataset;
+    ds->rasters = g_new0(GvRaster *, GDALGetRasterCount(dataset));
+    
+    g_ptr_array_add( manager->datasets, ds );
+
+    return dataset;
+}
+
+static gint gv_manager_raster_destroy_cb( GtkObject * raster_in, 
+                                          gpointer cb_data )
+
+{
+    GvManager *manager = GV_MANAGER(cb_data);
+    GvRaster  *raster = GV_RASTER(raster_in);
+    GvDataset *ds = NULL;
+    int       i, active_rasters = 0;
+
+    /* 
+     * Find in our list.  The dataset must already be "under management".
+     */
+    for( i = 0; i < manager->datasets->len; i++ )
+    {
+        ds = (GvDataset *) g_ptr_array_index(manager->datasets, i);
+
+        if( raster->dataset == ds->dataset )
+            break;
+    }
+
+    if( i == manager->datasets->len )
+    {
+        g_warning( "gv_manager_raster_destroy_cb(): can't find dataset." );
+        return FALSE;
+    }
+
+    /*
+     * Find our GvRaster.
+     */
+
+    for( i = 0; i < GDALGetRasterCount(ds->dataset); i++ )
+    {
+        if( ds->rasters[i] == raster )
+            ds->rasters[i] = NULL;
+        else if( ds->rasters[i] != NULL )
+            active_rasters++;
+    }
+
+    /*
+     * We apparently no longer need this GDALDataset.  Dereference it, and
+     * remove from the list. 
+     */
+    if( active_rasters == 0 )
+    {
+        if( GDALDereferenceDataset( ds->dataset ) < 1 )
+            GDALClose( ds->dataset );
+        
+        g_free( ds->rasters );
+        g_free( ds );
+        g_ptr_array_remove_fast( manager->datasets, ds );
+    }
+
+    return FALSE;
+}
+
+GvRaster *gv_manager_get_dataset_raster( GvManager *manager, 
+                                         GDALDatasetH dataset, int band )
+
+{
+    int       i;
+    GvDataset *ds = NULL;
+    GvSampleMethod sm;
+    const char *sm_pref;
+
+    if( band < 1 || band > GDALGetRasterCount(dataset) )
+        return NULL;
+
+    /* 
+     * Find in our list.  The dataset must already be "under management".
+     */
+    for( i = 0; i < manager->datasets->len; i++ )
+    {
+        ds = (GvDataset *) g_ptr_array_index(manager->datasets, i);
+
+        if( dataset == ds->dataset )
+            break;
+    }
+
+    if( i == manager->datasets->len )
+    {
+        g_warning( "gv_manager_get_dataset_raster called with unmanaged dataset" );
+        return NULL;
+    }
+
+    /*
+     * Figure out the sample method to use from the preferences.
+     */
+    sm_pref = gv_manager_get_preference( manager, "default_raster_sample");
+    if( sm_pref != NULL && EQUAL(sm_pref,"average") )
+        sm = GvSMAverage;
+    else if( sm_pref != NULL && EQUAL(sm_pref,"average_8bit_phase") )
+        sm = GvSMAverage8bitPhase;
+    else
+        sm = GvSMSample;
+
+    /* 
+     * Create a new GvRaster if it doesn't already exist.
+     */
+
+    if( ds->rasters[band-1] == NULL )
+    {
+        GDALRasterBandH gdal_band;
+
+        gdal_band = GDALGetRasterBand( ds->dataset, band );
+        if( GDALGetRasterColorInterpretation(gdal_band) == GCI_PaletteIndex )
+            sm = GvSMSample;
+
+        ds->rasters[band-1] = GV_RASTER(gv_raster_new( ds->dataset, band, sm));
+        gtk_signal_connect(
+            GTK_OBJECT(ds->rasters[band-1]), "destroy", 
+            GTK_SIGNAL_FUNC(gv_manager_raster_destroy_cb),
+            GTK_OBJECT(manager));
+    }
+
+    return ds->rasters[band-1];
+}
+
+void gv_manager_set_busy( GvManager *manager, int busy_flag )
+
+{
+    if( !manager->busy_flag == !busy_flag )
+        return;
+
+    if( !manager->busy_flag )
+        gtk_idle_add( gv_manager_idle_handler, NULL );
+
+    manager->busy_flag = busy_flag;
+
+    gtk_signal_emit(GTK_OBJECT(manager), 
+                    manager_signals[BUSY_CHANGED]);
+}
+
+int gv_manager_get_busy( GvManager *manager )
+
+{
+    return manager->busy_flag;
+}
+
+static gint gv_manager_idle_handler( gpointer cb_data )
+
+{
+    GvManager *manager = gv_get_manager();
+    GvIdleTask *task;
+
+    if( manager->idle_tasks == NULL )
+    {
+        gv_manager_set_busy( manager, FALSE );
+        return FALSE;
+    }
+
+    /* remove task from list for now */
+    task = manager->idle_tasks;
+    manager->idle_tasks = task->next;
+    task->next = NULL;
+
+    if( task->callback( task->task_info ) )
+        gv_manager_queue_task( manager, task->name, task->priority, 
+                               task->callback, task->task_info );
+
+    g_free( task->name );
+    g_free( task );
+
+    return TRUE;
+}
+
+void gv_manager_queue_task( GvManager *manager, const char *task_name, 
+                            int priority, GtkFunction callback, 
+                            void *task_info)
+
+{
+    GvIdleTask *task = g_new0( GvIdleTask, 1 );
+
+    task->name = g_strdup( task_name );
+    task->priority = priority;
+    task->callback = callback;
+    task->task_info = task_info;
+
+    if( manager->idle_tasks == NULL 
+        || manager->idle_tasks->priority > task->priority )
+    {
+        task->next = manager->idle_tasks;
+        manager->idle_tasks = task;
+    }
+    else
+    {
+        GvIdleTask *link;
+
+        for( link = manager->idle_tasks; link != NULL; link = link->next ) 
+        {
+            if( link->next == NULL )
+            {
+                link->next = task;
+                break;
+            }
+            else if( link->next->priority > task->priority )
+            {
+                task->next = link->next;
+                link->next = task;
+                break;
+            }
+        }
+    }
+
+    gv_manager_set_busy( manager, TRUE );
+}
+
+void gv_manager_dequeue_task( GvManager *manager, GvIdleTask *task )
+
+{
+    GvIdleTask **prev_ptr = &(manager->idle_tasks);
+
+    while( *prev_ptr != NULL )
+    {
+        if( *prev_ptr == task )
+        {
+            *prev_ptr = task->next;
+            
+            g_free( task->name );
+            g_free( task );
+        }
+        else
+            prev_ptr = &((*prev_ptr)->next);
+    }
+}

Added: packages/openev/branches/upstream/current/gvmanager.h
===================================================================
--- packages/openev/branches/upstream/current/gvmanager.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvmanager.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,118 @@
+/******************************************************************************
+ * $Id: gvmanager.h,v 1.6 2004/02/10 15:38:31 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Preferences (and other?) manager.  Singleton object.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvmanager.h,v $
+ * Revision 1.6  2004/02/10 15:38:31  andrey_kiselev
+ * Added gv_manager_add_dataset() function.
+ *
+ * Revision 1.5  2001/01/30 14:29:33  warmerda
+ * added gv_manager_dump
+ *
+ * Revision 1.4  2000/06/29 14:38:22  warmerda
+ * added busy status, and idle task list
+ *
+ * Revision 1.3  2000/06/26 15:12:00  warmerda
+ * Added dataset/gvraster management
+ *
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_MANAGER_H__
+#define __GV_MANAGER_H__
+
+#include <gtk/gtkobject.h>
+#include "gvtypes.h"
+#include "gvproperties.h"
+#include "gvraster.h"
+
+#define GV_TYPE_MANAGER              (gv_manager_get_type ())
+#define GV_MANAGER(obj)              (GTK_CHECK_CAST ((obj), GV_TYPE_MANAGER, GvManager))
+#define GV_MANAGER_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_MANAGER, GvManagerClass))
+#define GV_IS_MANAGER(obj)           (GTK_CHECK_TYPE ((obj), GV_TYPE_MANAGER))
+#define GV_IS_MANAGER_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_MANAGER))
+
+typedef struct _GvManager       GvManager;
+typedef struct _GvManagerClass  GvManagerClass;
+
+typedef struct {
+    GDALDatasetH  dataset;
+
+    GvRaster     **rasters;
+} GvDataset;
+
+typedef struct _idle_task {
+    struct _idle_task *next;
+    char         *name;
+    int          priority;
+    GtkFunction  callback;
+    void         *task_info;
+} GvIdleTask;
+
+struct _GvManager
+{
+    GtkObject  object;
+
+    GvProperties preferences;
+
+    GPtrArray *datasets;
+
+    int        busy_flag;    /* TRUE if busy */
+
+    GvIdleTask*idle_tasks; 
+};
+
+struct _GvManagerClass
+{
+    GtkObjectClass parent_class;
+
+    void (* preferences_changed)(GvManager *man);
+    void (* busy_changed)(GvManager *man);
+};
+
+GtkType    gv_manager_get_type   (void);
+GvManager* gv_manager_new        (void);
+GvManager* gv_get_manager        (void);
+
+void       gv_manager_dump( GvManager * );
+const char* gv_manager_get_preference(GvManager *, const char *);
+GvProperties* gv_manager_get_preferences(GvManager *);
+void       gv_manager_set_preference(GvManager *, const char *, const char *);
+
+GDALDatasetH gv_manager_add_dataset( GvManager *, GDALDatasetH );
+GDALDatasetH gv_manager_get_dataset( GvManager *, const char * );
+GvRaster    *gv_manager_get_dataset_raster( GvManager *manager,
+                                            GDALDatasetH dataset, int band );
+
+void       gv_manager_set_busy( GvManager *manager, int busy_flag );
+int        gv_manager_get_busy( GvManager *manager );
+void       gv_manager_queue_task( GvManager *manager,
+                                  const char *task_name, int priority,
+                                  GtkFunction callback, void *task_info );
+void       gv_manager_dequeue_task( GvManager *manager, GvIdleTask *task );
+
+#endif /* __GV_MANAGER_H__ */

Added: packages/openev/branches/upstream/current/gvmesh.c
===================================================================
--- packages/openev/branches/upstream/current/gvmesh.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvmesh.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1165 @@
+/******************************************************************************
+ * $Id: gvmesh.c,v 1.37 2005/08/30 12:31:32 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Geometric mesh mapping tile s/t coordinates to display x/y/z
+ *           coordinates.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvmesh.c,v $
+ * Revision 1.37  2005/08/30 12:31:32  andrey_kiselev
+ * Avoid initialization warnings in gv_mesh_extents().
+ *
+ * Revision 1.36  2004/02/18 16:07:27  andrey_kiselev
+ * Use gv_raster_get_nodata() instead of GDALGetRasterNoDataValue().
+ *
+ * Revision 1.35  2002/09/11 18:24:28  warmerda
+ * fixed serious hosing in gv_mesh_get_height() for multi tile meshes
+ *
+ * Revision 1.34  2002/09/10 21:13:49  warmerda
+ * fixed serious bugs in gv_mesh_get_height() esp for partial tiles
+ *
+ * Revision 1.33  2002/04/12 14:40:35  gmwalter
+ * Removed the gvmesh rescale function (not needed because of view area
+ * rescaling).
+ *
+ * Revision 1.31  2002/03/07 02:33:45  warmerda
+ * Fixed add_height() function so that mesh nodes for which no value is ever
+ * found are set to default_height instead of 1000.0.  Also, values falling
+ * outside the source height raster are now set to default_height instead of 0.0.
+ *
+ * Revision 1.30  2001/08/22 16:20:38  warmerda
+ * use gv_mesh_reset_to_identity for setting to raw
+ *
+ * Revision 1.29  2001/08/08 13:18:08  warmerda
+ * fixed case of edge tiles not of reduced size
+ *
+ * Revision 1.28  2001/08/08 02:57:38  warmerda
+ * implement gv_mesh_finalize(), remove unused normals code
+ *
+ * Revision 1.27  2001/07/13 22:13:35  warmerda
+ * added function to get height from mesh
+ *
+ * Revision 1.26  2001/03/29 03:53:18  warmerda
+ * use three iterations in nodata fill-in
+ *
+ * Revision 1.25  2001/03/29 03:38:58  warmerda
+ * improved nodata propagation to go two steps
+ *
+ * Revision 1.24  2001/03/28 22:38:16  warmerda
+ * fill in mesh nodata values with average of ajacent real mesh points
+ *
+ * Revision 1.23  2000/08/23 19:06:42  warmerda
+ * fixed problems with overly large levels of detail
+ *
+ * Revision 1.22  2000/08/23 18:35:11  warmerda
+ * avoid off-by-one errors and honour nodata in setting mesh from dem file
+ *
+ * Revision 1.21  2000/08/18 20:33:56  warmerda
+ * don't return a mesh that is more resolved than the raster
+ *
+ * Revision 1.20  2000/08/16 20:28:31  warmerda
+ * fixed calculation of corner tiles in get extents
+ *
+ * Revision 1.19  2000/07/03 20:57:32  warmerda
+ * moved tile selection for draw to gvrasterlayer
+ *
+ * Revision 1.18  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "gvviewarea.h"
+#include <GL/gl.h>
+#include "gvmesh.h"
+#include "gvraster.h"
+#include "gvrasterlayer.h"
+#include "gvmanager.h"
+
+static void gv_mesh_class_init(GvMeshClass *klass);
+static void gv_mesh_init(GvMesh *mesh);
+static void gv_mesh_finalize(GtkObject *object);
+
+#define GV_MESH_RIGHT_X_BIT 1
+#define GV_MESH_LEFT_X_BIT  2
+#define GV_MESH_TOP_Y_BIT   4
+#define GV_MESH_BOT_Y_BIT   8
+
+#define DEG2RAD         0.01745329252
+#define RAD2DEG         57.2986885
+
+GtkType
+gv_mesh_get_type(void)
+{
+    static GtkType mesh_type = 0;
+
+    if (!mesh_type)
+    {
+	static const GtkTypeInfo mesh_info =
+	{
+	    "GvMesh",
+	    sizeof(GvMesh),
+	    sizeof(GvMeshClass),
+	    (GtkClassInitFunc) gv_mesh_class_init,
+	    (GtkObjectInitFunc) gv_mesh_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	mesh_type = gtk_type_unique(gv_data_get_type(), &mesh_info);
+    }
+    return mesh_type;
+}
+
+static void
+gv_mesh_init(GvMesh *mesh)
+{
+    
+    mesh->vertices = NULL;
+    mesh->tex_coords = NULL;
+
+}
+
+static void
+gv_mesh_class_init(GvMeshClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    data_class = (GvDataClass*) klass;
+
+    object_class = (GtkObjectClass*) klass;
+    object_class->finalize = gv_mesh_finalize;
+}
+
+static GArray *
+gv_mesh_get_tile_tex_coords( GvMesh *mesh, int tile, int lod )
+
+{
+    gint     tiles_across, tiles_down;
+    GPtrArray *parray;
+
+    if( lod >= mesh->raster->max_lod )
+        lod = mesh->raster->max_lod - 1;
+    else if( lod < 0 )
+        lod = 0;
+
+    tiles_across = mesh->raster->tiles_across;
+    tiles_down = mesh->raster->tiles_down;
+
+    if( tile >= tiles_across * (tiles_down-1) )
+    {
+        if( (tile % tiles_across) == (tiles_across-1) )
+            parray = mesh->tex_coords_bottom_right;
+        else
+            parray = mesh->tex_coords_bottom;
+    }
+    else if( (tile % tiles_across) == (tiles_across-1) )
+        parray = mesh->tex_coords_right;
+    else
+        parray = mesh->tex_coords;
+
+    return (GArray *) g_ptr_array_index(parray,lod);
+}
+
+gint *
+gv_mesh_get_tile_corner_coords( GvMesh *mesh, int tile)
+{
+    gint tiles_across = mesh->raster->tiles_across;
+    /* gint tiles_down   = mesh->raster->tiles_down; */
+
+    gint tile_x = mesh->raster->tile_x;
+    gint tile_y = mesh->raster->tile_y;
+
+    gint *coords = (gint *)malloc( 8*sizeof(gint) );
+    if(coords == NULL)
+    {
+        g_error("Out of memory!!!");
+        return NULL;
+    }
+
+    /* top left corner */
+    coords[0] = tile_x * (tile % tiles_across);
+    coords[1] = tile_y * (tile / tiles_across);
+
+    /* top right corner */
+    coords[2] = coords[0] + tile_x;
+    coords[3] = coords[1];
+
+    /* bottom left corner */
+    coords[4] = coords[0];
+    coords[5] = coords[1] + tile_y;
+
+    /* bottom right corner */
+    coords[6] = coords[0] + tile_x;
+    coords[7] = coords[1] + tile_y;
+
+    /* Check that aren't past edge of image on far right and bottom */
+    if ( coords[2] >= mesh->raster->width)
+    {
+        coords[2] = mesh->raster->width - 1;
+        coords[6] = mesh->raster->width - 1;
+    }
+
+    if ( coords[5] >= mesh->raster->height)
+    {
+        coords[5] = mesh->raster->height - 1;
+        coords[7] = mesh->raster->height - 1;
+    }
+
+    return coords;
+    
+}
+
+static void 
+gv_mesh_build_tex_coord( GvMesh *mesh, GArray *tex_coords, gint lod,
+                         gint ds_x, gint ds_y, 
+                         gint stride_x, gint stride_y )
+
+{
+    gint   i, e;
+    gint   tile_x = mesh->tile_x;
+    gint   tile_y = mesh->tile_y;
+    gint   edge_width;
+
+    /* 
+    ** Edge mesh tiles (bottom, right and bottom/right) that are not a
+    ** reduced size, will be computed as having a remainder of zero, and
+    ** given a size of 2.  Adjust these. 
+    */
+
+    if( ds_x == 2 )
+        ds_x += mesh->tile_x-GV_TILE_OVERLAP;
+
+    if( ds_y == 2 )
+        ds_y += mesh->tile_y-GV_TILE_OVERLAP;
+
+    /* Compute */
+
+    edge_width = (1 << lod) * GV_TILE_OVERLAP/2;
+
+    for( i = 0; i <= tile_y; i += stride_y )
+    {
+        for( e = 0; e <= tile_x; e += stride_x )
+        {
+            float s, t;
+
+            s = (float) e / (float) tile_x;
+            t = (float) i / (float) tile_y;
+
+            s = (s * (ds_x-edge_width*2) + edge_width) / tile_x;
+            t = (t * (ds_y-edge_width*2) + edge_width) / tile_y;
+
+            g_array_append_val( tex_coords, s );
+            g_array_append_val( tex_coords, t );
+        }
+    }
+}
+                              
+
+GvMesh *
+gv_mesh_new_identity( GvRaster *raster, gint detail )
+{
+    gint begin_x, begin_y;
+    gint stride_x, stride_y;
+    gint i, e, j, k, mesh_lod;
+    gint tiles_down, tiles_across, tile;
+    gint tile_x = raster->tile_x;
+    gint tile_y = raster->tile_y;
+    gint x = raster->width;
+    gint y = raster->height;
+    GArray *tex_coords;
+    
+    GvMesh *mesh = (GvMesh *) GV_DATA(gtk_type_new(gv_mesh_get_type()));
+
+    mesh->raster = raster;
+
+    while( (tile_x >> detail) == 0 || (tile_y >> detail) == 0 )
+        detail--;
+
+    stride_x = tile_x >> detail;
+    stride_y = tile_y >> detail;
+
+    mesh->detail = detail;
+    mesh->tile_x = tile_x;
+    mesh->tile_y = tile_y;
+    mesh->width = x;
+    mesh->height = y;
+
+    tiles_across = raster->tiles_across;
+    tiles_down   = raster->tiles_down;
+
+    mesh->max_tiles = tiles_across * tiles_down;
+    mesh->vertices = g_array_new( FALSE, FALSE, sizeof( GArray *) );
+
+    /* Set top left coords */
+    
+    mesh->corner_coords[0] = 0.0;
+    mesh->corner_coords[1] = (float) y;
+
+    /* Set top right coords */
+    
+    mesh->corner_coords[2] = (float) x;
+    mesh->corner_coords[3] = (float) y;
+
+    /* Set bottom left coords */
+
+    mesh->corner_coords[4] = 0.0;
+    mesh->corner_coords[5] = 0.0;
+
+    /* Set bottom right coords */
+
+    mesh->corner_coords[6] = (float) x;
+    mesh->corner_coords[7] = 0.0;
+
+    /* prepare meshes for each level of detail */
+
+    mesh->tex_coords = g_ptr_array_new();
+    mesh->tex_coords_right = g_ptr_array_new();
+    mesh->tex_coords_bottom = g_ptr_array_new();
+    mesh->tex_coords_bottom_right = g_ptr_array_new();
+
+    for( mesh_lod = 0; mesh_lod < raster->max_lod; mesh_lod++ )
+    {
+        /* First we set the full tile s/t coordinate mappings */
+        tex_coords = g_array_new( FALSE, FALSE, sizeof( float ) );
+        g_ptr_array_add(mesh->tex_coords, tex_coords );
+        gv_mesh_build_tex_coord( mesh, tex_coords, mesh_lod, 
+                                 tile_x, tile_y, stride_x, stride_y );
+
+        /* Generate right hand side partial tile s/t coordinates */
+        tex_coords = g_array_new( FALSE, FALSE, sizeof( float ) );;
+        g_ptr_array_add(mesh->tex_coords_right, tex_coords );
+        gv_mesh_build_tex_coord( mesh, tex_coords, mesh_lod, 
+                                 (mesh->width % (tile_x-GV_TILE_OVERLAP)) + 2,
+                                 tile_y, 
+                                 stride_x, stride_y );
+        /* Generate bottom side partial tile s/t coordinates */
+        tex_coords = g_array_new( FALSE, FALSE, sizeof( float ) );;
+        g_ptr_array_add(mesh->tex_coords_bottom, tex_coords );
+        gv_mesh_build_tex_coord( mesh, tex_coords, mesh_lod, 
+                                 tile_x,
+                                 (mesh->height % (tile_y-GV_TILE_OVERLAP)) + 2,
+                                 stride_x, stride_y );
+
+        /* Generate bottom/right partial tile s/t coordinates */
+        tex_coords = g_array_new( FALSE, FALSE, sizeof( float ) );;
+        g_ptr_array_add(mesh->tex_coords_bottom_right, tex_coords );
+        gv_mesh_build_tex_coord( mesh, tex_coords, mesh_lod, 
+                                 (mesh->width  % (tile_x-GV_TILE_OVERLAP)) + 2,
+                                 (mesh->height % (tile_y-GV_TILE_OVERLAP)) + 2,
+                                 stride_x, stride_y );
+    }
+
+    /* generate output raster coordinates for every tile */
+    tile = 0; 
+    for( i = 0; i < tiles_down; i++ )
+    {
+	for( e = 0; e < tiles_across; e++ )
+	{
+	    GArray *tile_vertices;
+            int     tex_index = 0;
+
+            tex_coords = gv_mesh_get_tile_tex_coords( mesh, tile, 0 );
+
+	    begin_x = e * (tile_x-GV_TILE_OVERLAP) - GV_TILE_OVERLAP/2;
+	    begin_y = i * (tile_y-GV_TILE_OVERLAP) - GV_TILE_OVERLAP/2;
+
+	    tile_vertices = g_array_new( FALSE, FALSE, sizeof( float ) );
+
+	    for( j = 0; j <= tile_y; j += stride_y )
+	    {
+		for( k = 0; k <= tile_x; k += stride_x )
+		{
+		    float t_x, t_y, t_z;
+                    float s, t;
+
+                    s = g_array_index( tex_coords, float, 2*tex_index );
+                    t = g_array_index( tex_coords, float, 2*tex_index+1);
+                    tex_index++;
+
+		    t_x = begin_x + s*tile_x;
+		    t_y = y - (begin_y + t*tile_y);
+		    t_z = 0.0;
+		    
+		    g_array_append_val( tile_vertices, t_x );
+		    g_array_append_val( tile_vertices, t_y );
+		    g_array_append_val( tile_vertices, t_z );
+		}
+	    }
+
+	    g_array_append_val( mesh->vertices, tile_vertices );
+            tile++;
+	}
+    }
+
+    return mesh;
+}
+
+void 
+gv_mesh_reset_to_identity( GvMesh *mesh )
+
+{
+    int	i, e, tile, tiles_across, tiles_down, stride_x, stride_y;
+    int	begin_x, begin_y;
+    gint tile_x = mesh->tile_x;
+    gint tile_y = mesh->tile_y;
+
+    stride_x = mesh->tile_x >> mesh->detail;
+    stride_y = mesh->tile_y >> mesh->detail;
+
+    tiles_across = mesh->raster->tiles_across;
+    tiles_down   = mesh->raster->tiles_down;
+
+    /* generate output raster coordinates for every tile */
+    tile = 0; 
+    for( i = 0; i < tiles_down; i++ )
+    {
+	for( e = 0; e < tiles_across; e++ )
+	{
+            GArray *tex_coords;
+	    GArray *tile_vertices;
+            int     tex_index = 0, out_vert = 0, j, k;
+
+            tex_coords = gv_mesh_get_tile_tex_coords( mesh, tile, 0 );
+
+	    begin_x = e * (tile_x-GV_TILE_OVERLAP) - GV_TILE_OVERLAP/2;
+	    begin_y = i * (tile_y-GV_TILE_OVERLAP) - GV_TILE_OVERLAP/2;
+
+	    tile_vertices = g_array_index( mesh->vertices, GArray *, tile );
+
+	    for( j = 0; j <= tile_y; j += stride_y )
+	    {
+		for( k = 0; k <= tile_x; k += stride_x )
+		{
+		    float t_x, t_y;
+                    float s, t;
+
+                    s = g_array_index( tex_coords, float, 2*tex_index );
+                    t = g_array_index( tex_coords, float, 2*tex_index+1);
+                    tex_index++;
+
+		    t_x = begin_x + s*tile_x;
+  		    t_y = begin_y + t*tile_y;
+		    
+		    g_array_index(tile_vertices, float, out_vert++) = t_x;
+		    g_array_index(tile_vertices, float, out_vert++) = t_y;
+                    out_vert++;
+		}
+	    }
+
+            tile++;
+	}
+    }
+}
+
+
+/* Add height values to mesh based on provided hight raster
+   - go through mesh->vertices and for every x-y co-ord transform back to image co-ordinates
+   - query height raster at x-y point and set mesh->verticies z value to it
+ */
+void
+gv_mesh_add_height( GvMesh *mesh, GvRaster *raster, 
+                    double default_height )
+{
+    int     i, j, success, iteration, max_iteration;
+    float   z_float;
+    double  x, y, z, imaginary;
+    double  nodata_value;
+
+    success = gv_raster_get_nodata( raster, &nodata_value );
+    if( !success )
+        nodata_value = -1e8;
+
+    /* number of tiles */
+    for( i=0; i < mesh->vertices->len; i++)
+    {
+        GArray *tile_vertices;
+        
+        tile_vertices = g_array_index( mesh->vertices, GArray *, i);
+
+        /* Vertices in tile */
+        for( j=0; j < (tile_vertices->len/3); j++)
+        {
+            /* get xy in image space */
+            x = (float) g_array_index( tile_vertices, float, 3*j);
+            y = (float) g_array_index( tile_vertices, float, 3*j+1);
+            z = (float) g_array_index( tile_vertices, float, 3*j+2);
+
+            /*            printf("x y z %f %f %f", x, y, z); */
+
+            if (!gv_raster_georef_to_pixel(raster, &x, &y, &z))
+            {
+                fprintf(stderr, "ERROR raster_georef_to_pixel failed!!!\n");
+                break;
+            }
+
+            if( x > -1.0 && x < 0.0 )
+                x = 0.0;
+            if( y > -1.0 && y < 0.0 )
+                y = 0.0;
+            if( x >= raster->width && x < raster->width+1 )
+                x = raster->width - 0.01;
+            if( y >= raster->height && y < raster->height+1 )
+                y = raster->height - 0.01;
+
+            /* Check if mesh xy values outside of height raster - leave as 0 */
+            if( x >= 0.0 && x < raster->width
+                && y >= 0.0 && y < raster->height )
+            {
+                if (!gv_raster_get_sample(raster, x, y, &z, &imaginary))
+                {
+                    fprintf(stderr, 
+                            "ERROR raster_get_sample failed for (x y z) %f %f\n", 
+                            x, y);
+                    z_float = (float)nodata_value;
+                }
+                else
+                {
+                    z_float = z;
+                }
+            }
+            else
+                z_float = (float)nodata_value;
+
+            g_array_index( tile_vertices, float, 3*j+2) = z_float;
+        }
+
+        mesh->vertices = g_array_remove_index(mesh->vertices, i);
+        g_array_insert_val(mesh->vertices, i, tile_vertices);
+    }
+
+    /* 
+     * Make another pass, trying to fill in mesh elevations for vertices
+     * where we got nodata, or fell off the available data.
+     *
+     * We run this twice to propagate out values an extra step.
+     */
+
+    max_iteration = 3;
+    for( iteration = 0; iteration < max_iteration; iteration++ )
+    {
+        /* number of tiles */
+        for( i=0; i < mesh->vertices->len; i++)
+        {
+            GArray *tile_vertices;
+            int	j, k;
+            int	mesh_xsize, mesh_ysize;
+            float	*data;
+
+            tile_vertices = g_array_index( mesh->vertices, GArray *, i);
+            data = (float *) tile_vertices->data;
+
+            mesh_xsize = (1 << mesh->detail) + 1;
+            mesh_ysize = (1 << mesh->detail) + 1;
+
+            g_assert( mesh_xsize * mesh_ysize * 3 == tile_vertices->len );
+
+            for( j = 0; j < mesh_ysize; j++ )
+            {
+                for( k = 0; k < mesh_xsize; k++ )
+                {
+                    float	sum = 0; 
+                    int	count = 0;
+                    int	t_i;
+
+                    t_i = j * mesh_xsize + k;
+
+                    /* skip entries with an OK value */
+                    if( data[3*t_i+2] != (float)nodata_value )
+                        continue;
+
+                    /* check mesh entry to left */
+                    if( k > 0 && data[3*(t_i-1)+2] != (float)nodata_value )
+                    {
+                        sum += data[3*(t_i-1)+2];
+                        count++;
+                    }
+
+                    /* check mesh entry to right */
+                    if( k+1 < mesh_xsize && data[3*(t_i+1)+2] != (float)nodata_value )
+                    {
+                        sum += data[3*(t_i+1)+2];
+                        count++;
+                    }
+                                                   
+                    /* check mesh entry above */
+                    if( j > 0 
+                        && data[3*(t_i-mesh_xsize)+2] != (float)nodata_value )
+                    {
+                        sum += data[3*(t_i-mesh_xsize)+2];
+                        count++;
+                    }
+
+                    /* check mesh entry below */
+                    if( j+1 < mesh_ysize 
+                        && data[3*(t_i+mesh_xsize)+2] != (float)nodata_value )
+                    {
+                        sum += data[3*(t_i+mesh_xsize)+2];
+                        count++;
+                    }
+
+                    /* if we got hits, average them and assign */
+                    if( count > 0 )
+                    {
+                        data[3*t_i+2] = sum / count;
+                    }
+                    else if( iteration == max_iteration-1 )
+                    {
+                        data[3*t_i+2] = default_height;
+                    }
+                }
+            }
+        }    
+    }
+}
+
+/* 
+ * Set upper/lower bounds for the height (eg. if a DEM
+ * defaults to -32767 for patches of missing data within
+ * the image, gv_mesh_add_height will treat it as valid data.  
+ * This function takes a pass through the vertices
+ * and clamps if necessary).  This will also get rid of the
+ * effects of a very low nodata_value (when the DEM doesn't
+ * cover the full area of the raster being draped atop it).
+ */
+
+void
+gv_mesh_clamp_height( GvMesh *mesh, int bclamp_min, int bclamp_max, 
+                    double min_height, double max_height )
+{
+
+    int i;
+
+    /* 
+     * Clamp minimum z value to min_height if bclamp_min = 1
+     * Clamp maximum z value to max_height if bclamp_max = 1
+     */
+
+    /* number of tiles */
+    for( i=0; i < mesh->vertices->len; i++)
+    {
+        GArray *tile_vertices;
+        int	j, k;
+        int	mesh_xsize, mesh_ysize;
+        float	*data;
+
+        tile_vertices = g_array_index( mesh->vertices, GArray *, i);
+        data = (float *) tile_vertices->data;
+
+        mesh_xsize = (1 << mesh->detail) + 1;
+        mesh_ysize = (1 << mesh->detail) + 1;
+
+        g_assert( mesh_xsize * mesh_ysize * 3 == tile_vertices->len );
+
+
+        for( j = 0; j < mesh_ysize; j++ )
+        {
+            for( k = 0; k < mesh_xsize; k++ )
+            {
+                int	t_i;
+
+                t_i = j * mesh_xsize + k;
+
+                if (( bclamp_min == 1 ) && ( data[3*t_i+2] < min_height ))
+                    data[3*t_i+2] = min_height;
+                
+                if (( bclamp_max == 1 ) && ( data[3*t_i+2] > max_height ))
+                    data[3*t_i+2] = max_height;
+                
+            }
+        }    
+    }
+}
+
+
+/*
+ * This function assumes the mesh is the result of gv_mesh_new_identity, and
+ * maps a raw raster space into an equivelent OpenGL model space.  Now
+ * transform the model space outputs to be georeferenced coordinates instead
+ * of pixel/line identity values.
+ *
+ * The "geotransform" is the usual six word affine transform.
+ */
+
+void
+gv_mesh_set_transform( GvMesh *mesh, gint xsize, gint ysize, 
+                       double *geotransform )
+{
+    int    tile;
+
+    for( tile = 0; tile < mesh->max_tiles; tile++ )
+    {
+        GArray *verts;
+        float  *xyz_verts;
+        int    xyz_offset;
+
+	verts = g_array_index( mesh->vertices, GArray *, tile );
+        
+        xyz_verts = (float *) verts->data;
+        for( xyz_offset = 0; xyz_offset < verts->len; xyz_offset += 3 )
+        {
+            float     x_out, y_out;
+
+            x_out = geotransform[0] 
+                + xyz_verts[0] * geotransform[1] 
+                + (ysize - xyz_verts[1]) * geotransform[2];
+            y_out = geotransform[3] 
+                + xyz_verts[0] * geotransform[4] 
+                + (ysize - xyz_verts[1]) * geotransform[5];
+
+            xyz_verts[0] = x_out;
+            xyz_verts[1] = y_out;
+
+            xyz_verts += 3;
+        }
+    }
+}
+
+
+
+GvMeshTile *
+gv_mesh_get( GvMesh *mesh, gint tile, gint raster_lod, gint detail, 
+             GvMeshTile *tile_info )
+{
+    gint i, e, step;
+    gint dimensions = (1 << mesh->detail) + 1;
+    GArray *verts;
+    static GArray *g_indices = NULL;
+
+    if( tile_info == NULL )
+    {
+	if( ( tile_info = g_new( GvMeshTile, 1 ) ) == NULL )
+	{
+	    return NULL;
+	}
+    }
+
+    if( detail < raster_lod )
+        detail = raster_lod;
+    
+    if( tile < mesh->max_tiles )
+    {
+	if( mesh->tex_coords )
+        {
+            GArray  *tex_coords; 
+
+            tex_coords = gv_mesh_get_tile_tex_coords( mesh, tile, detail );
+	    tile_info->tex_coords = (float *) tex_coords->data;
+        }
+	else
+	    tile_info->tex_coords = NULL;
+
+	verts = g_array_index( mesh->vertices, GArray *, tile );
+
+	if( verts == NULL )
+	{
+	    fprintf( stderr, "Missing vertices information\n" );
+	}
+	
+	tile_info->vertices = (float *)verts->data;
+
+	/* Figure out the spacing for the index */
+
+        /*  What does this do ??? */
+	if( detail >= mesh->detail )
+	{
+            /* Use mesh spacing */
+	    step = 1;
+	    detail = mesh->detail;
+	} else {
+            /* Use raster spacing */
+	    step = 1 << (mesh->detail - detail);
+	}
+
+        /* Note: overwrites above calculations */
+        step = 1;
+        detail = mesh->detail;
+       
+
+	/* Now we build the indices and get ready to return the tile */
+
+	tile_info->restarts = 0;
+	tile_info->list_type = GL_TRIANGLE_STRIP;
+
+	if( g_indices )
+	{
+	    g_array_set_size( g_indices, 0 );
+	} else {
+	    g_indices = g_array_new( FALSE, FALSE, sizeof( int ) );
+	}
+
+	for( i = 0; i < dimensions-1; i += step )
+	{
+	    for( e = 0; e < dimensions; e += step )
+	    {
+		gint val;
+
+		val = i * dimensions + e;
+
+		g_array_append_val( g_indices, val );
+
+		val = (i+step) * dimensions + e;
+
+		g_array_append_val( g_indices, val );
+	    }
+	    tile_info->restarts++;
+	}
+
+	switch( tile_info->list_type )
+	{
+	    case 0:
+		tile_info->range = dimensions*4;
+		break;
+	    case GL_TRIANGLE_STRIP:
+                /* Only this case works right now */
+		tile_info->range = ( (1 << detail ) + 1 ) * 2; 
+		break;
+	    case 2:
+		tile_info->range = 2+dimensions;
+		break;
+	    case 3:
+		tile_info->range = dimensions*2;
+		break;
+	}
+	
+	tile_info->restarts--;
+	tile_info->indices = (gint *) g_indices->data;
+    } else {
+	return NULL;
+    }
+    return tile_info;
+    
+}
+
+void gv_mesh_extents( GvMesh *mesh, GvRect *rect )
+{
+    float x[4], y[4];
+    int dimensions;
+    int tile;
+    GArray *verts;
+
+    if( mesh )
+    {
+        gint   tiles_per_row;
+
+	/* First we get the top left tile */
+
+	if( ( verts = g_array_index( mesh->vertices, GArray *, 0 ) ) == NULL )
+	{
+	    return;
+	}
+
+	dimensions = ( 1 << mesh->detail ) + 1;
+
+	x[0] = g_array_index( verts, float, 0 );
+	y[0] = g_array_index( verts, float, 1 );
+
+	/* The we do the top right tile */
+
+        tiles_per_row = mesh->raster->tiles_across;
+        tile = tiles_per_row - 1;
+
+	if( ( verts = g_array_index( mesh->vertices, GArray *, tile ) ) == NULL )
+	{
+	    return;
+	}
+
+	x[1] = g_array_index( verts, float, 3*(dimensions-1) );
+	y[1] = g_array_index( verts, float, 3*(dimensions-1)+1 );
+
+	/* The we do the bottom left tile */
+
+        tile = mesh->raster->tiles_across * (mesh->raster->tiles_down-1);
+
+	if( ( verts = g_array_index( mesh->vertices, GArray *, tile ) ) == NULL )
+	{
+	    return;
+	}
+
+	x[2] = g_array_index( verts, float, 3*(dimensions*(dimensions-1)) );
+	y[2] = g_array_index( verts, float, 3*(dimensions*(dimensions-1))+1);
+	
+	/* The we do the last tile */
+
+	if( ( verts = g_array_index( mesh->vertices, GArray *, mesh->max_tiles-1 ) ) == NULL )
+	{
+	    return;
+	}
+
+	x[3] = g_array_index( verts, float, 3*(dimensions*dimensions-1) );
+	y[3] = g_array_index( verts, float, 3*(dimensions*dimensions-1)+1 );
+	
+        /* Return results */
+        
+        rect->x = MIN( MIN( MIN( x[0], x[1] ), x[2] ), x[3] );
+        rect->y = MIN( MIN( MIN( y[0], y[1] ), y[2] ), y[3] );
+
+        rect->width = MAX( MAX( MAX( x[0], x[1] ), x[2] ), x[3] ) - rect->x;
+        rect->height = MAX( MAX( MAX( y[0], y[1] ), y[2] ), y[3] ) - rect->y;
+    }
+
+    return;
+}
+
+/************************************************************************/
+/*                         gv_mesh_get_height()                         */
+/*                                                                      */
+/*      Fetch the mesh height at the indicated location.                */
+/************************************************************************/
+
+float gv_mesh_get_height( GvMesh *mesh,
+                          double x, double y, int *success )
+
+{
+    int		tile, tile_in_x, tile_in_y, vert_off;
+    int		mesh_pnts_per_line, mesh_pnts_per_column, vert_x, vert_y;
+    int         factor;
+    double      stride_x, stride_y;
+    GvRaster    *raster = mesh->raster;
+    double      pl_x, pl_y;
+    GArray      *tile_vertices;
+    float	x_vert[4], y_vert[4], z_vert[4], u, v;
+    int         tile_width, tile_height;
+
+    if( success != NULL )
+        *success = FALSE;
+    
+/* -------------------------------------------------------------------- */
+/*      What pixel/line location are we looking for.  Provide a         */
+/*      little bit of fudging if necessary to avoid stuff on the        */
+/*      edge falling out of bounds.                                     */
+/* -------------------------------------------------------------------- */
+    
+    pl_x = x;
+    pl_y = y;
+
+    gv_raster_georef_to_pixel( raster, &pl_x, &pl_y, NULL );
+
+    if( pl_x < 0.0 && pl_x > -0.01 )
+        pl_x += 0.01;
+    if( pl_y < 0.0 && pl_y > -0.01 )
+        pl_y += 0.01;
+    if( pl_x >= raster->width && pl_x < raster->width + 0.01 )
+        pl_x -= 0.01;
+    if( pl_y >= raster->height && pl_y < raster->height + 0.01 )
+        pl_y -= 0.01;
+
+    if( pl_x < 0 || pl_x >= raster->width
+        || pl_y < 0 || pl_y >= raster->height )
+    {
+        CPLDebug( "OpenEV", 
+                  "Didn't get a value (1) for location Geo:(%g,%g) PL:(%g,%g) Size:(%d,%d)", 
+                  x, y, pl_x, pl_y, raster->width, raster->height );
+        return 0.0;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      What tile does this fall in.                                    */
+/* -------------------------------------------------------------------- */
+    tile_in_x = ((int) pl_x) / (raster->tile_x-GV_TILE_OVERLAP);
+    tile_in_y = ((int) pl_y) / (raster->tile_y-GV_TILE_OVERLAP);
+    
+    tile = tile_in_x + tile_in_y * raster->tiles_across;
+    
+/* -------------------------------------------------------------------- */
+/*      Where does our request fall within the tile.                    */
+/* -------------------------------------------------------------------- */
+    factor = 1 << mesh->detail;
+
+    mesh_pnts_per_line = factor + 1;
+    mesh_pnts_per_column = factor + 1;
+
+    if( tile_in_x == raster->tiles_across - 1 )
+        tile_width = mesh->width 
+            - (raster->tile_x - GV_TILE_OVERLAP) * tile_in_x;
+    else
+        tile_width = mesh->tile_x;
+
+    if( tile_in_y == raster->tiles_down - 1 )
+        tile_height = mesh->height 
+            - (raster->tile_y - GV_TILE_OVERLAP) * tile_in_y;
+    else
+        tile_height = mesh->tile_y;
+
+    stride_x = tile_width / (float) factor;
+    stride_y = tile_height / (float) factor;
+
+    pl_x -= tile_in_x * (raster->tile_x - GV_TILE_OVERLAP);
+    pl_y -= tile_in_y * (raster->tile_y - GV_TILE_OVERLAP);
+
+    vert_x = (int) (pl_x / stride_x);
+    vert_y = (int) (pl_y / stride_y);
+
+    vert_off = vert_x + vert_y * mesh_pnts_per_line;
+
+/* -------------------------------------------------------------------- */
+/*      Find the neighbouring vertices.                                 */
+/* -------------------------------------------------------------------- */
+    g_assert( tile >= 0 && tile < mesh->vertices->len );
+    tile_vertices = g_array_index( mesh->vertices, GArray *, tile );
+    
+    if( vert_off < 0 || vert_off*3 >= tile_vertices->len )
+    {
+        CPLDebug( "OpenEV", 
+                  "Didn't get a value for location (%g,%g), vert_off=%d\n", 
+                  x, y, vert_off );
+        return 0.0;
+    }
+
+    x_vert[0] = g_array_index( tile_vertices, float, 3*vert_off   );
+    y_vert[0] = g_array_index( tile_vertices, float, 3*vert_off+1 );
+    z_vert[0] = g_array_index( tile_vertices, float, 3*vert_off+2 );
+
+    if( vert_x+1 < mesh_pnts_per_line )
+    {
+        x_vert[1] = g_array_index( tile_vertices, float, 3*(vert_off+1)   );
+        y_vert[1] = g_array_index( tile_vertices, float, 3*(vert_off+1)+1 );
+        z_vert[1] = g_array_index( tile_vertices, float, 3*(vert_off+1)+2 );
+    }
+    else
+    {
+        x_vert[1] = x_vert[0];
+        y_vert[1] = y_vert[0];
+        z_vert[1] = z_vert[0];
+    }
+        
+    if( vert_y+1 < mesh_pnts_per_column )
+    {
+        int	off = vert_off + mesh_pnts_per_line;
+
+        x_vert[2] = g_array_index( tile_vertices, float, 3*off     );
+        y_vert[2] = g_array_index( tile_vertices, float, 3*off + 1 );
+        z_vert[2] = g_array_index( tile_vertices, float, 3*off + 2 );
+    }
+    else
+    {
+        x_vert[2] = x_vert[0];
+        y_vert[2] = y_vert[0];
+        z_vert[2] = z_vert[0];
+    }
+        
+    if( vert_y+1 < mesh_pnts_per_column && vert_x+1 < mesh_pnts_per_line )
+    {
+        int	off = vert_off + mesh_pnts_per_line + 1;
+
+        x_vert[3] = g_array_index( tile_vertices, float, 3*off     );
+        y_vert[3] = g_array_index( tile_vertices, float, 3*off + 1 );
+        z_vert[3] = g_array_index( tile_vertices, float, 3*off + 2 );
+    }
+    else if( vert_y+1 < mesh_pnts_per_column )
+    {
+        x_vert[3] = x_vert[2];
+        y_vert[3] = y_vert[2];
+        z_vert[3] = z_vert[2];
+    }
+    else
+    {
+        x_vert[3] = x_vert[1];
+        y_vert[3] = y_vert[1];
+        z_vert[3] = z_vert[1];
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Interpolate the value within this grid square.                  */
+/* -------------------------------------------------------------------- */
+    u = (pl_x - vert_x * stride_x) / (float) stride_x;
+    v = (pl_y - vert_y * stride_y) / (float) stride_y;
+
+    if( u < -0.0001 || u > 1.0001 || v < -0.0001 || v > 1.0001 )
+        g_warning( "illegal u or v in gv_mesh_get_height()" );
+
+#ifdef notdef
+    /* Note: this will produce false positives for "on the edges" conditions*/
+    if( x < MIN(x_vert[0],x_vert[1]) 
+        || x > MAX(x_vert[0],x_vert[1]) 
+        || y < MIN(y_vert[0],y_vert[2])
+        || y > MAX(y_vert[0],y_vert[2]) )
+    {
+        CPLDebug( "OpenEV", 
+                  "x/y outside of quadrant in gv_mesh_get_height(), uv=%g,%g\n"
+                  "  x,y=%g,%g   quad=(%g,%g),(%g,%g),(%g,%g),(%g,%g)",
+                  u, v, 
+                  x, y, 
+                  x_vert[0], y_vert[0],
+                  x_vert[1], y_vert[1],
+                  x_vert[2], y_vert[2],
+                  x_vert[3], y_vert[3] );
+    }
+#endif
+
+    if( success != NULL )
+        *success = TRUE;
+    
+    return z_vert[0] * (1.0-u) * (1.0-v)
+        + z_vert[1] * (u) * (1.0-v)
+        + z_vert[2] * (1.0-u) * (v)
+        + z_vert[3] * (u) * (v);
+}
+
+/************************************************************************/
+/*                          gv_mesh_finalize()                          */
+/*                                                                      */
+/*      Final destruction of GvMesh.                                    */
+/************************************************************************/
+
+static void
+gv_mesh_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvMesh    *mesh = GV_MESH(object);
+    int		i;
+    GPtrArray  *tex_coords;
+    
+    CPLDebug( "OpenEV", "gv_mesh_finalize(%s)\n", 
+              gv_data_get_name(GV_DATA(object)) );
+
+    /* free all vertices */
+    for( i = 0; i < mesh->vertices->len; i++ )
+        g_array_free( g_array_index( mesh->vertices, GArray *, i ), TRUE );
+
+    g_array_free( mesh->vertices, TRUE );
+
+    /* free texture coordinates */
+    tex_coords = mesh->tex_coords;
+    for( i = 0; i < tex_coords->len; i++ )
+        g_array_free( (GArray *) g_ptr_array_index(tex_coords,i), TRUE );
+
+    tex_coords = mesh->tex_coords_right;
+    for( i = 0; i < tex_coords->len; i++ )
+        g_array_free( (GArray *) g_ptr_array_index(tex_coords,i), TRUE );
+
+    tex_coords = mesh->tex_coords_bottom_right;
+    for( i = 0; i < tex_coords->len; i++ )
+        g_array_free( (GArray *) g_ptr_array_index(tex_coords,i), TRUE );
+
+    tex_coords = mesh->tex_coords_bottom;
+    for( i = 0; i < tex_coords->len; i++ )
+        g_array_free( (GArray *) g_ptr_array_index(tex_coords,i), TRUE );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}

Added: packages/openev/branches/upstream/current/gvmesh.h
===================================================================
--- packages/openev/branches/upstream/current/gvmesh.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvmesh.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,125 @@
+/******************************************************************************
+ * $Id: gvmesh.h,v 1.19 2002/04/12 14:40:36 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Geometric mesh mapping tile s/t coordinates to display x/y/z
+ *           coordinates.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvmesh.h,v $
+ * Revision 1.19  2002/04/12 14:40:36  gmwalter
+ * Removed the gvmesh rescale function (not needed because of view area
+ * rescaling).
+ *
+ * Revision 1.17  2002/03/07 02:31:56  warmerda
+ * added default_height to add_height functions
+ *
+ * Revision 1.16  2001/08/22 16:20:38  warmerda
+ * use gv_mesh_reset_to_identity for setting to raw
+ *
+ * Revision 1.15  2001/08/08 02:57:38  warmerda
+ * implement gv_mesh_finalize(), remove unused normals code
+ *
+ * Revision 1.14  2001/07/13 22:13:35  warmerda
+ * added function to get height from mesh
+ *
+ * Revision 1.13  2001/04/19 21:46:35  warmerda
+ * don't include ourself
+ *
+ * Revision 1.12  2000/07/03 20:57:32  warmerda
+ * moved tile selection for draw to gvrasterlayer
+ *
+ * Revision 1.11  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_MESH_H__
+#define __GV_MESH_H__
+
+#include "gvdata.h"
+#include "gvviewarea.h"
+#include "gvraster.h"
+
+#define GV_TYPE_MESH            (gv_mesh_get_type ())
+#define GV_MESH(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_MESH, GvMesh))
+#define GV_MESH_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_MESH, GvMeshClass))
+#define GV_IS_MESH(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_MESH))
+#define GV_IS_MESH_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_MESH))
+
+typedef struct _GvMesh       GvMesh;
+typedef struct _GvMeshClass  GvMeshClass;
+typedef struct _GvMeshTile   GvMeshTile;
+
+struct _GvMeshTile {
+    gint list_type; /* 0 = TRIANGLES, 1 = TRISTRIPS, 2 = QUADS, 3 = QUADSTRIPGS */
+    gint range;     /* Only used for "strip" processing */
+    gint restarts;
+    gint *indices;
+    gfloat *vertices;
+    gfloat *tex_coords;
+};
+
+struct _GvMesh
+{
+    GvData data;
+
+    struct _GvRaster *raster;
+
+    gint max_tiles;
+    gint tile_x, tile_y;
+    gint width, height;
+    gint detail;
+    gfloat corner_coords[8];
+
+    GArray *vertices;
+
+    GPtrArray *tex_coords;
+    GPtrArray *tex_coords_right;
+    GPtrArray *tex_coords_bottom;
+    GPtrArray *tex_coords_bottom_right;
+};
+
+struct _GvMeshClass
+{
+    GvDataClass parent_class;
+};
+
+struct _GvRaster;
+
+GtkType gv_mesh_get_type(void);
+
+GvMeshTile *gv_mesh_get(GvMesh *mesh, gint tile, gint raster_lod, gint detail,
+                        GvMeshTile *tile_info );
+gint * gv_mesh_get_tile_corner_coords( GvMesh *mesh, int tile);
+GvMesh *gv_mesh_new_identity( struct _GvRaster *raster, gint detail );
+void gv_mesh_reset_to_identity( GvMesh * );
+void gv_mesh_add_height(GvMesh *mesh, struct _GvRaster *raster, 
+                        double default_height );
+void gv_mesh_clamp_height(GvMesh *mesh, int bclamp_min, int bclamp_max,
+                          double min_height, double max_height);
+void gv_mesh_set_transform( GvMesh *mesh, gint xsize, gint ysize,
+                            double *geotransform );
+void gv_mesh_extents( GvMesh *mesh, GvRect *rect );
+float gv_mesh_get_height( GvMesh *mesh, double x, double y, int *success );
+
+#endif /* __GV_RASTER_H__ */

Added: packages/openev/branches/upstream/current/gvnodetool.c
===================================================================
--- packages/openev/branches/upstream/current/gvnodetool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvnodetool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,537 @@
+/******************************************************************************
+ * $Id: gvnodetool.c,v 1.13 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Node editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvnodetool.c,v $
+ * Revision 1.13  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.12  2002/09/30 20:49:35  warmerda
+ * maintain ring integrity when deleting a node
+ *
+ * Revision 1.11  2002/09/30 20:09:27  warmerda
+ * Ensure that first/last vertex of closed rings are kept in sync.  Added
+ * "track_node" concept.  Only works for GvShapesLayer areas, not the old
+ * GvAreaLayer.
+ *
+ * Revision 1.10  2000/08/08 20:58:47  warmerda
+ * recover from layer destruction
+ *
+ * Revision 1.9  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.8  2000/07/24 14:00:24  warmerda
+ * set_layer with NULL should be allowed
+ *
+ * Revision 1.7  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.6  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvnodetool.h"
+#include "gvshapeslayer.h"
+#include "gvundo.h"
+#include "cpl_error.h"
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include <GL/gl.h>
+
+static void gv_node_tool_class_init(GvNodeToolClass *klass);
+static void gv_node_tool_init(GvNodeTool *tool);
+static void gv_node_tool_draw(GvTool *tool);
+static void gv_node_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_node_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_node_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_node_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_node_tool_deactivate(GvTool *tool, GvViewArea *view);
+static void gv_node_tool_layer_change(GvNodeTool *tool, gpointer info);
+static gint gv_node_tool_configure(GvNodeTool *tool);
+static GdkCursor* gv_node_tool_make_cursor(void);
+
+GtkType
+gv_node_tool_get_type(void)
+{
+    static GtkType node_tool_type = 0;
+
+    if (!node_tool_type)
+    {
+	static const GtkTypeInfo node_tool_info =
+	{
+	    "GvNodeTool",
+	    sizeof(GvNodeTool),
+	    sizeof(GvNodeToolClass),
+	    (GtkClassInitFunc) gv_node_tool_class_init,
+	    (GtkObjectInitFunc) gv_node_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	node_tool_type = gtk_type_unique(gv_tool_get_type(),
+					 &node_tool_info);
+    }
+    return node_tool_type;
+}
+
+static void
+gv_node_tool_class_init(GvNodeToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_node_tool_deactivate;
+    tool_class->button_press = gv_node_tool_button_press;
+    tool_class->button_release = gv_node_tool_button_release;
+    tool_class->motion_notify = gv_node_tool_motion_notify;
+    tool_class->key_press = gv_node_tool_key_press;
+}
+static void
+gv_node_tool_init(GvNodeTool *tool)
+{
+    GV_TOOL(tool)->cursor = gv_node_tool_make_cursor();
+    tool->layer = NULL;
+    tool->node_selected = FALSE;
+    tool->dragging = FALSE;
+    tool->tracking_node.shape_id = -1;
+}
+
+GvTool *
+gv_node_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_NODE_TOOL));
+}
+
+static gint gv_node_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvNodeTool *tool = (GvNodeTool *) data;
+
+    if( tool->layer == GV_SHAPE_LAYER(layer) )
+        gv_node_tool_set_layer( tool, NULL );
+    
+    return 0;
+}
+
+void
+gv_node_tool_set_layer(GvNodeTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	g_warning("gv_node_tool_set_layer(): inactive tool");
+	return;
+    }
+
+    if( layer != NULL && gv_data_is_read_only( GV_DATA(layer) ) )
+    {
+        g_warning( "gv_node_tool_set_layer(): layer is read-only" );
+        return;
+    }
+
+    /* Disconnect from the previous layer (for draw) */
+    if (tool->layer)
+    {
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gtk_signal_disconnect_by_data(GTK_OBJECT(tool->layer), (gpointer)tool);
+    }
+    
+    tool->layer = layer;
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+
+        /* Redraw when the layer draws */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "draw",
+					GTK_SIGNAL_FUNC(gv_node_tool_draw),
+					GTK_OBJECT(tool));
+
+	/* Trap changes to the layer (e.g. undo) and clear node selection */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "changed",
+					GTK_SIGNAL_FUNC(
+					    gv_node_tool_layer_change),
+					GTK_OBJECT(tool));	
+
+        /* Recover if layer destroyed */
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy", 
+            GTK_SIGNAL_FUNC(gv_node_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+/**************************************************************/
+
+static void
+gv_node_tool_button_press(GvTool *rtool, GdkEventButton *event)
+{
+    GvNodeTool *tool = GV_NODE_TOOL(rtool);
+
+    if (event->button == 1)
+    {
+	gint shape_id, before;
+	
+	if (!gv_node_tool_configure(tool)) return;
+
+	if (gv_shape_layer_pick_shape(tool->layer, GV_TOOL(tool)->view,
+				      event->x, event->y, &shape_id))
+	{
+	    if (!gv_shape_layer_is_selected(tool->layer, shape_id))
+	    {
+		/* Select this shape (and only this shape) */
+		gv_shape_layer_clear_selection(tool->layer);
+		gv_shape_layer_select_shape(tool->layer, shape_id);
+
+                /* New shape: deselect current node */
+		tool->node_selected = FALSE;
+                tool->tracking_node.shape_id = -1;
+	    }
+	    else
+	    {
+		if (gv_shape_layer_pick_node(tool->layer, GV_TOOL(tool)->view,
+					     event->x, event->y, &before,
+					     &tool->edit_node))
+		{
+		    /* Capture pointer position */
+		    gv_view_area_map_pointer(GV_TOOL(tool)->view,
+					     event->x, event->y,
+					     &tool->v_tail.x, &tool->v_tail.y);
+		    tool->v_head = tool->v_tail;
+
+                    if( !gv_tool_check_bounds( GV_TOOL(tool), 
+                                               tool->v_tail.x,
+                                               tool->v_tail.y ) )
+                        return;
+		    
+		    /* Select the node for edit and start dragging */
+		    tool->node_selected = TRUE;
+		    tool->dragging = TRUE;
+
+                    /* If this node is the first in a ring of an area and
+                       the last vertex of the ring matches it, capture that
+                       node for "tracking" purposes. */
+
+                    tool->tracking_node.shape_id = -1;
+                    if( GV_IS_SHAPES_LAYER(tool->layer) 
+                        && tool->edit_node.node_id == 0 )
+                    {
+                        GvShape *shape;
+                        int ring = tool->edit_node.ring_id;
+                        int last_node;
+
+                        shape = gv_shapes_get_shape( 
+                            GV_SHAPES_LAYER(tool->layer)->data,
+                            tool->edit_node.shape_id );
+                        last_node = gv_shape_get_nodes( shape, ring ) - 1;
+                        
+                        if( gv_shape_type(shape) == GVSHAPE_AREA 
+                            && gv_shape_get_x( shape, ring, 0 )
+                               == gv_shape_get_x( shape, ring, last_node )
+                            && gv_shape_get_y( shape, ring, 0 )
+                               == gv_shape_get_y( shape, ring, last_node )
+                            && gv_shape_get_z( shape, ring, 0 )
+                               == gv_shape_get_z( shape, ring, last_node ) )
+                        {
+                            tool->tracking_node.shape_id = 
+                                tool->edit_node.shape_id;
+                            tool->tracking_node.ring_id = 
+                                tool->edit_node.ring_id;
+                            tool->tracking_node.node_id = last_node;
+                            gv_shape_layer_get_node( tool->layer, 
+                                                     &tool->tracking_node );
+                            tool->v_orig_tracking = 
+                                *(tool->tracking_node.vertex);
+                        }
+                    }
+
+		    if (before)
+		    {
+			/* Insert a new node */
+			tool->edit_node.vertex = &tool->v_tail;
+			tool->changing = TRUE;
+			gv_shape_layer_insert_node(tool->layer,
+						   &tool->edit_node);
+			tool->changing = FALSE;
+		    }
+
+		    /* Get pointer to vertex */
+		    gv_shape_layer_get_node(tool->layer, &tool->edit_node);
+
+		    /* Snapshot the original node position */
+		    tool->v_orig = *(tool->edit_node.vertex);
+		}
+		else
+		{
+		    /* Click away from a node: deselect the current node */
+		    tool->node_selected = FALSE;
+		}
+	    }
+	}
+	else
+	{
+	    /* Click away from shape: deselect the current node,
+	       but not the current shape */
+	    tool->node_selected = FALSE;
+            tool->tracking_node.shape_id = -1;
+	}
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+}
+
+static void
+gv_node_tool_button_release(GvTool *rtool, GdkEventButton *event)
+{
+    GvNodeTool *tool = GV_NODE_TOOL(rtool);
+
+    if (tool->dragging && event->button == 1)
+    {
+	/* End dragging */
+	tool->dragging = FALSE;
+	
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+
+        gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+				 &tool->v_tail.x, &tool->v_tail.y );
+
+	/* Filter out clicks that wern't really drags */
+	if (tool->v_tail.x != tool->v_head.x ||
+	    tool->v_tail.y != tool->v_head.y)
+	{
+	    GvVertex temp;
+            GvVertex temp_tracking;
+            gint undo_group;
+	    
+	    /* Actually the node is already moved, but the layer doesn't
+	       know this, so we put back the original vertex position
+	       and move it properly */
+	    temp.x = tool->v_orig.x + (tool->v_tail.x - tool->v_head.x);
+	    temp.y = tool->v_orig.y + (tool->v_tail.y - tool->v_head.y);
+	
+	    *(tool->edit_node.vertex) = tool->v_orig;
+	    tool->edit_node.vertex = &temp;
+
+            if( tool->tracking_node.shape_id != -1 )
+            {
+                temp_tracking = temp;
+                *(tool->tracking_node.vertex) = tool->v_orig_tracking;
+                tool->tracking_node.vertex = &temp_tracking;
+            }
+
+	    tool->changing = TRUE;
+            undo_group = gv_undo_start_group();
+
+	    gv_shape_layer_move_node(tool->layer, &tool->edit_node);
+
+            if( tool->tracking_node.shape_id != -1 )
+            {
+                gv_shape_layer_move_node(tool->layer, &tool->tracking_node);
+                gv_shape_layer_get_node(tool->layer, &tool->tracking_node);
+            }
+
+	    gv_shape_layer_get_node(tool->layer, &tool->edit_node);
+
+            gv_undo_end_group( undo_group );
+	    tool->changing = FALSE;
+	}
+    }
+}
+
+static void
+gv_node_tool_motion_notify(GvTool *rtool, GdkEventMotion *event)
+{
+    GvNodeTool *tool = GV_NODE_TOOL(rtool);
+
+    if (tool->dragging)
+    {
+	/* Drag selected node */
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+
+        gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+				 &tool->v_tail.x, &tool->v_tail.y );
+
+	/* Move the node by dereferencing the vertex pointer (!).
+	   We promise to put it back when the mouse button is released. */
+	tool->edit_node.vertex->x = tool->v_orig.x + (tool->v_tail.x -
+						      tool->v_head.x);
+	tool->edit_node.vertex->y = tool->v_orig.y + (tool->v_tail.y -
+						      tool->v_head.y);
+
+        if( tool->tracking_node.shape_id != -1 )
+            *(tool->tracking_node.vertex) = *(tool->edit_node.vertex);
+
+	/* Inform layer of node motion */
+	gv_shape_layer_node_motion(tool->layer, tool->edit_node.shape_id);
+	
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+    }
+}
+
+static void
+gv_node_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvNodeTool *tool = GV_NODE_TOOL(rtool);
+
+    if (!gv_node_tool_configure(tool)) return;
+
+    switch (event->keyval)
+    {
+	case GDK_Delete:
+	case GDK_BackSpace:
+        {
+            gint undo_group;
+
+            if( !tool->node_selected )
+                return;
+
+	    /* Delete the currently selected node (forces redraw) */
+	    /* This also clears the node selection (via. *_layer_changed) */
+          
+            undo_group = gv_undo_start_group();
+            if( tool->tracking_node.shape_id != -1 )
+            {
+                GvNodeInfo second_node;
+                GvVertex  new_first_vert;
+
+                second_node.shape_id = tool->edit_node.shape_id;
+                second_node.ring_id = tool->edit_node.ring_id;
+                second_node.node_id = 1;
+
+                gv_shape_layer_get_node( tool->layer, &second_node );
+                new_first_vert = *(second_node.vertex);
+
+                tool->tracking_node.vertex = &new_first_vert;
+
+                gv_shape_layer_move_node(tool->layer, &tool->tracking_node);
+            }
+
+	    gv_shape_layer_delete_node(tool->layer, &tool->edit_node);
+            gv_undo_end_group( undo_group );
+
+            tool->tracking_node.shape_id = -1;
+            tool->node_selected = FALSE;
+        }
+        break;
+    }
+}
+
+static void
+gv_node_tool_draw(GvTool *rtool)
+{
+    GvNodeTool *tool = GV_NODE_TOOL(rtool);
+
+    if (tool->node_selected)
+    {
+	glPointSize(4.0);
+	glBegin(GL_POINTS);
+	glVertex2v((GLgeocoord*)tool->edit_node.vertex);
+	glEnd();
+    }
+}
+
+static void
+gv_node_tool_deactivate(GvTool *rtool, GvViewArea *view)
+{
+    GvNodeTool *tool = GV_NODE_TOOL(rtool);
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+
+    if (tool->layer)
+    {
+	gv_shape_layer_clear_selection(tool->layer);
+	gv_view_area_queue_draw(view);	
+    }
+    tool->node_selected = FALSE;
+    tool->dragging = FALSE;
+}
+
+static void
+gv_node_tool_layer_change(GvNodeTool *tool, gpointer info)
+{
+    if (!tool->changing)
+    {
+	/* Unexpected change in layer (or node delete) occured:
+	   clear node selection. */
+	tool->node_selected = FALSE;
+	tool->dragging = FALSE;
+    }
+}
+
+static gint
+gv_node_tool_configure(GvNodeTool *tool)
+{
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	/* Attempt to find a line layer to edit */
+	layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+					       GV_TYPE_SHAPE_LAYER,
+                                               FALSE);
+	if (!layer)
+	{
+	    g_warning("gv_node_tool_configure(): no shape layer in view");
+	    return FALSE;
+	}
+
+	gv_node_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+
+    return tool->layer != NULL;
+}
+
+#include "pics/node_cursor.xbm"
+#include "pics/node_cursor_mask.xbm"
+
+static GdkCursor *
+gv_node_tool_make_cursor(void)
+{
+    GdkCursor *cursor;
+    GdkPixmap *source, *mask;
+    GdkColor fg, bg;
+
+    gdk_color_parse("black", &fg);
+    gdk_color_parse("white", &bg);    
+    source = gdk_bitmap_create_from_data(NULL, node_cursor_bits,
+					 node_cursor_width,
+					 node_cursor_height);
+    mask = gdk_bitmap_create_from_data(NULL, node_cursor_mask_bits,
+				       node_cursor_mask_width,
+				       node_cursor_mask_height);
+    cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg,
+					node_cursor_x_hot,
+					node_cursor_y_hot);
+    gdk_pixmap_unref(source);
+    gdk_pixmap_unref(mask);
+
+    return cursor;
+}

Added: packages/openev/branches/upstream/current/gvnodetool.h
===================================================================
--- packages/openev/branches/upstream/current/gvnodetool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvnodetool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,79 @@
+/******************************************************************************
+ * $Id: gvnodetool.h,v 1.4 2002/09/30 20:09:27 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Node editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvnodetool.h,v $
+ * Revision 1.4  2002/09/30 20:09:27  warmerda
+ * Ensure that first/last vertex of closed rings are kept in sync.  Added
+ * "track_node" concept.  Only works for GvShapesLayer areas, not the old
+ * GvAreaLayer.
+ *
+ * Revision 1.3  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_NODE_TOOL_H__
+#define __GV_NODE_TOOL_H__
+
+#include "gvtool.h"
+#include "gvshapelayer.h"
+
+#define GV_TYPE_NODE_TOOL            (gv_node_tool_get_type ())
+#define GV_NODE_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_NODE_TOOL, GvNodeTool))
+#define GV_NODE_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_NODE_TOOL, GvNodeToolClass))
+#define GV_IS_NODE_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_NODE_TOOL))
+#define GV_IS_NODE_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_NODE_TOOL))
+
+typedef struct _GvNodeTool       GvNodeTool;
+typedef struct _GvNodeToolClass  GvNodeToolClass;
+
+struct _GvNodeTool
+{
+    GvTool tool;
+
+    GvShapeLayer *layer;
+    gint node_selected : 1;
+    gint dragging : 1;
+    gint changing : 1;
+    GvVertex v_orig;
+    GvNodeInfo edit_node;
+    GvVertex v_head;
+    GvVertex v_tail;
+
+    GvNodeInfo tracking_node;
+    GvVertex v_orig_tracking;
+};
+
+struct _GvNodeToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_node_tool_get_type(void);
+GvTool* gv_node_tool_new(void);
+void gv_node_tool_set_layer(GvNodeTool *tool, GvShapeLayer *layer);
+
+#endif /* __GV_NODE_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvogr.c
===================================================================
--- packages/openev/branches/upstream/current/gvogr.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvogr.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,360 @@
+/******************************************************************************
+ * $Id: gvogr.c,v 1.5 2004/01/20 16:05:02 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Read/write link to OGR.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvogr.c,v $
+ * Revision 1.5  2004/01/20 16:05:02  warmerda
+ * added setting of _ogr_driver_name for S52 viewer
+ *
+ * Revision 1.4  2003/01/06 21:19:45  warmerda
+ * added one-layer access
+ *
+ * Revision 1.3  2003/01/06 18:21:51  warmerda
+ * Added debug on unexpected NULL
+ *
+ * Revision 1.2  2002/11/04 16:33:45  sduclos
+ * Add gard against NULL GeometryRef handle
+ *
+ * Revision 1.1  2002/09/26 19:24:09  warmerda
+ * New
+ *
+ * Revision 1.8  2002/09/09 17:41:36  warmerda
+ * cleanup dataset if iLayer out of range
+ *
+ * Revision 1.7  2002/07/15 18:42:10  pgs
+ * added _filename to gv_shapes that come from shape files or ogr
+ *
+ * Revision 1.6  2002/05/07 02:51:15  warmerda
+ * preliminary support for GVSHAPE_COLLECTION
+ *
+ * Revision 1.5  2001/11/09 15:20:24  warmerda
+ * get ogr style string as _gv_ogrfs
+ *
+ * Revision 1.4  2000/11/13 20:04:13  warmerda
+ * fixed memory leak with geometryless features
+ *
+ * Revision 1.3  2000/10/24 04:26:18  warmerda
+ * easy of use, and bug fixes for ogr link
+ *
+ * Revision 1.2  2000/07/24 01:30:21  warmerda
+ * added read support for multipolygons
+ *
+ * Revision 1.1  2000/07/03 12:48:22  warmerda
+ * New
+ *
+ */
+
+#include "ogr_api.h"
+#include "gvshapes.h"
+#include "cpl_error.h"
+
+// helper
+int _getGeoPtCount(OGRGeometryH hGeom, int iGeo, OGRGeometryH *hGeomRef )
+{
+    int vert_count = 0;
+
+    *hGeomRef  = OGR_G_GetGeometryRef( hGeom, iGeo );
+    if( NULL != *hGeomRef )
+    {
+        vert_count = OGR_G_GetPointCount( *hGeomRef );
+    }
+    else
+    {
+        /* FIXME: something is wrong in OGR if we get here
+         * ie the geometry handle doesn't refer to a geometry!
+         */
+        CPLDebug( "OpenEV", "gvogr.c:_getGeoPtCount() .. got null geometry!" );
+    }
+
+    return vert_count;
+}
+
+/************************************************************************/
+/*                      ogr_geometry_to_gv_shape()                      */
+/************************************************************************/
+
+static GvShape *ogr_geometry_to_gv_shape( OGRGeometryH hGeom )
+
+{
+    GvShape *gv_shape = NULL;
+    OGRwkbGeometryType eType;
+
+    if( hGeom == NULL )
+    {
+        // Use collection - which can be empty - to represent a geometryless
+        // feature.
+
+        return gv_shape_new( GVSHAPE_COLLECTION );
+    }
+
+    eType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
+
+    if( eType == wkbPoint )
+    {
+        gv_shape = gv_shape_new( GVSHAPE_POINT );
+        gv_shape_set_xyz( gv_shape, 0, 0,
+                          OGR_G_GetX( hGeom, 0 ),
+                          OGR_G_GetY( hGeom, 0 ),
+                          OGR_G_GetZ( hGeom, 0 ) );
+    }
+
+    else if( eType == wkbLineString )
+    {
+        int    node, count = OGR_G_GetPointCount(hGeom);
+
+        gv_shape = gv_shape_new( GVSHAPE_LINE );
+        for( node = count-1; node >= 0; node-- )
+            gv_shape_set_xyz( gv_shape, 0, node,
+                              OGR_G_GetX( hGeom, node ),
+                              OGR_G_GetY( hGeom, node ),
+                              OGR_G_GetZ( hGeom, node ) );
+    }
+
+    else if( eType == wkbPolygon )
+    {
+        OGRGeometryH hRing;
+        int        iRing = 0, nRingCount = OGR_G_GetGeometryCount( hGeom );
+
+        gv_shape = gv_shape_new( GVSHAPE_AREA );
+
+        for( iRing = 0; iRing < nRingCount; iRing++ )
+        {
+            int node;
+            int vert_count = _getGeoPtCount( hGeom, iRing, &hRing );
+
+            //hRing      = OGR_G_GetGeometryRef( hGeom, iRing );
+            //vert_count = OGR_G_GetPointCount(hRing);
+
+            for( node = vert_count - 1; node >= 0; node-- )
+                gv_shape_set_xyz( gv_shape, iRing, node,
+                                  OGR_G_GetX( hRing, node ),
+                                  OGR_G_GetY( hRing, node ),
+                                  OGR_G_GetZ( hRing, node ) );
+        }
+    }
+
+    else if( eType == wkbMultiPolygon )
+    {
+        int iPoly, nShapeRing = 0;
+
+        gv_shape = gv_shape_new( GVSHAPE_AREA );
+
+        for( iPoly = 0; iPoly < OGR_G_GetGeometryCount( hGeom ); iPoly++ )
+        {
+            //OGRGeometryH hPoly, hRing;
+            OGRGeometryH hPoly;
+            int         iRing, nRingCount;
+
+            hPoly      = OGR_G_GetGeometryRef( hGeom, iPoly );
+            nRingCount = OGR_G_GetGeometryCount( hPoly );
+            
+            for( iRing = 0; iRing < nRingCount; iRing++ )
+            {
+                OGRGeometryH hRing;
+                int vert_count = _getGeoPtCount( hPoly, iRing, &hRing );
+                int node;
+
+                //hRing      = OGR_G_GetGeometryRef( hPoly, iRing );
+                //vert_count = OGR_G_GetPointCount(hRing);
+
+                for( node = vert_count - 1; node >= 0; node-- )
+                    gv_shape_set_xyz( gv_shape, nShapeRing, node,
+                                     OGR_G_GetX( hRing, node ),
+                                     OGR_G_GetY( hRing, node ),
+                                     OGR_G_GetZ( hRing, node ) );
+                nShapeRing++;
+            }
+        }
+    }
+
+    else if( eType == wkbGeometryCollection
+             || eType == wkbMultiLineString
+             || eType == wkbMultiPoint )
+    {
+        int         iGeom, nGeomCount;
+
+        nGeomCount = OGR_G_GetGeometryCount( hGeom );
+
+        gv_shape = gv_shape_new( GVSHAPE_COLLECTION );
+
+        for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
+        {
+            OGRGeometryH hSubGeom = OGR_G_GetGeometryRef( hGeom, iGeom );
+            GvShape *sub_shape;
+
+            sub_shape = ogr_geometry_to_gv_shape( hSubGeom );
+            gv_shape_collection_add_shape( gv_shape, sub_shape );
+        }
+    }
+
+    return gv_shape;
+}
+
+/************************************************************************/
+/*                      gv_shapes_from_ogr_layer()                      */
+/************************************************************************/
+
+GvData *gv_shapes_from_ogr_layer(OGRLayerH hLayer)
+
+{
+    int         field_count = 0, field_index;
+    GvShapes    *shapes_data;
+    GvProperties *properties;
+    OGRFeatureDefnH  hDefn;
+    OGRFeatureH  hFeature;
+
+    hDefn = OGR_L_GetLayerDefn( hLayer );
+    field_count = OGR_FD_GetFieldCount( hDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create shapes layer, and assign some metadata about the         */
+/*      field definitions.                                              */
+/* -------------------------------------------------------------------- */
+    shapes_data = GV_SHAPES(gv_shapes_new());
+    gv_data_set_name( GV_DATA(shapes_data), OGR_FD_GetName(hDefn) );
+
+    properties = gv_data_get_properties( GV_DATA(shapes_data) );
+
+    for(field_index = 0; field_index < field_count; field_index++ )
+    {
+        OGRFieldDefnH hField = OGR_FD_GetFieldDefn( hDefn, field_index );
+        OGRFieldType eFldType;
+        char      prop_value[64], prop_name[64];
+
+        sprintf( prop_name, "_field_name_%d", field_index+1 );
+        gv_properties_set( properties, prop_name, 
+                           OGR_Fld_GetNameRef( hField ) );
+
+        sprintf( prop_name, "_field_width_%d", field_index+1 );
+        sprintf( prop_value, "%d", OGR_Fld_GetWidth( hField ) );
+        gv_properties_set( properties, prop_name, prop_value );
+
+        eFldType = OGR_Fld_GetType( hField );
+        if( eFldType == OFTReal )
+        {
+            sprintf( prop_name, "_field_precision_%d", field_index+1 );
+            sprintf( prop_value, "%d", OGR_Fld_GetPrecision(hField) );
+            gv_properties_set( properties, prop_name, prop_value );
+        }
+
+        sprintf( prop_name, "_field_type_%d", field_index+1 );
+        if( eFldType == OFTInteger )
+            gv_properties_set( properties, prop_name, "integer" );
+        else if( eFldType == OFTReal )
+            gv_properties_set( properties, prop_name, "float" );
+        else
+            gv_properties_set( properties, prop_name, "string" );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy all the shapes, and their attributes.                      */
+/* -------------------------------------------------------------------- */
+    OGR_L_ResetReading( hLayer );
+    while( (hFeature = OGR_L_GetNextFeature(hLayer)) != NULL )
+    {
+        GvShape    *gv_shape = NULL;
+
+        gv_shape = ogr_geometry_to_gv_shape( OGR_F_GetGeometryRef(hFeature) );
+
+        if( gv_shape != NULL )
+        {
+            properties = gv_shape_get_properties( gv_shape );
+
+            for( field_index = 0; field_index < field_count; field_index++ )
+            {
+                if( OGR_F_IsFieldSet( hFeature, field_index ) )
+                {
+                    gv_properties_set( properties,
+                           OGR_Fld_GetNameRef( 
+                               OGR_F_GetFieldDefnRef(hFeature,field_index) ),
+                           OGR_F_GetFieldAsString( hFeature, field_index ) );
+                }
+            }
+
+            if( OGR_F_GetStyleString(hFeature) != NULL )
+                gv_properties_set( properties, "_gv_ogrfs",
+                                   OGR_F_GetStyleString( hFeature ) );
+        }
+
+        if( gv_shape != NULL )
+            gv_shapes_add_shape( shapes_data, gv_shape );
+
+        OGR_F_Destroy( hFeature );
+    }
+
+    return GV_DATA(shapes_data);
+}
+
+/************************************************************************/
+/*                         gv_shapes_from_ogr()                         */
+/************************************************************************/
+
+GvData *gv_shapes_from_ogr(const char *filename, int iLayer)
+
+{
+    OGRLayerH        hLayer;
+    OGRDataSourceH hDS;
+    GvData *psData;
+    OGRSFDriverH     hDriver;
+
+/* -------------------------------------------------------------------- */
+/*      Open the OGR dataset.                                           */
+/* -------------------------------------------------------------------- */
+    OGRRegisterAll();
+
+    hDS = OGROpen( filename, FALSE, &hDriver );
+    if( hDS == NULL )
+        return NULL;
+
+
+
+    if( iLayer < 0 || iLayer >= OGR_DS_GetLayerCount(hDS) )
+    {
+        OGR_DS_Destroy( hDS );
+        return NULL;
+    }
+
+    hLayer =  OGR_DS_GetLayer( hDS, iLayer );
+
+/* -------------------------------------------------------------------- */
+/*      Actually read the shapes.                                       */
+/* -------------------------------------------------------------------- */
+    psData = gv_shapes_from_ogr_layer( hLayer );
+
+    if( psData != NULL )
+    {
+        gv_data_set_property( psData, "_filename", filename );
+        gv_data_set_property( psData, "_ogr_driver_name", 
+                              OGR_Dr_GetName(hDriver) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    OGR_DS_Destroy( hDS );
+
+    return psData;
+}
+

Added: packages/openev/branches/upstream/current/gvpointlayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvpointlayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpointlayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,305 @@
+/******************************************************************************
+ * $Id: gvpointlayer.c,v 1.6 2002/11/05 18:56:24 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer for GvPoints.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpointlayer.c,v $
+ * Revision 1.6  2002/11/05 18:56:24  sduclos
+ * fix gcc warning
+ *
+ * Revision 1.5  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.4  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvpointlayer.h"
+#include <GL/gl.h>
+
+#define DEFAULT_POINT_SIZE 6
+
+static void gv_point_layer_class_init(GvPointLayerClass *klass);
+static void gv_point_layer_init(GvPointLayer *layer);
+static void gv_point_layer_draw(GvPointLayer *layer, GvViewArea *view);
+static void gv_point_layer_extents(GvPointLayer *layer, GvRect *rect);
+static void gv_point_layer_data_change(GvPointLayer *layer, gpointer change_info);
+static void gv_point_layer_draw_selected(GvPointLayer *layer, GvViewArea *view);
+static void gv_point_layer_delete_selected(GvPointLayer *layer);
+static void gv_point_layer_translate_selected(GvPointLayer *layer, GvVertex *delta);
+static void gv_point_layer_pick_shape(GvPointLayer *layer);
+
+GtkType
+gv_point_layer_get_type(void)
+{
+    static GtkType point_layer_type = 0;
+
+    if (!point_layer_type)
+    {
+	static const GtkTypeInfo point_layer_info =
+	{
+	    "GvPointLayer",
+	    sizeof(GvPointLayer),
+	    sizeof(GvPointLayerClass),
+	    (GtkClassInitFunc) gv_point_layer_class_init,
+	    (GtkObjectInitFunc) gv_point_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	point_layer_type = gtk_type_unique(gv_shape_layer_get_type(),
+					   &point_layer_info);
+    }
+    return point_layer_type;
+}
+
+static void
+gv_point_layer_class_init(GvPointLayerClass *klass)
+{
+    typedef void (*f)();
+    GvDataClass *data_class;
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    data_class = (GvDataClass*) klass;
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    data_class->changed = (f) gv_point_layer_data_change;
+    
+    layer_class->draw            = (f) gv_point_layer_draw;
+    layer_class->extents_request = (f) gv_point_layer_extents;
+
+    shape_layer_class->draw_selected      = (f) gv_point_layer_draw_selected;
+    shape_layer_class->delete_selected    = (f) gv_point_layer_delete_selected;
+    shape_layer_class->translate_selected = (f) gv_point_layer_translate_selected;
+    shape_layer_class->pick_shape         = (f) gv_point_layer_pick_shape;
+}
+
+static void
+gv_point_layer_init(GvPointLayer *layer)
+{
+    GvColor default_point_color = {0.0, 1.0, 1.0, 1.0};  /* cyan */
+    
+    layer->data = NULL;
+    layer->point_size = DEFAULT_POINT_SIZE;
+    gv_color_copy(GV_SHAPE_LAYER(layer)->color, default_point_color);
+}
+
+GtkObject *
+gv_point_layer_new(GvPoints *data)
+{
+    GvPointLayer *layer = GV_POINT_LAYER(gtk_type_new(
+	gv_point_layer_get_type()));
+    
+    if (data)
+    {
+	layer->data = data;
+    }
+    else
+    {
+	layer->data = GV_POINTS(gv_points_new());
+    }
+
+    /* Set the selected array to reflect the data length if points already exist */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_points_num_points(layer->data));
+
+    gv_data_set_parent(GV_DATA(layer), GV_DATA(layer->data));
+    
+    return GTK_OBJECT(layer);
+}
+
+gint
+gv_point_layer_select_new_point(GvPointLayer *layer, GvVertex *vertex)
+{
+    gint point_id;
+
+    point_id = gv_points_new_point(layer->data, vertex);
+
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_points_num_points(layer->data));
+    gv_shape_layer_select_shape(GV_SHAPE_LAYER(layer), point_id);
+
+    return point_id;
+}
+
+/*******************************************************/
+
+static void
+gv_point_layer_draw(GvPointLayer *layer, GvViewArea *view)
+{
+    gint i, points;
+    GvPoint *point;
+    gint *selected, presentation;
+    gvgeocoord dx, dy, x, y;
+    gint hit_selected = FALSE;
+
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    points = gv_points_num_points(layer->data);
+
+    /* Crosshairs are "sprites": always drawn upright, the same size */
+    dx = layer->point_size;
+    dy = 0.0;
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);    
+    
+    glColor4fv(GV_SHAPE_LAYER(layer)->color);
+    glBegin(GL_LINES);
+    for (i=0; i < points; ++i)
+    {
+	if (selected[i] && !presentation)
+	{
+	    hit_selected = 1;
+	    continue;
+	}
+
+	point = gv_points_get_point(layer->data, i);
+	x = point->v.x;
+	y = point->v.y;
+
+	glVertex2(x-dx, y-dy);
+	glVertex2(x+dx, y+dy);
+	glVertex2(x+dy, y-dx);
+	glVertex2(x-dy, y+dx);
+    }
+    glEnd();
+
+    if (hit_selected && ! GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+	gv_point_layer_draw_selected(layer, view);
+    } 
+}
+
+static void
+gv_point_layer_draw_selected(GvPointLayer *layer, GvViewArea *view)
+{
+    gint i, points;
+    GvPoint *point;
+    gint *selected;
+    gvgeocoord dx, dy, bx, by, x, y;
+
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    points = gv_points_num_points(layer->data);
+
+    /* Crosshairs are "sprites": always drawn upright, the same size */
+    dx = layer->point_size;
+    dy = 0.0;
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);    
+    bx = by = layer->point_size + 2;
+    gv_view_area_correct_for_transform(view, bx, by, &bx, &by);    
+
+    glColor4fv(GV_SHAPE_LAYER(layer)->color);
+    for (i=0; i < points; ++i)
+    {
+	if (selected[i])
+	{
+	    point = gv_points_get_point(layer->data, i);
+	    x = point->v.x;
+	    y = point->v.y;
+
+	    /* Draw crosshairs */
+	    glBegin(GL_LINES);
+	    glVertex2(x-dx, y-dy);
+	    glVertex2(x+dx, y+dy);
+	    glVertex2(x+dy, y-dx);
+	    glVertex2(x-dy, y+dx);
+	    glEnd();
+
+	    /* Draw box around crosshairs */
+	    glBegin(GL_LINE_LOOP);
+	    glVertex2(x-bx, y-by);
+	    glVertex2(x+by, y-bx);
+	    glVertex2(x+bx, y+by);
+	    glVertex2(x-by, y+bx);
+	    glEnd();
+	}
+    }    
+}
+
+static void
+gv_point_layer_pick_shape(GvPointLayer *layer)
+{
+    gint i, points;
+    GvPoint *point;
+
+    if (!gv_layer_is_visible(GV_LAYER(layer))) return;
+    points = gv_points_num_points(layer->data);
+
+    glPointSize(layer->point_size * 2.0);
+    for (i=0; i < points; ++i)
+    {
+	point = gv_points_get_point(layer->data, i);
+
+	glLoadName(i);
+	glBegin(GL_POINTS);
+	glVertex2v((GLgeocoord*)&point->v);
+	glEnd();
+    }
+}
+
+static void
+gv_point_layer_delete_selected(GvPointLayer *layer)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+	/* This will force a selection clear */
+	gv_points_delete_points(layer->data, sel->len, (gint*)sel->data);
+    }
+    g_array_free(sel, TRUE);
+}
+
+static void
+gv_point_layer_translate_selected(GvPointLayer *layer, GvVertex *delta)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+	/* This will force a selection clear */
+	gv_points_translate_points(layer->data, sel->len, (gint*)sel->data,
+				   delta->x, delta->y);
+    }
+    g_array_free(sel, TRUE);
+}
+
+static void
+gv_point_layer_extents(GvPointLayer *layer, GvRect *rect)
+{
+    gv_points_get_extents(layer->data, rect);
+}
+
+static void
+gv_point_layer_data_change(GvPointLayer *layer, gpointer change_info)
+{
+    /* Reset the selected array to reflect the data length */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+				  gv_points_num_points(layer->data));
+}

Added: packages/openev/branches/upstream/current/gvpointlayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvpointlayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpointlayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,66 @@
+/******************************************************************************
+ * $Id: gvpointlayer.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer for GvPoints.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpointlayer.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_POINT_LAYER_H__
+#define __GV_POINT_LAYER_H__
+
+#include "gvshapelayer.h"
+#include "gvpoints.h"
+
+#define GV_TYPE_POINT_LAYER            (gv_point_layer_get_type ())
+#define GV_POINT_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_POINT_LAYER, GvPointLayer))
+#define GV_POINT_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_POINT_LAYER, GvPointLayerClass))
+#define GV_IS_POINT_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_POINT_LAYER))
+#define GV_IS_POINT_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_POINT_LAYER))
+
+typedef struct _GvPointLayer       GvPointLayer;
+typedef struct _GvPointLayerClass  GvPointLayerClass;
+
+struct _GvPointLayer
+{
+    GvShapeLayer shape_layer;
+
+    GvPoints *data;
+    gvfloat point_size;
+};
+
+struct _GvPointLayerClass
+{
+    GvShapeLayerClass parent_class;
+};
+
+GtkType gv_point_layer_get_type(void);
+GtkObject* gv_point_layer_new(GvPoints *data);
+
+gint gv_point_layer_select_new_point(GvPointLayer *layer, GvVertex *vertex);
+
+#endif /* __GV_POINT_LAYER_H__ */

Added: packages/openev/branches/upstream/current/gvpoints.c
===================================================================
--- packages/openev/branches/upstream/current/gvpoints.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpoints.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,405 @@
+/******************************************************************************
+ * $Id: gvpoints.c,v 1.7 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Points data container (superceed by GvShapes)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpoints.c,v $
+ * Revision 1.7  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.6  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gextra.h"
+#include "gvpoints.h"
+
+typedef struct _GvPointsMemento GvPointsMemento;
+
+struct _GvPointsMemento
+{
+    GvDataMemento base;
+    GArray *point_ids;
+    GArray *points;
+};
+
+static void gv_points_class_init(GvPointsClass *klass);
+static void gv_points_init(GvPoints *points);
+static void gv_points_get_memento(GvData *points, gpointer info, GvDataMemento **memento);
+static void gv_points_set_memento(GvData *points, GvDataMemento *memento);
+static void gv_points_del_memento(GvData *points, GvDataMemento *memento);
+static void gv_points_changed(GvData *points, gpointer data);
+static void gv_points_finalize(GtkObject *object);
+
+GtkType
+gv_points_get_type(void)
+{
+    static GtkType points_type = 0;
+
+    if (!points_type)
+    {
+	static const GtkTypeInfo points_info =
+	{
+	    "GvPoints",
+	    sizeof(GvPoints),
+	    sizeof(GvPointsClass),
+	    (GtkClassInitFunc) gv_points_class_init,
+	    (GtkObjectInitFunc) gv_points_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	points_type = gtk_type_unique(gv_data_get_type(), &points_info);
+    }
+    return points_type;
+}
+
+static void
+gv_points_init(GvPoints *points)
+{
+    points->points = g_array_new(FALSE, FALSE, sizeof(GvPoint));
+    points->extents_valid = FALSE;
+}
+
+static void
+gv_points_class_init(GvPointsClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    object_class = (GtkObjectClass*) klass;
+    data_class = (GvDataClass*) klass;
+
+    object_class->finalize = gv_points_finalize;
+    
+    data_class->changed = gv_points_changed;
+    data_class->get_memento = gv_points_get_memento;
+    data_class->set_memento = gv_points_set_memento;
+    data_class->del_memento = gv_points_del_memento;
+}
+
+GvData *
+gv_points_new(void)
+{
+    return GV_DATA(gtk_type_new(gv_points_get_type()));
+}
+
+gint
+gv_points_new_point(GvPoints *points, GvVertex *vertex)
+{    
+    int point_id;
+    GvPoint point;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    change_info.shape_id = &point_id;
+
+    if (vertex)
+    {
+	point.v = *vertex;
+    }
+    else
+    {
+	point.v.x = point.v.y = 0.0;
+    }
+    point.meta = NULL;
+    point_id = points->points->len;
+
+    gv_data_changing(GV_DATA(points), &change_info);
+    
+    g_array_append_val(points->points, point);
+
+    gv_data_changed(GV_DATA(points), &change_info);
+    
+    return point_id;
+}    
+
+void
+gv_points_delete_points(GvPoints *points, gint num_points, gint *point_id)
+{
+    GvShapeChangeInfo change_info = {GV_CHANGE_DELETE, 0, NULL};
+
+    change_info.num_shapes = num_points;
+    change_info.shape_id = point_id;
+
+    gv_data_changing(GV_DATA(points), &change_info);
+
+    /* FIXME: can't use g_array_remove_index_fast() here since there is no
+       g_array_insert_index_fast() function. */
+    
+    if (num_points == 1)
+    {
+	g_array_remove_index(points->points, *point_id);
+    }
+    else
+    {
+	/* Strategy: sort the line_id buffer and delete lines in desending
+	   order, so that indicies remain valid */
+	gint *id, i;
+
+	id = g_memdup_type(point_id, int, num_points);
+	g_sort_type(id, gint, num_points);
+
+	for (i=num_points-1; i >= 0; --i)
+	{
+	    g_array_remove_index(points->points, id[i]);
+	}
+	g_free(id);
+    }
+    gv_data_changed(GV_DATA(points), &change_info);
+}
+
+void
+gv_points_translate_points(GvPoints *points, gint num_points, gint *point_id,
+			   gvgeocoord dx, gvgeocoord dy)
+{
+    GvPoint *point;
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_points;
+    change_info.shape_id = point_id;
+
+    gv_data_changing(GV_DATA(points), &change_info);
+
+    for (i=0; i < num_points; ++i)
+    {
+	g_return_if_fail(point_id[i] >= 0 &&
+			 point_id[i] < points->points->len);
+	point = gv_points_get_point(points, point_id[i]);
+
+	point->v.x += dx;
+	point->v.y += dy;
+    }
+    gv_data_changed(GV_DATA(points), &change_info);
+}
+
+void
+gv_points_set_point(GvPoints *points, gint point_id, GvVertex *vertex)
+{
+    GvPoint *point;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &point_id;
+
+    g_return_if_fail(vertex);
+    g_return_if_fail(point_id >= 0 && point_id < points->points->len);
+
+    gv_data_changing(GV_DATA(points), &change_info);
+
+    point = gv_points_get_point(points, point_id);
+    point->v.x = vertex->x;
+    point->v.y = vertex->y;
+
+    gv_data_changed(GV_DATA(points), &change_info);
+}
+
+void
+gv_points_get_extents(GvPoints *points, GvRect *rect)
+{
+    if (!points->extents_valid)
+    {
+	gint i, num_points;
+	GvVertex vmax, vmin, v;
+
+	vmin.x = vmin.y = GV_MAXFLOAT;
+	vmax.x = vmax.y = -GV_MAXFLOAT;
+	num_points = gv_points_num_points(points);
+	for (i=0; i < num_points; ++i)
+	{
+	    v = gv_points_get_point(points, i)->v;
+	    if (v.x < vmin.x) vmin.x = v.x;
+	    if (v.x > vmax.x) vmax.x = v.x;
+	    if (v.y < vmin.y) vmin.y = v.y;
+	    if (v.y > vmax.y) vmax.y = v.y;
+	}
+
+	if (num_points == 0)
+	{
+	    points->extents.x = 0;
+	    points->extents.y = 0;
+	    points->extents.width = 0;
+	    points->extents.height = 0;
+	}
+	else
+	{
+	    points->extents.x = vmin.x;
+	    points->extents.y = vmin.y;
+	    points->extents.width = vmax.x - vmin.x;
+	    points->extents.height = vmax.y - vmin.y;
+	}
+	points->extents_valid = TRUE;
+    }
+
+    *rect = points->extents;
+}
+
+/*********************************************/
+
+static void
+gv_points_replace_points(GvPoints *points, gint num_points, gint *point_id,
+			 GvPoint *pts)
+{
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_points;
+    change_info.shape_id = point_id;
+
+    gv_data_changing(GV_DATA(points), &change_info);
+
+    for (i=0; i < num_points; ++i)
+    {
+	g_array_index(points->points, GvPoint, point_id[i]) = pts[i];
+    }
+
+    gv_data_changed(GV_DATA(points), &change_info);
+}
+
+static void
+gv_points_insert_points(GvPoints *points, gint num_points, gint *point_id,
+			GvPoint *pts)
+{
+    /* The point_id array must be in ascending order! */
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 0, NULL};
+
+    change_info.num_shapes = num_points;
+    change_info.shape_id = point_id;
+
+    gv_data_changing(GV_DATA(points), &change_info);
+
+    for (i=0; i < num_points; ++i)
+    {
+	/* FIXME: need to write a g_array_insert_index_fast() function,
+	   but that requires access to the elt_size private member of
+	   GArray */
+	g_array_insert_val(points->points, point_id[i], pts[i]);
+    }
+
+    gv_data_changed(GV_DATA(points), &change_info);    
+}
+
+static void
+gv_points_get_memento(GvData *gv_data, gpointer data,
+		      GvDataMemento **memento)
+{
+    GvPoints	*points = GV_POINTS(gv_data);
+    GvPointsMemento *mem;
+    GvShapeChangeInfo *info = (GvShapeChangeInfo *) data;
+    int i;
+
+    mem = g_new(GvPointsMemento, 1);
+    mem->base.data = GV_DATA(points);
+    mem->base.type = info->change_type;
+
+    mem->point_ids = g_array_new(FALSE, FALSE, sizeof(gint));
+    g_array_append_vals(mem->point_ids, info->shape_id, info->num_shapes);
+
+    /* Grab points in ascending order */
+    if (info->num_shapes > 1)
+    {
+	g_sort_type(mem->point_ids->data, gint, mem->point_ids->len);
+    }
+
+    if (info->change_type == GV_CHANGE_ADD)
+    {
+	mem->points = NULL;
+    }
+    else
+    {
+	mem->points = g_array_new(FALSE, FALSE, sizeof(GvPoint));
+	for (i=0; i < info->num_shapes; ++i)
+	{
+	    GvPoint *point = gv_points_get_point(points, info->shape_id[i]);
+	    g_array_append_val(mem->points, *point);
+	}
+    }
+
+    *memento = (GvDataMemento*)mem;
+}
+
+static void
+gv_points_set_memento(GvData *gv_data, GvDataMemento *data_memento)
+{
+    GvPoints	*points = GV_POINTS(gv_data);
+    GvPointsMemento *memento = (GvPointsMemento *) data_memento;
+
+    switch (memento->base.type)
+    {
+	case GV_CHANGE_ADD:
+	    gv_points_delete_points(points, memento->point_ids->len,
+				    (gint*)memento->point_ids->data);
+	    break;
+
+	case GV_CHANGE_REPLACE:
+	    gv_points_replace_points(points, memento->point_ids->len,
+				     (gint*)memento->point_ids->data,
+				     (GvPoint*)memento->points->data);
+	    break;
+
+	case GV_CHANGE_DELETE:
+	    gv_points_insert_points(points, memento->point_ids->len,
+				     (gint*)memento->point_ids->data,
+				     (GvPoint*)memento->points->data);
+	    break;
+    }
+
+    gv_points_del_memento((GvData *) points, (GvDataMemento *) memento);
+}
+
+static void
+gv_points_del_memento(GvData *gv_data, GvDataMemento *data_memento)
+{
+    GvPointsMemento *memento = (GvPointsMemento *) data_memento;
+
+    if (memento->points)
+    {
+	g_array_free(memento->points, TRUE);
+    }
+    g_array_free(memento->point_ids, TRUE);
+    g_free(memento);
+}
+
+static void
+gv_points_changed(GvData *gv_data, gpointer data)
+{
+    GvPoints	*points = GV_POINTS(gv_data);
+
+    points->extents_valid = FALSE;
+}
+
+static void
+gv_points_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvPoints *points = GV_POINTS(object);
+
+    g_array_free(points->points, TRUE);
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}

Added: packages/openev/branches/upstream/current/gvpoints.h
===================================================================
--- packages/openev/branches/upstream/current/gvpoints.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpoints.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,89 @@
+/******************************************************************************
+ * $Id: gvpoints.h,v 1.4 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Points data container (superceed by GvShapes)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpoints.h,v $
+ * Revision 1.4  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.3  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_POINTS_H__
+#define __GV_POINTS_H__
+
+#include "gvdata.h"
+
+#define GV_TYPE_POINTS            (gv_points_get_type ())
+#define GV_POINTS(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_POINTS, GvPoints))
+#define GV_POINTS_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_POINTS, GvPointsClass))
+#define GV_IS_POINTS(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_POINTS))
+#define GV_IS_POINTS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_POINTS))
+
+typedef struct _GvPoint        GvPoint;
+typedef struct _GvPoints       GvPoints;
+typedef struct _GvPointsClass  GvPointsClass;
+
+struct _GvPoint
+{
+    GvVertex v;
+    gpointer meta;
+};
+
+struct _GvPoints
+{
+    GvData data;
+
+    GArray *points;
+    GvRect extents;
+    guint extents_valid : 1;
+};
+
+struct _GvPointsClass
+{
+    GvDataClass parent_class;
+};
+
+GtkType    gv_points_get_type (void);
+
+GvData* gv_points_new(void);
+gint gv_points_new_point(GvPoints *points, GvVertex *vertex);
+void gv_points_delete_points(GvPoints *points, gint num_points, gint *point_id);
+void gv_points_translate_points(GvPoints *points, gint num_points, gint *point_id, gvgeocoord dx, gvgeocoord dy);
+void gv_points_set_point(GvPoints *points, gint point_id, GvVertex *vertex);
+void gv_points_get_extents(GvPoints *points, GvRect *rect);
+
+#define gv_points_num_points(pts) \
+     (pts->points->len)
+#define gv_points_get_point(pts,id) \
+     (&g_array_index(pts->points, GvPoint, id))
+#define gv_points_get_meta(pts,id) \
+     (gv_points_get_point(pts,id)->meta)
+#define gv_points_set_meta(pts,id,data) \
+     gv_points_get_point(pts,id)->meta = data
+
+#endif /*__GV_POINTS_H__ */

Added: packages/openev/branches/upstream/current/gvpointtool.c
===================================================================
--- packages/openev/branches/upstream/current/gvpointtool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpointtool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,286 @@
+/******************************************************************************
+ * $Id: gvpointtool.c,v 1.11 2001/04/09 18:15:20 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Point editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpointtool.c,v $
+ * Revision 1.11  2001/04/09 18:15:20  warmerda
+ * improved warning
+ *
+ * Revision 1.10  2000/08/08 20:58:47  warmerda
+ * recover from layer destruction
+ *
+ * Revision 1.9  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.8  2000/07/24 14:00:24  warmerda
+ * set_layer with NULL should be allowed
+ *
+ * Revision 1.7  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.6  2000/07/17 17:11:54  warmerda
+ * added delete key support
+ *
+ * Revision 1.5  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvpointtool.h"
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtksignal.h>
+
+static void gv_point_tool_class_init(GvPointToolClass *klass);
+static void gv_point_tool_init(GvPointTool *tool);
+static void gv_point_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_point_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_point_tool_deactivate(GvTool *tool, GvViewArea *view);
+static gint gv_point_tool_configure(GvTool *tool);
+
+GtkType
+gv_point_tool_get_type(void)
+{
+    static GtkType point_tool_type = 0;
+
+    if (!point_tool_type)
+    {
+	static const GtkTypeInfo point_tool_info =
+	{
+	    "GvPointTool",
+	    sizeof(GvPointTool),
+	    sizeof(GvPointToolClass),
+	    (GtkClassInitFunc) gv_point_tool_class_init,
+	    (GtkObjectInitFunc) gv_point_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	point_tool_type = gtk_type_unique(gv_tool_get_type(),
+					  &point_tool_info);
+    }
+    return point_tool_type;
+}
+
+static void
+gv_point_tool_class_init(GvPointToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_point_tool_deactivate;
+    tool_class->button_press = gv_point_tool_button_press;
+    tool_class->key_press = gv_point_tool_key_press;
+}
+
+static void
+gv_point_tool_init(GvPointTool *tool)
+{
+    GV_TOOL(tool)->cursor = gdk_cursor_new(GDK_TCROSS);
+    tool->layer = NULL;
+    tool->named_layer = NULL;
+}
+
+GvTool *
+gv_point_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_POINT_TOOL));
+}
+
+static gint gv_point_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvPointTool *tool = (GvPointTool *) data;
+
+    if( tool->layer == GV_SHAPE_LAYER(layer) )
+        gv_point_tool_set_layer( tool, NULL );
+    
+    return 0;
+}
+
+void
+gv_point_tool_set_layer(GvPointTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	g_warning("gv_point_tool_set_layer(): inactive tool");
+	return;
+    }
+
+    if( layer != NULL 
+        && !GV_IS_POINT_LAYER(layer) && !GV_IS_SHAPES_LAYER(layer) )
+    {
+        g_warning( "gv_point_tool_set_layer(): not a point capable layer" );
+        return;
+    }
+
+    if( layer != NULL && gv_data_is_read_only( GV_DATA(layer) ) )
+    {
+        g_warning( "gv_point_tool_set_layer(): layer is read-only" );
+        return;
+    }
+
+    /* Disconnect from the previous layer */
+    if (tool->layer)
+    {
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+        gtk_signal_disconnect_by_data( GTK_OBJECT(tool->layer), 
+                                       GTK_OBJECT(tool) );
+    }
+    
+    tool->layer = layer;
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy", 
+            GTK_SIGNAL_FUNC(gv_point_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+void
+gv_point_tool_set_named_layer(GvPointTool *tool, gchar *name)
+{
+    if (tool->named_layer)
+    {
+	g_free(tool->named_layer);
+	tool->named_layer = NULL;
+    }
+    if (name)
+    {
+	tool->named_layer = g_strdup(name);	
+    }
+    /* Tool layer will be updated next time it is configured */
+}
+
+static void
+gv_point_tool_button_press(GvTool *tool, GdkEventButton *event)
+{
+    if (event->button == 1)
+    {
+	GvVertex vertex;
+	
+	if (!gv_point_tool_configure(tool)) return;
+
+	/* Get pointer location */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &vertex.x, &vertex.y);
+
+        if( !gv_tool_check_bounds( GV_TOOL(tool), vertex.x, vertex.y ) )
+            return;
+
+	/* Add a new point */
+        if( GV_IS_POINT_LAYER(GV_POINT_TOOL(tool)->layer) )
+        {
+            gv_point_layer_select_new_point(
+                GV_POINT_LAYER(GV_POINT_TOOL(tool)->layer), 
+                &vertex);
+        }
+        else
+        {
+            GvShape   *new_point;
+
+            new_point = gv_shape_new( GVSHAPE_POINT );
+            gv_shape_set_xyz( new_point, 0, 0, vertex.x, vertex.y, 0.0 );
+            gv_shapes_layer_select_new_shape( 
+                GV_SHAPES_LAYER(GV_POINT_TOOL(tool)->layer), 
+                new_point );
+        }
+    }
+}
+
+static void
+gv_point_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvPointTool *tool = GV_POINT_TOOL(rtool);
+
+    if (!gv_point_tool_configure(GV_TOOL(tool))) return;
+    
+    switch (event->keyval)
+    {
+	case GDK_Delete:
+	case GDK_BackSpace:
+	    /* Delete the currently selected lines (forces redraw) */
+	    gv_shape_layer_delete_selected(tool->layer);
+	    break;
+    }
+}
+
+static void
+gv_point_tool_deactivate(GvTool *r_tool, GvViewArea *view)
+{
+    GvPointTool  *tool = GV_POINT_TOOL(r_tool);
+
+    /* Disconnect from layer */
+    if (tool->layer)
+    {
+	gv_point_tool_set_layer(tool, NULL);
+    }
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+}
+
+static gint
+gv_point_tool_configure(GvTool *r_tool)
+{
+    GvPointTool  *tool = GV_POINT_TOOL(r_tool);
+
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	if (tool->named_layer)
+	{
+	    /* Look for named layer if given */
+	    layer = gv_view_area_get_named_layer(GV_TOOL(tool)->view,
+						 tool->named_layer);
+	}
+	else
+	{
+	    /* Attempt to find a point layer to edit */
+	    layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+						   GV_TYPE_POINT_LAYER,
+                                                   FALSE);
+            if(  layer == NULL )
+                layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+                                                       GV_TYPE_SHAPES_LAYER,
+                                                       FALSE);
+	}
+	if (!layer)
+	{
+	    g_warning("gv_point_tool_configure(): no editable point layer in view");
+	    return FALSE;
+	}
+
+	gv_point_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+    return tool->layer != NULL;
+}

Added: packages/openev/branches/upstream/current/gvpointtool.h
===================================================================
--- packages/openev/branches/upstream/current/gvpointtool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpointtool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * $Id: gvpointtool.h,v 1.4 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Point editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpointtool.h,v $
+ * Revision 1.4  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_POINT_TOOL_H__
+#define __GV_POINT_TOOL_H__
+
+#include "gvtool.h"
+#include "gvpointlayer.h"
+#include "gvshapeslayer.h"
+
+#define GV_TYPE_POINT_TOOL            (gv_point_tool_get_type ())
+#define GV_POINT_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_POINT_TOOL, GvPointTool))
+#define GV_POINT_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_POINT_TOOL, GvPointToolClass))
+#define GV_IS_POINT_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_POINT_TOOL))
+#define GV_IS_POINT_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_POINT_TOOL))
+
+typedef struct _GvPointTool       GvPointTool;
+typedef struct _GvPointToolClass  GvPointToolClass;
+
+struct _GvPointTool
+{
+    GvTool tool;
+
+    GvShapeLayer *layer;
+    gchar *named_layer;
+};
+
+struct _GvPointToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_point_tool_get_type(void);
+GvTool* gv_point_tool_new(void);
+void gv_point_tool_set_layer(GvPointTool *tool, GvShapeLayer *layer);
+void gv_point_tool_set_named_layer(GvPointTool *tool, gchar *name);
+
+#endif /* __GV_POINT_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvpoitool.c
===================================================================
--- packages/openev/branches/upstream/current/gvpoitool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpoitool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,254 @@
+/******************************************************************************
+ * $Id: gvpoitool.c,v 1.3 2003/01/14 16:16:24 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Point of interest editing mode.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ *
+ */
+
+#include "gvpoitool.h"
+#include <gtk/gtksignal.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <stdio.h>
+
+#define DEFAULT_POI_SIZE 6.0
+
+/* Signals */
+enum
+{
+    POI_CHANGED,
+    LAST_SIGNAL
+};
+
+static void gv_poi_tool_class_init(GvPoiToolClass *klass);
+static void gv_poi_tool_init(GvPoiTool *tool);
+static void gv_poi_tool_draw(GvTool *tool);
+static void gv_poi_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_poi_tool_deactivate(GvTool *tool, GvViewArea *view);
+
+static guint poitool_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_poi_tool_get_type(void)
+{
+    static GtkType poi_tool_type = 0;
+
+    if (!poi_tool_type)
+    {
+	static const GtkTypeInfo poi_tool_info =
+	{
+	    "GvPoiTool",
+	    sizeof(GvPoiTool),
+	    sizeof(GvPoiToolClass),
+	    (GtkClassInitFunc) gv_poi_tool_class_init,
+	    (GtkObjectInitFunc) gv_poi_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	poi_tool_type = gtk_type_unique(gv_tool_get_type(),
+					&poi_tool_info);
+    }
+    return poi_tool_type;
+}
+
+static void
+gv_poi_tool_class_init(GvPoiToolClass *klass)
+{
+    GvToolClass *tool_class;
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+    tool_class = (GvToolClass*)klass;
+
+    poitool_signals[POI_CHANGED] =
+	gtk_signal_new ("poi_changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvPoiToolClass,poi_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    gtk_object_class_add_signals(object_class, poitool_signals, LAST_SIGNAL);
+    
+    klass->poi_changed = NULL;
+
+    tool_class->deactivate = gv_poi_tool_deactivate;
+    tool_class->draw = gv_poi_tool_draw;
+    tool_class->button_release = gv_poi_tool_button_release;
+
+}
+static void
+gv_poi_tool_init(GvPoiTool *tool)
+{
+    GV_TOOL(tool)->cursor = gdk_cursor_new(GDK_TCROSS);
+    tool->poi_marked = FALSE;
+    tool->poi_size = DEFAULT_POI_SIZE;
+}
+
+GvTool *
+gv_poi_tool_new(void)
+{
+    GvTool *tool;
+
+    tool = GV_TOOL(gtk_type_new(GV_TYPE_POI_TOOL));
+
+    
+
+    return tool;
+    
+}
+
+gint
+gv_poi_tool_get_point(GvPoiTool *tool, GvVertex *point)
+{
+    if (!tool->poi_marked)
+    {
+	return FALSE;
+    }
+
+    point->x = tool->v_center.x;
+    point->y = tool->v_center.y;
+
+    return TRUE;
+}
+
+gint
+gv_poi_tool_new_point(GvPoiTool *tool, GvVertex *point)
+{
+    /* Create new POI */
+    tool->poi_marked = TRUE;
+    
+    tool->v_center.x = point->x;
+    tool->v_center.y = point->y;
+
+    gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                             &(tool->v_center.x), &(tool->v_center.y) );
+
+    gtk_signal_emit(GTK_OBJECT(tool), 
+                    poitool_signals[POI_CHANGED]);
+
+    gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+
+    return TRUE;
+}
+
+/**************************************************************/
+
+static void
+gv_poi_tool_button_release(GvTool *ptool, GdkEventButton *event)
+{
+    GvPoiTool *tool = GV_POI_TOOL(ptool);
+
+    if ((event->button == 1)  && !(event->state & GDK_CONTROL_MASK)
+                              && !(event->state & GDK_SHIFT_MASK) )
+    {
+
+	/* Set head and tail vertex to pointer position */
+	/* Map pointer position */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_center.x, &tool->v_center.y);
+
+        if( gv_tool_check_bounds( GV_TOOL(tool), 
+                                  tool->v_center.x, tool->v_center.y ) )
+        {
+            tool->poi_marked = TRUE;
+	    gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+            gtk_signal_emit(GTK_OBJECT(tool), 
+                        poitool_signals[POI_CHANGED]);
+        }
+    }
+    if ((event->button == 2)  && !(event->state & GDK_CONTROL_MASK)
+                              && !(event->state & GDK_SHIFT_MASK) )
+    {
+        /* If user presses 2nd button within the view window, */
+	/* clear the point and redraw view without it */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_center.x, &tool->v_center.y);
+
+        if( gv_tool_check_bounds( GV_TOOL(tool), 
+                                  tool->v_center.x, tool->v_center.y ) )
+        {
+            tool->poi_marked = FALSE;
+	    gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+
+            gtk_signal_emit(GTK_OBJECT(tool), 
+                        poitool_signals[POI_CHANGED]);
+        }
+    }
+
+}
+
+static void
+gv_poi_tool_draw(GvTool *ptool)
+{
+    GvPoiTool *tool = GV_POI_TOOL(ptool);
+    gvgeocoord dx, dy, bx, by, x, y;
+
+    if (tool->poi_marked)
+    {
+        dx = tool->poi_size;
+        dy = 0.0;
+        gv_view_area_correct_for_transform(GV_TOOL(tool)->view, dx, dy, &dx, &dy);    
+        bx = by = tool->poi_size + 2;
+        gv_view_area_correct_for_transform(GV_TOOL(tool)->view, bx, by, &bx, &by);
+     
+        x = tool->v_center.x;
+        y = tool->v_center.y;
+
+        glRenderMode(GL_RENDER);
+        glColor3f(1.0,0.5,0.0);
+
+	/* Draw crosshairs */
+	glBegin(GL_LINES);
+	glVertex2(x-dx, y-dy);
+	glVertex2(x+dx, y+dy);
+	glVertex2(x+dy, y-dx);
+	glVertex2(x-dy, y+dx);
+	glEnd();
+
+	/* Draw box around crosshairs */
+	glBegin(GL_LINE_LOOP);
+	glVertex2(x-bx, y-by);
+	glVertex2(x+by, y-bx);
+	glVertex2(x+bx, y+by);
+	glVertex2(x-by, y+bx);
+	glEnd();	
+    }
+}
+
+static void
+gv_poi_tool_deactivate(GvTool *ptool, GvViewArea *view)
+{
+    GvPoiTool *tool = GV_POI_TOOL(ptool);
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+
+    tool->poi_marked = FALSE;
+
+    gv_view_area_queue_draw(view);
+}

Added: packages/openev/branches/upstream/current/gvpoitool.h
===================================================================
--- packages/openev/branches/upstream/current/gvpoitool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpoitool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * $Id: gvpoitool.h,v 1.1 2002/02/28 18:52:22 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Pegion of interest editing mode.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ */
+
+#ifndef __GV_POI_TOOL_H__
+#define __GV_POI_TOOL_H__
+
+#include "gvtypes.h"
+#include "gvtool.h"
+
+#define GV_TYPE_POI_TOOL            (gv_poi_tool_get_type ())
+#define GV_POI_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_POI_TOOL, GvPoiTool))
+#define GV_POI_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_POI_TOOL, GvPoiToolClass))
+#define GV_IS_POI_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_POI_TOOL))
+#define GV_IS_POI_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_POI_TOOL))
+
+typedef struct _GvPoiTool       GvPoiTool;
+typedef struct _GvPoiToolClass  GvPoiToolClass;
+
+struct _GvPoiTool
+{
+    GvTool tool;
+
+    gint poi_marked : 1;
+    gvfloat poi_size;  /* Default size of lines to draw to mark point */
+
+    GvVertex v_center;
+};
+
+struct _GvPoiToolClass
+{
+    GvToolClass parent_class;
+
+    void (* poi_changed)(GvPoiTool *tool);
+};
+
+GtkType gv_poi_tool_get_type(void);
+GvTool* gv_poi_tool_new(void);
+
+gint gv_poi_tool_get_point(GvPoiTool *tool, GvVertex *point);
+gint gv_poi_tool_new_point(GvPoiTool *tool, GvVertex *point);
+
+#endif /* __GV_POI_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvpolylines.c
===================================================================
--- packages/openev/branches/upstream/current/gvpolylines.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpolylines.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,565 @@
+/******************************************************************************
+ * $Id: gvpolylines.c,v 1.10 2002/11/05 18:56:21 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Polylines data container (superceeded by GvShapes)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpolylines.c,v $
+ * Revision 1.10  2002/11/05 18:56:21  sduclos
+ * fix gcc warning
+ *
+ * Revision 1.9  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.8  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gextra.h"
+#include "gvpolylines.h"
+
+typedef struct _GvLinesMemento GvLinesMemento;
+
+struct _GvLinesMemento
+{
+    GvDataMemento base;
+    GArray *line_ids;
+    GPtrArray *lines;
+};
+
+static void gv_polylines_class_init(GvPolylinesClass *klass);
+static void gv_polylines_init(GvPolylines *pline);
+static void gv_polylines_replace_lines(GvPolylines *pline, gint num_lines, gint *line_ids, GArray **line);
+static void gv_polylines_insert_lines(GvPolylines *pline, gint num_lines, gint *line_ids, GArray **line);
+static void gv_polylines_get_memento(GvPolylines *pline, GvShapeChangeInfo *info, GvDataMemento **memento);
+static void gv_polylines_set_memento(GvPolylines *pline, GvLinesMemento *memento);
+static void gv_polylines_del_memento(GvPolylines *pline, GvLinesMemento *memento);
+static void gv_polylines_changed(GvPolylines *pline, gpointer data);
+static void gv_polylines_finalize(GtkObject *object);
+
+GtkType
+gv_polylines_get_type(void)
+{
+    static GtkType polylines_type = 0;
+
+    if (!polylines_type)
+    {
+	static const GtkTypeInfo polylines_info =
+	{
+	    "GvPolylines",
+	    sizeof(GvPolylines),
+	    sizeof(GvPolylinesClass),
+	    (GtkClassInitFunc) gv_polylines_class_init,
+	    (GtkObjectInitFunc) gv_polylines_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	polylines_type = gtk_type_unique(gv_data_get_type(), &polylines_info);
+    }
+    return polylines_type;
+}
+
+static void
+gv_polylines_init(GvPolylines *pline)
+{
+    pline->lines = g_ptr_array_new();
+    pline->extents_valid = FALSE;
+}
+
+static void
+gv_polylines_class_init(GvPolylinesClass *klass)
+{
+    typedef void (*f)();
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    object_class = (GtkObjectClass*) klass;
+    data_class = (GvDataClass*) klass;
+
+    object_class->finalize = gv_polylines_finalize;
+    
+    data_class->changed     = (f) gv_polylines_changed;
+    data_class->get_memento = (f) gv_polylines_get_memento;
+    data_class->set_memento = (f) gv_polylines_set_memento;
+    data_class->del_memento = (f) gv_polylines_del_memento;
+}
+
+GvData *
+gv_polylines_new(void)
+{
+    return GV_DATA(gtk_type_new(gv_polylines_get_type()));
+}
+
+gint
+gv_polylines_new_line(GvPolylines *pline)
+{    
+    GArray *line;
+    int line_id;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    change_info.shape_id = &line_id;
+
+    line_id = pline->lines->len;
+    line = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+    g_return_val_if_fail(line, 0);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    g_ptr_array_add(pline->lines, (gpointer)line);
+
+    gv_data_changed(GV_DATA(pline), &change_info);
+    
+    return line_id;
+}    
+
+gint
+gv_polylines_new_line_with_data(GvPolylines *pline, gint num_nodes,
+			       GvVertex *vertex)
+{
+    GArray *line;
+    gint line_id;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    change_info.shape_id = &line_id;
+
+    line_id = pline->lines->len;
+    line = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+    g_return_val_if_fail(line, 0);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    g_ptr_array_add(pline->lines, (gpointer)line);
+    g_array_append_vals(line, vertex, num_nodes);
+    
+    gv_data_changed(GV_DATA(pline), &change_info);
+    
+    return line_id;
+}
+
+gint
+gv_polylines_num_nodes(GvPolylines *pline, gint line_id)
+{
+    GArray *line;
+
+    g_return_val_if_fail(line_id >= 0 && line_id < pline->lines->len, 0);
+    line = gv_polylines_get_line(pline, line_id);
+    return line->len;
+}
+
+void
+gv_polylines_translate_lines(GvPolylines *pline, gint num_lines, gint *line_id,
+			     gvgeocoord dx, gvgeocoord dy)
+{
+    GArray *line;
+    int i, j;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.shape_id = line_id;
+    change_info.num_shapes = num_lines;
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+
+    for (j=0; j < num_lines; ++j)
+    {
+	g_return_if_fail(line_id[j] >= 0 && line_id[j] < pline->lines->len);
+	line = gv_polylines_get_line(pline, line_id[j]);
+
+	for (i=0; i < line->len; ++i)
+	{
+	    g_array_index(line, GvVertex, i).x += dx;
+	    g_array_index(line, GvVertex, i).y += dy;
+	}
+    }
+    gv_data_changed(GV_DATA(pline), &change_info);
+}
+
+void
+gv_polylines_delete_lines(GvPolylines *pline, gint num_lines, gint *line_id)
+{
+    GArray *line;
+    GvShapeChangeInfo change_info = {GV_CHANGE_DELETE, 0, NULL};
+
+    change_info.num_shapes = num_lines;
+    change_info.shape_id = line_id;
+    
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    if (num_lines == 1)
+    {
+	line = (GArray*)g_ptr_array_remove_index_fast(pline->lines, *line_id);
+	if (line) g_array_free(line, TRUE);
+    }
+    else
+    {
+	/* Strategy: sort the line_id buffer and delete lines in desending
+	   order, so that indicies remain valid */
+	gint *id, i;
+
+	id = g_memdup_type(line_id, gint, num_lines);
+	g_sort_type(id, gint, num_lines);
+
+	for (i=num_lines-1; i >= 0; --i)
+	{
+	    line = (GArray*)g_ptr_array_remove_index_fast(pline->lines, id[i]);
+	    if (line) g_array_free(line, TRUE);
+	}
+	g_free(id);
+    }
+    gv_data_changed(GV_DATA(pline), &change_info);    
+}
+
+void
+gv_polylines_set_nodes(GvPolylines *pline, gint line_id, gint num_nodes,
+		       GvVertex *vertex)
+{
+    GArray *line;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &line_id;
+    
+    g_return_if_fail(line_id >= 0 && line_id < pline->lines->len);
+    line = gv_polylines_get_line(pline, line_id);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    g_array_set_size(line, 0);
+    g_array_append_vals(line, vertex, num_nodes);
+
+    gv_data_changed(GV_DATA(pline), &change_info);    
+}
+
+void
+gv_polylines_append_nodes(GvPolylines *pline, gint line_id, gint num_nodes,
+			  GvVertex *vertex)
+{
+    GArray *line;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &line_id;
+
+    g_return_if_fail(line_id >= 0 && line_id < pline->lines->len);
+    line = gv_polylines_get_line(pline, line_id);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    g_array_append_vals(line, vertex, num_nodes);
+
+    gv_data_changed(GV_DATA(pline), &change_info);        
+}
+
+void
+gv_polylines_insert_nodes(GvPolylines *pline, gint line_id, gint node_id,
+			  gint num_nodes, GvVertex *vertex)
+{
+    GArray *line;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &line_id;
+
+    g_return_if_fail(line_id >= 0 && line_id < pline->lines->len);
+    line = gv_polylines_get_line(pline, line_id);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+ 
+    g_array_insert_vals(line, node_id, vertex, num_nodes);
+
+    gv_data_changed(GV_DATA(pline), &change_info);            
+}
+
+void
+gv_polylines_delete_nodes(GvPolylines *pline, gint line_id, gint num_nodes,
+			  gint *node_id)
+{
+    GArray *line;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &line_id;
+    
+    g_return_if_fail(line_id >= 0 && line_id < pline->lines->len);
+    line = gv_polylines_get_line(pline, line_id);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    if (num_nodes == 1)
+    {
+	/* Need to preserve node order, so we can't use *_remove_fast */
+	g_array_remove_index(line, *node_id);
+    }
+    else
+    {
+	/* Strategy: sort the node_id buffer and delete nodes in desending
+	   order, so that indicies remain valid */
+	gint *id, i;
+
+	id = g_memdup_type(node_id, gint, num_nodes);
+	g_sort_type(id, gint, num_nodes);
+
+	for (i=num_nodes-1; i >= 0; --i)
+	{
+	    g_array_remove_index(line, id[i]);
+	}
+	g_free(id);
+    }
+    gv_data_changed(GV_DATA(pline), &change_info);        
+}
+
+void
+gv_polylines_move_node(GvPolylines *pline, gint line_id, gint node_id,
+		       GvVertex *vertex)
+{
+    GArray *line;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 1, NULL};
+
+    change_info.shape_id = &line_id;
+
+    g_return_if_fail(line_id >= 0 && line_id < pline->lines->len);
+    line = gv_polylines_get_line(pline, line_id);
+    g_return_if_fail(node_id >= 0 && node_id < line->len);
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+    
+    g_array_index(line, GvVertex, node_id).x = vertex->x;
+    g_array_index(line, GvVertex, node_id).y = vertex->y;
+
+    gv_data_changed(GV_DATA(pline), &change_info);    
+}
+
+GvVertex *
+gv_polylines_get_node(GvPolylines *pline, gint line_id, gint node_id)
+{
+    GArray *line;
+
+    g_return_val_if_fail(line_id >= 0 && line_id < pline->lines->len, NULL);
+    line = gv_polylines_get_line(pline, line_id);
+    g_return_val_if_fail(node_id >= 0 && node_id < line->len, NULL);
+
+    return &g_array_index(line, GvVertex, node_id);
+}
+
+void
+gv_polylines_get_extents(GvPolylines *pline, GvRect *rect)
+{
+    if (!pline->extents_valid)
+    {
+	gint i, j, lines;
+	GArray *line;
+	GvVertex vmax, vmin, *v;
+
+	vmin.x = vmin.y = GV_MAXFLOAT;
+	vmax.x = vmax.y = -GV_MAXFLOAT;
+	lines = gv_polylines_num_lines(pline);
+	for (i=0; i < lines; ++i)
+	{
+	    line = gv_polylines_get_line(pline, i);
+	    for (j=0; j < line->len; ++j)
+	    {
+		v = &g_array_index(line, GvVertex, j);
+		if (v->x < vmin.x) vmin.x = v->x;
+		if (v->x > vmax.x) vmax.x = v->x;
+		if (v->y < vmin.y) vmin.y = v->y;
+		if (v->y > vmax.y) vmax.y = v->y;
+	    }
+	}
+
+	if (lines == 0 || (lines == 1 && line->len == 0))
+	{
+	    pline->extents.x = 0;
+	    pline->extents.y = 0;
+	    pline->extents.width = 0;
+	    pline->extents.height = 0;
+	}
+	else
+	{
+	    pline->extents.x = vmin.x;
+	    pline->extents.y = vmin.y;
+	    pline->extents.width = vmax.x - vmin.x;
+	    pline->extents.height = vmax.y - vmin.y;
+	}
+	pline->extents_valid = TRUE;
+    }
+    rect->x = pline->extents.x;
+    rect->y = pline->extents.y;
+    rect->width = pline->extents.width;
+    rect->height = pline->extents.height;
+}
+
+/*********************************************/
+
+static void
+gv_polylines_replace_lines(GvPolylines *pline, gint num_lines, gint *line_id,
+                           GArray **line)
+{
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_lines;
+    change_info.shape_id = line_id;
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+
+    for (i=0; i < num_lines; ++i)
+    {
+        g_array_free(gv_polylines_get_line(pline, line_id[i]), TRUE);
+
+        g_ptr_array_index(pline->lines, line_id[i]) = line[i];
+    }
+
+    gv_data_changed(GV_DATA(pline), &change_info);
+}
+
+static void
+gv_polylines_insert_lines(GvPolylines *pline, gint num_lines, gint *line_ids,
+			  GArray **line)
+{
+    /* The line_id array must be in ascending order! */
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 0, NULL};
+
+    change_info.num_shapes = num_lines;
+    change_info.shape_id = line_ids;
+
+    gv_data_changing(GV_DATA(pline), &change_info);
+
+    for (i=0; i < num_lines; ++i)
+    {
+	g_ptr_array_insert_fast(pline->lines, line_ids[i], line[i]);
+    }
+    
+    gv_data_changed(GV_DATA(pline), &change_info);
+}
+
+static void
+gv_polylines_get_memento(GvPolylines *pline, GvShapeChangeInfo *info,
+			 GvDataMemento **memento)
+{
+    GvLinesMemento *mem;
+    int i;
+
+    mem = g_new(GvLinesMemento, 1);
+    mem->base.data = GV_DATA(pline);
+    mem->base.type = info->change_type;
+
+    mem->line_ids = g_array_new(FALSE, FALSE, sizeof(gint));
+    g_array_append_vals(mem->line_ids, info->shape_id,	info->num_shapes);
+
+    /* Grab areas in ascending order */
+    if (info->num_shapes > 1)
+    {
+	g_sort_type(mem->line_ids->data, gint, mem->line_ids->len);
+    }
+
+    if (info->change_type == GV_CHANGE_ADD)
+    {
+	mem->lines = NULL;
+    }
+    else
+    {
+	mem->lines = g_ptr_array_new();
+	for (i=0; i < info->num_shapes; ++i)
+	{
+	    GArray *line = gv_polylines_get_line(pline, info->shape_id[i]);
+	    GArray *copy = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+	    g_array_append_vals(copy, line->data, line->len);
+	    g_ptr_array_add(mem->lines, copy);
+	}
+    }
+    
+    *memento = (GvDataMemento*)mem;    
+}
+
+static void
+gv_polylines_set_memento(GvPolylines *pline, GvLinesMemento *memento)
+{
+    switch (memento->base.type)
+    {
+	case GV_CHANGE_ADD:
+	    gv_polylines_delete_lines(pline, memento->line_ids->len,
+				      (gint*)memento->line_ids->data);
+	    break;
+
+	case GV_CHANGE_REPLACE:
+	    gv_polylines_replace_lines(pline, memento->line_ids->len,
+				       (gint*)memento->line_ids->data,
+				       (GArray**)memento->lines->pdata);
+	    break;
+	    
+	case GV_CHANGE_DELETE:
+	    gv_polylines_insert_lines(pline, memento->line_ids->len,
+				      (gint*)memento->line_ids->data,
+				      (GArray**)memento->lines->pdata);
+	    break;
+    }
+
+    if (memento->lines)
+    {
+	g_ptr_array_free(memento->lines, TRUE);
+	memento->lines = NULL;
+    }
+    gv_polylines_del_memento(pline, memento);
+}
+
+static void
+gv_polylines_del_memento(GvPolylines *pline, GvLinesMemento *memento)
+{
+    int i;
+
+    if (memento->lines)
+    {
+	for (i=0; i < memento->lines->len; ++i)
+	{
+	    g_array_free(g_ptr_array_index(memento->lines, i), TRUE);
+	}
+	g_ptr_array_free(memento->lines, TRUE);
+    }
+    g_array_free(memento->line_ids, TRUE);
+    g_free(memento);
+}
+
+static void
+gv_polylines_changed(GvPolylines *pline, gpointer data)
+{
+    pline->extents_valid = FALSE;
+}
+
+static void
+gv_polylines_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvPolylines *pline;
+    int i;
+
+    pline = GV_POLYLINES(object);
+
+    for (i=0; i < gv_polylines_num_lines(pline); i++)
+    {
+	g_array_free(gv_polylines_get_line(pline, i), TRUE);
+    }
+    g_ptr_array_free(pline->lines, TRUE);
+    
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}

Added: packages/openev/branches/upstream/current/gvpolylines.h
===================================================================
--- packages/openev/branches/upstream/current/gvpolylines.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpolylines.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,87 @@
+/******************************************************************************
+ * $Id: gvpolylines.h,v 1.7 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Polylines data container (superceeded by GvShapes)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpolylines.h,v $
+ * Revision 1.7  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.6  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_POLYLINES_H__
+#define __GV_POLYLINES_H__
+
+#include "gvdata.h"
+
+#define GV_TYPE_POLYLINES            (gv_polylines_get_type ())
+#define GV_POLYLINES(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_POLYLINES, GvPolylines))
+#define GV_POLYLINES_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_POLYLINES, GvPolylinesClass))
+#define GV_IS_POLYLINES(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_POLYLINES))
+#define GV_IS_POLYLINES_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_POLYLINES))
+
+typedef struct _GvPolylines       GvPolylines;
+typedef struct _GvPolylinesClass  GvPolylinesClass;
+
+struct _GvPolylines
+{
+    GvData data;
+
+    GPtrArray *lines;
+    GvRect extents;
+    guint extents_valid : 1;
+};
+
+struct _GvPolylinesClass
+{
+    GvDataClass parent_class;
+};
+
+GtkType    gv_polylines_get_type (void);
+
+GvData* gv_polylines_new(void);
+gint gv_polylines_new_line(GvPolylines *pline);
+gint gv_polylines_new_line_with_data(GvPolylines *pline, gint num_nodes, GvVertex *vertex);
+gint gv_polylines_num_nodes(GvPolylines *pline, gint line_id);
+
+void gv_polylines_delete_lines(GvPolylines *pline, gint num_lines, gint *line_id);
+void gv_polylines_translate_lines(GvPolylines *pline, gint num_lines, gint *line_id, gvgeocoord dx, gvgeocoord dy);
+void gv_polylines_set_nodes(GvPolylines *pline, gint line_id, gint num_nodes, GvVertex *vertex);
+void gv_polylines_append_nodes(GvPolylines *pline, gint line_id, gint num_nodex, GvVertex *vertex);
+void gv_polylines_insert_nodes(GvPolylines *pline, gint line_id, gint node_id, gint num_nodes, GvVertex *vertex);
+void gv_polylines_delete_nodes(GvPolylines *pline, gint line_id, gint num_nodes, gint *node_id);
+void gv_polylines_move_node(GvPolylines *pline, gint line_id, gint node_id, GvVertex *vertex);
+GvVertex* gv_polylines_get_node(GvPolylines *pline, gint line_id, gint node_id);
+void gv_polylines_get_extents(GvPolylines *pline, GvRect *rect);
+
+#define gv_polylines_num_lines(pline) \
+     (pline->lines->len)
+#define gv_polylines_get_line(pline,id) \
+     ((GArray*)g_ptr_array_index(pline->lines, id))
+
+#endif /*__GV_POLYLINES_H__ */
+

Added: packages/openev/branches/upstream/current/gvpquerylayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvpquerylayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpquerylayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,232 @@
+/******************************************************************************
+ * $Id: gvpquerylayer.c,v 1.11 2004/08/20 13:53:47 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Point query layer.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpquerylayer.c,v $
+ * Revision 1.11  2004/08/20 13:53:47  warmerda
+ * allow GvShapes to be passed into constructor
+ *
+ * Revision 1.10  2003/08/27 19:58:43  warmerda
+ * added force_simple flag for gv_view_area_bmfont_draw
+ *
+ * Revision 1.9  2003/02/27 03:59:21  warmerda
+ * added view to gv_shapes_layer_get_draw_info
+ *
+ * Revision 1.8  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.7  2000/08/04 14:14:12  warmerda
+ * GvShapes shape ids now persistent
+ *
+ * Revision 1.6  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvpquerylayer.h"
+#include "gvutils.h"
+#include <GL/gl.h>
+
+static void gv_pquery_layer_class_init(GvPqueryLayerClass *klass);
+static void gv_pquery_layer_init(GvPqueryLayer *layer);
+static void gv_pquery_layer_setup(GvLayer *layer, GvViewArea *view);
+static void gv_pquery_layer_draw(GvLayer *layer, GvViewArea *view);
+static void gv_pquery_layer_draw_selected(GvShapeLayer *layer, 
+                                          GvViewArea *view);
+
+GtkType
+gv_pquery_layer_get_type(void)
+{
+    static GtkType pquery_layer_type = 0;
+
+    if (!pquery_layer_type)
+    {
+	static const GtkTypeInfo pquery_layer_info =
+	{
+	    "GvPqueryLayer",
+	    sizeof(GvPqueryLayer),
+	    sizeof(GvPqueryLayerClass),
+	    (GtkClassInitFunc) gv_pquery_layer_class_init,
+	    (GtkObjectInitFunc) gv_pquery_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	pquery_layer_type = gtk_type_unique(gv_shapes_layer_get_type(),
+					    &pquery_layer_info);
+    }
+    return pquery_layer_type;
+}
+
+static void
+gv_pquery_layer_class_init(GvPqueryLayerClass *klass)
+{
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    layer_class->setup = gv_pquery_layer_setup;
+    layer_class->draw = gv_pquery_layer_draw;
+    shape_layer_class->draw_selected = gv_pquery_layer_draw_selected;
+}
+
+static void
+gv_pquery_layer_init(GvPqueryLayer *layer)
+{
+    layer->font = 0;
+}
+
+GtkObject *
+gv_pquery_layer_new( GvShapes *data )
+{
+    GvPqueryLayer *layer = GV_PQUERY_LAYER(gtk_type_new(
+	gv_pquery_layer_get_type()));
+
+    if( data == NULL )
+    {
+        data = GV_SHAPES(gv_shapes_new());
+        gv_data_set_name( GV_DATA(data), "Query Points" );
+    }
+
+    gv_shapes_layer_set_data( GV_SHAPES_LAYER(layer), data );
+
+    return GTK_OBJECT(layer);
+}
+
+/*******************************************************/
+
+static void
+gv_pquery_layer_setup(GvLayer *rlayer, GvViewArea *view)
+{
+    GvPqueryLayer *layer = GV_PQUERY_LAYER(rlayer);
+
+    layer->font = gv_view_area_bmfont_load(view, "fixed");
+}
+
+static void gv_pquery_layer_draw_text(GvViewArea * view, 
+                                      GvPqueryLayer *layer, GvShape *shape,
+                                      gvgeocoord dx, gvgeocoord dy )
+
+{
+    const char *text;
+    gvgeocoord    x, y;
+
+    x = gv_shape_get_x( shape, 0, 0 );
+    y = gv_shape_get_y( shape, 0, 0 );
+
+    /* FIXME: the label should be cached in the point structure */
+    text = gv_format_point_query( view, gv_data_get_properties(GV_DATA(layer)),
+                                  x, y );
+                                  
+    gv_view_area_bmfont_draw(view, layer->font, x+dx, y+dy, 
+                             (char *) text, 0);
+}
+
+static void
+gv_pquery_layer_draw(GvLayer *rlayer, GvViewArea *view)
+{
+    GvPqueryLayer *layer = GV_PQUERY_LAYER(rlayer);
+    gint i, points;
+    gint *selected, presentation;
+    gvgeocoord dx, dy;
+    gint hit_selected = FALSE;
+    GvShapeDrawInfo    drawinfo;
+    GvShape           *shape;
+    
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+
+    gv_shapes_layer_get_draw_info( view, GV_SHAPES_LAYER(rlayer), &drawinfo );
+    points = gv_shapes_num_shapes(GV_SHAPES_LAYER(layer)->data);
+    
+    /* Call the parent class draw func to make crosshairs */
+    (*((GvLayerClass*)gtk_type_class(GV_TYPE_SHAPES_LAYER))->draw)
+	(GV_LAYER(layer), view);
+
+    /* Get offset for text */
+    dx = dy = drawinfo.point_size;
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);    
+
+    glColor4fv(drawinfo.point_color);
+    for (i=0; i < points; ++i)
+    {
+	if (selected[i] && !presentation)
+	{
+	    hit_selected = 1;
+	    continue;
+	}
+
+        shape = gv_shapes_get_shape(GV_SHAPES_LAYER(layer)->data, i);
+        if( shape != NULL )
+            gv_pquery_layer_draw_text( view, layer, shape, dx, dy );
+    }
+
+    if (hit_selected && ! GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+	gv_pquery_layer_draw_selected(GV_SHAPE_LAYER(layer), view);
+    }     
+}
+
+static void
+gv_pquery_layer_draw_selected(GvShapeLayer *rlayer, GvViewArea *view)
+{
+    gint i, points;
+    gint *selected;
+    gvgeocoord dx, dy;
+    GvPqueryLayer *layer = GV_PQUERY_LAYER(rlayer);
+    GvShapeDrawInfo    drawinfo;
+    GvShape           *shape;
+
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+
+    gv_shapes_layer_get_draw_info( view, GV_SHAPES_LAYER(rlayer), &drawinfo );
+    points = gv_shapes_num_shapes(GV_SHAPES_LAYER(layer)->data);
+    
+    /* Call the parent class draw func to make crosshairs */
+    (*((GvShapeLayerClass*)gtk_type_class(GV_TYPE_SHAPES_LAYER))->
+     draw_selected)(GV_SHAPE_LAYER(layer), view);
+
+    /* Get offset for text */
+    dx = dy = drawinfo.point_size;
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);    
+
+    glColor4fv(drawinfo.point_color);
+    for (i=0; i < points; ++i)
+    {
+	if (!selected[i])
+	{
+	    continue;
+	}
+
+        shape = gv_shapes_get_shape(GV_SHAPES_LAYER(layer)->data, i);
+        if( shape != NULL )
+            gv_pquery_layer_draw_text( view, layer, shape, dx, dy );
+    }
+}
+
+

Added: packages/openev/branches/upstream/current/gvpquerylayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvpquerylayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvpquerylayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * $Id: gvpquerylayer.h,v 1.5 2004/08/20 13:53:48 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Point query layer.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvpquerylayer.h,v $
+ * Revision 1.5  2004/08/20 13:53:48  warmerda
+ * allow GvShapes to be passed into constructor
+ *
+ * Revision 1.4  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_PQUERY_LAYER_H__
+#define __GV_PQUERY_LAYER_H__
+
+#include "gvshapeslayer.h"
+
+#define GV_TYPE_PQUERY_LAYER            (gv_pquery_layer_get_type ())
+#define GV_PQUERY_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_PQUERY_LAYER, GvPqueryLayer))
+#define GV_PQUERY_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_PQUERY_LAYER, GvPqueryLayerClass))
+#define GV_IS_PQUERY_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_PQUERY_LAYER))
+#define GV_IS_PQUERY_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_PQUERY_LAYER))
+
+typedef struct _GvPqueryLayer       GvPqueryLayer;
+typedef struct _GvPqueryLayerClass  GvPqueryLayerClass;
+
+struct _GvPqueryLayer
+{
+    GvShapesLayer layer;
+
+    gint font;
+};
+
+struct _GvPqueryLayerClass
+{
+    GvShapesLayerClass parent_class;
+};
+
+GtkType gv_pquery_layer_get_type(void);
+GtkObject* gv_pquery_layer_new( GvShapes * );
+
+#endif /* __GV_PQUERY_LAYER_H__ */

Added: packages/openev/branches/upstream/current/gvprint.c
===================================================================
--- packages/openev/branches/upstream/current/gvprint.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvprint.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,409 @@
+/******************************************************************************
+ * $Id: gvprint.c,v 1.8 2001/02/15 16:36:51 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvViewArea printing support.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvprint.c,v $
+ * Revision 1.8  2001/02/15 16:36:51  warmerda
+ * fixed serious bug with print_handler - now deinitialized when done
+ *
+ * Revision 1.7  2000/08/07 18:42:48  warmerda
+ * added windows printing stubs
+ *
+ * Revision 1.6  2000/08/03 18:20:41  warmerda
+ * implemented print scaling and paper sizes properly
+ *
+ * Revision 1.5  2000/07/20 03:21:26  warmerda
+ * added is_rgb for print_to_file()
+ *
+ * Revision 1.4  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvviewarea.h"
+#include "gdal.h"
+#include "cpl_conv.h"
+
+typedef struct 
+{
+    gint     width;
+    gint     (*cb_func)(void *, const char *);
+    void     *cb_data;
+    gint     is_rgb; /* otherwise reduce to greyscale */
+    char    *text_buf;
+} gvPostScriptOptions;
+
+static gint print_handler( void * cb_data, void * scanline_in )
+
+{
+    unsigned char *scanline = (unsigned char *) scanline_in;
+    static GDALDatasetH  working_ds = NULL;
+    static int next_scanline = 0;
+    static GDALRasterBandH red_band, green_band, blue_band;
+    gint   width;
+
+    if( cb_data == NULL && scanline_in == NULL )
+    {
+        next_scanline = 0;
+        working_ds = NULL;
+        return -1;
+    }
+
+    if( working_ds != cb_data )
+    {
+        working_ds = (GDALDatasetH) cb_data;
+        next_scanline = 0;
+
+        red_band = GDALGetRasterBand( working_ds, 1 );
+        if( GDALGetRasterCount( working_ds ) >= 3 )
+        {
+            green_band = GDALGetRasterBand( working_ds, 2 );
+            blue_band = GDALGetRasterBand( working_ds, 3 );
+        }
+        else
+        {
+            green_band = blue_band = NULL;
+        }
+
+        if( red_band == NULL )
+            return -1;
+    }
+
+    width = GDALGetRasterXSize( working_ds );
+
+    if( green_band == NULL )
+    {
+        GByte	*grey;
+        int     i, value;
+
+        grey = g_new( GByte, width );
+        
+        for( i = 0; i < width; i++ )
+        {
+            value = *(scanline++);
+            value += *(scanline++);
+            value += *(scanline++);
+            value = (value+1) / 3;
+            grey[i] = value;
+        }
+        
+        GDALRasterIO( red_band, GF_Write, 0, next_scanline, width, 1, 
+                      grey, width, 1, GDT_Byte, 1, 0 );
+        g_free( grey );
+    }
+    else
+    {
+        GDALRasterIO( red_band, GF_Write, 0, next_scanline, width, 1, 
+                      scanline+0, width, 1, GDT_Byte, 3, 0 );
+        GDALRasterIO( green_band, GF_Write, 0, next_scanline, width, 1, 
+                      scanline+1, width, 1, GDT_Byte, 3, 0 );
+        GDALRasterIO( blue_band, GF_Write, 0, next_scanline, width, 1, 
+                      scanline+2, width, 1, GDT_Byte, 3, 0 );
+    }
+    
+    next_scanline++;
+
+    return 0;
+}
+
+gint
+gv_view_area_print_to_file(GvViewArea *view, int width, int height, 
+                           const char * filename, const char * format,
+                           int is_rgb )
+
+{
+    GDALDriverH   driver;
+    GDALDatasetH  dataset;
+    gint          errcode;
+
+    driver = GDALGetDriverByName( format );
+    if( driver == NULL )
+        return -1;
+
+    if( is_rgb )
+        dataset = GDALCreate( driver, filename, width, height, 3, GDT_Byte, 
+                              NULL );
+    else
+        dataset = GDALCreate( driver, filename, width, height, 1, GDT_Byte, 
+                              NULL );
+
+    if( dataset == NULL )
+        return -1;
+
+    errcode = gv_view_area_render_to_func( view, width, height, 
+                                           print_handler, dataset );
+    GDALClose( dataset );
+
+    print_handler( NULL, NULL );
+    
+    return errcode;
+}
+
+static gint postscript_handler( void * cb_data, void * scanline_in )
+
+{
+    unsigned char *scanline = (unsigned char *) scanline_in;
+    gvPostScriptOptions *options = (gvPostScriptOptions *) cb_data;
+    int i;
+    
+    if( options->is_rgb )
+    {
+        for( i = 0; i < options->width; i++ )
+            sprintf( options->text_buf + i*2, "%02x", 
+                     scanline[i*3] );
+
+        for( i = 0; i < options->width; i++ )
+            sprintf( options->text_buf + i*2 + options->width*2, "%02x", 
+                     scanline[i*3+1] );
+
+        for( i = 0; i < options->width; i++ )
+            sprintf( options->text_buf + i*2 + options->width*4, "%02x", 
+                     scanline[i*3+2] );
+
+        options->text_buf[options->width*6] = '\0';
+    }
+    else
+    {
+        for( i = 0; i < options->width; i++ )
+        {
+            int value;
+            
+            value = *(scanline++);
+            value += *(scanline++);
+            value += *(scanline++);
+            value = (value+1) / 3;
+
+            sprintf( options->text_buf + i*2, "%02x", value );
+        }
+        options->text_buf[options->width*2] = '\0';
+    }
+
+    strcat( options->text_buf, "\n" );
+
+    
+
+    return options->cb_func( options->cb_data, options->text_buf );
+}
+
+gint
+gv_view_area_render_postscript(GvViewArea *view, int width, int height, 
+                               float ulx, float uly, float lrx, float lry,
+                               int is_rgb,
+                               gint (*cb_func)(void *, const char *), 
+                               void * cb_data )
+
+{
+    gvPostScriptOptions   options;
+    int errcode;
+    char line[128];
+
+    /* write prolog */
+
+    cb_func( cb_data, "%!PS-Adobe-3.0 EPSF-3.0\n" );
+    cb_func( cb_data, "%%Creator: gview\n" );
+    cb_func( cb_data, "%%Title: gview_print\n" );
+    cb_func( cb_data, "%%CreationDate: Thu Apr  6 20:11:10 2000\n" );
+    cb_func( cb_data, "%%DocumentData: Clean7Bit\n" );
+    cb_func( cb_data, "%%Origin: 0 0\n" );
+#ifdef notdef
+    sprintf( line, "%%%%BoundingBox: 0 0 %d %d\n", width, height );
+    cb_func( cb_data, line );
+#endif
+    cb_func( cb_data, "%%LanguageLevel: 1\n" );
+    cb_func( cb_data, "%%Pages: 1\n" );
+    cb_func( cb_data, "%%EndComments\n" );
+    cb_func( cb_data, "%%BeginSetup\n" );
+    cb_func( cb_data, "%%EndSetup\n" );
+    cb_func( cb_data, "%%Page: 1 1\n" );
+    cb_func( cb_data, "gsave\n" );
+    cb_func( cb_data, "100 dict begin\n" );
+
+    sprintf( line, "%f %f translate\n", ulx*72.0, uly*72.0 );
+    cb_func( cb_data, line );
+
+    sprintf( line, "%f %f scale\n", (lrx - ulx)*72.0, (lry - uly)*72.0 );
+    cb_func( cb_data, line );
+
+    if( is_rgb )
+    {
+        sprintf( line, 
+                 "%%ImageData: %d %d 8 3 0 %d 2 \"true 3 colorimage\"\n", 
+                 width, height, width );
+        cb_func( cb_data, line );
+
+        sprintf( line, "/line0 %d string def\n", width );
+        cb_func( cb_data, line );
+        
+        sprintf( line, "/line1 %d string def\n", width );
+        cb_func( cb_data, line );
+        
+        sprintf( line, "/line2 %d string def\n", width );
+        cb_func( cb_data, line );
+    }
+    else
+    {
+        sprintf( line, 
+                 "%%ImageData: %d %d 8 1 0 %d 2 \"image\"\n", 
+                 width, height, width );
+        cb_func( cb_data, line );
+
+        sprintf( line, "/scanLine %d string def\n", width );
+        cb_func( cb_data, line );
+    }
+
+
+    sprintf( line, "%d %d 8\n", width, height );
+    cb_func( cb_data, line );
+
+    sprintf( line, "[%d 0 0 %d 0 %d]\n", width, -height, height );
+    cb_func( cb_data, line );
+
+    if( is_rgb )
+    {
+        cb_func( cb_data, "{currentfile line0 readhexstring pop}bind\n" );
+        cb_func( cb_data, "{currentfile line1 readhexstring pop}bind\n" );
+        cb_func( cb_data, "{currentfile line2 readhexstring pop}bind\n" );
+        cb_func( cb_data, "true 3 colorimage\n" );
+    }
+    else
+    {
+        cb_func( cb_data, "{currentfile scanLine readhexstring pop}bind\n" );
+        cb_func( cb_data, "image\n" );
+    }
+
+    /* now prepare and write image data */
+    options.cb_func = cb_func;
+    options.cb_data = cb_data;
+    options.width = width;
+    options.is_rgb = is_rgb;
+    options.text_buf = g_malloc(width * 6 + 3);
+    if( options.text_buf == NULL )
+        return -1;
+
+    errcode = gv_view_area_render_to_func( view, width, height, 
+                                           postscript_handler, &options );
+    
+    g_free( options.text_buf );
+
+    /* write postlog */
+
+    if( errcode == 0 )
+    {
+        cb_func( cb_data, "end\n" );
+        cb_func( cb_data, "grestore\n" );
+        cb_func( cb_data, "showpage\n" );
+        cb_func( cb_data, "%%Trailer\n" );
+        cb_func( cb_data, "%%Pages: 1\n" );
+        cb_func( cb_data, "%%EOF\n" );
+    }
+
+    return errcode;
+}
+
+static gint
+postscript_to_file_handler( void *cb_data, const char * text )
+
+{
+    FILE *fp = (FILE *) cb_data;
+
+    if( fputs(text, fp) < 0 )
+        return -1;
+    else
+        return 0;
+}
+
+#ifndef _WIN32
+static gint 
+gv_view_area_print_postscript_to_pipe(GvViewArea *view, 
+                                      int width, int height,
+                                      float ulx, float uly, 
+                                      float lrx, float lry,
+                                      int is_rgb, const char * filename )
+
+{
+    FILE  *fp;
+    int   errcode;
+
+    fp = popen( filename, "w" );
+    if( fp == NULL )
+        return -1;
+    
+    errcode = 
+        gv_view_area_render_postscript(view, width, height, ulx, uly, lrx, lry,
+                                       is_rgb,postscript_to_file_handler, fp );
+
+    pclose(fp);
+
+    return errcode;
+}
+#endif
+                                      
+gint 
+gv_view_area_print_postscript_to_file(GvViewArea *view, 
+                                      int width, int height, 
+                                      float ulx, float uly, 
+                                      float lrx, float lry,
+                                      int is_rgb,
+                                      const char * filename )
+
+{
+    FILE  *fp;
+    int   errcode;
+
+#ifndef _WIN32
+    if(filename[0] == '|')
+        return gv_view_area_print_postscript_to_pipe(view, width, height, 
+                                                     ulx, uly, lrx, lry,
+                                                     is_rgb, filename+1 );
+#endif
+
+    fp = fopen( filename, "wt" );
+    if( fp == NULL )
+        return -1;
+    
+    errcode = 
+        gv_view_area_render_postscript(view, width, height, ulx, uly, lrx, lry,
+                                       is_rgb,postscript_to_file_handler, fp );
+
+    fclose(fp);
+
+    return errcode;
+}
+             
+#ifndef WIN32                         
+void gv_view_area_page_setup()
+{
+}
+
+gint 
+gv_view_area_print_to_windriver(GvViewArea *view, int width, int height,
+                                float ulx, float uly, float lrx, float lry,
+                                int is_rgb )
+
+{
+    return 1;
+}
+#endif
+

Added: packages/openev/branches/upstream/current/gvproperties.c
===================================================================
--- packages/openev/branches/upstream/current/gvproperties.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvproperties.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,490 @@
+/******************************************************************************
+ * $Id: gvproperties.c,v 1.5 2002/09/09 16:22:45 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Generic string properties list.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvproperties.c,v $
+ * Revision 1.5  2002/09/09 16:22:45  warmerda
+ * use int instead of gint to avoid glib.h dependency
+ *
+ * Revision 1.4  2002/07/24 18:06:26  warmerda
+ * reimplement properties using quarks
+ *
+ * Revision 1.3  2000/09/21 02:55:11  warmerda
+ * added gv_properties_clear
+ *
+ * Revision 1.2  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvtypes.h"
+#include "cpl_port.h"
+#include "gvproperties.h"
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GvProperties                             */
+/*                                                                      */
+/*      Hash table based implementation.                                */
+/*                                                                      */
+/*      Each GvProperties is a guint32 pointer.  If it is NULL there    */
+/*      are no properties otherwise it points to an array of            */
+/*      integers:                                                       */
+/*                                                                      */
+/*        [0] = Max allocated name/values possible.                     */
+/*        [1] = Number of name/value property settings.                 */
+/*        [2] = key id of first property name.                          */
+/*        [3] = quark id of first property values.                      */
+/*        ...                                                           */
+/*                                                                      */
+/*      The property names are kept in a custom case insensitive        */
+/*      symbol table and hash table.  The property values (which        */
+/*      must be case sensitive) are implemented using normal GLib       */
+/*      quarks.                                                         */
+/* ==================================================================== */
+/************************************************************************/
+
+#ifdef USE_HASH_BASED_GVPROPERTIES
+
+#include <glib.h>
+
+#define	G_QUARK_BLOCK_SIZE			(512)
+
+static GHashTable   *gvpk_keyid_ht = NULL;
+static gchar       **gvpk_keyids = NULL;
+static GQuark        gvpk_keyid_seq_id = 0;
+
+#define PROP_KEYID(properties,i) ((*properties)[i*2+2])
+#define PROP_VALUEID(properties,i) ((*properties)[i*2+3])
+#define PROP_COUNT(properties)   ((*properties)[1])
+#define PROP_MAXCOUNT(properties)   ((*properties)[0])
+
+/************************************************************************/
+/*                           gvpk_str_equal()                           */
+/*                                                                      */
+/*      Case insensitive string compare.  Used for the property         */
+/*      names hash table.                                               */
+/************************************************************************/
+
+static gint gvpk_str_equal (gconstpointer v1, gconstpointer v2)
+
+{
+    const gchar *string1 = v1;
+    const gchar *string2 = v2;
+
+    return EQUAL(string1, string2);
+}
+
+/************************************************************************/
+/*                           gvpk_str_hash()                            */
+/*                                                                      */
+/*      31bit hash (like g_str_hash()) except that it is case           */
+/*      insensitive.  All upper case letters are implicitly treated     */
+/*      as lower case.                                                  */
+/************************************************************************/
+
+static guint gvpk_str_hash (gconstpointer key)
+{
+    const char *p = key;
+    guint h = 0, v;
+
+    for (; *p != '\0'; p++)
+    {
+        v = tolower(*p);
+        h = (h << 5) - h + v;
+    }
+    
+    return h;
+}
+
+/************************************************************************/
+/*                       gvpk_keyid_from_string()                       */
+/************************************************************************/
+
+guint32 gvpk_keyid_from_string( const char *string )
+
+{
+    guint32 keyid;
+
+    if (gvpk_keyid_ht)
+        keyid = (guint32) g_hash_table_lookup (gvpk_keyid_ht, string);
+    else
+    {
+        gvpk_keyid_ht = g_hash_table_new (gvpk_str_hash, gvpk_str_equal);
+        keyid = 0;
+    }
+
+    /* Does key does exist yet? */
+    if (!keyid)
+    {
+        /* grow key id table if full */
+        if (gvpk_keyid_seq_id % G_QUARK_BLOCK_SIZE == 0)
+            gvpk_keyids = g_renew (gchar*, gvpk_keyids, 
+                                   gvpk_keyid_seq_id + G_QUARK_BLOCK_SIZE);
+
+        /* add keyid to key id table */
+        gvpk_keyids[gvpk_keyid_seq_id] = g_strdup(string);
+        gvpk_keyid_seq_id++;
+
+        /* Add key to hash table */
+        keyid = gvpk_keyid_seq_id;
+        g_hash_table_insert (gvpk_keyid_ht, gvpk_keyids[keyid-1], 
+                             GUINT_TO_POINTER (keyid));
+    }
+
+    return keyid;        
+}
+
+
+/************************************************************************/
+/*                         gv_properties_set()                          */
+/*                                                                      */
+/*      Set a single name/value property set in the list.               */
+/************************************************************************/
+
+void gv_properties_set( GvProperties *properties, 
+                        const char * name, const char * value )
+
+{
+    guint keyid  = gvpk_keyid_from_string( name );
+    GQuark valueq = g_quark_from_string( value );
+    int   i;
+    
+/* -------------------------------------------------------------------- */
+/*      Initial allocation of properties.                               */
+/* -------------------------------------------------------------------- */
+    if( *properties == NULL )
+    {
+        *properties = g_new( guint32, 8 );
+        PROP_MAXCOUNT(properties) = 3;
+        PROP_COUNT(properties) = 0;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Does the key already exist in the properties list?  If so,      */
+/*      just reset the value.                                           */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < PROP_COUNT(properties); i++ )
+    {
+        if( PROP_KEYID(properties,i) == keyid )
+        {
+            PROP_VALUEID(properties,i) = (guint32) valueq;
+            return;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We need to add the name/value pair to the list.  Does the       */
+/*      allocation of the list need to be grown?                        */
+/* -------------------------------------------------------------------- */
+    if( PROP_MAXCOUNT(properties) == PROP_COUNT(properties) )
+    {
+        int new_max = PROP_MAXCOUNT(properties) * 2;
+        
+        *properties = g_renew( guint32, *properties, new_max * 2 + 2 );
+        PROP_MAXCOUNT(properties) = new_max;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Add the new name/value pair.                                    */
+/* -------------------------------------------------------------------- */
+    i = PROP_COUNT(properties);
+
+    PROP_COUNT(properties)++;
+
+    PROP_KEYID(properties,i) = keyid;
+    PROP_VALUEID(properties,i) = (guint32) valueq;
+}
+
+/************************************************************************/
+/*                         gv_properties_get()                          */
+/*                                                                      */
+/*      Fetch a single property or return NULL on failure.              */
+/************************************************************************/
+
+const char * gv_properties_get( GvProperties *properties, const char * name )
+
+{
+    guint32 keyid = gvpk_keyid_from_string( name );
+    int i;
+
+    if( *properties == NULL )
+        return NULL;
+
+    for( i = 0; i < PROP_COUNT(properties); i++ )
+    {
+        if( PROP_KEYID(properties,i) == keyid )
+            return g_quark_to_string( (GQuark) PROP_VALUEID(properties,i) );
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                        gv_properties_count()                         */
+/*                                                                      */
+/*      Return the number of name/value properties in this list.        */
+/************************************************************************/
+
+int gv_properties_count( GvProperties *properties )
+
+{
+    if( *properties == NULL )
+        return 0;
+    else
+        return PROP_COUNT(properties);
+}
+
+/************************************************************************/
+/*                  gv_properties_get_name_by_index()                   */
+/*                                                                      */
+/*      Fetch the propert name of the 'nth' entry in the property list. */
+/************************************************************************/
+
+const char * gv_properties_get_name_by_index( GvProperties * properties, 
+                                              int prop_index )
+
+{
+    guint32 keyid;
+
+    if( *properties == NULL )
+        return NULL;
+
+    if( prop_index < 0 || prop_index >= PROP_COUNT(properties) )
+        return NULL;
+
+    keyid = PROP_KEYID(properties,prop_index);
+    
+    g_assert( keyid >= 1 && keyid <= gvpk_keyid_seq_id );
+
+    return gvpk_keyids[keyid-1];
+}
+
+/************************************************************************/
+/*                  gv_properties_get_value_by_index()                  */
+/*                                                                      */
+/*      Fetch the value of the 'nth' entry in the property list.        */
+/************************************************************************/
+
+const char * gv_properties_get_value_by_index( GvProperties * properties, 
+                                               int prop_index )
+
+{
+    GQuark value_id;
+
+    if( *properties == NULL )
+        return NULL;
+
+    if( prop_index < 0 || prop_index >= PROP_COUNT(properties) )
+        return NULL;
+
+    value_id = (GQuark) PROP_VALUEID(properties,prop_index);
+
+    return g_quark_to_string( value_id );
+}
+
+/************************************************************************/
+/*                        g_properties_remove()                         */
+/*                                                                      */
+/*      Remove the indicated property, if is it present.                */
+/************************************************************************/
+
+void gv_properties_remove( GvProperties *properties, const char * key )
+
+{
+    guint32 keyid = gvpk_keyid_from_string( key );
+    int i;
+
+    if( *properties == NULL )
+        return;
+
+    for( i = 0; i < PROP_COUNT(properties); i++ )
+    {
+        if( PROP_KEYID(properties,i) == keyid )
+        {
+            int last_prop = PROP_COUNT(properties)-1;
+
+            PROP_KEYID(properties,i) = PROP_KEYID(properties,last_prop);
+            PROP_VALUEID(properties,i) = PROP_VALUEID(properties,last_prop);
+            PROP_COUNT(properties)--;
+            return;
+        }
+    }
+}
+
+/************************************************************************/
+/*                         gv_properties_init()                         */
+/*                                                                      */
+/*      Initialize a GvProperties value.                                */
+/************************************************************************/
+
+void gv_properties_init( GvProperties *properties )
+
+{
+    *properties = NULL;
+}
+
+/************************************************************************/
+/*                       gv_properties_destroy()                        */
+/*                                                                      */
+/*      Wipe whole properties list, recovering all allocation.          */
+/************************************************************************/
+void gv_properties_destroy( GvProperties *properties )
+
+{
+    if( *properties != NULL )
+    {
+        g_free( *properties );
+        *properties = NULL;
+    }
+}
+
+/************************************************************************/
+/*                        gv_properties_clear()                         */
+/*                                                                      */
+/*      Wipe all properties.                                            */
+/************************************************************************/
+
+void gv_properties_clear( GvProperties *properties )
+
+{
+    gv_properties_destroy( properties );
+}
+
+/************************************************************************/
+/*                         gv_properties_copy()                         */
+/*                                                                      */
+/*      Make an efficient copy of a properties list.  It is assumed     */
+/*      that the target GvProperties has not even been initialized.     */
+/************************************************************************/
+
+void gv_properties_copy( GvProperties *source, GvProperties *target )
+
+{
+    if( *source == NULL )
+    {
+        *target = NULL;
+        return;
+    }
+
+    *target = g_new( guint32, PROP_COUNT(source) * 2 + 2 );
+    memcpy( *target, *source, sizeof(guint32) * (PROP_COUNT(source)*2 + 2));
+    PROP_MAXCOUNT(target) = PROP_COUNT(target);
+}
+
+#endif /* notdef USE_QUARK_BASED_GVPROPERTIES */
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GvProperties                             */
+/*                                                                      */
+/*      CPL StringList based implementation.                            */
+/* ==================================================================== */
+/************************************************************************/
+
+#ifndef USE_HASH_BASED_GVPROPERTIES
+#include "cpl_string.h"
+
+void gv_properties_set( GvProperties *properties, 
+                        const char * name, const char * value )
+
+{
+    *properties = CSLSetNameValue( *properties, name, value );
+}
+
+const char * gv_properties_get( GvProperties *properties, const char * name )
+
+{
+    return CSLFetchNameValue( *properties, name );
+}
+
+int gv_properties_count( GvProperties *properties )
+
+{
+    return CSLCount( *properties );
+}
+
+const char * gv_properties_get_name_by_index( GvProperties * properties, 
+                                              int prop_index )
+
+{
+    static char *last_property = NULL;
+
+    if( last_property != NULL )
+        CPLFree( last_property );
+
+    last_property = NULL;
+    CPLParseNameValue( (*properties)[prop_index], &last_property );
+
+    return last_property;
+}
+
+const char * gv_properties_get_value_by_index( GvProperties * properties, 
+                                               int prop_index )
+
+{
+    return CPLParseNameValue( (*properties)[prop_index], NULL );
+}
+
+void gv_properties_remove( GvProperties *properties, const char * key )
+
+{
+    int   prop_index = CSLFindString( *properties, key );
+    char  **targets;
+
+    if( prop_index >= 0 )
+    {
+        *properties = CSLRemoveStrings( *properties, prop_index, 1, 
+                                        &targets );
+        CSLDestroy( targets );
+    }
+}
+
+void gv_properties_init( GvProperties *properties )
+
+{
+    *properties = NULL;
+}
+
+void gv_properties_destroy( GvProperties *properties )
+
+{
+    CSLDestroy( *properties );
+    *properties = NULL;
+}
+
+void gv_properties_clear( GvProperties *properties )
+
+{
+    gv_properties_destroy( properties );
+}
+
+void gv_properties_copy( GvProperties *source, GvProperties *target )
+
+{
+    *target = CSLDuplicate( *source );
+}
+#endif /* notdef USE_HASH_BASED_GVPROPERTIES */
+

Added: packages/openev/branches/upstream/current/gvproperties.h
===================================================================
--- packages/openev/branches/upstream/current/gvproperties.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvproperties.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * $Id: gvproperties.h,v 1.6 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Generic string properties list.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvproperties.h,v $
+ * Revision 1.6  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.5  2002/09/09 16:22:45  warmerda
+ * use int instead of gint to avoid glib.h dependency
+ *
+ * Revision 1.4  2002/07/24 18:06:26  warmerda
+ * reimplement properties using quarks
+ *
+ * Revision 1.3  2000/09/21 02:55:11  warmerda
+ * added gv_properties_clear
+ *
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_PROPERTIES_H__
+#define __GV_PROPERTIES_H__
+
+#define USE_HASH_BASED_GVPROPERTIES
+
+#ifdef USE_HASH_BASED_GVPROPERTIES
+#include <glib.h>
+  typedef guint32 *GvProperties;
+#else
+  typedef char **GvProperties;
+#endif
+
+void gv_properties_set( GvProperties *, const char *name, const char *value);
+const char * gv_properties_get( GvProperties *, const char *name );
+int  gv_properties_count( GvProperties * );
+const char *gv_properties_get_name_by_index( GvProperties *, int );
+const char *gv_properties_get_value_by_index( GvProperties *, int );
+void gv_properties_remove( GvProperties *, const char * );
+void gv_properties_init( GvProperties * );
+void gv_properties_copy( GvProperties *source, GvProperties *target );
+void gv_properties_destroy( GvProperties * );
+void gv_properties_clear( GvProperties * );
+
+#endif /*__GV_PROPERTIES_H__ */

Added: packages/openev/branches/upstream/current/gvraster.c
===================================================================
--- packages/openev/branches/upstream/current/gvraster.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvraster.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2605 @@
+/******************************************************************************
+ * $Id: gvraster.c,v 1.75 2004/09/20 13:15:35 pgs Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster data container. 
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvraster.c,v $
+ * Revision 1.75  2004/09/20 13:15:35  pgs
+ * added patch for isnan on win32
+ *
+ * Revision 1.74  2004/09/07 15:21:34  gmwalter
+ * Check for nan's as well as nodata
+ * when calculating scaling min/max
+ * values.
+ *
+ * Revision 1.73  2004/07/03 07:39:11  andrey_kiselev
+ * Grab double floats from the GDAL in gv_raster_get_sample().
+ *
+ * Revision 1.72  2004/01/22 19:57:10  andrey_kiselev
+ * Use gv_raster_get_nodata() function to fetch the NODATA value from the image
+ * using GDALGetRasterNoDataValue().
+ *
+ * Revision 1.71  2003/11/06 14:26:24  gmwalter
+ * Avoid mismatch between tiles being downsampled from higher-resolution
+ * tiles and tiles being loaded for the first time at lower resolution
+ * when overview downsampling method does not match openev's.
+ *
+ * Revision 1.70  2003/09/11 20:00:29  gmwalter
+ * Add ability to specify a preferred polynomial order for warping a raster,
+ * and add "safe mode" (only used if ATLANTIS_BUILD is defined).
+ *
+ * Revision 1.69  2003/06/25 14:45:07  gmwalter
+ * Fixed a bug in gv_georef_to_pixel in geotransform case (was ignoring
+ * rotational terms).
+ *
+ * Revision 1.68  2003/03/02 04:43:58  warmerda
+ * CInt32 and CFloat64 are complex too!
+ *
+ * Revision 1.67  2003/02/20 19:27:15  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.66  2003/02/07 20:06:50  andrey_kiselev
+ * Memory leaks fixed.
+ *
+ * Revision 1.65  2002/10/29 22:28:26  warmerda
+ * fill out tile in reads that are not full resolution
+ *
+ * Revision 1.64  2002/10/29 05:44:05  warmerda
+ * always flood image values to right edge and bottom of tile on load
+ *
+ * Revision 1.63  2002/10/08 22:56:18  warmerda
+ * Modified gv_raster_build_poly_transform() to back off using the highest
+ * possible order polynomial if the CRS_compute_georef_equations() call fails.
+ * This ensures that underdetermined sets of GCPs (ie. those with linear
+ * dependencies) can still produce useful polynomials, even if they are only
+ * 1st order.
+ *
+ * Revision 1.62  2002/02/15 22:10:09  warmerda
+ * ensure that setting zero gcps clear the poly transform
+ *
+ * Revision 1.61  2001/11/29 15:53:42  warmerda
+ * added autoscale_samples preference
+ *
+ * Revision 1.60  2001/11/28 19:18:29  warmerda
+ * Added set_gcps(), and get_gcps() methods on GvRaster, and the
+ * geotransform-changed signal generated when the gcps change.
+ *
+ * Revision 1.59  2001/10/17 16:22:38  warmerda
+ * added unhandled raster type check
+ *
+ * Revision 1.58  2001/10/16 18:50:06  warmerda
+ * now possible to pass sample set into autoscale
+ *
+ * Revision 1.57  2001/08/22 02:34:52  warmerda
+ * fixed failure to sort samples in some cases for autoscale
+ *
+ * Revision 1.56  2001/08/15 13:05:57  warmerda
+ * modified default autoscale std_dev to 2.5
+ *
+ * Revision 1.55  2001/08/14 17:03:24  warmerda
+ * added standard deviation autoscaling support
+ *
+ * Revision 1.54  2001/07/24 02:21:54  warmerda
+ * added 8bit phase averaging
+ *
+ * Revision 1.53  2001/07/13 22:15:36  warmerda
+ * added nodata aware averaging
+ *
+ * Revision 1.52  2001/04/02 18:10:46  warmerda
+ * expose gv_raster_autoscale() to python
+ *
+ * Revision 1.51  2001/01/08 17:47:23  warmerda
+ * fixed additional window edge conditions in gv_raster_tile_get_gdal
+ *
+ * Revision 1.50  2000/11/28 02:49:51  warmerda
+ * fixed edge handling bugs with rasters smaller than one tile
+ *
+ * Revision 1.49  2000/11/01 03:47:45  warmerda
+ * Fixed serious bug with memory corruption that is mostly likely to occur
+ * with large images.  See Bug 120968 on SourceForge.
+ *
+ * Revision 1.48  2000/09/27 19:18:50  warmerda
+ * Honour GvSMSample for real and complex images.
+ * Add GvRaster.sm field.  If set to GvSMSample always let GDAL do the
+ * decimation for faster loads.
+ *
+ * Revision 1.47  2000/08/25 20:06:34  warmerda
+ * Added support for GDAL bands with arbitrary overviews (ie. OGDI)
+ * Avoid having scaling min and max the same.
+ *
+ * Revision 1.46  2000/08/24 03:37:52  warmerda
+ * added PIXEL as a coordinate system
+ *
+ * Revision 1.45  2000/08/16 14:08:23  warmerda
+ * report data name, not file name
+ *
+ * Revision 1.44  2000/08/09 17:37:13  warmerda
+ * debug on finalize
+ *
+ * Revision 1.43  2000/08/02 19:17:30  warmerda
+ * added debug statement
+ *
+ * Revision 1.42  2000/07/27 20:33:12  warmerda
+ * set max lod to 7 instead of 4 for 4x4 textures
+ *
+ * Revision 1.41  2000/07/18 14:53:54  warmerda
+ * go directly to gdal in sample call, if full res raster not available
+ *
+ * Revision 1.40  2000/07/12 19:26:32  warmerda
+ * try to avoid using gcps if geotransform is set
+ *
+ * Revision 1.39  2000/07/10 14:27:53  warmerda
+ * use GRASS derived CRS code instead of Numerical Recipes gvgcpfit code
+ *
+ * Revision 1.38  2000/06/27 15:46:47  warmerda
+ * added gv_closest_gdal_lod to make optimal use of overviews
+ *
+ * Revision 1.37  2000/06/26 15:12:33  warmerda
+ * set name automatically
+ *
+ * Revision 1.36  2000/06/20 15:26:21  warmerda
+ * fixed more free/g_free problems
+ *
+ * Revision 1.35  2000/06/20 14:37:26  warmerda
+ * fixed free/g_free() problem
+ *
+ * Revision 1.34  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+
+
+#include <stdlib.h>
+#include <assert.h>
+#include "gvraster.h"
+#include "gvrastertypes.h"
+#include "gvrasteraverage.h"
+#include "gvmanager.h"
+#include "cpl_conv.h"
+
+#ifdef WIN32
+#include <float.h>   /* for isnan */
+#define isnan _isnan
+#endif
+
+#define USE_CRS
+#ifdef USE_CRS
+#  include "crs.h"
+#else
+#  include "gvgcpfit.h"
+#endif
+
+enum
+{
+    GEOTRANSFORM_CHANGED,
+    LAST_SIGNAL
+};
+
+typedef struct _GvRasterMemento
+{
+    GvDataMemento base;
+
+    int      x_off;
+    int      y_off;
+    int      width;
+    int      height;
+
+    void     *data;
+} GvRasterMemento;
+
+static void gv_raster_class_init(GvRasterClass *klass);
+static void gv_raster_init(GvRaster *raster);
+static void gv_raster_finalize(GtkObject *object);
+static gint gv_raster_build_poly_transform( GvRaster *raster );
+static gint gv_raster_build_poly_transformCL( GvRaster *raster, int poly_order );
+static void gv_raster_get_memento(GvData *raster, gpointer info, 
+                                  GvDataMemento **memento);
+static void gv_raster_set_memento(GvData *raster, GvDataMemento *memento);
+static void gv_raster_del_memento(GvData *raster, GvDataMemento *memento);
+
+static int gv_raster_check_poly_order( GvRaster *raster, int poly_order );
+
+static guint raster_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_raster_get_type(void)
+{
+    static GtkType raster_type = 0;
+
+    if (!raster_type)
+    {
+    static const GtkTypeInfo raster_info =
+    {
+        "GvRaster",
+        sizeof(GvRaster),
+        sizeof(GvRasterClass),
+        (GtkClassInitFunc) gv_raster_class_init,
+        (GtkObjectInitFunc) gv_raster_init,
+        /* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
+    };
+
+    raster_type = gtk_type_unique(gv_data_get_type(), &raster_info);
+    }
+    return raster_type;
+}
+
+static void
+gv_raster_init(GvRaster *raster)
+{
+    raster->poly_order = -1;
+    raster->poly_pixel_coeff = NULL;
+    raster->poly_line_coeff = NULL;
+    raster->poly_x_coeff = NULL;
+    raster->poly_y_coeff = NULL;
+    raster->poly_z_coeff = NULL;
+
+    raster->gcp_count = 0;
+    raster->gcp_list = NULL;
+
+    raster->geotransform[0] = 0.0;
+    raster->geotransform[1] = 1.0;
+    raster->geotransform[2] = 0.0;
+    raster->geotransform[3] = 0.0;
+    raster->geotransform[4] = 0.0;
+    raster->geotransform[5] = 1.0;
+
+    /* Linking/cursor specific transform (special case- default off) */
+    raster->poly_orderCL = -1;
+    raster->poly_pixel_coeffCL = NULL;
+    raster->poly_line_coeffCL = NULL;
+    raster->poly_x_coeffCL = NULL;
+    raster->poly_y_coeffCL = NULL;
+    raster->poly_z_coeffCL = NULL;
+    raster->gcp_countCL = 0;
+    raster->gcp_listCL = NULL;
+
+}
+
+static void
+gv_raster_class_init(GvRasterClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    raster_signals[GEOTRANSFORM_CHANGED] =
+    gtk_signal_new ("geotransform-changed",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET(GvRasterClass,
+                                          geotransform_changed),
+            gtk_marshal_NONE__INT,
+            GTK_TYPE_NONE, 0);
+
+    gtk_object_class_add_signals(object_class, raster_signals,
+                 LAST_SIGNAL);
+
+    object_class->finalize = gv_raster_finalize;
+
+    data_class = (GvDataClass *) klass;
+    data_class->get_memento = gv_raster_get_memento;
+    data_class->set_memento = gv_raster_set_memento;
+    data_class->del_memento = gv_raster_del_memento;
+
+}
+
+static void
+gv_raster_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvRaster    *raster = GV_RASTER(object);
+    
+    CPLDebug( "OpenEV", "gv_raster_finalize(%s)\n", 
+              gv_data_get_name(GV_DATA(object)) );
+
+    if( raster->cache != NULL )
+        gv_raster_cache_free( raster->cache );
+
+    if( GDALDereferenceDataset( raster->dataset ) < 1 )
+        GDALClose( raster->dataset );
+
+    if( raster->poly_order > -1 )
+    {
+        g_free( raster->poly_pixel_coeff );
+        g_free( raster->poly_line_coeff );
+        g_free( raster->poly_x_coeff );
+        g_free( raster->poly_y_coeff );
+        g_free( raster->poly_z_coeff );
+    }
+
+    if( raster->poly_orderCL > -1 )
+    {
+        g_free( raster->poly_pixel_coeffCL );
+        g_free( raster->poly_line_coeffCL );
+        g_free( raster->poly_x_coeffCL );
+        g_free( raster->poly_y_coeffCL );
+        g_free( raster->poly_z_coeffCL );
+    }
+
+    if( raster->gcp_count > 0 )
+    {
+        GDALDeinitGCPs( raster->gcp_count, raster->gcp_list );
+        CPLFree( raster->gcp_list );
+    }
+
+    if( raster->gcp_countCL > 0 )
+    {
+        GDALDeinitGCPs( raster->gcp_countCL, raster->gcp_listCL );
+        CPLFree( raster->gcp_listCL );
+    }       
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}
+
+
+static void gv_raster_changed( GvRaster *raster,
+                               void * raw_change_info )
+
+{
+    GvRasterChangeInfo *change_info = (GvRasterChangeInfo *) raw_change_info;
+
+    if( change_info != NULL )
+        gv_raster_flush_cache( raster, 
+                               change_info->x_off, change_info->y_off,
+                               change_info->width, change_info->height );
+    else
+        gv_raster_flush_cache( raster, 0, 0, -1, -1 );
+}
+    
+GvData *
+gv_raster_new( GDALDatasetH dataset, int real_band, 
+               GvSampleMethod sm )
+{
+    char     *name;
+    GvRaster *raster = GV_RASTER(gtk_type_new(gv_raster_get_type()));
+
+    gtk_signal_connect( GTK_OBJECT(raster), "changed",
+            gv_raster_changed, NULL );
+
+    raster->dataset = dataset;
+    GDALReferenceDataset( dataset );
+
+    raster->gdal_band = GDALGetRasterBand(dataset,real_band);
+
+    raster->sm = sm;
+
+    /* set the name */
+    name = (char *) g_malloc(strlen(GDALGetDescription(dataset))+8);
+    sprintf( name, "%s:%d", GDALGetDescription(dataset), real_band );
+    gv_data_set_name( GV_DATA(raster), name );
+    g_free( name );
+
+    switch( GDALGetRasterDataType(raster->gdal_band) )
+    {
+    case GDT_Byte:
+          if( sm == GvSMAverage )
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_byte_real_average;
+          else if( sm == GvSMAverage8bitPhase )
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_byte_realphase_average;
+          else
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_byte_real_sample;
+          raster->type = GV_RASTER_BYTE_REAL;
+          raster->gdal_type = GDT_Byte;
+          break;
+
+        case GDT_CInt16:
+        case GDT_CInt32:
+        case GDT_CFloat32:
+        case GDT_CFloat64:
+          if( sm == GvSMAverage )
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_float_complex_average;
+          else
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_float_complex_sample;
+          raster->type = GV_RASTER_FLOAT_COMPLEX;
+          raster->gdal_type = GDT_CFloat32;
+          break;
+          
+        default:
+          if( sm == GvSMAverage && gv_raster_get_nodata( raster, NULL ) )
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_float_real_average_nodata;
+          else if( sm == GvSMAverage )
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_float_real_average;
+          else
+              raster->average = (void *(*)(GvRaster*,void*,int,int))
+                  gv_raster_float_real_sample;
+          raster->type = GV_RASTER_FLOAT_REAL;
+          raster->gdal_type = GDT_Float32;
+          break;
+    }
+
+    if( raster->type == GV_RASTER_BYTE_REAL 
+        || !gv_raster_autoscale(raster,GvASAAutomatic,-1.0,0,NULL,NULL,NULL) )
+    {
+        raster->min = 0;
+        raster->max = 255;
+    }
+
+    raster->tile_x = 256;
+    raster->tile_y = 256;
+    raster->width = GDALGetRasterXSize(dataset);
+    raster->height = GDALGetRasterYSize(dataset);
+    raster->tiles_across = (raster->width + raster->tile_x-GV_TILE_OVERLAP-1)
+        / (raster->tile_x-GV_TILE_OVERLAP);
+    raster->tiles_down = (raster->height + raster->tile_y-GV_TILE_OVERLAP-1)
+        / (raster->tile_y-GV_TILE_OVERLAP);
+    raster->max_lod = 7;
+    raster->item_size = GDALGetDataTypeSize(raster->gdal_type) / 8;
+    
+    if( GDALGetGeoTransform(dataset, raster->geotransform) != CE_None )
+    {
+        raster->geotransform[0] = 0.0;
+        raster->geotransform[1] = 1.0;
+        raster->geotransform[2] = 0.0;
+        raster->geotransform[3] = 0.0;
+        raster->geotransform[4] = 0.0;
+        raster->geotransform[5] = 1.0;
+    }
+
+    if( GDALGetGCPCount(dataset) > 0
+        && raster->geotransform[0] == 0.0
+        && raster->geotransform[1] == 1.0
+        && raster->geotransform[2] == 0.0
+        && raster->geotransform[3] == 0.0
+        && raster->geotransform[4] == 0.0
+        && raster->geotransform[5] == 1.0 )
+    {
+        gv_data_set_projection( GV_DATA(raster), 
+                                GDALGetGCPProjection( dataset ) );
+
+        gv_raster_set_gcps( raster, 
+                            GDALGetGCPCount(dataset), 
+                            GDALGetGCPs(dataset) );
+    }
+    else
+    {
+        if( EQUAL(GDALGetProjectionRef( dataset ),"") 
+            && raster->geotransform[0] == 0.0
+            && raster->geotransform[1] == 1.0
+            && raster->geotransform[2] == 0.0
+            && raster->geotransform[3] == 0.0
+            && raster->geotransform[4] == 0.0
+            && raster->geotransform[5] == 1.0 )
+        {
+            gv_data_set_projection( GV_DATA(raster), "PIXEL" );
+        }
+        else
+        {
+            gv_data_set_projection( GV_DATA(raster), 
+                                    GDALGetProjectionRef( dataset ) );
+        }
+    }
+
+    raster->max_tiles = raster->tiles_across * raster->tiles_down;
+    if( ( raster->cache = gv_raster_cache_new( raster->max_tiles,
+                                               raster->max_lod ) ) == NULL )
+    {
+    g_free( raster );
+    return NULL;
+    }
+
+    return GV_DATA(raster);
+}
+
+gint *
+gv_raster_tile_xy_get( GvRaster *raster, gint tile, gint lod, gint *coords )
+{
+    gint tile_in_x, tile_in_y;
+
+    if( coords == NULL )
+    {
+    if( ( coords = g_new( int, 4 ) ) == NULL )
+    {
+        return NULL;
+    }
+    }
+
+    tile_in_x = tile % raster->tiles_across;
+
+    tile_in_y = tile / raster->tiles_across;
+
+    coords[0] = tile_in_x * (raster->tile_x - GV_TILE_OVERLAP) 
+        - GV_TILE_OVERLAP/2;
+    coords[1] = tile_in_y * (raster->tile_y - GV_TILE_OVERLAP)
+        - GV_TILE_OVERLAP/2;
+    coords[2] = coords[0] + raster->tile_x;
+    coords[3] = coords[1] + raster->tile_y;
+
+    return coords;
+}
+
+static int
+gv_raster_tile_get_gdal( GvRaster *raster, GDALRasterBandH band, gint *coords, 
+                         void *buffer, int buf_width, int buf_height, 
+                         int pixel_offset, int line_offset )
+    
+{
+    int    width, height;
+    int    size, factor, i;
+    int    win_xoff, win_yoff, win_xsize, win_ysize;
+    int    rd_buf_width, rd_buf_height, rd_xoff = 0, rd_yoff = 0;
+    unsigned char *buf_u;
+
+    width = coords[2] - coords[0];
+    height = coords[3] - coords[1];
+    size = buf_width * buf_height * raster->item_size;
+    factor = width / buf_width;
+
+    if( coords[0] + width > raster->width
+        || coords[1] + height > raster->height )
+    {
+        width = MIN(width,raster->width - coords[0] );
+        rd_buf_width = width / factor;
+        height = MIN(height,raster->height - coords[1] );
+        rd_buf_height = height / factor;
+    }
+    else
+    {
+        rd_buf_width = width / factor;
+        rd_buf_height = height / factor;
+    }
+
+    win_xoff = coords[0];
+    win_yoff = coords[1];
+    win_xsize = rd_buf_width * factor;
+    win_ysize = rd_buf_height * factor;
+
+    if( win_xsize == 0 || win_ysize == 0 )
+    {
+        /* We can't provide for the sliver requested, so return doing
+           nothing. */
+        return TRUE;
+    }
+
+    buf_u = (unsigned char *) buffer;
+
+    if( coords[0] < 0 )
+    {
+        assert( coords[0] == -1 );
+        win_xoff += 1;
+        win_xsize -= 1;
+        rd_buf_width -= 1;
+        buf_u += pixel_offset;
+        rd_xoff = 1;
+    }
+
+    if( coords[1] < 0 )
+    {
+        assert( coords[1] == -1 );
+        win_yoff += 1;
+        win_ysize -= 1;
+        rd_buf_height -= 1;
+        buf_u += line_offset;
+        rd_yoff = 1;
+    }
+
+#ifdef notdef    
+    CPLDebug( "OpenEV-RasterIO", 
+              "%s:%d (%d,%d %dx%d) to (%dx%d) of (%dx%d)", 
+              GDALGetDescription( raster->dataset ), 
+              -1, win_xoff, win_yoff, win_xsize, win_ysize, 
+              rd_buf_width, rd_buf_height, buf_width, buf_height ); 
+#endif
+    
+    GDALRasterIO( band, GF_Read, win_xoff, win_yoff, win_xsize, win_ysize,
+                  buf_u, rd_buf_width, rd_buf_height,
+                  raster->gdal_type, pixel_offset, line_offset );
+    buf_u = (unsigned char *) buffer;
+
+    /* do we need to set the left most pixel from one in? */
+    if( win_xoff == 0 && coords[0] == -1 )
+    {
+        for( i = 0; i < buf_height; i++ )
+        {
+            memcpy( buf_u + i*line_offset, 
+                    buf_u + i*line_offset + pixel_offset, 
+                    pixel_offset );
+        }
+    }
+
+    /* do we need to set the top scanline from the second? */
+    if( win_yoff == 0 && coords[1] == -1 )
+    {
+        memcpy( buf_u, buf_u + line_offset, line_offset );
+    }
+
+    /* do we need to set the right most real pixel + 1? */
+    if( rd_xoff + rd_buf_width < buf_width )
+    {
+        int     pixel_to_set;
+        unsigned char *u_buf_base = (unsigned char *) buffer;
+        
+        assert( rd_buf_width < buf_width );
+        pixel_to_set = rd_buf_width + rd_xoff;
+        assert( pixel_to_set >= 1 && pixel_to_set < buf_width );
+        
+        while( pixel_to_set < buf_width )
+        {
+            for( i = 0; i < buf_height; i++ )
+            {
+                assert( i >= 0 && i < raster->tile_y );
+                memcpy( u_buf_base + i*line_offset + pixel_to_set*pixel_offset,
+                        u_buf_base + i*line_offset + (pixel_to_set-1)*pixel_offset, 
+                        pixel_offset );
+            }
+            pixel_to_set++;
+        }
+    }
+
+    /* do we need to set the bottom most real pixel + 1? */
+    if( rd_yoff + rd_buf_height < buf_height )
+    {
+        int     line_to_set;
+        unsigned char *u_buf_base = (unsigned char *) buffer;
+
+        assert( buf_height != rd_buf_height );
+        line_to_set = rd_buf_height + rd_yoff;
+        assert( line_to_set >= 1 && line_to_set < buf_height );
+        assert( line_offset*factor == raster->item_size * raster->tile_x );
+
+        /* we will actually keep copying the line till we get to the bottom
+           of the buffer */
+        while( line_to_set < buf_height )
+        {
+            memcpy( u_buf_base + line_to_set*line_offset,
+                    u_buf_base + (line_to_set-1)*line_offset, 
+                    line_offset );
+            line_to_set++;
+        }
+    }
+
+    return TRUE;
+}
+
+/*
+ * Based on the available overviews, identify the closest appropriate
+ * lod available from the GDAL band. 
+ */
+
+static int gv_closest_gdal_lod( GDALRasterBandH band, int desired_lod )
+
+{
+    int  ov_index;
+    int  best_lod;
+    double best_factor = 1.0;
+    double desired_factor = (1 << desired_lod);
+
+#ifdef ATLANTIS_BUILD
+    if ((gv_manager_get_preference(gv_get_manager(),"safe_mode") != NULL) &&
+        (strcmp(gv_manager_get_preference(gv_get_manager(),"safe_mode"),"on") == 0))
+        return 0;
+#endif
+
+    if( GDALHasArbitraryOverviews( band ) )
+        return desired_lod;
+
+    for( ov_index = 0; ov_index < GDALGetOverviewCount( band ); ov_index++ )
+    {
+        GDALRasterBandH  oband = GDALGetOverview( band, ov_index );
+        double           ofactor;
+
+        ofactor = GDALGetRasterBandXSize( band ) / 
+            (double) GDALGetRasterBandXSize( oband );
+
+        /* We already found something better */
+        if( ofactor < best_factor )
+            continue;
+
+        /* We don't want to upsample by much! */
+        if( ofactor > desired_factor * 1.2 )
+            continue;
+
+        best_factor = ofactor;
+    }
+
+    best_lod = 0;
+    while( best_factor*0.8 > (1 << best_lod) )
+        best_lod++;
+
+    return best_lod;
+}
+
+void *
+gv_raster_tile_get( GvRaster *raster, gint tile, gint lod )
+{
+    gint *coords = NULL;
+    gint cur_lod;
+    gint i;
+    int need_to_free_buffer = 0;
+    void *buffer = NULL;
+    void *temp_buffer;
+    
+
+    if( raster->cache != NULL )
+    {
+    cur_lod = gv_raster_cache_get_best_lod( raster->cache, tile, lod );
+
+    if( cur_lod == lod )
+    {
+        return gv_raster_cache_get( raster->cache, tile, lod );
+    }
+        else if( cur_lod > lod || cur_lod == -1 )
+    {
+            int     width, height, pixel_offset, line_offset;
+            int     buf_width, buf_height;
+            int     size, factor;
+
+        coords = gv_raster_tile_xy_get( raster, tile, lod, coords );
+            
+            width = coords[2] - coords[0];
+            height = coords[3] - coords[1];
+
+            /* If sample mode is average, retrieve the closest resolution
+               overview tile from gdal and then average down as much as
+               necessary; otherwise (sample mode is decimate), let gdal do 
+               the downsampling (retrieve the tile from gdal using coordinates 
+               for cur_lod=lod). 
+            */
+
+            if( raster->sm == GvSMAverage )
+                cur_lod = gv_closest_gdal_lod( raster->gdal_band, lod );
+            else
+                cur_lod = lod;
+
+#ifdef ATLANTIS_BUILD
+            /* safe mode ignores overviews */
+            if ((gv_manager_get_preference(gv_get_manager(),"safe_mode") != NULL) &&
+                (strcmp(gv_manager_get_preference(gv_get_manager(),"safe_mode"),"on") == 0))
+            {
+                cur_lod = 0;
+            }
+#endif
+
+            factor = 1 << cur_lod;
+            buf_width = width / factor;
+            buf_height = height / factor;
+
+            size = buf_width * buf_height * raster->item_size;
+            buffer = g_malloc(size);
+            
+            pixel_offset = raster->item_size;
+            line_offset = buf_width * raster->item_size;
+
+            if( coords[0] + width > raster->width
+                || coords[1] + height > raster->height 
+                || coords[0] < 0 || coords[1] < 0 )
+                memset( buffer, 0, size );
+                
+            if( buffer != NULL )
+            {
+                if( !gv_raster_tile_get_gdal( raster, raster->gdal_band,
+                                              coords, 
+                                              buffer, buf_width, buf_height,
+                                              pixel_offset, line_offset ) )
+                {
+                    g_free( buffer );
+                    return NULL;
+                }
+            }
+        
+        if ( coords )
+        {
+        g_free( coords );
+        coords = NULL;
+        }
+            need_to_free_buffer = 1;
+
+    } else {
+            int     width, height, pixel_offset, line_offset;
+            int     buf_width, buf_height;
+            int     size, factor, gdal_lod;
+
+            /* When a tile is loaded, gdal returns overview values 
+               if present; otherwise it downsamples.  To avoid a
+               mismatch between tiles downsampled from higher
+               resolution tiles and tiles being loaded at lower
+               resolution from scratch, check for a gdal overview
+               close to this lod before downsampling the higher
+               resolution tile.  This code used to point buffer to
+               the higher-resolution tile which then got downsampled
+               in the for-loop below, but this caused display issues in
+               some cases (eg. if overviews were generated
+               using averaging, but openev's overview sampling is
+               set to decimate, then zooming out on an image with averaged
+               overviews will result in the area just zoomed out from
+               looking specklier than the surrounding areas
+               because the surroundings are loaded from the low-res averaged
+               overviews, but the area just zoomed out is decimated
+               from a high res averaged overview).  Now, this is changed so the
+               code first checks for a gdal overview that is better for 
+               downsampling than the current closest resolution cached
+               tile, but uses the cached tile if no better overview
+               is found.  This should hopefully avoid display 
+               inconsistencies, but still make use of the cached tile
+               if no overviews are present rather than downsampling
+               from scratch (otherwise this else could go entirely,
+               and the else if above could handle all cases where
+               cur_lod != lod).               
+            */
+            gdal_lod = gv_closest_gdal_lod( raster->gdal_band, lod );
+            if ((gdal_lod <= cur_lod) || (gdal_lod > lod))
+            {
+                /* No better overviews available to downsample */
+            buffer = gv_raster_cache_get( raster->cache, tile, cur_lod );
+            }
+            else
+            {
+                cur_lod=gdal_lod;
+                coords = gv_raster_tile_xy_get( raster, tile, lod, coords );
+            
+                width = coords[2] - coords[0];
+                height = coords[3] - coords[1];
+
+                if ((gv_manager_get_preference(gv_get_manager(),"safe_mode") != NULL) &&
+                (strcmp(gv_manager_get_preference(gv_get_manager(),"safe_mode"),"on") == 0))
+                {
+                    cur_lod = 0;
+                }
+
+                factor = 1 << cur_lod;
+                buf_width = width / factor;
+                buf_height = height / factor;
+
+                size = buf_width * buf_height * raster->item_size;
+                buffer = g_malloc(size);
+            
+                pixel_offset = raster->item_size;
+                line_offset = buf_width * raster->item_size;
+
+                if( coords[0] + width > raster->width
+                    || coords[1] + height > raster->height 
+                    || coords[0] < 0 || coords[1] < 0 )
+                    memset( buffer, 0, size );
+                
+                if( buffer != NULL )
+                {
+                    if( !gv_raster_tile_get_gdal( raster, raster->gdal_band,
+                                                  coords, 
+                                                  buffer, buf_width, buf_height,
+                                                  pixel_offset, line_offset ) )
+                    {
+                        g_free( buffer );
+                        return NULL;
+                    }
+                }
+        
+            if ( coords )
+            {
+            g_free( coords );
+                coords = NULL;
+            }
+                need_to_free_buffer = 1;
+            }
+    }
+
+    for( i = cur_lod; i < lod; i++ )
+    {
+        temp_buffer = raster->average( raster,
+                                           buffer, raster->tile_x >> i,
+                                           raster->tile_y >> i );
+
+        if( need_to_free_buffer )
+        {
+        g_free( buffer );
+        } else {
+        need_to_free_buffer = 1;
+        }
+
+        buffer = temp_buffer;
+    }
+
+        if( buffer != NULL )
+            gv_raster_cache_put( raster->cache, tile, lod, buffer,
+                                 (raster->item_size * ( raster->tile_x >> lod )
+                                  * ( raster->tile_y >> lod ) ) );
+    
+    return buffer;
+    }
+
+    return NULL;
+}
+
+void
+gv_raster_flush_cache( GvRaster *raster, 
+                       int x_off, int y_off, int width, int height )
+{
+    if( raster->gdal_band != NULL )
+        GDALFlushRasterCache( raster->gdal_band );
+
+    if( width < 1 || height < 1 )
+        gv_raster_cache_flush_all( raster->cache );
+    else
+    {
+        gint tile, lod;
+
+        for( tile = 0; tile < raster->cache->max_tiles; tile++ )
+        {
+            gint coords[4];
+
+            gv_raster_tile_xy_get( raster, tile, 0, coords );
+
+            if( x_off < coords[2] && y_off < coords[3] 
+                && x_off+width > coords[0] && y_off+height > coords[1] )
+            {
+                for( lod = 0; lod < raster->cache->max_lod; lod++ )
+                    gv_raster_cache_del( raster->cache, tile, lod );
+            }
+        }
+    }
+}
+
+/************************************************************************/
+/*                         gv_raster_get_nodata()                       */
+/************************************************************************/
+
+/**
+ * Queries NODATA value for the specified raster.
+ *
+ * @param raster Pointer to GvRaster object.
+ *
+ * @param nodata Pointer to the variable which should be filled with the
+ * NODATA value.
+ *
+ * @return TRUE if specified raster object has NODATA set and FALSE otherwise.
+ */
+
+gint
+gv_raster_get_nodata(GvRaster *raster, double *nodata )
+
+{
+    int nodata_set = FALSE;
+
+    g_return_val_if_fail( GV_IS_RASTER( raster ), FALSE );
+    g_return_val_if_fail( raster != NULL, FALSE);
+
+    if ( nodata )
+        *nodata = GDALGetRasterNoDataValue( raster->gdal_band, &nodata_set );
+    else
+        GDALGetRasterNoDataValue( raster->gdal_band, &nodata_set );
+
+    if( !nodata_set )
+        return FALSE;
+
+    return TRUE;
+}
+
+gint
+gv_raster_get_sample(GvRaster *raster, double x, double y, 
+                     double *real, double *imaginary )
+
+{
+    gint      pixel, line, tile_x_off, tile_y_off;
+    gint      tile, x_within_tile, y_within_tile;
+    void      *data;
+
+    pixel = (int) floor(x);
+    line = (int) floor(y);
+
+    if( pixel < 0 || line < 0 
+        || pixel >= raster->width || line >= raster->height )
+        return FALSE;
+
+    tile_x_off = pixel / (raster->tile_x-GV_TILE_OVERLAP);
+    tile_y_off = line / (raster->tile_y-GV_TILE_OVERLAP);
+    x_within_tile = pixel 
+        - (tile_x_off * (raster->tile_x-GV_TILE_OVERLAP) - GV_TILE_OVERLAP/2);
+    y_within_tile = line 
+        - (tile_y_off * (raster->tile_y-GV_TILE_OVERLAP) - GV_TILE_OVERLAP/2);
+
+    tile = tile_x_off + tile_y_off * raster->tiles_across;
+    if( gv_raster_cache_get_best_lod( raster->cache, tile, 0 ) == 0 )
+    {
+        data = gv_raster_tile_get( raster, tile, 0 );
+
+        data = ((unsigned char *) data) 
+       + (x_within_tile + y_within_tile * raster->tile_x) * raster->item_size;
+
+        switch( raster->type )
+        {
+          case GV_RASTER_BYTE_REAL:
+            *real = ((unsigned char *) data)[0];
+            *imaginary = 0.0;
+            break;
+
+          case GV_RASTER_BYTE_COMPLEX:
+            *real = ((unsigned char *) data)[0];
+            *imaginary = ((unsigned char *) data)[1];
+            break;
+
+          case GV_RASTER_FLOAT_REAL:
+            *real = ((float *) data)[0];
+            *imaginary = 0.0;
+            break;
+
+          case GV_RASTER_FLOAT_COMPLEX:
+            *real = ((float *) data)[0];
+            *imaginary = ((float *) data)[1];
+            break;
+
+          default:
+            printf( "Unsupported raster type in gv_raster_get_sample().\n" );
+            return FALSE;
+        }
+    }
+    else
+    {
+        /* use GDAL directly to get value, on assumption it can do a single
+           pixel more efficiently than a whole GvRaster tile */
+
+        double   value[2];
+
+        GDALRasterIO( raster->gdal_band, GF_Read, pixel, line, 1, 1, 
+                      value, 1, 1, GDT_CFloat64, 0, 0 );
+
+        *real = value[0];
+        *imaginary = value[1];
+    }
+
+    return TRUE;
+}
+
+static int 
+gv_float_compare( const void *float1, const void *float2 )
+{
+    const float *f1 = (float *) float1;
+    const float *f2 = (float *) float2;
+
+    if( *f1 < *f2 )
+        return -1;
+    else if( *f1 > *f2 )
+        return 1;
+    else
+        return 0;
+}
+
+gint
+gv_raster_autoscale( GvRaster *raster, GvAutoScaleAlg alg, double alg_param,
+                     int sample_count, float *sample_set,
+                     double *min_out, double *max_out )
+
+
+{
+    int        local_samples = FALSE;
+    int        tail_size, i, sorted = FALSE;
+    double     no_data, raster_min=0.0, raster_max=255.0;
+
+/* -------------------------------------------------------------------- */
+/*      Collect a set of sample points.                                 */
+/* -------------------------------------------------------------------- */
+    if( sample_set == NULL )
+    {
+        local_samples = TRUE;
+        if( gv_manager_get_preference(gv_get_manager(),"autoscale_samples") )
+        {
+            sample_count = atoi(
+              gv_manager_get_preference(gv_get_manager(),"autoscale_samples"));
+            sample_count = MAX(10,sample_count);
+        }
+        else
+            sample_count = 10000;
+
+        sample_set = g_new(float,sample_count);
+        sample_count = GDALGetRandomRasterSample(raster->gdal_band, 
+                                                 sample_count, sample_set );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Strip out any nodata values found.                              */
+/* -------------------------------------------------------------------- */
+    if( gv_raster_get_nodata( raster, &no_data ) )
+    {
+        int j = 0;
+
+        for( i = 0; i < sample_count; i++ )
+        {
+            if( ( sample_set[i] != (float)no_data ) &&
+                !(isnan(sample_set[i])) )
+                sample_set[j++] = sample_set[i];
+        }
+
+        sample_count = j;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we didn't find a nodata value, try stripping away the        */
+/*      most extreme value after sorting.  This should let us get       */
+/*      rid of stuff that is likely a nodata value, but in a            */
+/*      classified image will unfortunately get rid of the lowest       */
+/*      and highest classes.                                            */
+/* -------------------------------------------------------------------- */
+    else if( sample_count > 2 )
+    {
+        int j = 0;
+
+        for( i = 0; i < sample_count; i++ )
+        {
+            if ( !(isnan(sample_set[i])) )
+                sample_set[j++] = sample_set[i];
+        }
+
+        sample_count = j;
+
+        qsort( sample_set, sample_count, sizeof(float), gv_float_compare );
+        sorted = TRUE;
+
+        if( sample_set[0] != sample_set[sample_count-1] )
+        {
+            for( i = 1; 
+                 i < sample_count && sample_set[i] == sample_set[0]; 
+                 i++ ) {}
+
+            if( i > 1 )
+            {
+                memmove( sample_set, sample_set + i, 
+                         sizeof(float) * (sample_count - i) );
+                sample_count -= i;
+            }
+        }
+
+        if( sample_set[0] != sample_set[sample_count-1] )
+        {
+            for( i = 1; 
+                 i < sample_count 
+                     && sample_set[sample_count-i-1] == sample_set[sample_count-1]; 
+                 i++ ) {}
+
+            if( i > 1 )
+                sample_count -= i;
+        }
+    }
+
+    if( sample_count < 2 )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Determine the algorithm to use.                                 */
+/* -------------------------------------------------------------------- */
+    if( alg == GvASAAutomatic )
+    {
+        const char *alg_str;
+
+        alg_str= gv_manager_get_preference(gv_get_manager(),"scale_algorithm");
+
+        if( alg_str != NULL && EQUAL(alg_str,"percent_tail_trim") )
+        {
+            alg = GvASAPercentTailTrim;
+            alg_param = -1.0;
+        }
+        else if( alg_str != NULL && EQUAL(alg_str,"std_deviation") )
+        {
+            alg = GvASAStdDeviation;
+            alg_param = -1.0;
+        }
+        else
+        {
+#ifdef ATLANTIS_BUILD
+            alg = GvASAStdDeviation;
+#else
+            alg = GvASAPercentTailTrim;
+#endif
+            alg_param = -1.0;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Determine the parameter value to use.  A -1 means use what's    */
+/*      in the preferences.                                             */
+/* -------------------------------------------------------------------- */
+    if( alg_param < -0.9 && alg == GvASAStdDeviation )
+    {
+        const char *alg_param_str;
+        
+        alg_param = 2.5;
+        alg_param_str = 
+            gv_manager_get_preference(gv_get_manager(),
+                                      "scale_std_deviations");
+        if( alg_param_str != NULL )
+            alg_param = atof(alg_param_str);
+    }
+    else if( alg_param < -0.9 && alg == GvASAPercentTailTrim )
+    {
+        const char *alg_param_str;
+        
+        alg = GvASAPercentTailTrim;
+        alg_param = 0.02;
+        alg_param_str = 
+            gv_manager_get_preference(gv_get_manager(),"scale_percent_tail");
+        if( alg_param_str != NULL )
+            alg_param = atof(alg_param_str);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Implementation for percent tail trim method.                    */
+/* -------------------------------------------------------------------- */
+    if( alg == GvASAPercentTailTrim )
+    {
+        if( !sorted )
+        {
+            qsort( sample_set, sample_count, sizeof(float), gv_float_compare );
+            sorted = TRUE;
+        }
+        
+        if( alg_param > 0.5 )
+            alg_param = alg_param / 100.0;
+
+        tail_size = (int) (alg_param * sample_count);
+        
+        raster_min = sample_set[tail_size];
+        raster_max = sample_set[sample_count - tail_size - 1];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Implement standard deviation method.                            */
+/* -------------------------------------------------------------------- */
+    else if( alg == GvASAStdDeviation )
+    {
+        double  sum = 0.0, sum_squares = 0.0, mean, std_dev;
+
+        /* compute the std deviation & mean */
+
+        for( i = 0; i < sample_count; i++ )
+            sum += sample_set[i];
+
+        mean = sum / sample_count;
+
+        for( i = 0; i < sample_count; i++ )
+            sum_squares += (mean - sample_set[i]) * (mean - sample_set[i]);
+
+        std_dev = sqrt(sum_squares / (sample_count - 1));
+
+        raster_min = mean - std_dev * alg_param;
+        raster_max = mean + std_dev * alg_param;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Ensure we don't end up with a degenerate range.                 */
+/* -------------------------------------------------------------------- */
+    if( raster_min == raster_max )
+    {
+        raster_min = raster_min - 0.5;
+        raster_max = raster_max + 0.5;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Return results or assign directly to raster.                    */
+/* -------------------------------------------------------------------- */
+    if( min_out != NULL && max_out != NULL )
+    {
+        *min_out = raster_min;
+        *max_out = raster_max;
+    }
+    else
+    {
+        raster->min = raster_min;
+        raster->max = raster_max;
+    }
+
+    if( local_samples )
+        g_free( sample_set );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                  gv_raster_collect_random_sample()                   */
+/*                                                                      */
+/*      Collect random samples from available loaded tiles.             */
+/************************************************************************/
+
+int  gv_raster_collect_random_sample( GvRaster *raster, 
+                                      int sample_count, float *sample_set,
+                                      int xoff, int yoff, int xsize, int ysize,
+                                      GArray *tile_list )
+
+{
+    int     result, tile, as_count, itile, cs_count;
+    float       sampling_rate;
+
+    if( xoff >= raster->width || yoff >= raster->height
+        || xoff + xsize < 0 || yoff + ysize < 0 )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      If we don't have a tile list, create one locally from the       */
+/*      bounding rectangle.                                             */
+/* -------------------------------------------------------------------- */
+    if( tile_list == NULL )
+    {
+        tile_list = g_array_new( FALSE, FALSE, sizeof(int) );
+
+        for( tile = 0; tile < raster->max_tiles; tile++ )
+        {
+            gint  coords[4];
+            
+            gv_raster_tile_xy_get( raster, tile, 0, coords );
+            if( coords[0] > xoff + xsize || coords[2] < xoff
+                || coords[1] > yoff + ysize || coords[3] < yoff )
+                continue;
+
+            g_array_append_val( tile_list, tile );
+        }
+
+        result = gv_raster_collect_random_sample( 
+            raster, sample_count, sample_set, 
+            xoff, yoff, xsize, ysize, 
+            tile_list );
+
+        g_array_free( tile_list, TRUE );
+
+        return result;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Count available values so we can work out a sampling rate.      */
+/* -------------------------------------------------------------------- */
+    as_count = 0;
+    for( itile = 0; itile < tile_list->len; itile++ )
+    {
+        int lod, lod_factor;
+        gint    coords[4];
+
+        /* do we have this tile? Pick largested avail tile */
+
+        tile = ((int *) tile_list->data)[itile];
+        if( tile == -1 )
+            continue;
+
+        for( lod = 0; lod < raster->max_lod; lod++ )
+        {
+            if( raster->cache->tiles[lod][tile] != NULL )
+                break;
+        }
+
+        if( lod == raster->max_lod )
+            continue;
+
+        /* compute the applicable region in full res */
+        gv_raster_tile_xy_get( raster, tile, lod, coords );
+
+        if( xoff > coords[2] || yoff > coords[3] 
+            || xoff + xsize < coords[0] || yoff + ysize < coords[1] )
+            continue;
+        
+        if( xoff > coords[0] )
+            coords[0] = xoff; 
+        if( yoff > coords[1] )
+            coords[1] = yoff;
+        if( xoff+xsize < coords[2] )
+            coords[2] = xoff+xsize;
+        if( yoff+ysize < coords[3] )
+            coords[3] = yoff+ysize;
+
+        if( lod > 0 )
+            lod_factor = 2 << (lod-1);
+        else
+            lod_factor = 1;
+
+        as_count += (((coords[2] - coords[0]) / lod_factor)
+                   * ((coords[3] - coords[1]) / lod_factor));
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute sampling rate.                                          */
+/* -------------------------------------------------------------------- */
+    if( as_count <= sample_count )
+        sampling_rate = 1;
+    else
+        sampling_rate = as_count / (double) sample_count;
+
+/* -------------------------------------------------------------------- */
+/*      collect samples from tiles.                                     */
+/* -------------------------------------------------------------------- */
+    cs_count = 0;
+
+    for( itile = 0; itile < tile_list->len; itile++ )
+    {
+        int lod, lod_factor, i, x, y, lod_xsize, lod_ysize;
+        gint    coords[4];
+        float   fi;
+        void    *data = NULL;
+
+        /* do we have this tile? Pick largested avail tile */
+
+        tile = ((int *) tile_list->data)[itile];
+        if( tile == -1 )
+            continue;
+
+        for( lod = 0; lod < raster->max_lod; lod++ )
+        {
+            if( raster->cache->tiles[lod][tile] != NULL )
+            {
+                data = raster->cache->tiles[lod][tile]->data;
+                break;
+            }
+        }
+
+        if( data == NULL )
+            continue;
+
+        if( lod > 0 )
+            lod_factor = 2 << (lod-1);
+        else
+            lod_factor = 1;
+
+        /* compute the applicable region in full res */
+        gv_raster_tile_xy_get( raster, tile, lod, coords );
+
+        if( xoff > coords[2] || yoff > coords[3] 
+            || xoff + xsize < coords[0] || yoff + ysize < coords[1] )
+            continue;
+
+        as_count = (((coords[2] - coords[0]) / lod_factor)
+                   * ((coords[3] - coords[1]) / lod_factor));
+
+        lod_xsize = (coords[2] - coords[0]) / lod_factor;
+        lod_ysize = (coords[2] - coords[0]) / lod_factor;
+
+        for( fi = 0.5; fi < as_count; fi += sampling_rate )
+        {
+            i = (int) fi;
+
+            x = coords[0] + (i % lod_xsize) * lod_factor;
+            y = coords[1] + (i / lod_xsize) * lod_factor;
+
+            if( x < xoff || x >= xoff+xsize || y < yoff || y >= yoff+ysize )
+                continue;
+            
+            if( cs_count == sample_count )
+                continue;
+
+            if( raster->type == GV_RASTER_BYTE_REAL )
+                sample_set[cs_count++] = ((unsigned char *) data)[i];
+            else if( raster->type == GV_RASTER_FLOAT_COMPLEX )
+            {
+                float   real, imag;
+
+                real = ((float *) data)[i*2];
+                imag = ((float *) data)[i*2+1];
+                sample_set[cs_count++] = sqrt(real*real + imag*imag);
+            }
+            else if( raster->type == GV_RASTER_FLOAT_REAL )
+                sample_set[cs_count++] = ((float *) data)[i];
+        }
+    }
+
+    return cs_count;
+}
+
+/************************************************************************/
+/*                    gv_raster_collect_histogram()                     */
+/*                                                                      */
+/*      Collect histogram information from available loaded tiles.      */
+/************************************************************************/
+
+int  gv_raster_collect_histogram( GvRaster *raster, 
+                                  double scale_min, double scale_max, 
+                                  int bucket_count, int *histogram,
+                                  int include_out_of_range,
+                                  int xoff, int yoff, int xsize, int ysize,
+                                  GArray *tile_list )
+
+{
+    int     tile, itile, cs_count, tile_values, i;
+    float   scale_coef;
+
+    if( xoff >= raster->width || yoff >= raster->height
+        || xoff + xsize < 0 || yoff + ysize < 0 )
+        return 0;
+
+    if( (scale_max - scale_min) == 0.0 )
+        return 0;
+
+    scale_coef = bucket_count / (scale_max - scale_min);
+
+/* -------------------------------------------------------------------- */
+/*      If we don't have a tile list, create one locally from the       */
+/*      bounding rectangle.                                             */
+/* -------------------------------------------------------------------- */
+    if( tile_list == NULL )
+    {
+        int result;
+
+        tile_list = g_array_new( FALSE, FALSE, sizeof(int) );
+
+        for( tile = 0; tile < raster->max_tiles; tile++ )
+        {
+            gint  coords[4];
+            
+            gv_raster_tile_xy_get( raster, tile, 0, coords );
+            if( coords[0] > xoff + xsize || coords[2] < xoff
+                || coords[1] > yoff + ysize || coords[3] < yoff )
+                continue;
+
+            g_array_append_val( tile_list, tile );
+        }
+
+        result = gv_raster_collect_histogram( 
+            raster, scale_min, scale_max, bucket_count, histogram, 
+            include_out_of_range,
+            xoff, yoff, xsize, ysize, 
+            tile_list );
+
+        g_array_free( tile_list, TRUE );
+
+        return result;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Zero histogram.                                                 */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < bucket_count; i++ )
+        histogram[i] = 0;
+
+/* -------------------------------------------------------------------- */
+/*      collect samples from tiles.                                     */
+/* -------------------------------------------------------------------- */
+    cs_count = 0;
+
+    for( itile = 0; itile < tile_list->len; itile++ )
+    {
+        int lod, lod_factor, x, y, lod_xsize, lod_ysize;
+        gint    coords[4];
+        void    *data = NULL;
+
+        /* do we have this tile? Pick largested avail tile */
+
+        tile = ((int *) tile_list->data)[itile];
+        if( tile == -1 )
+            continue;
+
+        for( lod = 0; lod < raster->max_lod; lod++ )
+        {
+            if( raster->cache->tiles[lod][tile] != NULL )
+            {
+                data = raster->cache->tiles[lod][tile]->data;
+                break;
+            }
+        }
+
+        if( data == NULL )
+            continue;
+
+        if( lod > 0 )
+            lod_factor = 2 << (lod-1);
+        else
+            lod_factor = 1;
+
+        /* compute the applicable region in full res */
+        gv_raster_tile_xy_get( raster, tile, lod, coords );
+
+        if( xoff > coords[2] || yoff > coords[3] 
+            || xoff + xsize < coords[0] || yoff + ysize < coords[1] )
+            continue;
+
+        lod_xsize = (coords[2] - coords[0]) / lod_factor;
+        lod_ysize = (coords[2] - coords[0]) / lod_factor;
+        tile_values = lod_xsize * lod_ysize;
+
+        for( i = 0; i < tile_values; i++ )
+        {
+            float   value;
+
+            x = coords[0] + (i % lod_xsize) * lod_factor;
+            y = coords[1] + (i / lod_xsize) * lod_factor;
+
+            if( x < xoff || x >= xoff+xsize || y < yoff || y >= yoff+ysize )
+                continue;
+            
+            if( raster->type == GV_RASTER_BYTE_REAL )
+                value = ((unsigned char *) data)[i];
+            else if( raster->type == GV_RASTER_FLOAT_COMPLEX )
+            {
+                float   real, imag;
+
+                real = ((float *) data)[i*2];
+                imag = ((float *) data)[i*2+1];
+                value = sqrt(real*real + imag*imag);
+            }
+            else if( raster->type == GV_RASTER_FLOAT_REAL )
+                value = ((float *) data)[i];
+            else
+            {
+                assert( FALSE );
+                value = 0.0;
+            }
+
+            if( value < scale_min )
+            {
+                if( include_out_of_range )
+                {
+                    histogram[0]++;
+                    cs_count++;
+                }
+            }
+            else if( value > scale_max )
+            {
+                if( include_out_of_range )
+                {
+                    histogram[bucket_count-1]++;
+                    cs_count++;
+                }
+            }
+            else
+            {
+                int bucket;
+
+                bucket = (int) ((value - scale_min) * scale_coef);
+                bucket = MIN(bucket,bucket_count-1);
+                histogram[bucket]++;
+                cs_count++;
+            }
+        }
+    }
+
+    return cs_count;
+}
+
+/* Approximate width or height of central pixel in georef coordinates */
+
+double gv_raster_pixel_size( GvRaster *raster )
+
+{
+    double  x1, y1, x2, y2;
+
+    x1 = raster->width / 2; 
+    y1 = raster->height / 2;
+
+    x2 = x1 + 1.0;
+    y2 = y1 + 1.0;
+    
+    gv_raster_pixel_to_georef( raster, &x1, &y1, NULL );
+    gv_raster_pixel_to_georef( raster, &x2, &y2, NULL );
+
+    return (ABS(x2-x1) + ABS(y2-y1)) * 0.5;
+}
+
+static void
+gv_raster_get_memento(GvData *data, gpointer change_info,
+                      GvDataMemento **memento)
+{
+    GvRaster       *raster = GV_RASTER(data);
+    GvRasterMemento *mem;
+    GvRasterChangeInfo *info = (GvRasterChangeInfo *) change_info;
+
+    *memento = NULL;
+    if( change_info == NULL )
+        return;
+
+    mem = g_new(GvRasterMemento, 1);
+    mem->base.data = data;
+    mem->base.type = info->change_type;
+
+    mem->x_off = info->x_off;
+    mem->y_off = info->y_off;
+    mem->width = info->width;
+    mem->height = info->height;
+
+    mem->data = g_malloc(info->width * info->height * raster->item_size);
+    if( mem->data == NULL )
+        return;
+
+    GDALRasterIO( raster->gdal_band, GF_Read, 
+                  mem->x_off, mem->y_off, mem->width, mem->height, 
+                  mem->data, mem->width, mem->height, raster->gdal_type, 
+                  0, 0 );
+
+    *memento = (GvDataMemento*)mem;
+}
+
+static void
+gv_raster_set_memento(GvData *data, GvDataMemento *data_memento)
+{
+    GvRaster       *raster = GV_RASTER(data);
+    GvRasterMemento *mem = (GvRasterMemento *) data_memento;
+    GvRasterChangeInfo change_info;
+
+    GDALRasterIO( raster->gdal_band, GF_Write, 
+                  mem->x_off, mem->y_off, mem->width, mem->height, 
+                  mem->data, mem->width, mem->height, raster->gdal_type, 
+                  0, 0 );
+
+    change_info.change_type = GV_CHANGE_REPLACE;
+    change_info.x_off = mem->x_off;
+    change_info.y_off = mem->y_off;
+    change_info.width = mem->width;
+    change_info.height = mem->height;
+    gv_data_changed( GV_DATA(raster), &change_info );
+
+    gv_raster_del_memento( data, data_memento );
+}
+
+static void
+gv_raster_del_memento(GvData *data, GvDataMemento *data_memento)
+{
+    GvRasterMemento *memento = (GvRasterMemento *) data_memento;
+    
+    if (memento->data)
+        g_free( memento->data );
+
+    g_free(memento);    
+}
+
+/************************************************************************/
+/*                         gv_raster_set_gcps()                         */
+/************************************************************************/
+
+int gv_raster_set_gcps( GvRaster *raster, int gcp_count, 
+                        const GDAL_GCP *gcps )
+
+{
+    int success;
+
+    if( raster->gcp_count > 0 )
+    {
+        GDALDeinitGCPs( raster->gcp_count, raster->gcp_list );
+        CPLFree( raster->gcp_list );
+        raster->gcp_list = NULL;
+    }
+
+    raster->gcp_count = gcp_count;
+    if( gcp_count == 0 )
+    {
+        raster->poly_order = -1;
+        gtk_signal_emit(GTK_OBJECT(raster), 
+                        raster_signals[GEOTRANSFORM_CHANGED]);
+        return TRUE;
+    }
+
+    raster->gcp_list = GDALDuplicateGCPs( gcp_count, gcps );
+
+    success = gv_raster_build_poly_transform( raster );
+
+    if( !success )
+        gtk_signal_emit(GTK_OBJECT(raster), 
+                        raster_signals[GEOTRANSFORM_CHANGED]);
+
+    return success;
+}
+
+int gv_raster_set_gcpsCL( GvRaster *raster, int gcp_count, 
+                        const GDAL_GCP *gcps,int poly_order )
+
+{
+    int success;
+
+    if( raster->gcp_countCL > 0 )
+    {
+        GDALDeinitGCPs( raster->gcp_countCL, raster->gcp_listCL );
+        CPLFree( raster->gcp_listCL );
+        raster->gcp_listCL = NULL;
+    }
+
+    raster->gcp_countCL = gcp_count;
+    if( gcp_count == 0 )
+    {
+        raster->poly_orderCL = -1;
+        return TRUE;
+    }
+
+    raster->gcp_listCL = GDALDuplicateGCPs( gcp_count, gcps );
+
+    success = gv_raster_build_poly_transformCL( raster,poly_order );
+
+    return success;
+}
+
+/************************************************************************/
+/*                      gv_raster_get_gcp_count()                       */
+/************************************************************************/
+
+int gv_raster_get_gcp_count( GvRaster *raster )
+
+{
+    return raster->gcp_count;
+}
+
+int gv_raster_get_gcp_countCL( GvRaster *raster )
+
+{
+    return raster->gcp_countCL;
+}
+
+/************************************************************************/
+/*                         gv_raster_get_gcps()                         */
+/************************************************************************/
+
+const GDAL_GCP *gv_raster_get_gcps( GvRaster *raster )
+
+{
+    return raster->gcp_list;
+}
+
+const GDAL_GCP *gv_raster_get_gcpsCL( GvRaster *raster )
+
+{
+    return raster->gcp_listCL;
+}
+
+/* ==================================================================== */
+/*      USE_CRS: GCP fitting using GRASS derived crs.c/h code.          */
+/* ==================================================================== */
+
+#if defined(USE_CRS)
+
+/* build polynomial coefficients based on gcps */
+
+static gint gv_raster_build_poly_transform( GvRaster *raster )
+
+{
+    double  *pixel, *line, *x, *y;
+#ifdef ATLANTIS_BUILD
+    double   diff_x=0.0, diff_y=0.0;
+#endif
+    int         i, *status, success;
+    struct Control_Points cps;
+
+    int poly_order_pref=1; /* polynomial order preference- only used if preference is set */
+
+    /* defaults */
+#ifdef ATLANTIS_BUILD
+    if( raster->gcp_count == 0 )
+        return FALSE;
+#else
+    if( raster->gcp_count < 3 )
+        return FALSE;
+#endif
+
+    if( raster->gcp_count >= 10 )
+        raster->poly_order = 2; /* 3rd order not too stable with CRS code */
+    else if( raster->gcp_count >= 6 )
+        raster->poly_order = 2;
+    else
+        raster->poly_order = 1;
+
+
+    /* User has specified a preference for polynomial order.  Do a sanity */
+    /* check, then use it if it's reasonable */
+    if (gv_data_get_property( GV_DATA(raster), "poly_order_preference" ) != NULL )
+    {
+        poly_order_pref = atoi(gv_data_get_property( GV_DATA(raster), "poly_order_preference" ));
+        raster->poly_order = gv_raster_check_poly_order(raster,poly_order_pref);
+        if (poly_order_pref != raster->poly_order)
+        {
+            g_warning("gv_raster_build_poly_transform(): Inappropriate polynomial order setting.");
+        }
+
+    }
+
+    pixel = g_new(double,raster->gcp_count);
+    line = g_new(double,raster->gcp_count);
+    x = g_new(double,raster->gcp_count);
+    y = g_new(double,raster->gcp_count);
+    status = g_new(int,raster->gcp_count);
+
+    for( i = 0; i < raster->gcp_count; i++ )
+    {
+        x[i] = raster->gcp_list[i].dfGCPX;
+        y[i] = raster->gcp_list[i].dfGCPY;
+        pixel[i] = raster->gcp_list[i].dfGCPPixel;
+        line[i] = raster->gcp_list[i].dfGCPLine;
+        status[i] = 1; /* use this point */
+    }
+
+    raster->poly_x_coeff = g_new(double,20);
+    raster->poly_y_coeff = g_new(double,20);
+    raster->poly_z_coeff = NULL;
+    raster->poly_pixel_coeff = g_new(double,20);
+    raster->poly_line_coeff = g_new(double,20);
+
+    cps.count = raster->gcp_count;
+    cps.e1 = pixel;
+    cps.n1 = line;
+    cps.e2 = x;
+    cps.n2 = y;
+    cps.status = status;
+
+#ifdef ATLANTIS_BUILD
+    if( raster->gcp_count <3 )
+    {
+        for( i = 0; i < raster->gcp_count; i++ )
+        {
+            diff_x += (x[i]-pixel[i]);
+            diff_y += (y[i]-line[i]);
+        } 
+        raster->poly_pixel_coeff[0] = -diff_x/(double)raster->gcp_count;
+        raster->poly_line_coeff[0] = -diff_y/(double)raster->gcp_count;
+        raster->poly_x_coeff[0] = -raster->poly_pixel_coeff[0];
+        raster->poly_y_coeff[0] = -raster->poly_line_coeff[0];
+        raster->poly_x_coeff[1] = raster->poly_pixel_coeff[1]=1.0;
+        raster->poly_y_coeff[2] = raster->poly_line_coeff[2]=1.0;
+
+        return TRUE;
+    }
+    
+    success = CRS_compute_georef_equations( &cps, 
+                                      raster->poly_x_coeff, 
+                                      raster->poly_y_coeff,
+                                      raster->poly_pixel_coeff, 
+                                      raster->poly_line_coeff,
+                                      raster->poly_order );
+
+#else    
+    while( (success = 
+            CRS_compute_georef_equations( &cps, 
+                                          raster->poly_x_coeff, 
+                                          raster->poly_y_coeff,
+                                          raster->poly_pixel_coeff, 
+                                          raster->poly_line_coeff,
+                                          raster->poly_order )) != 1 
+           && raster->poly_order > 1 )
+    {
+        raster->poly_order--;
+    }
+#endif
+
+    if( success != 1 )
+    {
+        g_warning( "CRS_compute_georef_equations failed." );
+        raster->poly_order = -1;
+        return FALSE;
+    }
+
+    g_free( x );
+    g_free( y );
+    g_free( pixel );
+    g_free( line );
+    g_free( status );
+
+    gtk_signal_emit(GTK_OBJECT(raster), 
+                    raster_signals[GEOTRANSFORM_CHANGED]);
+
+    return TRUE;
+}
+
+static gint gv_raster_build_poly_transformCL( GvRaster *raster, int poly_order )
+
+{
+    double  *pixel, *line, *x, *y;
+    double   diff_x=0.0, diff_y=0.0;
+    int         i, *status;
+    struct Control_Points cps;
+
+    if( raster->gcp_countCL == 0 )
+        return FALSE;
+
+    if (poly_order < 0)
+    {
+        /* default to 1 (poly_order < 0 used to indicate defaults should be used)*/
+        raster->poly_orderCL = 1;
+    }
+    else
+    {
+        raster->poly_orderCL = poly_order;
+        if ((raster->gcp_countCL < 6) && (poly_order > 1))
+        {
+            g_warning("Not enough gcp's for polynomial order > 1: resetting to 1.");
+            raster->poly_orderCL = 1;
+        }   
+        else if ((raster->gcp_countCL < 10) && (poly_order > 2))
+        {
+            g_warning("Not enough gcp's for polynomial order > 2: resetting to 2.");
+            raster->poly_orderCL = 2;
+        }   
+    }
+
+    pixel = g_new(double,raster->gcp_countCL);
+    line = g_new(double,raster->gcp_countCL);
+    x = g_new(double,raster->gcp_countCL);
+    y = g_new(double,raster->gcp_countCL);
+    status = g_new(int,raster->gcp_countCL);
+
+    for( i = 0; i < raster->gcp_countCL; i++ )
+    {
+        x[i] = raster->gcp_listCL[i].dfGCPX;
+        y[i] = raster->gcp_listCL[i].dfGCPY;
+        pixel[i] = raster->gcp_listCL[i].dfGCPPixel;
+        line[i] = raster->gcp_listCL[i].dfGCPLine;
+        status[i] = 1; /* use this point */
+    }
+
+    raster->poly_x_coeffCL = g_new(double,20);
+    raster->poly_y_coeffCL = g_new(double,20);
+    raster->poly_z_coeffCL = NULL;
+    raster->poly_pixel_coeffCL = g_new(double,20);
+    raster->poly_line_coeffCL = g_new(double,20);
+
+    cps.count = raster->gcp_countCL;
+    cps.e1 = pixel;
+    cps.n1 = line;
+    cps.e2 = x;
+    cps.n2 = y;
+    cps.status = status;
+
+    if( raster->gcp_countCL <3 )
+    {
+        for( i = 0; i < raster->gcp_countCL; i++ )
+        {
+            diff_x += (x[i]-pixel[i]);
+            diff_y += (y[i]-line[i]);
+        } 
+        raster->poly_pixel_coeffCL[0] = -diff_x/(double)raster->gcp_countCL;
+        raster->poly_line_coeffCL[0] = -diff_y/(double)raster->gcp_countCL;
+        raster->poly_x_coeffCL[0] = -raster->poly_pixel_coeffCL[0];
+        raster->poly_y_coeffCL[0] = -raster->poly_line_coeffCL[0];
+        raster->poly_x_coeffCL[1] = raster->poly_pixel_coeffCL[1]=1.0;
+        raster->poly_y_coeffCL[2] = raster->poly_line_coeffCL[2]=1.0;
+
+        return TRUE;
+    }
+    
+    if( CRS_compute_georef_equations( &cps, 
+                                      raster->poly_x_coeffCL, 
+                                      raster->poly_y_coeffCL,
+                                      raster->poly_pixel_coeffCL, 
+                                      raster->poly_line_coeffCL,
+                                      raster->poly_orderCL ) != 1 )
+    {
+        g_warning( "CRS_compute_georef_equations failed." );
+        raster->poly_orderCL = -1;
+        return FALSE;
+    }
+
+    g_free( x );
+    g_free( y );
+    g_free( pixel );
+    g_free( line );
+    g_free( status );
+
+    return TRUE;
+}
+                                    
+/* Transform pixel/line coordinates to georeferenced coordinates */
+
+gint gv_raster_pixel_to_georef( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+    if( raster->poly_order > -1 )
+    {
+        CRS_georef( *x, *y, x, y, 
+                    raster->poly_x_coeff, raster->poly_y_coeff, 
+                    raster->poly_order );
+
+        /* z is left unchanged */
+    }
+    else
+    {
+        double  x_out, y_out;
+
+        x_out = raster->geotransform[0] 
+            + *x * raster->geotransform[1] + *y * raster->geotransform[2];
+        y_out = raster->geotransform[3] 
+            + *x * raster->geotransform[4] + *y * raster->geotransform[5];
+
+        *x = x_out;
+        *y = y_out;
+    }
+
+    return TRUE;
+}
+
+gint gv_raster_pixel_to_georefCL( GvRaster *raster, 
+                                double *x, double *y, double *z )
+{                  
+    if (raster->poly_orderCL > -1 )
+    {
+        CRS_georef( *x, *y, x, y, 
+                    raster->poly_x_coeffCL, raster->poly_y_coeffCL, 
+                    raster->poly_orderCL );
+
+        /* z is left unchanged */
+    }
+    else
+    {
+        /* Default to standard transformation if cursor-link */
+        /* specific transforms aren't defined.               */
+        gv_raster_pixel_to_georef(raster,x,y,z);
+    }
+
+    return TRUE;
+}   
+
+/* Transform georeferenced coordinates to pixel/line coordinates */
+
+gint gv_raster_georef_to_pixel( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+    if( raster->poly_order > -1 )
+    {
+        CRS_georef( *x, *y, x, y, 
+                    raster->poly_pixel_coeff, raster->poly_line_coeff, 
+                    raster->poly_order );
+
+        /* z is left unchanged */
+    }
+    else
+    {
+        double x_out, y_out, det;
+
+        det = ((raster->geotransform[1]*raster->geotransform[5])-
+               (raster->geotransform[2]*raster->geotransform[4]));
+
+        if (fabs(det) < 0.000000000000001)
+            return FALSE;
+
+        /* Original code commented out below: it left out the rotational terms! */
+        /*        *x = (*x - raster->geotransform[0]) / raster->geotransform[1]; */
+        /*  *y = (*y - raster->geotransform[3]) / raster->geotransform[5]; */
+
+        x_out = ((raster->geotransform[5]*(*x - raster->geotransform[0])) -
+                 (raster->geotransform[2]*(*y - raster->geotransform[3])))/ det;
+
+        y_out = ((raster->geotransform[1]*(*y - raster->geotransform[3])) -
+                 (raster->geotransform[4]*(*x - raster->geotransform[0])))/ det;
+ 
+        *x = x_out;
+        *y = y_out;
+
+    }
+
+    return TRUE;
+}
+
+gint gv_raster_georef_to_pixelCL( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+
+    if( raster->poly_orderCL > -1 )
+    {
+        CRS_georef( *x, *y, x, y, 
+                    raster->poly_pixel_coeffCL, raster->poly_line_coeffCL, 
+                    raster->poly_orderCL );
+        /* z is left unchanged */
+    }
+    else
+    {
+        /* Default to standard transformation if cursor-link */
+        /* specific transforms aren't defined.               */
+        gv_raster_georef_to_pixel(raster,x,y,z);
+    }
+
+    return TRUE;
+}
+
+#endif /* defined(USE_CRS) */
+
+/* ==================================================================== */
+/*      not defined USE_CRS: GCP fitting using EarthView derived        */
+/*      code in gvgcpfit.c                                              */
+/* ==================================================================== */
+
+#if !defined(USE_CRS)
+
+/* build polynomial coefficients based on gcps */
+
+static gint gv_raster_build_poly_transform( GvRaster *raster )
+
+{
+    double  *pixel, *line, *x, *y, *z, rms;
+    int         i;
+
+    int poly_order_pref=1; /* polynomial order preference- only used if preference is set */
+
+    if( raster->gcp_count < 3 )
+        return FALSE;
+
+    if( raster->gcp_count >= 10 )
+        raster->poly_order = 3;
+    else if( raster->gcp_count >= 6 )
+        raster->poly_order = 2;
+    else
+        raster->poly_order = 1;
+
+    /* User has specified a preference for polynomial order.  Do a sanity */
+    /* check, then use it if it's reasonable */
+    if (gv_data_get_property( GV_DATA(raster), "poly_order_preference" ) != NULL )
+    {
+        poly_order_pref = atoi(gv_data_get_property( GV_DATA(raster), "poly_order_preference" ));
+        raster->poly_order = gv_raster_check_poly_order(raster,poly_order_pref);
+        if (poly_order_pref != raster->poly_order)
+        {
+            g_warning("gv_raster_build_poly_transform(): Inappropriate polynomial order setting.");
+        }
+
+    }
+
+    pixel = g_new(double,raster->gcp_count);
+    line = g_new(double,raster->gcp_count);
+    x = g_new(double,raster->gcp_count);
+    y = g_new(double,raster->gcp_count);
+    z = g_new(double,raster->gcp_count);
+
+    for( i = 0; i < raster->gcp_count; i++ )
+    {
+        x[i] = raster->gcp_list[i].dfGCPX;
+        y[i] = raster->gcp_list[i].dfGCPY;
+        z[i] = raster->gcp_list[i].dfGCPZ;
+        pixel[i] = raster->gcp_list[i].dfGCPPixel;
+        line[i] = raster->gcp_list[i].dfGCPLine;
+    }
+
+    raster->poly_x_coeff = g_new(double,20);
+    raster->poly_y_coeff = g_new(double,20);
+    raster->poly_z_coeff = g_new(double,20);
+    raster->poly_pixel_coeff = g_new(double,20);
+    raster->poly_line_coeff = g_new(double,20);
+
+    if( TwoDPolyFit( &rms, raster->poly_x_coeff, raster->poly_order, 
+                     raster->gcp_count, x, pixel, line ) != SUCCESS )
+    {
+        raster->poly_order = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_y_coeff, raster->poly_order, 
+                     raster->gcp_count, y, pixel, line ) != SUCCESS )
+    {
+        raster->poly_order = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_z_coeff, raster->poly_order, 
+                     raster->gcp_count, z, pixel, line ) != SUCCESS )
+    {
+        raster->poly_order = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_pixel_coeff, raster->poly_order, 
+                     raster->gcp_count, pixel, x, y ) != SUCCESS )
+    {
+        raster->poly_order = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_line_coeff, raster->poly_order, 
+                     raster->gcp_count, line, x, y ) != SUCCESS )
+    {
+        raster->poly_order = -1;
+        return FALSE;
+    }
+
+    g_free( x );
+    g_free( y );
+    g_free( z );
+    g_free( pixel );
+    g_free( line );
+
+    gtk_signal_emit(GTK_OBJECT(raster), 
+                    raster_signals[GEOTRANSFORM_CHANGED]);
+
+    return TRUE;
+}
+static gint gv_raster_build_poly_transformCL( GvRaster *raster, int poly_order )
+
+{
+    double  *pixel, *line, *x, *y, *z, rms;
+    int         i;
+
+    if( raster->gcp_countCL < 3 )
+        return FALSE;
+
+    if (poly_order < 0)
+    {
+        /* poly_order < 0 used to indicate defaults should be used */
+        if( raster->gcp_countCL >= 10 )
+            raster->poly_orderCL = 3;
+        else if( raster->gcp_countCL >= 6 )
+            raster->poly_orderCL = 2;
+        else
+            raster->poly_orderCL = 1;
+        
+    }
+    else
+    {
+        raster->poly_orderCL = poly_order;
+        if ((raster->gcp_countCL < 6) && (poly_order > 1))
+        {
+            g_warning("Not enough gcp's for polynomial order > 1: resetting to 1.");
+            raster->poly_orderCL = 1;
+        }   
+        else if ((raster->gcp_countCL < 10) && (poly_order > 2))
+        {
+            g_warning("Not enough gcp's for polynomial order > 2: resetting to 2.");
+            raster->poly_orderCL = 2;
+        }   
+    }
+
+
+    pixel = g_new(double,raster->gcp_countCL);
+    line = g_new(double,raster->gcp_countCL);
+    x = g_new(double,raster->gcp_countCL);
+    y = g_new(double,raster->gcp_countCL);
+    z = g_new(double,raster->gcp_countCL);
+
+    for( i = 0; i < raster->gcp_countCL; i++ )
+    {
+        x[i] = raster->gcp_listCL[i].dfGCPX;
+        y[i] = raster->gcp_listCL[i].dfGCPY;
+        z[i] = raster->gcp_listCL[i].dfGCPZ;
+        pixel[i] = raster->gcp_listCL[i].dfGCPPixel;
+        line[i] = raster->gcp_listCL[i].dfGCPLine;
+    }
+
+    raster->poly_x_coeffCL = g_new(double,20);
+    raster->poly_y_coeffCL = g_new(double,20);
+    raster->poly_z_coeffCL = g_new(double,20);
+    raster->poly_pixel_coeffCL = g_new(double,20);
+    raster->poly_line_coeffCL = g_new(double,20);
+
+    if( TwoDPolyFit( &rms, raster->poly_x_coeffCL, raster->poly_orderCL, 
+                     raster->gcp_countCL, x, pixel, line ) != SUCCESS )
+    {
+        raster->poly_orderCL = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_y_coeffCL, raster->poly_orderCL, 
+                     raster->gcp_countCL, y, pixel, line ) != SUCCESS )
+    {
+        raster->poly_orderCL = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_z_coeffCL, raster->poly_orderCL, 
+                     raster->gcp_countCL, z, pixel, line ) != SUCCESS )
+    {
+        raster->poly_orderCL = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_pixel_coeffCL, raster->poly_orderCL, 
+                     raster->gcp_countCL, pixel, x, y ) != SUCCESS )
+    {
+        raster->poly_orderCL = -1;
+        return FALSE;                           
+    }
+
+    if( TwoDPolyFit( &rms, raster->poly_line_coeffCL, raster->poly_orderCL, 
+                     raster->gcp_countCL, line, x, y ) != SUCCESS )
+    {
+        raster->poly_orderCL = -1;
+        return FALSE;
+    }
+
+    g_free( x );
+    g_free( y );
+    g_free( z );
+    g_free( pixel );
+    g_free( line );
+
+    return TRUE;
+}
+                                          
+/* Transform pixel/line coordinates to georeferenced coordinates */
+
+gint gv_raster_pixel_to_georef( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+    if( raster->poly_order > -1 )
+    {
+        double  x_out, y_out, z_out;
+
+        x_out = TwoDPolyEval( raster->poly_x_coeff, raster->poly_order, 
+                              *x, *y );
+
+        y_out = TwoDPolyEval( raster->poly_y_coeff, raster->poly_order, 
+                              *x, *y );
+
+        z_out = TwoDPolyEval( raster->poly_z_coeff, raster->poly_order, 
+                              *x, *y );
+
+        *x = x_out;
+        *y = y_out;
+        
+        if( z != NULL )
+            *z = z_out;
+    }
+    else
+    {
+        double  x_out, y_out;
+
+        x_out = raster->geotransform[0] 
+            + *x * raster->geotransform[1] + *y * raster->geotransform[2];
+        y_out = raster->geotransform[3] 
+            + *x * raster->geotransform[4] + *y * raster->geotransform[5];
+
+        *x = x_out;
+        *y = y_out;
+    }
+
+    return TRUE;
+}
+
+gint gv_raster_pixel_to_georefCL( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+    if( raster->poly_orderCL > -1 )
+    {
+        double  x_out, y_out, z_out;
+
+        x_out = TwoDPolyEval( raster->poly_x_coeffCL, raster->poly_orderCL, 
+                              *x, *y );
+
+        y_out = TwoDPolyEval( raster->poly_y_coeffCL, raster->poly_orderCL, 
+                              *x, *y );
+
+        z_out = TwoDPolyEval( raster->poly_z_coeffCL, raster->poly_orderCL, 
+                              *x, *y );
+
+        *x = x_out;
+        *y = y_out;
+        
+        if( z != NULL )
+            *z = z_out;
+    }
+    else
+    {
+        /* Default to standard transformation if cursor-link */
+        /* specific transforms aren't defined.               */
+        gv_raster_pixel_to_georef(raster,x,y,z);
+    }
+
+    return TRUE;
+}
+
+/* Transform georeferenced coordinates to pixel/line coordinates */
+
+gint gv_raster_georef_to_pixel( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+    if( raster->poly_order > -1 )
+    {
+        double  x_out, y_out;
+
+        x_out = TwoDPolyEval( raster->poly_pixel_coeff, raster->poly_order, 
+                              *x, *y );
+
+        y_out = TwoDPolyEval( raster->poly_line_coeff, raster->poly_order, 
+                              *x, *y );
+
+        *x = x_out;
+        *y = y_out;
+    }
+    else
+    {
+        *x = (*x - raster->geotransform[0]) / raster->geotransform[1];
+        *y = (*y - raster->geotransform[3]) / raster->geotransform[5];
+    }
+
+    return TRUE;
+}
+gint gv_raster_georef_to_pixelCL( GvRaster *raster, 
+                                double *x, double *y, double *z )
+
+{
+    if( raster->poly_orderCL > -1 )
+    {
+        double  x_out, y_out;
+
+        x_out = TwoDPolyEval( raster->poly_pixel_coeffCL, raster->poly_orderCL, 
+                              *x, *y );
+
+        y_out = TwoDPolyEval( raster->poly_line_coeffCL, raster->poly_orderCL, 
+                              *x, *y );
+
+        *x = x_out;
+        *y = y_out;
+    }
+    else
+    {
+        /* Default to standard transformation if cursor-link */
+        /* specific transforms aren't defined.               */
+        gv_raster_georef_to_pixel(raster,x,y,z); 
+    }
+
+    return TRUE;
+}
+
+#endif /* !defined(USE_CRS) */
+
+/* ========================================================================= */
+/* gv_raster_check_poly_order: Sanity check the desired polynomial order     */
+/* input: raster, desired polynomial order.                                  */
+/* output: closest matching polynomial order.                                */
+/* ========================================================================= */
+static int gv_raster_check_poly_order( GvRaster *raster, 
+                                       int desired_poly_order )
+{
+    int poly_order;
+
+    poly_order = desired_poly_order;
+
+    if (poly_order > 3)
+    {
+
+        if( raster->gcp_count >= 10 )
+            poly_order = 3;
+        else if( raster->gcp_count >= 6 )
+            poly_order = 2;
+        else
+            poly_order = 1;
+        
+    }
+    else if (poly_order == 3)
+    {
+        if( raster->gcp_count < 6 )
+            poly_order = 1;
+        else if (raster->gcp_count < 10)
+            poly_order = 2;
+
+    }
+    else if (poly_order == 2)
+    {
+        if( raster->gcp_count < 6 )
+            poly_order = 1;
+        
+    }
+    else if (poly_order < 1)
+        poly_order = 1;
+    
+    return poly_order;
+
+}
+
+void gv_raster_set_poly_order_preference( GvRaster *raster, int poly_order )
+{
+    char txt[16];
+    int success;
+
+    sprintf(txt,"%d",poly_order);
+    gv_data_set_property(GV_DATA(raster),"poly_order_preference",txt );
+
+    if (raster->gcp_count > 0)
+    {
+        /* If gcps have already been set, rebuild poly transform */
+        success = gv_raster_build_poly_transform( raster );
+
+        if( !success )
+            gtk_signal_emit(GTK_OBJECT(raster), 
+                            raster_signals[GEOTRANSFORM_CHANGED]);
+    }
+
+}

Added: packages/openev/branches/upstream/current/gvraster.h
===================================================================
--- packages/openev/branches/upstream/current/gvraster.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvraster.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,223 @@
+/******************************************************************************
+ * $Id: gvraster.h,v 1.28 2004/01/22 19:57:11 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster data container. 
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvraster.h,v $
+ * Revision 1.28  2004/01/22 19:57:11  andrey_kiselev
+ * Use gv_raster_get_nodata() function to fetch the NODATA value from the image
+ * using GDALGetRasterNoDataValue().
+ *
+ * Revision 1.27  2003/09/11 20:00:30  gmwalter
+ * Add ability to specify a preferred polynomial order for warping a raster,
+ * and add "safe mode" (only used if ATLANTIS_BUILD is defined).
+ *
+ * Revision 1.26  2003/02/20 19:27:16  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.25  2001/11/28 19:18:29  warmerda
+ * Added set_gcps(), and get_gcps() methods on GvRaster, and the
+ * geotransform-changed signal generated when the gcps change.
+ *
+ * Revision 1.24  2001/10/16 18:50:06  warmerda
+ * now possible to pass sample set into autoscale
+ *
+ * Revision 1.23  2001/08/14 17:03:24  warmerda
+ * added standard deviation autoscaling support
+ *
+ * Revision 1.22  2001/07/24 02:21:54  warmerda
+ * added 8bit phase averaging
+ *
+ * Revision 1.21  2001/07/13 22:15:36  warmerda
+ * added nodata aware averaging
+ *
+ * Revision 1.20  2001/04/02 18:10:46  warmerda
+ * expose gv_raster_autoscale() to python
+ *
+ * Revision 1.19  2000/09/27 19:18:50  warmerda
+ * Honour GvSMSample for real and complex images.
+ * Add GvRaster.sm field.  If set to GvSMSample always let GDAL do the
+ * decimation for faster loads.
+ *
+ * Revision 1.18  2000/06/26 15:12:47  warmerda
+ * added get_min, get_max macros
+ *
+ * Revision 1.17  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_RASTER_H__
+#define __GV_RASTER_H__
+
+#include "gvdata.h"
+#include "gvrastercache.h"
+#include "gvmesh.h"
+
+#include "gdal.h"
+
+#define GV_TYPE_RASTER            (gv_raster_get_type ())
+#define GV_RASTER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_RASTER, GvRaster))
+#define GV_RASTER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_RASTER, GvRasterClass))
+#define GV_IS_RASTER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_RASTER))
+#define GV_IS_RASTER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_RASTER))
+
+typedef enum {
+    GvSMAverage = 0,
+    GvSMSample = 1,
+    GvSMAverage8bitPhase = 2
+} GvSampleMethod;
+
+typedef enum {
+    GvASAAutomatic = 0, 
+    GvASAPercentTailTrim = 1,
+    GvASAStdDeviation = 2
+} GvAutoScaleAlg;
+
+typedef struct _GvRaster       GvRaster;
+typedef struct _GvRasterClass  GvRasterClass;
+
+#define GV_TILE_OVERLAP           2
+
+struct _GvRaster
+{
+    GvData data;
+
+    gint max_tiles,                         /* total tiles */
+        width,                              /* width of raster */
+        height,                             /* height of raster */
+        tile_x,                             /* tile width */
+        tile_y,                             /* tile height */
+        tiles_across,                       /* tiles per row */
+        tiles_down,                         /* tiles per column */
+        max_lod;                            /* maximum level of detail(+1) */
+
+    float min, max;                          /* suggested scaling min/max */
+    
+    GvSampleMethod sm;                       /* Sample method */
+    void *(*average)(GvRaster *raster, void *buffer, int tsize_x, int tsize_y);
+
+    int type;                                /* GV_RASTER_* */
+    int item_size;                           /* size of pixel group */
+
+    gv_raster_cache *cache;                  /* read cache */
+
+    double  geotransform[6];                 /* pl to georef transform */
+
+    int	    gcp_count;			     /* 0 if not relativant */
+    GDAL_GCP *gcp_list;
+    int     poly_order;                      /* -1 if not intialized */
+    double  *poly_pixel_coeff;
+    double  *poly_line_coeff;
+    double  *poly_x_coeff;
+    double  *poly_y_coeff;
+    double  *poly_z_coeff;
+
+    /* Separate transformations to be used by ghost cursor and linking */
+    /* DEFAULTS TO REGULAR TRANSFORMATION IF THESE AREN'T SET       */
+    int	    gcp_countCL;			     /* 0 if not relativant */
+    GDAL_GCP *gcp_listCL;
+    int     poly_orderCL;                      /* -1 if not intialized */
+    double  *poly_pixel_coeffCL;
+    double  *poly_line_coeffCL;
+    double  *poly_x_coeffCL;
+    double  *poly_y_coeffCL;
+    double  *poly_z_coeffCL;
+
+    /* End of cursor/link-specific transform stuff */
+
+    GDALDatasetH     dataset;                /* source dataset */
+    GDALRasterBandH  gdal_band;              /* real band */
+
+    GDALDataType gdal_type;                  /* GDT_Byte, etc */
+};
+
+struct _GvRasterClass
+{
+    GvDataClass parent_class;
+    void (* geotransform_changed)(GvRaster *raster);
+};
+
+GtkType gv_raster_get_type(void);
+
+GvData* gv_raster_new( GDALDatasetH dataset, int real_band, GvSampleMethod sm);
+
+void *gv_raster_tile_get(GvRaster *raster, int tile, int lod );
+gint *gv_raster_tile_xy_get(GvRaster *raster, int tile, int lod, gint *coords);
+gint gv_raster_datatype_get(GvRaster *raster );
+gint gv_raster_dataformat_get(GvRaster *raster );
+void gv_raster_flush_cache(GvRaster *raster,
+                           int x_off, int y_off, int width, int height );
+gint gv_raster_get_nodata(GvRaster *raster, double *nodata );
+gint gv_raster_get_sample(GvRaster *raster, double x, double y,
+                          double *real, double *imaginary );
+double gv_raster_pixel_size( GvRaster *raster );
+gint gv_raster_pixel_to_georef(GvRaster *raster, 
+                               double *x, double *y, double *z );
+gint gv_raster_georef_to_pixel(GvRaster *raster, 
+                               double *x, double *y, double *z );
+gint gv_raster_autoscale( GvRaster *raster, GvAutoScaleAlg alg, 
+                          double alg_param, 
+                          int sample_count, float *sample_set,
+                          double *out_min, double *out_max );
+int  gv_raster_collect_random_sample( GvRaster *raster, 
+                                      int sample_count, float *sample_set,
+                                      int xoff, int yoff, int xsize, int ysize,
+                                      GArray *tile_list );
+                                  
+int  
+gv_raster_collect_histogram(GvRaster *raster, 
+                            double scale_min, double scale_max, 
+                            int bucket_count, int *histogram,
+                            int include_out_of_range, 
+                            int xoff, int yoff, int xsize, int ysize,
+                            GArray *tile_list );
+                                  
+#define gv_raster_get_min(raster) ((GV_RASTER(raster))->min)
+#define gv_raster_get_max(raster) ((GV_RASTER(raster))->max)
+
+int gv_raster_set_gcps(GvRaster *raster, int gcp_count, const GDAL_GCP *gcps);
+int gv_raster_get_gcp_count(GvRaster *raster);
+const GDAL_GCP *gv_raster_get_gcps(GvRaster *raster);
+
+
+/* Cursor/Link specific transforms (only used if set)*/
+int gv_raster_set_gcpsCL(GvRaster *raster, int gcp_count, const GDAL_GCP *gcps, int poly_order);
+int gv_raster_get_gcp_countCL(GvRaster *raster);
+const GDAL_GCP *gv_raster_get_gcpsCL(GvRaster *raster);
+gint gv_raster_pixel_to_georefCL(GvRaster *raster, 
+                               double *x, double *y, double *z );
+gint gv_raster_georef_to_pixelCL(GvRaster *raster, 
+                               double *x, double *y, double *z );
+
+/* Set preferred polynomial order for this raster (optional) */
+void gv_raster_set_poly_order_preference( GvRaster *raster, int poly_order );
+
+#endif /* __GV_RASTER_H__ */
+

Added: packages/openev/branches/upstream/current/gvrasteraverage.c
===================================================================
--- packages/openev/branches/upstream/current/gvrasteraverage.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasteraverage.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,384 @@
+/******************************************************************************
+ * $Id: gvrasteraverage.c,v 1.18 2004/02/18 16:07:27 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster downsampling kernels (averaged and otherwise)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasteraverage.c,v $
+ * Revision 1.18  2004/02/18 16:07:27  andrey_kiselev
+ * Use gv_raster_get_nodata() instead of GDALGetRasterNoDataValue().
+ *
+ * Revision 1.17  2002/11/27 04:35:59  warmerda
+ * try to mitigate the downward bias of averaging due to truncation in 8bit
+ *
+ * Revision 1.16  2001/07/24 02:23:53  warmerda
+ * removed debugging printf
+ *
+ * Revision 1.15  2001/07/24 02:21:54  warmerda
+ * added 8bit phase averaging
+ *
+ * Revision 1.14  2001/07/16 15:07:45  warmerda
+ * rewrote complex averaging to precisely average magnitudes
+ *
+ * Revision 1.13  2001/07/13 22:15:36  warmerda
+ * added nodata aware averaging
+ *
+ * Revision 1.12  2001/01/30 19:27:51  warmerda
+ * added logic to try and preserve stddev in complex average code
+ *
+ * Revision 1.11  2000/09/27 19:17:28  warmerda
+ * added new _sample functions
+ *
+ * Revision 1.10  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <math.h>
+#include "gvraster.h"
+#include "gvrasteraverage.h"
+
+#ifndef M_PI
+#  define M_PI 3.14159265358979323846
+#endif
+
+/* This function does the equivelent of the GDAL "MAGPHASE" averaging.
+ * It produces a magnitude weighted average of phase (by operating in 
+ * real/imaginary space) and then scales the resulting magnitude by 
+ * the averaged magnitude to preserve the average magnitude.
+ */
+
+float *gv_raster_float_complex_average( GvRaster *raster, 
+                                        float *iq, int tsize_x, int tsize_y )
+{
+    int i, base, averaged_size, next_base;
+    float *iq_average, target_mag, ratio, prefix_mag;
+
+    averaged_size = (tsize_x * tsize_y) / 4;
+
+    if( ( iq_average = g_new( float, averaged_size*2 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    for( i = 0; i < averaged_size; i++ )
+    {
+	base = ( ( ( i * 2 ) / tsize_x ) * tsize_x * 2 ) + ( 4 * i );
+        next_base = base + tsize_x*2;
+
+	iq_average[2*i] = ( iq[base] + iq[base+2] + iq[next_base] + iq[next_base+2] ) / 4;
+	iq_average[2*i+1] = ( iq[base+1] + iq[base+3] + iq[next_base+1] + iq[next_base+3] ) / 4;
+
+        /*
+         * Compute average of input magnitudes and use this to scale the output
+         * magnitude.
+         */
+        target_mag = (
+            sqrt(iq[base]*iq[base] + iq[base+1]*iq[base+1])
+          + sqrt(iq[base+2]*iq[base+2] + iq[base+3]*iq[base+3]) 
+          + sqrt(iq[next_base]*iq[next_base] + iq[next_base+1]*iq[next_base+1])
+          + sqrt(iq[next_base+2]*iq[next_base+2] + iq[next_base+3]*iq[next_base+3])
+            ) / 4.0;
+
+        prefix_mag = sqrt(iq_average[2*i]*iq_average[2*i]
+                          + iq_average[2*i+1]*iq_average[2*i+1]);
+
+        if( prefix_mag != 0.0 && target_mag != 0.0 )
+        {
+            ratio = target_mag / prefix_mag;
+            iq_average[2*i] *= ratio;
+            iq_average[2*i+1] *= ratio;
+        }
+    }
+    return iq_average;
+}
+
+float *gv_raster_float_complex_sample( GvRaster *raster,
+                                       float *iq, int tsize_x, int tsize_y )
+{
+    float *avg, *buf_out;
+    int i, e;
+
+    if( ( avg = g_new( float, tsize_x * tsize_y / 2 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    buf_out = avg;
+    for( i = 0; i < tsize_y; i += 2 )
+    {
+        float	*buf_in;
+
+        buf_in = iq + i * tsize_x * 2;
+
+	for( e = tsize_x/2; e > 0; e-- )
+	{
+            *(buf_out++) = *(buf_in++);
+            *(buf_out++) = *(buf_in++);
+            buf_in += 2;
+	}
+    }
+
+    return avg;
+}
+
+float *gv_raster_float_real_average( GvRaster *raster, 
+                                     float *buffer, int tsize_x, int tsize_y )
+{
+    float *avg, total;
+    int i, e, base;
+
+    if( ( avg = g_new( float, tsize_x * tsize_y / 4 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    for( i = 0; i < tsize_y >> 1; i++ )
+    {
+	for( e = 0; e < tsize_x >> 1; e++ )
+	{
+	    base = 2*((i*tsize_x)+e);
+	    total = buffer[base];
+	    total += buffer[base+1];
+	    total += buffer[base+tsize_x];
+	    total += buffer[base+tsize_x+1];
+
+	    avg[i*(tsize_y>>1)+e] = total / 4;
+	}
+    }
+
+    return avg;
+
+}
+
+float *gv_raster_float_real_average_nodata( GvRaster *raster, float *buffer, 
+                                            int tsize_x, int tsize_y )
+{
+    int     i, e, base;
+    float   *avg, total;
+    double  nodata;
+
+    gv_raster_get_nodata( raster, &nodata );
+
+    if( ( avg = g_new( float, tsize_x * tsize_y / 4 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    for( i = 0; i < tsize_y >> 1; i++ )
+    {
+	for( e = 0; e < tsize_x >> 1; e++ )
+	{
+            int		count = 0;
+
+            total = 0;
+	    base = 2*((i*tsize_x)+e);
+            
+            if( buffer[base] != (float)nodata )
+            {
+                total += buffer[base];
+                count++;
+            }
+            if( buffer[base+1] != (float)nodata )
+            {
+                total += buffer[base+1];
+                count++;
+            }
+            if( buffer[base+tsize_x] != (float)nodata )
+            {
+                total += buffer[base+tsize_x];
+                count++;
+            }
+            if( buffer[base+tsize_x+1] != (float)nodata )
+            {
+                total += buffer[base+tsize_x+1];
+                count++;
+            }
+
+            if( count > 0 )
+                avg[i*(tsize_y>>1)+e] = total / count;
+            else
+                avg[i*(tsize_y>>1)+e] = (float)nodata;
+	}
+    }
+
+    return avg;
+
+}
+
+float *gv_raster_float_real_sample( GvRaster *raster, 
+                                    float *buffer, int tsize_x, int tsize_y )
+{
+    float *avg, *buf_out;
+    int i, e;
+
+    if( ( avg = g_new( float, tsize_x * tsize_y / 4 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    buf_out = avg;
+    for( i = 0; i < tsize_y; i += 2 )
+    {
+        float	*buf_in;
+
+        buf_in = buffer + i * tsize_x;
+
+	for( e = tsize_x/2; e > 0; e-- )
+	{
+            *(buf_out++) = *buf_in;
+            buf_in += 2;
+	}
+    }
+
+    return avg;
+}
+
+/* Convert 8bit phase into complex value with magnitude 1. */
+
+static void gvrf_8bit_phase_to_complex( int in_phase, float *r, float *i )
+{
+    float	phase = (in_phase / 256.0) * 2 * M_PI;
+
+    if( phase > M_PI )
+        phase -= 2*M_PI;
+
+    *r = cos(phase);
+    *i = sin(phase);
+}
+
+/*
+** This is a specialized algorithm to average 8bit phase information.
+** The phase is rescaled into 0->2PI (from 0-255), translated into unit
+** length complex vectors and then averaged.  The resulting complex value
+** is converted back into phase and rescaled into 0-255. 
+*/
+
+unsigned char *gv_raster_byte_realphase_average( GvRaster *raster, 
+                          unsigned char *buffer, int tsize_x, int tsize_y )
+{
+    unsigned char *avg;
+    int i, e, base;
+
+    if( ( avg = g_new( unsigned char, tsize_x * tsize_y / 4 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    for( i = 0; i < tsize_y >> 1; i++ )
+    {
+	for( e = 0; e < tsize_x >> 1; e++ )
+	{
+            float total_r = 0.0, total_i = 0.0;
+            float real, imag, phase;
+
+	    base = 2*((i*tsize_x)+e);
+
+            gvrf_8bit_phase_to_complex( buffer[base], &real, &imag );
+            total_r += real;
+            total_i += imag;
+
+            gvrf_8bit_phase_to_complex( buffer[base+1], &real, &imag );
+            total_r += real;
+            total_i += imag;
+
+            gvrf_8bit_phase_to_complex( buffer[base+tsize_x], &real, &imag );
+            total_r += real;
+            total_i += imag;
+
+            gvrf_8bit_phase_to_complex( buffer[base+tsize_x+1], &real, &imag );
+            total_r += real;
+            total_i += imag;
+
+            real = total_r * 0.25;
+            imag = total_i * 0.25;
+            
+            phase = atan2(imag, real);
+            while( phase < 0.0 )
+                phase += 2 * M_PI;
+            while( phase >= 2*M_PI )
+                phase -= 2 * M_PI;
+
+	    avg[i*(tsize_y>>1)+e] = floor((phase * 256) / (2*M_PI));
+	}
+    }
+
+    return avg;
+}
+
+unsigned char *gv_raster_byte_real_average( GvRaster *raster, 
+                                            unsigned char *buffer, 
+                                            int tsize_x, int tsize_y )
+{
+    unsigned char *avg;
+    int i, e, total, base;
+
+    if( ( avg = g_new( unsigned char, tsize_x * tsize_y / 4 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    for( i = 0; i < tsize_y >> 1; i++ )
+    {
+	for( e = 0; e < tsize_x >> 1; e++ )
+	{
+	    base = 2*((i*tsize_x)+e);
+	    total = buffer[base];
+	    total += buffer[base+1];
+	    total += buffer[base+tsize_x];
+	    total += buffer[base+tsize_x+1];
+
+	    avg[i*(tsize_y>>1)+e] = (total+2) / 4;
+	}
+    }
+
+    return avg;
+}
+
+unsigned char *gv_raster_byte_real_sample( GvRaster *raster, 
+                                           unsigned char *buffer, 
+                                           int tsize_x, int tsize_y )
+{
+    unsigned char *avg;
+    int i, e, base;
+
+    if( ( avg = g_new( unsigned char, tsize_x * tsize_y / 4 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    for( i = 0; i < tsize_y >> 1; i++ )
+    {
+	for( e = 0; e < tsize_x >> 1; e++ )
+	{
+	    base = 2*((i*tsize_x)+e);
+	    avg[i*(tsize_y>>1)+e] = buffer[base];
+	}
+    }
+
+    return avg;
+}
+

Added: packages/openev/branches/upstream/current/gvrasteraverage.h
===================================================================
--- packages/openev/branches/upstream/current/gvrasteraverage.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasteraverage.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * $Id: gvrasteraverage.h,v 1.8 2001/07/24 02:21:54 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster downsampling kernels (averaged and otherwise)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasteraverage.h,v $
+ * Revision 1.8  2001/07/24 02:21:54  warmerda
+ * added 8bit phase averaging
+ *
+ * Revision 1.7  2001/07/13 22:15:36  warmerda
+ * added nodata aware averaging
+ *
+ * Revision 1.6  2000/09/27 19:17:28  warmerda
+ * added new _sample functions
+ *
+ * Revision 1.5  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef _GV_RASTER_AVERAGE_H_
+#define _GV_RASTER_AVERAGE_H_
+
+unsigned char *gv_raster_byte_real_average( GvRaster *raster, 
+                                            unsigned char *buffer,
+                                            int tsize_x, int tsize_y );
+unsigned char *gv_raster_byte_real_sample( GvRaster *raster,
+                                           unsigned char *buffer,
+                                           int tsize_x, int tsize_y );
+
+unsigned char *gv_raster_byte_realphase_average( GvRaster *raster,
+                             unsigned char *buffer, int tsize_x, int tsize_y );
+
+float *gv_raster_float_real_average_nodata( GvRaster *raster, float *buffer, 
+                                            int tsize_x, int tsize_y );
+float *gv_raster_float_real_average( GvRaster *raster,
+                                     float *buffer, int tsize_x, int tsize_y );
+float *gv_raster_float_real_sample( GvRaster *raster,
+                                    float *buffer, int tsize_x, int tsize_y );
+
+float *gv_raster_float_complex_average(GvRaster *raster,
+                                       float *buffer,int tsize_x,int tsize_y);
+float *gv_raster_float_complex_sample(GvRaster *raster,
+                                      float *buffer,int tsize_x,int tsize_y);
+
+#endif

Added: packages/openev/branches/upstream/current/gvrastercache.c
===================================================================
--- packages/openev/branches/upstream/current/gvrastercache.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrastercache.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,304 @@
+/******************************************************************************
+ * $Id: gvrastercache.c,v 1.12 2000/06/20 13:26:55 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvRaster tile caching.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrastercache.c,v $
+ * Revision 1.12  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "gvrastercache.h"
+
+static gint gv_cache_max = 20 * 1024 * 1024;
+static gint gv_cache_used = 0;
+
+static gv_raster_cache_tile *lru_head = NULL;
+static gv_raster_cache_tile *lru_tail = NULL;
+
+
+static void gv_raster_cache_tile_touch( gv_raster_cache_tile * );
+static int gv_raster_cache_purge_lru();
+
+gv_raster_cache *
+gv_raster_cache_new( gint tiles, gint lod )
+{
+    gv_raster_cache *cache;
+    int i;
+
+    if( ( cache = g_new( gv_raster_cache, 1 ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    cache->max_lod = lod;
+    cache->max_tiles = tiles;
+
+    /* Allocate tile pointer arrays, but not the tiles themselves */
+    if( (cache->tiles = g_new(gv_raster_cache_tile**,cache->max_lod)) == NULL )
+    {
+	g_free( cache );
+	return NULL;
+    }
+
+    for( i = 0; i < cache->max_lod; i++ )
+    {
+        cache->tiles[i] = g_new0(gv_raster_cache_tile*,cache->max_tiles);
+        if( cache->tiles[i] == NULL )
+            return NULL;
+    }
+
+    return cache;
+}
+
+void *gv_raster_cache_get( gv_raster_cache *cache, gint tile, gint lod )
+{
+    if( tile < cache->max_tiles && lod < cache->max_lod 
+        && cache->tiles[lod][tile] )
+    {
+        gv_raster_cache_tile_touch( cache->tiles[lod][tile] );
+        return cache->tiles[lod][tile]->data;
+    } else {
+        return NULL;
+    }
+}
+
+int gv_raster_cache_get_max()
+
+{
+    return gv_cache_max;
+}
+
+void gv_raster_cache_set_max( int new_max )
+
+{
+    gv_cache_max = new_max;
+
+    /* free space to ensure we stay below our limit */
+    while( gv_cache_used > gv_cache_max )
+    {
+        if( !gv_raster_cache_purge_lru() )
+            break;
+    }
+}
+
+int gv_raster_cache_get_used()
+
+{
+    return gv_cache_used;
+}
+
+void
+gv_raster_cache_put( gv_raster_cache *cache, gint tile, gint lod, void *data, gint size )
+{
+    gv_raster_cache_tile *tile_obj;
+
+    if( tile >= cache->max_tiles || lod >= cache->max_lod )
+    {
+        g_warning( "Illegal tile or lod in gv_raster_cache_put" );
+        return;
+    }
+
+    /* If there is an existing tile, just blow it away */
+    tile_obj = cache->tiles[lod][tile];
+    if( tile_obj != NULL )
+        gv_raster_cache_del( cache, tile, lod );
+
+    /* free space to ensure we stay below our limit */
+    while( gv_cache_used + size > gv_cache_max )
+    {
+        if( !gv_raster_cache_purge_lru() )
+            break;
+    }
+
+    /* create a new tile */
+    tile_obj = cache->tiles[lod][tile] = g_new0(gv_raster_cache_tile,1);
+    tile_obj->tile = tile;
+    tile_obj->lod = lod;
+    tile_obj->cache = cache;
+    tile_obj->data = data;
+    tile_obj->size = size;
+
+    /* increment total used space */
+    gv_cache_used += size;
+
+    /* put into LRU list */
+    gv_raster_cache_tile_touch( tile_obj );
+}
+
+void
+gv_raster_cache_del( gv_raster_cache *cache, gint tile, gint lod )
+{
+    gv_raster_cache_tile *tile_obj;
+
+    if( tile >= cache->max_tiles || lod >= cache->max_lod )
+        return;
+
+    if( cache->tiles[lod][tile] == NULL )
+        return;
+
+    tile_obj = cache->tiles[lod][tile];
+    cache->tiles[lod][tile] = NULL;
+
+    /* removed from LRU list */
+    if( lru_head == tile_obj )
+        lru_head = tile_obj->next;
+
+    if( lru_tail == tile_obj )
+        lru_tail = tile_obj->prev;
+
+    if( tile_obj->prev != NULL )
+        tile_obj->prev->next = tile_obj->next;
+    
+    if( tile_obj->next != NULL )
+        tile_obj->next->prev = tile_obj->prev;
+
+    /* Free tile data, and tile object itself */
+    gv_cache_used -= tile_obj->size;
+    g_free( tile_obj->data );
+    g_free( tile_obj );
+}
+
+/*
+ * Touch a cache tile, moving it to the end of the LRU list.
+ */
+static void gv_raster_cache_tile_touch( gv_raster_cache_tile * tile_obj )
+{
+    /* don't put the most reduced LOD tiles on LRU list
+       ... they are `locked in' */
+    if( tile_obj->lod == tile_obj->cache->max_lod-1 )
+        return;
+
+    /* Remove from LRU list (if it's there) */
+    if( lru_head == tile_obj )
+        lru_head = tile_obj->next;
+
+    if( lru_tail == tile_obj )
+        lru_tail = tile_obj->prev;
+
+    if( tile_obj->prev != NULL )
+        tile_obj->prev->next = tile_obj->next;
+    
+    if( tile_obj->next != NULL )
+        tile_obj->next->prev = tile_obj->prev;
+
+    /* Push on the tail of the LRU list */
+    if( lru_tail == NULL )
+    {
+        lru_head = lru_tail = tile_obj;
+        tile_obj->next = NULL;
+        tile_obj->prev = NULL;
+    }
+    else
+    {
+        lru_tail->next = tile_obj;
+        tile_obj->prev = lru_tail;
+        tile_obj->next = NULL;
+        lru_tail = tile_obj;
+    }
+}
+
+/*
+ * Purge the least recently used tile for a given level of detail. 
+ */
+static int 
+gv_raster_cache_purge_lru()
+{
+    if( lru_head == NULL )
+        return FALSE;
+
+    gv_raster_cache_del( lru_head->cache, lru_head->tile, lru_head->lod );
+
+    return TRUE;
+}
+
+/*
+ * Flush all tiles in the cache without destroying the cache. 
+ */
+void 
+gv_raster_cache_flush_all( gv_raster_cache *cache )
+{ 
+    gint i, lod;
+
+    for( lod = 0; lod < cache->max_lod; lod++ )
+    {
+        for( i = 0; i < cache->max_tiles; i++ )
+        {
+            gv_raster_cache_del( cache, i, lod );
+        }
+    }
+}
+
+int
+gv_raster_cache_get_best_lod( gv_raster_cache *cache, gint tile, gint lod )
+{
+    int retval, i;
+
+    if( cache->tiles[lod][tile] )
+    {
+	return lod;
+    } else {
+
+	retval = -1;
+
+	for( i = cache->max_lod-1; i >= 0; i-- )
+	{
+	    if( i == lod )
+		continue;
+
+	    if( cache->tiles[i][tile] )
+	    {
+		if( i > lod )
+		{
+		    retval = i;
+		} else {
+		    return i;
+		}
+	    }
+	}
+
+	return retval;
+    }
+}
+
+void
+gv_raster_cache_free( gv_raster_cache * cache )
+{
+    int   lod;
+
+    if( cache == NULL )
+        return;
+
+    gv_raster_cache_flush_all( cache );
+    
+    for( lod = 0; lod < cache->max_lod; lod++ )
+            g_free( cache->tiles[lod] );
+    
+    g_free( cache->tiles );
+
+    g_free( cache );
+}

Added: packages/openev/branches/upstream/current/gvrastercache.h
===================================================================
--- packages/openev/branches/upstream/current/gvrastercache.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrastercache.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * $Id: gvrastercache.h,v 1.10 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvRaster tile caching.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrastercache.h,v $
+ * Revision 1.10  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_RASTER_CACHE_H__
+#define __GV_RASTER_CACHE_H__
+
+#include <gtk/gtk.h>
+
+
+typedef struct _gv_raster_cache gv_raster_cache;
+typedef struct _gv_raster_cache_tile gv_raster_cache_tile;
+
+struct _gv_raster_cache_tile {
+    gv_raster_cache_tile *prev;
+    gv_raster_cache_tile *next;
+    gv_raster_cache      *cache;
+
+    int  tile;
+    int  lod;
+    int  lru;
+    int  size;
+    void *data;
+};
+
+struct _gv_raster_cache {
+    int max_lod;
+    int max_tiles;
+
+    gv_raster_cache_tile ***tiles;  /* double dimensioned as [lod][tile] */
+};
+
+gv_raster_cache *gv_raster_cache_new( gint max_tiles, gint max_lod );
+
+void gv_raster_cache_free( gv_raster_cache * );
+
+void *gv_raster_cache_get( gv_raster_cache *cache, gint tile, gint lod );
+
+void gv_raster_cache_put( gv_raster_cache *cache, gint tile, gint lod, void *data, gint size );
+
+int gv_raster_cache_get_best_lod( gv_raster_cache *cache, gint tile, gint lod );
+
+void gv_raster_cache_flush_all( gv_raster_cache * );
+void gv_raster_cache_del( gv_raster_cache *cache, gint tile, gint lod );
+int gv_raster_cache_get_max();
+void gv_raster_cache_set_max(int);
+int gv_raster_cache_get_used();
+
+#endif

Added: packages/openev/branches/upstream/current/gvrasterconvert.c
===================================================================
--- packages/openev/branches/upstream/current/gvrasterconvert.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterconvert.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,511 @@
+/******************************************************************************
+ * $Id: gvrasterconvert.c,v 1.20 2004/06/23 14:35:03 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster conversion functions (float->byte, apply lut, etc)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterconvert.c,v $
+ * Revision 1.20  2004/06/23 14:35:03  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.19  2004/04/02 17:01:02  gmwalter
+ * Updated nodata support for complex and
+ * rgb data.
+ *
+ * Revision 1.18  2003/05/18 11:02:56  andrey_kiselev
+ * Memory leak fixed.
+ *
+ * Revision 1.17  2001/10/17 20:12:53  warmerda
+ * fixed type casting warning
+ *
+ * Revision 1.16  2001/10/17 16:23:51  warmerda
+ * added support for composing complex lut and pct
+ *
+ * Revision 1.15  2001/08/21 23:44:47  warmerda
+ * fixed nodata_mask freeing logic
+ *
+ * Revision 1.14  2001/08/17 00:54:41  warmerda
+ * fixed nodata_mask leak
+ *
+ * Revision 1.13  2000/09/29 04:27:02  warmerda
+ * fixed nodata handling in grayscale case
+ *
+ * Revision 1.12  2000/08/25 20:11:07  warmerda
+ * added preliminary nodata support
+ *
+ * Revision 1.11  2000/06/26 15:13:11  warmerda
+ * include alpha in RGB if alpha blending set on
+ *
+ * Revision 1.10  2000/06/23 12:54:59  warmerda
+ * largely rewrote
+ *
+ * Revision 1.9  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "gvrasterlayer.h"
+
+static unsigned char *
+gv_raster_layer_gltile_complex( GvRasterLayer *layer, int tile, int lod, 
+                                int *format, int *type, int *needs_free )
+
+{
+    unsigned char *raw_data;
+    int i, pixel_count;
+    int * rgba_data, *LUT = (int *) layer->pc_lut;
+    unsigned char *nodata_mask = NULL;
+    unsigned char *rgba_ptr=NULL;
+
+    if( layer->pc_lut == NULL )
+    {								
+        g_warning("gv_raster_layer_gltile_complex without pc_lut!" );
+        return NULL;
+    }
+
+    pixel_count = (layer->tile_x >> lod) * (layer->tile_y >> lod);
+
+    if( layer->source_list[0].nodata_active )
+    {
+        nodata_mask = g_new( unsigned char, pixel_count );
+        memset( nodata_mask, 1, pixel_count );
+    }
+
+    if( layer->pc_lut_composed != NULL )
+        LUT = (int *) layer->pc_lut_composed;
+
+    raw_data = gv_raster_layer_srctile_xy_get( layer, 0, tile, lod, 
+                                               needs_free, nodata_mask );
+
+    rgba_data = g_new(int,pixel_count);
+        
+    for( i = 0; i < pixel_count; i++ )
+    {
+        rgba_data[i] = LUT[raw_data[i*2+1]+raw_data[i*2]*256];
+    }
+
+    if( nodata_mask != NULL )
+    {
+        rgba_ptr = (unsigned char *) rgba_data;
+        for( i = 0; i < pixel_count; i++ )
+        {
+            if( nodata_mask[i] == 0 )
+                rgba_ptr[i*4+3] = 0;
+        }
+        rgba_ptr = NULL;
+    }
+    
+    if( *needs_free )
+        g_free( raw_data );
+
+    if( nodata_mask != NULL )
+        g_free( nodata_mask );
+        
+    *format = GL_RGBA;
+    *type = GL_UNSIGNED_BYTE;
+    *needs_free = TRUE;
+
+    return (unsigned char *) rgba_data;
+}
+
+static unsigned char *
+gv_raster_layer_gltile_single( GvRasterLayer *layer, int tile, int lod, 
+                               int *format, int *type, int *needs_free )
+
+{
+    int            pixel_count;
+    unsigned char *raw_data;
+    unsigned char *nodata_mask = NULL;
+
+    pixel_count = (layer->tile_x >> lod) * (layer->tile_y >> lod);
+
+    if( layer->source_list[0].nodata_active )
+    {
+        nodata_mask = g_new( unsigned char, pixel_count );
+        memset( nodata_mask, 1, pixel_count );
+    }
+
+    raw_data = gv_raster_layer_srctile_xy_get( layer, 0, tile, lod, 
+                                               needs_free, nodata_mask );
+
+    *format = GL_LUMINANCE;
+    *type = GL_UNSIGNED_BYTE;
+
+    if( layer->pc_lut != NULL )
+    {								
+        int i;
+        int * rgba_data, *LUT = (int *) layer->pc_lut;
+
+        rgba_data = g_new(int,pixel_count);
+        
+        for( i = 0; i < pixel_count; i++ )
+        {
+            rgba_data[i] = LUT[raw_data[i]];
+        }
+
+        if( *needs_free )
+            g_free( raw_data );
+        
+        *needs_free = TRUE;
+        raw_data = (unsigned char *) rgba_data;
+        *format = GL_RGBA;
+        
+        if( nodata_mask != NULL )
+        {
+            for( i = 0; i < pixel_count; i++ )
+            {
+                if( nodata_mask[i] == 0 )
+                    rgba_data[i] = 0;
+            }
+        }
+    }
+    else if( nodata_mask != NULL )
+    {
+        int i;
+        unsigned char * la_data;
+
+        la_data = g_new(unsigned char,pixel_count*2);
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            if( nodata_mask[i] == 0 )
+            {
+                la_data[i*2+0] = 0;
+                la_data[i*2+1] = 0;
+            }
+            else
+            {
+                la_data[i*2+0] = raw_data[i];
+                la_data[i*2+1] = 255;
+            }
+        }
+        
+        if( *needs_free )
+            g_free( raw_data );
+        
+        *needs_free = TRUE;
+        raw_data = (unsigned char *) la_data;
+        *format = GL_LUMINANCE_ALPHA;
+    }
+
+    if( nodata_mask != NULL )
+        g_free( nodata_mask );
+
+    return raw_data;
+}
+
+static unsigned char *
+gv_raster_layer_gltile_rgba( GvRasterLayer *layer, int tile, int lod, 
+                             int *format, int *type, int *needs_free )
+
+{
+    unsigned char *red, *green, *blue, *alpha=NULL, *ret_data=NULL;
+    int free_red, free_green, free_blue, free_alpha=FALSE;
+    int pixel_count;
+    unsigned char *nodata_r=NULL, *nodata_g=NULL;
+    unsigned char *nodata_b=NULL, *nodata_a=NULL;
+
+    /* variables that are only used for complex bands*/
+    char *LUT=NULL;
+    int i;
+    unsigned char *cred=NULL, *cgreen=NULL, *cblue=NULL, *calpha=NULL;
+
+
+    /* Is alpha blending enabled? */
+    if( layer->gl_info.blend_enable )
+    {
+        *format = GL_RGBA;
+        *type = GL_UNSIGNED_BYTE;
+    }
+    else
+    {
+        *format = GL_RGB;
+        *type = GL_UNSIGNED_BYTE;
+    }
+
+    /* Get nodata information */
+    pixel_count = (layer->tile_x >> lod) * (layer->tile_y >> lod);
+
+    if( layer->source_list[0].nodata_active )
+    {
+        nodata_r = g_new( unsigned char, pixel_count );
+        memset( nodata_r, 1, pixel_count );
+    }
+
+    if( layer->source_list[1].nodata_active )
+    {
+        nodata_g = g_new( unsigned char, pixel_count );
+        memset( nodata_g, 1, pixel_count );
+    }
+
+    if( layer->source_list[2].nodata_active )
+    {
+        nodata_b = g_new( unsigned char, pixel_count );
+        memset( nodata_b, 1, pixel_count );
+    }
+
+    /* Get source data */
+
+    red   = gv_raster_layer_srctile_xy_get( layer, 0, tile, lod, &free_red,
+                                            nodata_r );
+    green = gv_raster_layer_srctile_xy_get( layer, 1, tile, lod, &free_green,
+                                            nodata_g );
+    blue  = gv_raster_layer_srctile_xy_get( layer, 2, tile, lod, &free_blue,
+                                            nodata_b );
+    
+    if( red == NULL || green == NULL || blue == NULL )
+    {
+        g_warning( "failed to fetch component in gv_raster_layer_gltile_rgba");
+        if ( nodata_r != NULL )
+            g_free( nodata_r );
+        if ( nodata_g != NULL )
+            g_free( nodata_g );
+        if ( nodata_b != NULL )
+            g_free( nodata_b );
+
+        return NULL;
+    }
+
+    cred = g_new( unsigned char, pixel_count );
+    if ( (layer->source_list[0].data != NULL) &&
+         (layer->source_list[0].data->gdal_type == GDT_CFloat32) )
+    {
+        if (layer->source_list[0].lut_rgba_composed != NULL)
+            LUT = (char *) layer->source_list[0].lut_rgba_composed;
+        else
+            LUT = (char *) layer->pc_lut_rgba_complex;
+        for ( i = 0; i < pixel_count; i++ )
+        {
+            cred[i] = LUT[red[i*2+1]*4+red[i*2]*4*256+0];
+        }
+    }
+    else
+    {
+        memcpy(cred, red, pixel_count);
+    }
+
+    cgreen = g_new( unsigned char, pixel_count );
+    if ((layer->source_list[1].data != NULL) &&
+        ( layer->source_list[1].data->gdal_type == GDT_CFloat32 ))
+    {
+        if (layer->source_list[1].lut_rgba_composed != NULL)
+            LUT = (char *) layer->source_list[1].lut_rgba_composed;
+        else
+            LUT = (char *) layer->pc_lut_rgba_complex;
+        for ( i = 0; i < pixel_count; i++ )
+        {
+            cgreen[i] = LUT[green[i*2+1]*4+green[i*2]*4*256+1];
+        }
+    }
+    else
+    {
+        memcpy(cgreen, green, pixel_count);
+    }
+
+
+    cblue = g_new( unsigned char, pixel_count );
+    if ( (layer->source_list[2].data != NULL) &&
+         (layer->source_list[2].data->gdal_type == GDT_CFloat32) )
+    {
+        if (layer->source_list[2].lut_rgba_composed != NULL)
+            LUT = (char *) layer->source_list[2].lut_rgba_composed;
+        else
+            LUT = (char *) layer->pc_lut_rgba_complex;
+        for ( i = 0; i < pixel_count; i++ )
+        {
+            cblue[i] = LUT[blue[i*2+1]*4+blue[i*2]*4*256+2];
+        }
+    }
+    else
+    {
+        memcpy(cblue, blue, pixel_count);
+    }
+
+        
+    if( *format == GL_RGBA )
+    {
+        if( layer->source_list[3].nodata_active )
+        {
+            nodata_a = g_new( unsigned char, pixel_count );
+            memset( nodata_a, 1, pixel_count );
+        }
+        alpha = gv_raster_layer_srctile_xy_get( layer, 3, tile, lod, 
+                                                &free_alpha, nodata_a );
+
+        calpha = g_new( unsigned char, pixel_count );
+        if ( (layer->source_list[3].data != NULL) &&
+             (layer->source_list[3].data->gdal_type == GDT_CFloat32) )
+        {
+            if (layer->source_list[3].lut_rgba_composed != NULL)
+                LUT = (char *) layer->source_list[3].lut_rgba_composed;
+            else
+                LUT = (char *) layer->pc_lut_rgba_complex;
+            for ( i = 0; i < pixel_count; i++ )
+            {
+                calpha[i] = LUT[alpha[i*2+1]*4+alpha[i*2]*4*256+0];
+            }
+        }
+        else
+        {
+            memcpy(calpha, alpha, pixel_count);
+        }
+
+    }
+
+    /* Interleave */
+
+    if( *format == GL_RGBA )
+    {
+        int i, pixel_count;
+
+        pixel_count = (layer->tile_x >> lod) * (layer->tile_y >> lod);
+        ret_data = (unsigned char *) g_malloc(pixel_count*4);
+        
+        for( i = 0; i < pixel_count; i++ )
+        {
+            ret_data[i*4  ] = cred[i];
+            ret_data[i*4+1] = cgreen[i];
+            ret_data[i*4+2] = cblue[i];
+            ret_data[i*4+3] = calpha[i];
+        }
+        /* If this really slows things down, then could combine
+           all of these into a single for loop by creating a
+           case-type statement:
+           if ((nodata_r != NULL) && (nodata_g != NULL) 
+               && (nodata_b != NULL) && (nodata_a != NULL))
+           {
+             (single for loop that assigns r,g,b,a and checks nodata_r,
+             nodata_g, nodata_b, nodata_a)
+           }
+           else if ((nodata_r != NULL) && (nodata_g != NULL)
+                    && (nodata_b != NULL))
+           {
+             (single for loop that assigns r,g,b,a and checks nodata_r,
+             nodata_g, nodata_b)
+           }
+           etc.
+        */
+        if (nodata_r != NULL)
+        {
+            for( i = 0; i < pixel_count; i++ )
+            {
+                if ( nodata_r[i] == 0 )
+                    ret_data[i*4+3] = 0;
+            }
+        }
+        if (nodata_g != NULL)
+        {
+            for( i = 0; i < pixel_count; i++ )
+            {
+                if ( nodata_g[i] == 0 )
+                    ret_data[i*4+3] = 0;
+            }
+        }
+        if (nodata_b != NULL)
+        {
+            for( i = 0; i < pixel_count; i++ )
+            {
+                if ( nodata_b[i] == 0 )
+                    ret_data[i*4+3] = 0;
+            }
+        }
+        if (nodata_a != NULL)
+        {
+            for( i = 0; i < pixel_count; i++ )
+            {
+                if ( nodata_a[i] == 0 )
+                    ret_data[i*4+3] = 0;
+            }
+        }
+    }
+    else
+    {
+        int i, pixel_count;
+
+        pixel_count = (layer->tile_x >> lod) * (layer->tile_y >> lod);
+        ret_data = (unsigned char *) g_malloc(pixel_count*3);
+        
+        for( i = 0; i < pixel_count; i++ )
+        {
+            ret_data[i*3  ] = cred[i];
+            ret_data[i*3+1] = cgreen[i];
+            ret_data[i*3+2] = cblue[i];
+        }
+    }
+    *needs_free = TRUE;
+
+    if( free_red )
+        g_free( red );
+
+    if( free_green )
+        g_free( green );
+
+    if( free_blue )
+        g_free( blue );
+
+    if( free_alpha )
+        g_free( alpha );
+
+    if ( nodata_r != NULL )
+        g_free( nodata_r );
+    if ( nodata_g != NULL )
+        g_free( nodata_g );
+    if ( nodata_b != NULL )
+        g_free( nodata_b );
+    if ( nodata_a != NULL )
+        g_free( nodata_a );
+    if ( cred != NULL );
+        g_free( cred );
+    if ( cgreen != NULL );
+        g_free( cgreen );
+    if ( cblue != NULL );
+        g_free( cblue );
+    if ( calpha != NULL );
+        g_free( calpha );
+
+    return ret_data;
+}
+
+unsigned char *
+gv_raster_layer_gltile_get( GvRasterLayer *layer, int tile, int lod, 
+                            int *format, int *type, int *needs_free )
+
+{
+    if( layer->mode == GV_RLM_SINGLE )
+        return gv_raster_layer_gltile_single( layer, tile, lod, format, type,
+                                              needs_free );
+    else if( layer->mode == GV_RLM_RGBA )
+        return gv_raster_layer_gltile_rgba( layer, tile, lod, format, type,
+                                            needs_free );
+    else if( layer->mode == GV_RLM_COMPLEX )
+        return gv_raster_layer_gltile_complex( layer, tile, lod, format, type,
+                                               needs_free );
+    else 
+    {
+        g_warning( "unexpected layer mode in gv_raster_layer_gltile_get()" );
+        return NULL;
+    }
+        
+}

Added: packages/openev/branches/upstream/current/gvrasterize.c
===================================================================
--- packages/openev/branches/upstream/current/gvrasterize.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterize.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,353 @@
+/******************************************************************************
+ * $Id: gvrasterize.c,v 1.5 2004/11/19 23:59:37 gmwalter Exp $
+ *
+ * Project:  CIETMAP / OpenEV
+ * Purpose:  Vector rasterization code high level API (initially GvAreaShapes
+ *           to GDAL raster).
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterize.c,v $
+ * Revision 1.5  2004/11/19 23:59:37  gmwalter
+ * Check in Aude's rasterization updates.
+ *
+ * Revision 1.4  2001/03/29 14:59:56  warmerda
+ * added fill_short flag to control handling of slivers
+ *
+ * Revision 1.3  2000/12/04 01:21:48  warmerda
+ * fixed allocation of panParts
+ *
+ * Revision 1.2  2000/09/15 02:21:10  warmerda
+ * Fixed off by one error in gvrasterize.c
+ *
+ * Revision 1.1  2000/09/15 01:29:04  warmerda
+ * New
+ *
+ */
+
+#include "gvrasterize.h"
+#include "cpl_conv.h"
+
+typedef struct {
+    unsigned char * pabyChunkBuf;
+    int nXSize;
+    int nYSize;
+    GDALDataType eType;
+    double dfBurnValue;
+} GvRasterizeInfo;
+
+/************************************************************************/
+/*                           gvBurnScanline()                           */
+/************************************************************************/
+
+void gvBurnScanline( void *pCBData, int nY, int nXStart, int nXEnd )
+
+{
+    GvRasterizeInfo *psInfo = (GvRasterizeInfo *) pCBData;
+
+    CPLAssert( nY >= 0 && nY < psInfo->nYSize );
+    CPLAssert( nXStart <= nXEnd );
+    CPLAssert( nXStart < psInfo->nXSize );
+
+   /*  CPLAssert( nXEnd > 0 ); */
+    CPLAssert( nXEnd >= 0 );
+
+    if( nXStart < 0 )
+        nXStart = 0;
+    if( nXEnd >= psInfo->nXSize )
+        nXEnd = psInfo->nXSize - 1;
+
+    if( psInfo->eType == GDT_Byte )
+    {
+        unsigned char *pabyInsert;
+        unsigned char nBurnValue = (unsigned char) psInfo->dfBurnValue;
+
+        pabyInsert = psInfo->pabyChunkBuf + nY * psInfo->nXSize + nXStart;
+        memset( pabyInsert, nBurnValue, nXEnd - nXStart + 1 );
+    }
+    else
+    {
+        int	nPixels = nXEnd - nXStart + 1;
+        float   *pafInsert;
+
+        pafInsert = ((float *) psInfo->pabyChunkBuf) 
+            + nY * psInfo->nXSize + nXStart;
+
+        while( nPixels-- > 0 )
+            *(pafInsert++) = psInfo->dfBurnValue;
+    }
+}
+
+
+/************************************************************************/
+/*                       gv_rasterize_one_shape()                       */
+/************************************************************************/
+static void 
+gv_rasterize_one_shape( unsigned char * pabyChunkBuf, int nYOff, int nYSize,
+                        GDALDataType eType, GvRaster * raster, 
+                        GvShape * shape, double dfBurnValue, int bFillShort )
+
+{
+    int   nParts, *panPartSize, nPoints, i;
+    llPoint *pasPoints;
+    GvRasterizeInfo sInfo;
+
+    sInfo.nXSize = raster->width;
+    sInfo.nYSize = nYSize;
+    sInfo.pabyChunkBuf = pabyChunkBuf;
+    sInfo.eType = eType;
+    sInfo.dfBurnValue = dfBurnValue;
+
+/* -------------------------------------------------------------------- */
+/*      Prepare parts, and points arrays                                */
+/* -------------------------------------------------------------------- */
+    nParts = gv_shape_get_rings( shape );
+    panPartSize = (int *) CPLMalloc(nParts * sizeof(int));
+
+    nPoints = 0;
+    for( i = 0; i < nParts; i++ )
+    {
+        panPartSize[i] = gv_shape_get_nodes( shape, i );
+        nPoints += panPartSize[i];
+    }
+
+    pasPoints = (llPoint *) CPLMalloc(sizeof(llPoint) * nPoints);
+    
+/* -------------------------------------------------------------------- */
+/*      Transform points, taking into account our chunk buffer offset.  */
+/* -------------------------------------------------------------------- */
+    nPoints = 0;
+    for( i = 0; i < nParts; i++ )
+    {
+        int    node, part_points = 0;
+
+        for( node = 0; node < panPartSize[i]; node++ )
+        {
+            double	dfX, dfY, dfZ;
+            int		nX, nY;
+
+            dfX = gv_shape_get_x( shape, i, node );
+            dfY = gv_shape_get_y( shape, i, node );
+            dfZ = 0.0;
+            gv_raster_georef_to_pixel( raster, &dfX, &dfY, &dfZ );
+            
+            nX = (int) dfX;
+            nY = ((int) dfY) - nYOff;
+
+            if( node == 0
+                || pasPoints[nPoints-1].x != nX 
+                || pasPoints[nPoints-1].y != nY )
+            {
+                pasPoints[nPoints].x = nX;
+                pasPoints[nPoints].y = nY;
+                nPoints++;
+                part_points++;
+            }
+        }
+        panPartSize[i] = part_points;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Perform the rasterization.                                      */
+/* -------------------------------------------------------------------- */
+    llImageFilledPolygon( raster->width, nYSize, 
+                          nParts, panPartSize, pasPoints, bFillShort,
+                          gvBurnScanline, &sInfo );
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    CPLFree( pasPoints );
+    CPLFree( panPartSize );
+}
+
+
+/************************************************************************/
+/*                       gv_rasterize_one_shape()                       */
+/************************************************************************/
+static void 
+gv_rasterize_new_one_shape( unsigned char * pabyChunkBuf, int nYOff, int nYSize,
+                            GDALDataType eType, GvRaster * raster, 
+                            GvShape * shape, double dfBurnValue, int method )
+
+{
+    int   nParts, *panPartSize, nPoints, i;
+    dllPoint *pasPoints;
+    GvRasterizeInfo sInfo;
+
+    sInfo.nXSize = raster->width;
+    sInfo.nYSize = nYSize;
+    sInfo.pabyChunkBuf = pabyChunkBuf;
+    sInfo.eType = eType;
+    sInfo.dfBurnValue = dfBurnValue;
+
+/* -------------------------------------------------------------------- */
+/*      Prepare parts, and points arrays                                */
+/* -------------------------------------------------------------------- */
+    nParts = gv_shape_get_rings( shape );
+    panPartSize = (int *) CPLMalloc(nParts * sizeof(int));
+
+    nPoints = 0;
+    for( i = 0; i < nParts; i++ )
+    {
+        panPartSize[i] = gv_shape_get_nodes( shape, i );
+        nPoints += panPartSize[i];
+    }
+
+    pasPoints = (dllPoint *) CPLMalloc(sizeof(dllPoint) * nPoints);
+    
+/* -------------------------------------------------------------------- */
+/*      Transform points, taking into account our chunk buffer offset.  */
+/* -------------------------------------------------------------------- */
+    nPoints = 0;
+    for( i = 0; i < nParts; i++ )
+    {
+        int    node, part_points = 0;
+
+        for( node = 0; node < panPartSize[i]; node++ )
+        {
+            double	dfX, dfY, dfZ;
+
+
+            dfX = gv_shape_get_x( shape, i, node );
+            dfY = gv_shape_get_y( shape, i, node );
+            dfZ = 0.0;
+            gv_raster_georef_to_pixel( raster, &dfX, &dfY, &dfZ );
+    
+
+            if( node == 0
+                || pasPoints[nPoints-1].x != dfX 
+                || pasPoints[nPoints-1].y != dfY )
+            {
+                pasPoints[nPoints].x = dfX;
+                pasPoints[nPoints].y = dfY - nYOff;
+                nPoints++;
+                part_points++;
+            }
+        }
+        panPartSize[i] = part_points;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Perform the rasterization.                                      */
+/* -------------------------------------------------------------------- */
+    dllImageFilledPolygon( raster->width, nYSize, 
+                           nParts, panPartSize, pasPoints, 
+                           gvBurnScanline, &sInfo, method );
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    CPLFree( pasPoints );
+    CPLFree( panPartSize );
+}
+
+
+/************************************************************************/
+/*                     gv_raster_rasterize_shapes()                     */
+/************************************************************************/
+
+int gv_raster_rasterize_shapes( GvRaster *raster, 
+                                int shape_count, GvShape **shapes,
+                                double dfBurnValue, int bFillShort )
+
+{
+    GDALDataType   eType;
+    int            nYChunkSize, nScanlineBytes;
+    unsigned char *pabyChunkBuf;
+    int            iY;
+    GvRasterChangeInfo change_info;
+
+/* -------------------------------------------------------------------- */
+/*      Establish a chunksize to operate on.  The larger the chunk      */
+/*      size the less times we need to make a pass through all the      */
+/*      shapes.                                                         */
+/* -------------------------------------------------------------------- */
+    if( raster->gdal_type == GDT_Byte )
+        eType = GDT_Byte;
+    else
+        eType = GDT_Float32;
+
+    nScanlineBytes = raster->width * (GDALGetDataTypeSize(eType)/8);
+    nYChunkSize = 10000000 / nScanlineBytes;
+    if( nYChunkSize > raster->height )
+        nYChunkSize = raster->height;
+
+    pabyChunkBuf = (unsigned char *) VSIMalloc(nYChunkSize * nScanlineBytes);
+    if( pabyChunkBuf == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, 
+                  "Unable to allocate rasterization buffer." );
+        return FALSE;
+    }
+
+/* ==================================================================== */
+/*      Loop over image in designated chunks.                           */
+/* ==================================================================== */
+    for( iY = 0; iY < raster->height; iY += nYChunkSize )
+    {
+        int	nThisYChunkSize;
+        int     iShape;
+
+        nThisYChunkSize = nYChunkSize;
+        if( nThisYChunkSize + iY > raster->height )
+            nThisYChunkSize = raster->height - iY;
+
+        GDALRasterIO( raster->gdal_band, GF_Read, 
+                      0, iY, raster->width, nThisYChunkSize, 
+                      pabyChunkBuf, raster->width, nThisYChunkSize, eType, 
+                      0, 0 );
+
+        for( iShape = 0; iShape < shape_count; iShape++ )
+        {
+
+            if (bFillShort < 2 )
+                gv_rasterize_one_shape( pabyChunkBuf, iY, nThisYChunkSize,
+                                        eType, raster, 
+                                        shapes[iShape], dfBurnValue, bFillShort );
+            else
+                gv_rasterize_new_one_shape( pabyChunkBuf, iY, nThisYChunkSize,
+                                            eType, raster, 
+                                            shapes[iShape], dfBurnValue, bFillShort - 2 );
+        }
+
+        GDALRasterIO( raster->gdal_band, GF_Write, 
+                      0, iY, raster->width, nThisYChunkSize, 
+                      pabyChunkBuf, raster->width, nThisYChunkSize, eType, 
+                      0, 0 );
+    }
+    
+    VSIFree( pabyChunkBuf );
+
+/* -------------------------------------------------------------------- */
+/*      Invalidate the raster to force a reload.                        */
+/* -------------------------------------------------------------------- */
+    change_info.change_type = GV_CHANGE_REPLACE;
+    change_info.x_off = 0;
+    change_info.y_off = 0;
+    change_info.width = raster->width;
+    change_info.height = raster->height;
+
+    gv_data_changed( GV_DATA(raster), &change_info );
+
+    return TRUE;
+}
+

Added: packages/openev/branches/upstream/current/gvrasterize.h
===================================================================
--- packages/openev/branches/upstream/current/gvrasterize.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterize.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,81 @@
+/******************************************************************************
+ * $Id: gvrasterize.h,v 1.3 2004/11/19 23:59:37 gmwalter Exp $
+ *
+ * Project:  CIETMAP / OpenEV
+ * Purpose:  Vector rasterization code (initially GvAreaShapes to GDAL raster).
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterize.h,v $
+ * Revision 1.3  2004/11/19 23:59:37  gmwalter
+ * Check in Aude's rasterization updates.
+ *
+ * Revision 1.2  2001/03/29 14:59:56  warmerda
+ * added fill_short flag to control handling of slivers
+ *
+ * Revision 1.1  2000/09/15 01:29:04  warmerda
+ * New
+ *
+ */
+
+#ifndef __GVRASTERIZE_H__
+#define __GVRASTERIZE_H__
+
+#include "gdal.h"
+#include "gvraster.h"
+#include "gvshapes.h"
+
+CPL_C_START
+
+/* -------------------------------------------------------------------- */
+/*      Low level rasterizer API.                                       */
+/* -------------------------------------------------------------------- */
+typedef struct {
+    int x, y;
+} llPoint;
+
+typedef struct {
+    double x, y;
+} dllPoint;
+
+
+typedef void (*llScanlineFunc)( void *pCBData, int nY, int nXStart, int nXEnd);
+
+
+void llImageFilledPolygon( int nRasterXSize, int nRasterYSize, 
+                       int nPartCount, int * panPartSize, llPoint * pasPoints,
+                       int bFillShort,
+                       llScanlineFunc pfnScanlineFunc, void * pCBData );
+                                            
+void dllImageFilledPolygon(int nRasterXSize, int nRasterYSize, 
+                          int nPartCount, int *panPartSize, dllPoint *p,
+                          llScanlineFunc pfnScanlineFunc, void *pCBData, 
+                          int method);
+
+
+/* -------------------------------------------------------------------- */
+/*      High level API - GvShapes burned into GDAL raster.              */
+/* -------------------------------------------------------------------- */
+int gv_raster_rasterize_shapes( GvRaster *raster, 
+                                int shape_count, GvShape **shapes,
+                                double dfBurnValue, int bFillShort );
+
+#endif /* ndef __GVRASTERIZE_H__ */

Added: packages/openev/branches/upstream/current/gvrasterlayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvrasterlayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterlayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2259 @@
+/******************************************************************************
+ * $Id: gvrasterlayer.c,v 1.87 2005/08/30 12:44:11 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster display layer (managed textures, redraw, etc)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterlayer.c,v $
+ * Revision 1.87  2005/08/30 12:44:11  andrey_kiselev
+ * Fixed compilation of macro in gv_raster_layer_view_extents() (gcc 4 related).
+ *
+ * Revision 1.86  2004/06/23 14:35:04  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.85  2004/02/20 10:40:36  andrey_kiselev
+ * Use gv_raster_get_nodata() instead of GDALGetNoDataValue().
+ *
+ * Revision 1.84  2004/02/18 16:57:41  andrey_kiselev
+ * Fixed setting min/max levels in gv_raster_layer_init().
+ *
+ * Revision 1.83  2003/09/11 20:00:31  gmwalter
+ * Add ability to specify a preferred polynomial order for warping a raster,
+ * and add "safe mode" (only used if ATLANTIS_BUILD is defined).
+ *
+ * Revision 1.82  2003/02/20 19:27:16  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.81  2003/02/07 20:06:49  andrey_kiselev
+ * Memory leaks fixed.
+ *
+ * Revision 1.80  2003/02/06 08:19:24  warmerda
+ * Added support for force_load property on GvRasterLayer
+ *
+ * Revision 1.79  2003/01/24 16:54:52  warmerda
+ * turn some g_warning()s into CPLDebug calls for CIETmap/Paul
+ *
+ * Revision 1.78  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.77  2002/10/29 05:43:20  warmerda
+ * added some debugging logic in texture load code
+ *
+ * Revision 1.76  2002/04/12 14:40:36  gmwalter
+ * Removed the gvmesh rescale function (not needed because of view area
+ * rescaling).
+ *
+ * Revision 1.74  2002/03/20 19:19:00  warmerda
+ * add support for exact_render flag on GvViewArea
+ *
+ * Revision 1.73  2002/03/07 02:31:56  warmerda
+ * added default_height to add_height functions
+ *
+ * Revision 1.72  2001/12/10 18:48:19  warmerda
+ * ifdef out disconnect from prototype data in teardown
+ *
+ * Revision 1.71  2001/11/29 15:53:42  warmerda
+ * added autoscale_samples preference
+ *
+ * Revision 1.70  2001/11/28 19:23:04  warmerda
+ * Added logic to keep track if the mesh is dirty (out of date), and to
+ * refresh it before a redraw.  It is marked dirty when the prototype data
+ * emits a geotransform-changed signal.
+ *
+ * Revision 1.69  2001/10/25 20:30:46  warmerda
+ * added interp_mode preference to control default subpixel interp
+ *
+ * Revision 1.68  2001/10/17 16:23:51  warmerda
+ * added support for composing complex lut and pct
+ *
+ * Revision 1.67  2001/10/16 18:50:29  warmerda
+ * added autoscale and histogram functions
+ *
+ * Revision 1.66  2001/10/12 17:44:18  warmerda
+ * avoid extra redraws when many raster layers displayed
+ *
+ * Revision 1.65  2001/09/25 20:04:16  warmerda
+ * cleanup idle tasks on finalize
+ *
+ * Revision 1.64  2001/08/22 16:20:38  warmerda
+ * use gv_mesh_reset_to_identity for setting to raw
+ *
+ * Revision 1.63  2001/08/08 02:57:55  warmerda
+ * removed unused support for normals
+ *
+ * Revision 1.62  2001/07/24 21:21:45  warmerda
+ * added EV style phase colormap
+ *
+ * Revision 1.61  2001/07/16 15:20:12  warmerda
+ * default to magnitude for complex images, instead of phase/magnitude
+ *
+ * Revision 1.60  2001/07/03 14:26:05  warmerda
+ * added set/get raw ability
+ *
+ * Revision 1.59  2001/01/30 19:34:29  warmerda
+ * make gv_raster_layer_purge_all_textures() public
+ *
+ * Revision 1.58  2001/01/30 14:32:32  warmerda
+ * No longer purges textures on a display-change.  Added
+ * gv_raster_layer_purge_all_textures(), to clear textures on in cases where
+ * this is necessary.  Now can change alpha with texture_mode_set() without
+ * throwing away textures.
+ *
+ * Revision 1.57  2000/10/06 15:35:33  warmerda
+ * set nodata value by default if present on GDALRasterBand
+ *
+ * Revision 1.56  2000/08/31 20:20:19  warmerda
+ * always destroy old textures before resizing them in ...texture_load
+ *
+ * Revision 1.55  2000/08/25 20:12:47  warmerda
+ * added preliminary nodata support
+ *
+ * Revision 1.54  2000/08/24 17:00:45  srawlin
+ * fixed 3D LOD calculation to account for image flip
+ *
+ * Revision 1.53  2000/08/24 15:48:39  srawlin
+ * Added flip in 3D mode tile list calculation
+ *
+ * Revision 1.52  2000/08/18 21:32:29  warmerda
+ * Fixed bug where tiles would pile up on the missing_tex list over multiple
+ * calls to the gv_raster_layer_draw() if there were no intermediate calls
+ * to the idle handler.
+ *
+ * Introduced a hack into gv_raster_layer_texture_load() to call glGetError()
+ * every now and then to try and work around a flaw in Xi Graphics GL drivers.
+ *
+ * Revision 1.51  2000/08/09 17:37:58  warmerda
+ * disconnect view callback on teardown, clear sources on destroy
+ *
+ * Revision 1.50  2000/07/25 17:51:07  warmerda
+ * change debug to use CPLDebug
+ *
+ * Revision 1.49  2000/07/25 14:19:49  warmerda
+ * dequeue old draw tasks before setting new ones in draw
+ *
+ * Revision 1.48  2000/07/24 21:25:37  warmerda
+ * always set fragment color, even if modulate off
+ *
+ * Revision 1.47  2000/07/18 15:32:53  warmerda
+ * set upper bound on redraw time to 2.0 seconds
+ *
+ * Revision 1.46  2000/07/18 15:04:50  warmerda
+ * tuning of idle handler texture logic
+ *
+ * Revision 1.45  2000/07/17 19:47:56  warmerda
+ * try to wait 3*redraw time
+ *
+ * Revision 1.44  2000/07/17 19:31:50  warmerda
+ * added tentative support for scaling redraw wait to actual redraw time
+ *
+ * Revision 1.43  2000/07/07 17:54:42  warmerda
+ * Modified 3D tile-in-view selection to use corners of tiles, and to compute
+ * the view cone based on a windows corners.
+ *
+ * Revision 1.42  2000/07/03 20:58:31  warmerda
+ * eye_pos in georef coordinates now
+ *
+ * Revision 1.40  2000/06/29 14:38:37  warmerda
+ * use GvManager for idle tasks
+ *
+ * Revision 1.39  2000/06/28 12:09:40  warmerda
+ * initial fragment color to all white
+ *
+ * Revision 1.38  2000/06/27 21:25:41  warmerda
+ * rewrote texture caching completely
+ *
+ * Revision 1.37  2000/06/23 12:56:18  warmerda
+ * added multiple GvRasterSource support
+ *
+ * Revision 1.36  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <gtk/gtksignal.h>
+#include "gvrasterlayer.h"
+#include "gvrastertypes.h"
+#include "gvrasteraverage.h"
+#include "gvrasterlut.h"
+#include "gvmanager.h"
+#include "ogr_srs_api.h"
+
+
+#if !defined(GL_CLAMP_TO_EDGE) && defined(GL_CLAMP)
+#  define GL_CLAMP_TO_EDGE GL_CLAMP
+#endif
+
+
+static void gv_raster_layer_class_init(GvRasterLayerClass *klass);
+static void gv_raster_layer_init(GvRasterLayer *layer);
+static void gv_raster_layer_draw(GvLayer *layer, GvViewArea *area);
+static void gv_raster_layer_extents(GvLayer *layer, GvRect *rect);
+static gint gv_raster_layer_texture_load( GvRasterLayer *layer, gint tile_num, gint lod );
+static gint gv_raster_layer_idle_handler( gpointer data );
+static void gv_raster_layer_setup( GvLayer *layer, GvViewArea *view );
+static void gv_raster_layer_teardown( GvLayer *layer, GvViewArea *view );
+static void gv_raster_layer_state_changed( GvViewArea *view, GvRasterLayer *layer );
+static void gv_raster_layer_finalize( GtkObject *object );
+static void gv_raster_layer_destroy( GtkObject *object );
+static gint gv_raster_layer_texture_size( gint format, gint type );
+static void gv_raster_layer_gl_disp_set( GvRasterLayer *layer );
+static void gv_raster_layer_gl_disp_unset( void );
+static gint gv_raster_layer_reproject( GvLayer *layer, 
+                                       const char *new_projection );
+static int gvrl_to_georef_cb( int pt_count, double *x, double *y, double *z, 
+                              void *cb_data );
+static int gvrl_to_raw_cb( int pt_count, double *x, double *y, double *z, 
+                           void *cb_data );
+static int
+gv_mesh_transform_with_func(GvMesh *mesh, 
+                            int (*trfunc)(int,double*,double*,double*,void *),
+                            void *cb_data );
+
+struct _GvRasterLayerIdleInfo {
+    GvRasterLayer *layer;
+    GvViewArea *area;
+    int lod;
+};
+
+enum {
+    GV_RASTER_LAYER_BLEND_OFF = 0,
+    GV_RASTER_LAYER_BLEND_FILTER,
+    GV_RASTER_LAYER_BLEND_ADD,
+    GV_RASTER_LAYER_BLEND_MULTIPLY,
+    GV_RASTER_LAYER_BLEND_CUSTOM
+};
+
+GtkType
+gv_raster_layer_get_type(void)
+{
+    static GtkType raster_layer_type = 0;
+
+    if (!raster_layer_type)
+    {
+	static const GtkTypeInfo area_layer_info =
+	{
+	    "GvRasterLayer",
+	    sizeof(GvRasterLayer),
+	    sizeof(GvRasterLayerClass),
+	    (GtkClassInitFunc) gv_raster_layer_class_init,
+	    (GtkObjectInitFunc) gv_raster_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	raster_layer_type = gtk_type_unique(gv_layer_get_type(),
+					    &area_layer_info);
+    }
+    return raster_layer_type;
+}
+
+static void
+gv_raster_layer_init(GvRasterLayer *layer)
+{
+    int   i;
+
+    layer->prototype_data = NULL;
+    layer->source_count = 0;
+    layer->mode = 0;
+    layer->mesh = NULL;
+    layer->pc_lut = NULL;
+
+    for( i = 0; i < MAX_RASTER_SOURCE; i++ )
+    {
+        layer->source_list[i].lut = NULL;
+        layer->source_list[i].lut_rgba_composed = NULL;
+        layer->source_list[i].data = NULL;
+        layer->source_list[i].const_value = 0;
+        layer->source_list[i].min = 0.0;
+        layer->source_list[i].max = 255.0;
+        layer->source_list[i].nodata_active = FALSE;
+        layer->source_list[i].nodata_real = -1e8;
+        layer->source_list[i].nodata_imaginary = 0.0;
+    }
+}
+
+static void
+gv_raster_layer_class_init(GvRasterLayerClass *klass)
+{
+    GvLayerClass *layer_class;
+
+    layer_class = (GvLayerClass*) klass;
+    layer_class->draw = gv_raster_layer_draw;
+    layer_class->extents_request = gv_raster_layer_extents;
+    layer_class->setup = gv_raster_layer_setup;
+    layer_class->teardown = gv_raster_layer_teardown;
+    layer_class->reproject = gv_raster_layer_reproject;
+    
+    ((GtkObjectClass *) klass)->destroy = gv_raster_layer_destroy;
+    ((GtkObjectClass *) klass)->finalize = gv_raster_layer_finalize;
+}
+
+GtkObject *
+gv_raster_layer_new(int mode, GvRaster *prototype_data, 
+                    GvProperties creation_properties)
+{
+    GvRasterLayer *layer;
+    double nodata_real=-1e8, nodata_imaginary=0.0;
+    int nodata_active = FALSE;
+    const char *interp_pref;
+
+    if( prototype_data == NULL )
+        return NULL;
+
+    layer = GV_RASTER_LAYER(gtk_type_new(gv_raster_layer_get_type()));
+
+    layer->tile_x = prototype_data->tile_x;
+    layer->tile_y = prototype_data->tile_y;
+    layer->prototype_data = prototype_data;
+    layer->pc_lut = NULL;
+    layer->pc_lut_composed = NULL;
+    layer->pc_lut_rgba_complex = NULL;
+
+    if( mode == GV_RLM_AUTO )
+    {
+        GDALColorInterp interp;
+
+        interp = GDALGetRasterColorInterpretation( prototype_data->gdal_band );
+        if( GDALDataTypeIsComplex( prototype_data->gdal_type ) )
+            mode = GV_RLM_COMPLEX;
+        else if( interp == GCI_RedBand 
+                 || interp == GCI_GreenBand
+                 || interp == GCI_BlueBand )
+            mode = GV_RLM_RGBA;
+        else
+            mode = GV_RLM_SINGLE;
+    }
+    layer->mode = mode;
+
+    if( gv_properties_get(&creation_properties,"mesh_lod") != NULL)
+    {
+        layer->mesh = gv_mesh_new_identity( prototype_data, 
+                    atoi(gv_properties_get(&creation_properties,"mesh_lod")));
+    } else {
+        layer->mesh = gv_mesh_new_identity( prototype_data, 0 );
+    }
+
+    layer->mesh_is_raw = TRUE;
+    layer->mesh_is_dirty = FALSE;
+    gv_mesh_transform_with_func( layer->mesh, gvrl_to_raw_cb, 
+                                 prototype_data );
+
+    if( gv_properties_get(&creation_properties,"raw") == NULL )
+    {
+        gv_mesh_transform_with_func( layer->mesh, gvrl_to_georef_cb, 
+                                     prototype_data );
+
+        gv_data_set_projection( GV_DATA(layer), 
+                           gv_data_get_projection(GV_DATA(prototype_data)) );
+
+        layer->mesh_is_raw = FALSE;
+    }
+
+    memset( layer->source_list + 0, 0, 
+            sizeof(GvRasterSource) * MAX_RASTER_SOURCE);
+
+    /* Default GL parameters */
+    layer->gl_info.blend_enable = 0;
+    layer->gl_info.alpha_test = 0;
+    
+    layer->gl_info.tex_env_mode = GL_REPLACE;
+    layer->gl_info.fragment_color[0] = 1.0;
+    layer->gl_info.fragment_color[1] = 1.0;
+    layer->gl_info.fragment_color[2] = 1.0;
+    layer->gl_info.fragment_color[3] = 1.0;
+
+    layer->gl_info.s_wrap = GL_CLAMP;
+    layer->gl_info.t_wrap = GL_CLAMP;
+
+    interp_pref = gv_properties_get(&creation_properties,"interp_mode");
+    if( interp_pref == NULL )
+        interp_pref = gv_manager_get_preference(gv_get_manager(),
+                                                "interp_mode");
+
+#ifdef ATLANTIS_BUILD
+    /* Default to nearest for atlantis builds */
+    if( interp_pref == NULL || strcmp(interp_pref,"linear") != 0 )
+    {
+        layer->gl_info.mag_filter = GL_NEAREST;
+        layer->gl_info.min_filter = GL_NEAREST;
+    }
+    else
+    {
+        layer->gl_info.mag_filter = GL_LINEAR;
+        layer->gl_info.min_filter = GL_LINEAR;
+    }
+#else
+    if( interp_pref == NULL || strcmp(interp_pref,"nearest") != 0 )
+    {
+        layer->gl_info.mag_filter = GL_LINEAR;
+        layer->gl_info.min_filter = GL_LINEAR;
+    }
+    else
+    {
+        layer->gl_info.mag_filter = GL_NEAREST;
+        layer->gl_info.min_filter = GL_NEAREST;
+    }
+#endif
+    /* Setup texture related information */
+    layer->tile_list = g_array_new( FALSE, FALSE, sizeof( int ) ) ;
+    layer->missing_tex = g_array_new( FALSE, FALSE, sizeof( int ) );
+    
+    /* Allocate texture structures */
+
+    if( ( layer->textures = g_new0( GvRasterLayerTexObj *, 
+                                    prototype_data->max_tiles ) ) == NULL )
+    {
+	return NULL;
+    }
+
+    /* FIXME ... should we always do this? */
+    gv_data_set_parent(GV_DATA(layer), GV_DATA(prototype_data));
+
+    /* check for nodata value */
+    nodata_active = gv_raster_get_nodata( prototype_data, &nodata_real );
+
+    /* Setup mode dependent information */
+    switch( mode )
+    {
+      case GV_RLM_SINGLE:
+        layer->source_count = 1;
+        if( GDALGetRasterColorTable( prototype_data->gdal_band ) != NULL )
+        {
+            gv_raster_layer_set_source( layer, 0, prototype_data, 0, 255.0,
+                                        0, NULL, nodata_active, nodata_real, 
+                                        nodata_imaginary );
+            gv_raster_layer_apply_gdal_color_table( layer, 
+                    GDALGetRasterColorTable( prototype_data->gdal_band ) );
+        }
+        else
+            gv_raster_layer_set_source( layer, 0, prototype_data, 
+                                    prototype_data->min, prototype_data->max, 
+                                    0, NULL, nodata_active, nodata_real, 
+                                    nodata_imaginary );
+        break;
+
+      case GV_RLM_COMPLEX:
+        layer->source_count = 1;
+        layer->pc_lut = NULL;
+        gv_raster_layer_lut_color_wheel_new_ev(layer, FALSE, TRUE );
+        gv_raster_layer_set_source( layer, 0, prototype_data, 
+                                    prototype_data->min, 
+                                    prototype_data->max, 
+                                    0, NULL, nodata_active, nodata_real, 
+                                    nodata_imaginary );
+        break;
+
+      case GV_RLM_RGBA:
+        layer->source_count = 4;
+
+        layer->pc_lut_rgba_complex = NULL;
+        gv_raster_layer_lut_color_wheel_new_ev(layer, FALSE, TRUE );
+
+        gv_raster_layer_set_source( layer, 0, prototype_data, 
+                                    prototype_data->min, prototype_data->max, 
+                                    0, NULL, nodata_active, nodata_real, 
+                                    nodata_imaginary );
+        gv_raster_layer_set_source( layer, 1, prototype_data, 
+                                    prototype_data->min, prototype_data->max, 
+                                    0, NULL, nodata_active, nodata_real, 
+                                    nodata_imaginary );
+        gv_raster_layer_set_source( layer, 2, prototype_data, 
+                                    prototype_data->min, prototype_data->max, 
+                                    0, NULL, nodata_active, nodata_real, 
+                                    nodata_imaginary );
+        gv_raster_layer_set_source( layer, 3, NULL, 0, 255, 
+                                    255, NULL, FALSE, 0.0, 0.0 );
+        break;
+
+      default:
+        g_warning( "unexpected raster layer mode" );
+        return NULL;
+    }
+
+    return GTK_OBJECT(layer);
+}
+
+static void gv_raster_layer_destroy( GtkObject *gtk_object )
+
+{
+    GvLayerClass *parent_class;
+    GvRasterLayer *rlayer = GV_RASTER_LAYER(gtk_object);
+    int          isource;
+
+    CPLDebug( "OpenEV", "gv_raster_layer_destroy(%s)\n", 
+              gv_data_get_name(GV_DATA(rlayer)) );
+
+    /* clear any "source" references */
+    for( isource = 0; isource < rlayer->source_count; isource++ )
+        gv_raster_layer_set_source( rlayer, isource, NULL, 0, 0, 0, NULL,
+                                    FALSE, 0.0, 0.0 );
+
+    gtk_object_unref( GTK_OBJECT(rlayer->mesh) );
+    rlayer->mesh = NULL;
+
+    if ( rlayer->textures )
+	g_free( rlayer->textures );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_layer_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(gtk_object);         
+}
+
+/*
+** Clean up any outstanding idle tasks against this raster layer.
+*/
+
+static void gv_raster_layer_cleanup_idle( GvRasterLayer *layer )
+
+{
+    GvIdleTask *task_list;
+
+    /* Ensure that any existing idle task for this layer is blown away */
+    for( task_list = gv_get_manager()->idle_tasks;
+         task_list != NULL; task_list = task_list->next ) 
+    {
+        if( strcmp(task_list->name,"raster-layer-update") == 0 )
+        {
+            struct _GvRasterLayerIdleInfo *info;
+
+            info = (struct _GvRasterLayerIdleInfo *) task_list->task_info;
+            if( info->layer == layer )
+            {
+                g_assert( GV_LAYER(layer)->pending_idle );
+                GV_LAYER(layer)->pending_idle = FALSE;
+
+                gv_manager_dequeue_task( gv_get_manager(), task_list );
+                g_free( info );
+                break;
+            }
+        }
+    }
+
+    g_assert( !GV_LAYER(layer)->pending_idle );
+    GV_LAYER(layer)->pending_idle = FALSE;
+}
+
+static void gv_raster_layer_finalize( GtkObject * gtk_object )
+
+{
+    GvLayerClass *parent_class;
+    GvRasterLayer *rlayer = GV_RASTER_LAYER(gtk_object);
+
+    CPLDebug( "OpenEV", "gv_raster_layer_finalize(%s)\n", 
+              gv_data_get_name(GV_DATA(rlayer)) );
+
+    if( rlayer->pc_lut != NULL )
+    {
+        g_free(rlayer->pc_lut);
+        rlayer->pc_lut = NULL;
+    }
+    if( rlayer->pc_lut_composed != NULL )
+    {
+        g_free(rlayer->pc_lut_composed);
+        rlayer->pc_lut_composed = NULL;
+    }
+
+    gv_raster_layer_cleanup_idle( rlayer );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_layer_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(gtk_object);         
+}
+
+void gv_raster_layer_purge_all_textures( GvRasterLayer *layer )
+
+{
+    gint texture; 
+
+    for( texture = 0; 
+         texture < layer->prototype_data->max_tiles; 
+         texture++ )
+    {
+        if( layer->textures[texture] )
+            gv_raster_layer_purge_texture( layer, texture );
+    }
+}
+
+static void gv_raster_layer_raster_changed( GvRaster *raster,
+                                           void * raw_change_info,
+                                           GvRasterLayer *layer )
+{
+    GvRasterChangeInfo *change_info = (GvRasterChangeInfo *) raw_change_info;
+    gint texture; 
+
+    if( change_info != NULL 
+        && (change_info->width > 0 && change_info->height > 0) )
+    {
+        for( texture = 0; 
+             texture < layer->prototype_data->max_tiles; 
+             texture++ )
+        {
+            gint  coords[4];
+
+            gv_raster_tile_xy_get( raster, texture, 0, coords );
+
+            if( change_info->x_off < coords[2] 
+                && change_info->y_off < coords[3] 
+                && change_info->x_off+change_info->width > coords[0] 
+                && change_info->y_off+change_info->height > coords[1] )
+            {
+                if( layer->textures[texture] )
+                    gv_raster_layer_purge_texture( layer, texture );
+            }
+        }
+    }
+    else
+    {
+        gv_raster_layer_purge_all_textures( layer );
+    }
+}
+    
+static void 
+gv_raster_layer_raster_geotransform_changed( GvRaster *raster,
+                                             int junk,
+                                             GvRasterLayer *layer )
+{
+    if( layer->mesh_is_raw )
+        return;
+
+    if( layer->mesh_is_dirty )
+        return;
+
+    layer->mesh_is_dirty = TRUE;
+    gv_raster_layer_purge_all_textures( layer );
+    gv_view_area_queue_draw( GV_LAYER(layer)->view );
+}
+    
+static void gv_raster_layer_setup( GvLayer *layer, GvViewArea *view )
+{
+    gtk_signal_connect( GTK_OBJECT(view), "view-state-changed",
+			gv_raster_layer_state_changed, layer );
+    /* FIXME: we need this on all sources. */
+    gtk_signal_connect( GTK_OBJECT(GV_RASTER_LAYER(layer)->prototype_data), 
+                        "changed",
+			gv_raster_layer_raster_changed, layer );
+    gtk_signal_connect( GTK_OBJECT(GV_RASTER_LAYER(layer)->prototype_data), 
+                        "geotransform-changed",
+			gv_raster_layer_raster_geotransform_changed, layer );
+}
+
+static void gv_raster_layer_teardown( GvLayer *layer, GvViewArea *view )
+{
+    gv_raster_layer_cleanup_idle( GV_RASTER_LAYER(layer) );
+
+    gtk_signal_disconnect_by_data( GTK_OBJECT(view), GTK_OBJECT(layer) );
+#ifdef notdef
+    gtk_signal_disconnect_by_data( 
+        GTK_OBJECT(GV_RASTER_LAYER(layer)->prototype_data), 
+        GTK_OBJECT(layer) );
+#endif
+}
+
+static void gv_raster_layer_state_changed( GvViewArea *view, GvRasterLayer *layer )
+{
+    if( layer->tile_list )
+	g_array_set_size( layer->tile_list, 0 );
+}
+    
+
+static void
+gv_raster_layer_draw( GvLayer *layer, GvViewArea *area )
+{
+    GvRasterLayer *raster_layer = GV_RASTER_LAYER(layer);
+    gint *list = NULL,
+	lod=0, i = 0, e;
+    struct _GvRasterLayerIdleInfo *idle_info = NULL;
+    double   pixel_size, pixel_zoom;
+    int      tiles_to_force_load = 0;
+
+    GvMeshTile *mesh = NULL;
+
+    if(!gv_layer_is_visible(layer)) return;
+
+    /* Ensure that any existing idle task for this layer is blown away */
+    gv_raster_layer_cleanup_idle( raster_layer );
+
+    g_array_set_size( raster_layer->missing_tex, 0 );
+
+    /* Ensure the mesh is up to date */
+    gv_raster_layer_refresh_mesh( raster_layer );
+
+    /* Get the list of tiles to draw for current view */
+
+    if( !raster_layer->tile_list->len )
+    {
+	raster_layer->tile_list =
+            gv_mesh_tilelist_get( raster_layer->mesh, area, raster_layer,
+                                  raster_layer->tile_list );
+    }
+    
+    list = (gint *) raster_layer->tile_list->data;
+
+    /* 2D - Mode Decide level of detail (LOD). Note: each tile gets same LOD */
+    /*   see below for 3D mode LOD calculation */
+    pixel_size = gv_raster_layer_pixel_size(raster_layer);
+    pixel_zoom = log(pixel_size) / log(2);
+
+    if ( !area->flag_3d )
+    {
+        /* 2D Mode */
+        lod = (int) (-area->state.zoom - pixel_zoom);
+
+        if( lod < 0 )
+            lod = 0;
+
+        if( lod >= raster_layer->prototype_data->max_lod )
+            lod = raster_layer->prototype_data->max_lod-1;
+
+    } else {
+        /* 3D Case */
+
+        /* Simple distance to Z axis - works pretty well, but not efficent for large images, need reduced LOD in distance */
+        /* lod = (int) ((area->state.eye_pos[2]+250)/128.0 - pixel_zoom); */
+    }
+
+    /*
+    ** Find if we are willing to force load any tiles during rendering in
+    ** an attempt to reduce flicker. 
+    */
+    if( gv_data_get_property( GV_DATA(raster_layer), "force_load" ) != NULL )
+        tiles_to_force_load = 
+            atoi(gv_data_get_property( GV_DATA(raster_layer), "force_load" ));
+#ifdef ATLANTIS_BUILD
+    else if( (gv_manager_get_preference(gv_get_manager(),"safe_mode") != NULL) &&
+             (strcmp(gv_manager_get_preference(gv_get_manager(),"safe_mode"),"on") == 0))
+        tiles_to_force_load = 100;
+#endif
+    gv_raster_layer_gl_disp_set( raster_layer );
+
+    for( i = 0; list[i] != -1; i++ )
+    {
+        int	process_when_idle;
+        int     skip_render;
+        double  tile_dist = 0.0;
+        GvRasterLayerTexObj *tex;
+
+        tex = raster_layer->textures[list[i]];
+
+        /* 3D Mode - Calculate individula LODs for each tile based on 
+           distance from centre of tile to view position */
+        if ( area->flag_3d )
+        {
+            gint *tile_coords;
+            double x_center, y_center, z_center;
+            double temp_x, temp_y, temp_z, pixel_ratio;
+            int   debug3d = 0;
+            
+            if( gv_manager_get_preference(gv_get_manager(),"DEBUG3D") != NULL )
+                debug3d =
+                  atoi(gv_manager_get_preference(gv_get_manager(),"DEBUG3D"));
+
+
+            /* Get Corners to get LOD*/
+            tile_coords = gv_mesh_get_tile_corner_coords( raster_layer->mesh, 
+                                                          list[i]);
+            x_center = (tile_coords[0]+tile_coords[2])/2.0;
+            y_center = (tile_coords[1]+tile_coords[5])/2.0;
+            z_center = 0.0;
+            gv_raster_layer_pixel_to_view(raster_layer,
+                                          &x_center,&y_center,&z_center);
+
+            /* Calculate the optimal LOD for this tile */
+
+            /*  ??? Are these distances correct???  They seem to work. */
+            if( debug3d > 1 )
+            {
+                printf( "--- lod calc ---: tile_center=(%f,%f,%f)\n", 
+                        x_center, y_center, z_center );
+            }
+
+            /* Must account for image flip - eye_pos is okay, just the tile */
+            temp_x = area->state.eye_pos[0] - (x_center  * area->state.flip_x);
+            temp_y = area->state.eye_pos[1] - (y_center * area->state.flip_y);
+            temp_z = area->state.eye_pos[2] - z_center;
+            tile_dist = sqrt(temp_x*temp_x + temp_y*temp_y + temp_z*temp_z);
+
+            /* How many texture pixels are required to cover one screen pixel
+             * We are assuming that fov is 90 degrees so that the distance
+             * from center of view to top edge of view (shape_y*0.5) is the
+             * same as the distance from eye_pos to center of view. 
+             */
+
+            pixel_ratio = (ABS(tile_dist) / pixel_size)
+            				/ (area->state.shape_y*0.5);
+            
+            lod = (int) (log(pixel_ratio)/log(2));
+
+            if( debug3d )
+                printf( "tile_dist=%f, pixel_size=%f, ratio=%f, lod=%d\n", 
+                        tile_dist, pixel_size, pixel_ratio, lod );
+
+            free(tile_coords);
+
+            if( lod < 0 )
+                lod = 0;
+
+            if( lod >= raster_layer->prototype_data->max_lod )
+                lod = raster_layer->prototype_data->max_lod-1;
+        }
+
+        if( (tex == NULL || tex->lod != lod) && area->exact_render )
+        {
+            gv_raster_layer_texture_load( raster_layer, list[i], lod );
+            tex = raster_layer->textures[list[i]];
+            CPLAssert( tex->lod == lod );
+        }
+
+        if( tex == NULL && tiles_to_force_load > 0 )
+        {
+            CPLDebug( "OpenEV", "force loading tile for layer %s.", 
+                      gv_data_get_name( GV_DATA(raster_layer) ) );
+
+            gv_raster_layer_texture_load( raster_layer, list[i], lod );
+            tex = raster_layer->textures[list[i]];
+            tiles_to_force_load--;
+
+            // Re-enable textures that are disabled by loader. 
+            gv_raster_layer_gl_disp_set( raster_layer );
+        }
+
+        /* Get mesh for tile */
+        if( tex != NULL )
+            mesh = gv_mesh_get( raster_layer->mesh, list[i], tex->lod, 
+                                lod, mesh );
+        else
+            mesh = NULL;
+
+        if( tex == NULL || tex->tex_obj == 0 )
+        {
+            /* we have no texture at all. Skip rendering, and queue
+               idle request to fetch data */
+            process_when_idle = TRUE;
+            skip_render = TRUE;
+        } 
+	else if( mesh == NULL )
+	{
+            /* this should never happen, right? */
+            skip_render = TRUE;
+            process_when_idle = FALSE;
+	}
+
+        else if( tex->lod != lod )
+        {
+            /* we have a texture, but it's not the optimal lod, render
+               it, and queue fetching of the correct lod */
+
+            process_when_idle = TRUE;
+            skip_render = FALSE;
+        }
+        else
+        {
+            /* general case of having the desired texture available */
+
+            process_when_idle = FALSE;
+            skip_render = FALSE;
+        }
+
+        if( process_when_idle )
+        {
+            /* add this to missing texture list */
+
+            g_array_append_val( raster_layer->missing_tex, list[i] );
+
+            if( !idle_info )
+            {
+                if( (idle_info = g_new(struct _GvRasterLayerIdleInfo,1)) 
+                    != NULL )
+                {
+                    idle_info->layer = raster_layer;
+                    idle_info->area = area;
+                    idle_info->lod = lod;
+                    gv_manager_queue_task( gv_get_manager(), 
+                                           "raster-layer-update", 10, 
+                                           gv_raster_layer_idle_handler, 
+                                           idle_info );
+                    GV_LAYER(layer)->pending_idle = TRUE;
+                }
+            }
+        }
+
+        if( skip_render )
+            continue;
+
+        /* mark last used time, but only if it is the optimal resolution */
+        if( !process_when_idle )
+            gv_raster_layer_touch_texture( raster_layer, list[i] );
+
+        /* bind texture to be displayed */
+        glBindTexture( GL_TEXTURE_2D, tex->tex_obj );
+
+        /* Draw mesh */
+        if( mesh->tex_coords )
+        {
+            glEnableClientState( GL_TEXTURE_COORD_ARRAY );
+            glTexCoordPointer( 2, GL_FLOAT, 0, mesh->tex_coords );
+        }
+
+        glEnableClientState( GL_VERTEX_ARRAY );
+        glVertexPointer( 3, GL_FLOAT, 0, mesh->vertices );
+
+        for( e = 0; e <= mesh->restarts; e++ )
+        {
+            glDrawElements( mesh->list_type, mesh->range, GL_UNSIGNED_INT, 
+                            &(mesh->indices[e*mesh->range]) );
+        }
+
+        glDisableClientState( GL_VERTEX_ARRAY );
+        glDisableClientState( GL_NORMAL_ARRAY );
+        glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+
+	if ( mesh )
+	{
+	    g_free( mesh );
+	    mesh = NULL;
+	}
+    }
+
+    gv_raster_layer_gl_disp_unset();
+}
+
+/* Idle handler */
+
+static gint
+gv_raster_layer_idle_handler( gpointer data )
+{
+    struct _GvRasterLayerIdleInfo *idle_info = (struct _GvRasterLayerIdleInfo *) data;
+    GvRasterLayer *layer = idle_info->layer;
+    gint i, tile, temp_lod, lod;
+    gboolean tile_loaded = FALSE;
+    GvViewArea *view = GV_LAYER(layer)->view;
+
+    g_assert( GV_LAYER(layer)->pending_idle );
+    GV_LAYER(layer)->pending_idle = FALSE;
+
+    if( !GTK_WIDGET_REALIZED( GTK_WIDGET(view) )
+        || !gtk_gl_area_make_current(GTK_GL_AREA(view))
+        || layer->missing_tex->len == 0 )
+    {
+        g_free( idle_info );
+        return FALSE;
+    }
+
+    tile_loaded = FALSE;
+    lod = idle_info->lod;
+
+    /* first scan for any completely missing textures */
+    for( i = 0; i < layer->missing_tex->len; i++ )
+    {
+	tile = g_array_index( layer->missing_tex, int, i );
+
+        /* try to fill in textures where we have no lod at all */
+	if( layer->textures[tile] == NULL )
+	{
+            /* FIXME: Need to check all sources? */
+	    if( ( temp_lod = gv_raster_cache_get_best_lod( 
+                			layer->prototype_data->cache,
+                                        tile, lod ) ) == -1 )
+                temp_lod = lod;
+
+            /* force downsamping if higher resolution available, to avoid
+             tieing up too much texture memory */
+            if( temp_lod < lod )
+                temp_lod = lod;
+
+            gv_raster_layer_texture_load( layer, tile, temp_lod );
+
+            g_array_index( layer->missing_tex, int, i ) = -1;
+
+            /* if too long has elapsed, break out to give the user a chance
+               to provide input */
+            if( gv_view_area_redraw_timeout(view) )
+                break;
+	}
+    }
+
+    /* make another pass processing anything that needs updating. */
+    for( i = 0; 
+         i < layer->missing_tex->len && !gv_view_area_redraw_timeout(view);
+         i++ )
+    {
+	tile = g_array_index( layer->missing_tex, int, i );
+        if( tile < 0 )
+            continue;
+
+        gv_raster_layer_texture_load( layer, tile, lod );
+    }
+
+    g_array_set_size( layer->missing_tex, 0 );
+
+    /*
+     * We only requeue a draw if we ran out of time, or if we know there
+     * are no other layers with pending work they would like to do before 
+     * a redraw.
+     */
+    if( gv_view_area_redraw_timeout(view) 
+        || !gv_view_area_pending_idle_work(view) )
+        gv_view_area_queue_draw( view);
+
+    g_free( idle_info );
+    return FALSE;
+}
+
+
+/* Load the named texture (tile_num / lod) into a bound texture 
+   (via glBindTextures/glTexImage2D) */
+
+static gint
+gv_raster_layer_texture_load( GvRasterLayer *layer, gint tile_num, gint lod )
+{
+    gint size;
+    void *buffer;
+    int format, type, needs_free;
+    static int counter = 0;
+
+    /* give up with an error if we fail to get the cache tile */
+    buffer = gv_raster_layer_gltile_get(layer,tile_num,lod, &format, &type,
+                                        &needs_free );
+    if( buffer == NULL )
+        return 1;
+
+    size = gv_raster_layer_texture_size( format, type );
+    size *= ( layer->tile_x >> lod ) * ( layer->tile_y >> lod );
+
+    glEnable( GL_TEXTURE_2D );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 
+                     layer->gl_info.mag_filter );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
+                     layer->gl_info.min_filter );
+
+#ifdef notdef
+    if( layer->textures[tile_num] == NULL )
+    {
+        GLuint tex_obj;
+
+        /* Never been allocated -- here we go */
+        glGenTextures( 1, &tex_obj );
+
+        gv_raster_layer_create_texture( layer, tile_num, tex_obj, lod, size );
+    }
+    else
+    {
+        gv_raster_layer_reset_texture( layer, tile_num, lod, size );
+    }
+#else
+    {
+        GLuint   tex_obj;
+        
+        if( layer->textures[tile_num] != NULL )
+            gv_raster_layer_purge_texture( layer, tile_num );
+
+        /* Never been allocated -- here we go */
+        glGenTextures( 1, &tex_obj );
+
+        gv_raster_layer_create_texture( layer, tile_num, tex_obj, lod, size );
+    }
+#endif
+    
+    if( layer->textures[tile_num] == NULL )
+    {
+        CPLDebug( "OpenEV", 
+                  "gv_raster_layer_texture_load(): "
+                   "unexpectedly missing texture" );
+        return 1;
+    }
+
+    glBindTexture( GL_TEXTURE_2D, layer->textures[tile_num]->tex_obj );
+
+    /* 
+     * We seem to see frequent crashes with Xi Graphics GL drivers if
+     * we do too many glTexImage calls without resyncronizing with the
+     * server.  The following hack is intended to introduce a round trip
+     * with the server "every now and then" while adding minimal wait
+     * overhead.
+     */
+    if( counter++ % 100 == 0 && glGetError() != 0 )
+    {
+        CPLDebug( "OpenEV", 
+                  "Got GL Error in gv_raster_layer_texture_load()" );
+    }
+
+    glTexImage2D( GL_TEXTURE_2D, 0, format,
+                  layer->tile_x >> lod,
+                  layer->tile_y >> lod,
+                  0, format, type, buffer );
+
+#ifdef notdef
+    if( layer->tile_x >> lod <= 16 )
+    {
+        int iX, iY, max = layer->tile_x >> lod;
+
+        printf(" format = %d, type = %d\n", format, type );
+        for( iY = 0; iY < max; iY++ )
+        {
+            for( iX = 0; iX < max; iX++ )
+                printf( "%02X%02X ", 
+                        ((unsigned char *) buffer)[(iX + iY*max)*2],
+                        ((unsigned char *) buffer)[(iX + iY*max)*2+1] );
+            printf( "\n" );
+        }
+    }
+#endif
+
+    glDisable( GL_TEXTURE_2D );
+
+    if( needs_free )
+        g_free( buffer );
+        
+    return 0;
+}
+
+static gint gv_raster_layer_texture_size( gint format, gint type )
+{
+    gint elem_size;
+    gint n_elems;
+
+    switch( type   )
+    {
+	case GL_UNSIGNED_BYTE:
+	    elem_size = sizeof( unsigned char );
+	    break;
+	default:
+	    elem_size = 1;
+	    fprintf( stderr, "Unknown type in gv_raster_layer_texture_size\n" );
+	    break;
+    }
+
+    switch( format )
+    {
+	case GL_RGBA:
+	    n_elems = 4;
+	    break;
+	case GL_RGB:
+	    n_elems = 3;
+	    break;
+	case GL_LUMINANCE:
+	    n_elems = 1;
+	    break;
+	case GL_LUMINANCE_ALPHA:
+	    n_elems = 2;
+	    break;
+	default:
+	    n_elems = 1;
+	    fprintf( stderr, "Unknown format in gv_raster_layer_texture_size\n" );
+	    break;
+    }
+
+    return n_elems * elem_size;
+}
+
+static void gv_raster_layer_gl_disp_set( GvRasterLayer *layer )
+{
+    glEnable( GL_TEXTURE_2D );
+
+    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, layer->gl_info.tex_env_mode );
+
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, layer->gl_info.s_wrap );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, layer->gl_info.t_wrap );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, layer->gl_info.mag_filter );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, layer->gl_info.min_filter );
+
+    if( layer->gl_info.tex_env_mode == GL_MODULATE )
+        glColor4fv( layer->gl_info.fragment_color );
+    else
+        glColor4f( 1.0, 1.0, 1.0, 1.0 );
+
+    if( layer->gl_info.blend_enable )
+    {
+	glEnable( GL_BLEND );
+
+	glBlendFunc( layer->gl_info.blend_src, layer->gl_info.blend_dst );
+    }
+
+    if( layer->gl_info.alpha_test )
+    {
+	glEnable( GL_ALPHA_TEST );
+
+	glAlphaFunc( layer->gl_info.alpha_test_mode,
+		     layer->gl_info.alpha_test_val );
+    }
+
+}
+
+static void gv_raster_layer_gl_disp_unset( void )
+{
+    glDisable( GL_TEXTURE_2D );
+    glDisable( GL_BLEND );
+    glDisable( GL_ALPHA_TEST );
+}
+
+static void
+gv_raster_layer_extents(GvLayer *layer, GvRect *rect)
+{
+    gv_mesh_extents( GV_RASTER_LAYER(layer)->mesh, rect );
+}
+
+
+long
+gv_raster_layer_texture_clamp_set( GvRasterLayer *layer, int s_clamp, int t_clamp )
+{
+    int modes[] = { GL_CLAMP, GL_REPEAT };
+    int max_modes = 2;
+
+    g_return_val_if_fail( layer != NULL, 1 );
+
+    if( s_clamp >= max_modes || t_clamp >= max_modes )
+	return 1;
+
+    layer->gl_info.s_wrap = modes[s_clamp];
+    layer->gl_info.t_wrap = modes[t_clamp];
+
+    gv_raster_layer_purge_all_textures( layer );
+    gv_layer_display_change( GV_LAYER(layer), NULL );
+
+    return 0;
+}
+
+long
+gv_raster_layer_zoom_set( GvRasterLayer *layer, int mag_mode, int min_mode )
+{
+    int modes[] = { GL_LINEAR, GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+    int max_mag_modes = 2;
+    int max_min_modes = 3;
+
+    if( mag_mode >= max_mag_modes || min_mode >= max_min_modes )
+	return 1;
+
+    layer->gl_info.mag_filter = modes[mag_mode];
+    layer->gl_info.min_filter = modes[min_mode];
+
+    gv_raster_layer_purge_all_textures( layer );
+    gv_layer_display_change( GV_LAYER(layer), NULL );
+
+    return 0;
+}
+
+long
+gv_raster_layer_zoom_get( GvRasterLayer *layer, int *mag_mode, int *min_mode )
+{
+    int modes[] = { GL_LINEAR, GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+    int max_mag_modes = 2;
+    int max_min_modes = 3;
+    int i;
+
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+
+    *mag_mode = -1;
+    *min_mode = -1;
+
+    for( i = 0; i < max_mag_modes; i++ )
+	if( layer->gl_info.mag_filter == modes[i] )
+	    *mag_mode = i;
+
+    for( i = 0; i < max_min_modes; i++ )
+	if( layer->gl_info.min_filter == modes[i] )
+	    *min_mode = i;
+
+    if( *mag_mode == -1 || *min_mode == -1 )
+	return 1;
+
+    return 0;
+}
+
+long
+gv_raster_layer_texture_mode_set( GvRasterLayer *layer, int texture_mode, GvColor color )
+{
+    int modes[] = { GL_REPLACE, GL_MODULATE };
+    int max_modes = 2;
+
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( texture_mode < max_modes, 1 );
+
+    if( modes[texture_mode] != layer->gl_info.tex_env_mode
+        || color[0] != layer->gl_info.fragment_color[0]
+        || color[1] != layer->gl_info.fragment_color[1]
+        || color[2] != layer->gl_info.fragment_color[2]
+        || color[3] != layer->gl_info.fragment_color[3] )
+    {
+        layer->gl_info.tex_env_mode = modes[texture_mode];
+        memcpy( layer->gl_info.fragment_color, color, sizeof( GvColor ) );
+        gv_layer_display_change( GV_LAYER(layer), NULL );
+    }
+
+    return 0;
+}
+
+long
+gv_raster_layer_texture_mode_get( GvRasterLayer *layer, int *texture_mode, GvColor *color )
+{
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( texture_mode != NULL, 1 );
+    g_return_val_if_fail( color != NULL, 1 );
+
+    switch( layer->gl_info.tex_env_mode )
+    {
+	case GL_REPLACE:
+	    *texture_mode = 0;
+	    break;
+	case GL_MODULATE:
+	    *texture_mode = 1;
+	    break;
+	default:
+	    return 1;
+    }
+
+    memcpy( color, layer->gl_info.fragment_color, sizeof( GvColor ) );
+
+    return 0;
+}
+
+long
+gv_raster_layer_alpha_set( GvRasterLayer *layer, int alpha_mode, float alpha_check_val )
+{
+    int modes[] = { 0, GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL };
+    int max_modes = 8;
+
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( alpha_mode < max_modes, 1 );
+
+    if( alpha_mode )
+    {
+	layer->gl_info.alpha_test = 1;
+
+	layer->gl_info.alpha_test_mode = modes[alpha_mode];
+
+	layer->gl_info.alpha_test_val = alpha_check_val;
+    } else {
+	layer->gl_info.alpha_test = 0;
+    }
+
+    gv_layer_display_change( GV_LAYER(layer), NULL );
+
+    return 0;
+}
+
+long
+gv_raster_layer_alpha_get( GvRasterLayer *layer, int *alpha_mode, float *alpha_check_val )
+{
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( alpha_mode != NULL, 1 );
+    g_return_val_if_fail( alpha_check_val != NULL, 1 );
+
+    if( layer->gl_info.alpha_test )
+    {
+	switch( layer->gl_info.alpha_test_mode )
+	{
+	    case GL_NEVER:
+		*alpha_mode = 1;
+		break;
+	    case GL_ALWAYS:
+		*alpha_mode = 2;
+		break;
+	    case GL_LESS:
+		*alpha_mode = 3;
+		break;
+	    case GL_LEQUAL:
+		*alpha_mode = 4;
+		break;
+	    case GL_EQUAL:
+		*alpha_mode = 5;
+		break;
+	    case GL_GEQUAL:
+		*alpha_mode = 6;
+		break;
+	    case GL_GREATER:
+		*alpha_mode = 7;
+		break;
+	    case GL_NOTEQUAL:
+		*alpha_mode = 8;
+		break;
+	    default:
+		return 1;
+	}
+    } else {
+	*alpha_mode = 0;
+    }
+
+    *alpha_check_val = layer->gl_info.alpha_test_val;
+
+    return 0;
+}
+
+long
+gv_raster_layer_blend_mode_set( GvRasterLayer *layer, int blend_mode, int sfactor, int dfactor )
+{
+    int factors[] = { GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA };
+    int max_factors = 7; /* This must match the entries in the factors[] list */
+    int max_modes = 5;
+    int new_blend_src, new_blend_dst, new_blend_enable;
+
+    g_return_val_if_fail( blend_mode < max_modes, 1 );
+
+    g_return_val_if_fail( ( blend_mode != GV_RASTER_LAYER_BLEND_MODE_CUSTOM ) 
+                 || ( sfactor < max_factors && dfactor < max_factors ), 1 );
+
+    switch( blend_mode )
+    {
+      case GV_RASTER_LAYER_BLEND_MODE_FILTER:
+        new_blend_src = GL_SRC_ALPHA;
+        new_blend_dst = GL_ONE_MINUS_SRC_ALPHA;
+        new_blend_enable = 1;
+        break;
+      case GV_RASTER_LAYER_BLEND_MODE_MULTIPLY:
+        new_blend_src = GL_DST_COLOR;
+        new_blend_dst = GL_ZERO;
+        new_blend_enable = 1;
+        break;
+      case GV_RASTER_LAYER_BLEND_MODE_ADD:
+        new_blend_src = GL_ONE;
+        new_blend_dst = GL_ONE;
+        new_blend_enable = 1;
+        break;
+      case GV_RASTER_LAYER_BLEND_MODE_CUSTOM:
+        new_blend_src = factors[sfactor];
+        new_blend_dst = factors[dfactor];
+        new_blend_enable = 1;
+        break;
+      default:
+        new_blend_enable = 0;
+        new_blend_src = layer->gl_info.blend_src;
+        new_blend_dst = layer->gl_info.blend_dst;
+        break;
+    }
+
+    if( layer->gl_info.blend_enable != new_blend_enable
+        || layer->gl_info.blend_src != new_blend_src
+        || layer->gl_info.blend_dst != new_blend_dst )
+    {
+        layer->gl_info.blend_src = new_blend_src;
+        layer->gl_info.blend_dst = new_blend_dst;
+        layer->gl_info.blend_enable = new_blend_enable;
+        gv_layer_display_change( GV_LAYER(layer), NULL );
+    }
+
+    return 0;
+}
+
+long
+gv_raster_layer_blend_mode_get( GvRasterLayer *layer, int *blend_mode, int *sfactor, int *dfactor )
+{
+    int factors[] = { GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA };
+    int max_factors = 7;
+    int i;
+
+    if( layer->gl_info.blend_enable )
+    {
+	*blend_mode = 4;
+
+	for( i = 0; i < max_factors; i++ )
+	{
+	    if( factors[i] == layer->gl_info.blend_src )
+	    {
+		*sfactor = i;
+		break;
+	    }
+      
+	    if( factors[i] == layer->gl_info.blend_dst )
+	    {
+		*dfactor = i;
+	    }
+	}
+    } else {
+	*blend_mode = 0;
+	*sfactor = 0;
+	*dfactor = 0;
+    }
+    return 0;
+}
+
+int gv_raster_layer_mode_get( GvRasterLayer *layer )
+
+{
+    return layer->mode;
+}
+
+		
+/* the following function should move to gvmesh.h/c when Paul is done with
+   them. */
+
+static int
+gv_mesh_transform_with_func(GvMesh *mesh, 
+                            int (*trfunc)(int,double*,double*,double*,void *),
+                            void *cb_data )
+{
+    int    tile;
+
+    for( tile = 0; tile < mesh->max_tiles; tile++ )
+    {
+        GArray *verts;
+        float  *xyz_verts;
+        int    xyz_offset;
+
+	verts = g_array_index( mesh->vertices, GArray *, tile );
+        
+        xyz_verts = (float *) verts->data;
+        for( xyz_offset = 0; xyz_offset < verts->len; xyz_offset += 3 )
+        {
+            double    x_out, y_out, z_out;
+
+            x_out = xyz_verts[0];
+            y_out = xyz_verts[1];
+            z_out = xyz_verts[2];
+
+            if( !trfunc( 1, &x_out, &y_out, &z_out, cb_data ) )
+                return FALSE;
+
+            xyz_verts[0] = x_out;
+            xyz_verts[1] = y_out;
+            xyz_verts[2] = z_out;
+
+            xyz_verts += 3;
+        }
+    }
+
+    return TRUE;
+}
+
+static int gvrl_reproject_cb( int pt_count, double *x, double *y, double *z, 
+                              void *cb_data )
+
+{
+    OGRCoordinateTransformationH *ct = (OGRCoordinateTransformationH) cb_data;
+
+    return OCTTransform( ct, pt_count, x, y, z );
+}
+
+static gint 
+gv_raster_layer_reproject( GvLayer *layer, 
+                           const char * new_projection )
+
+{
+    int success = TRUE;
+    OGRSpatialReferenceH   hSRSNew = NULL, hSRSOld = NULL;
+    OGRCoordinateTransformationH hTransform = NULL;
+
+    /* 
+     * Try and establish if we can, or need to do reprojection.
+     */
+    if( gv_data_get_projection(GV_DATA(layer)) == NULL )
+        return FALSE;
+
+    if( EQUAL(gv_data_get_projection(GV_DATA(layer)),"PIXEL") )
+        return FALSE;
+
+    hSRSNew = OSRNewSpatialReference( new_projection );
+    if( hSRSNew == NULL )
+        return FALSE;
+
+    hSRSOld = OSRNewSpatialReference(gv_data_get_projection(GV_DATA(layer)));
+    if( hSRSOld == NULL )
+    {
+        OSRDestroySpatialReference( hSRSNew );
+        return FALSE;
+    }
+
+    if( OSRIsSame( hSRSOld, hSRSNew ) )
+    {
+        OSRDestroySpatialReference( hSRSOld );
+        OSRDestroySpatialReference( hSRSNew ); 
+       
+        return TRUE;
+    }
+
+    /*
+     * Establish transformation.
+     */
+    
+    hTransform = OCTNewCoordinateTransformation( hSRSOld, hSRSNew );
+    if( hTransform == NULL )
+    {
+        OSRDestroySpatialReference( hSRSOld );
+        OSRDestroySpatialReference( hSRSNew ); 
+        
+        return FALSE;
+    }
+
+    /*
+     * Transform all the mesh points.
+     */
+
+    success = gv_mesh_transform_with_func( GV_RASTER_LAYER(layer)->mesh, 
+                                           gvrl_reproject_cb,
+                                           (void *) hTransform );
+
+    OCTDestroyCoordinateTransformation( hTransform );
+    OSRDestroySpatialReference( hSRSOld );
+    OSRDestroySpatialReference( hSRSNew ); 
+
+    if( success )
+        gv_data_set_projection( GV_DATA(layer), new_projection );
+
+    return success;
+}
+
+static int 
+gvrl_to_georef_cb( int pt_count, double *x, double *y, double *z, 
+                   void *cb_data )
+
+{
+    GvRaster *raster = GV_RASTER(cb_data);
+    int      i, success = TRUE;
+
+    for( i = 0; i < pt_count; i++ )
+    {
+        success |= gv_raster_pixel_to_georef( raster, x+i, y+i, z+i );
+    }
+
+    return success;
+}
+
+static int 
+gvrl_to_raw_cb( int pt_count, double *x, double *y, double *z, 
+                void *cb_data )
+
+{
+    GvRaster *raster = GV_RASTER(cb_data);
+    int      i, success = TRUE;
+
+    for( i = 0; i < pt_count; i++ )
+    {
+        y[i] = raster->height - y[i];
+    }
+
+    return success;
+}
+
+void gv_raster_layer_refresh_mesh( GvRasterLayer *layer )
+
+{
+    GvRaster	*prototype_data = layer->prototype_data;
+
+    if( !layer->mesh_is_dirty )
+        return;
+
+    /* Should we actually reset the mesh to raw? */
+    if( layer->mesh_is_raw )
+        return;
+
+    gv_mesh_reset_to_identity( layer->mesh );
+    
+    gv_mesh_transform_with_func( layer->mesh, gvrl_to_georef_cb, 
+                                 prototype_data );
+    layer->mesh_is_dirty = FALSE;
+}
+
+int gv_raster_layer_set_raw( GvRasterLayer *layer, int raw_enable )
+
+{
+    GvRaster	*prototype_data = layer->prototype_data;
+
+    if( !raw_enable == !layer->mesh_is_raw )
+        return TRUE;
+
+    if( !raw_enable )
+    {
+        gv_mesh_transform_with_func( layer->mesh, gvrl_to_georef_cb, 
+                                     prototype_data );
+
+        gv_data_set_projection( GV_DATA(layer), 
+                           gv_data_get_projection(GV_DATA(prototype_data)) );
+
+        layer->mesh_is_raw = FALSE;
+    }
+    else 
+    {
+        /* we can't rawify ungeoreferenced layers */
+        if( gv_data_get_projection( GV_DATA(layer) ) != NULL
+            && EQUAL(gv_data_get_projection( GV_DATA(layer) ),"PIXEL") )
+            return FALSE;
+
+        gv_mesh_reset_to_identity( layer->mesh );
+
+        gv_data_set_projection( GV_DATA(layer), NULL );
+
+        layer->mesh_is_raw = TRUE;
+    }
+
+    return TRUE;
+}
+
+gint gv_raster_layer_pixel_to_view(GvRasterLayer *layer, 
+                                   double *x, double *y, double *z )
+
+{
+    int      success = TRUE;
+
+    if( !layer->mesh_is_raw )
+    {
+        success = gv_raster_pixel_to_georef( layer->prototype_data, x, y, z );
+    }
+
+    if( success
+        && gv_data_get_projection(GV_DATA(layer->prototype_data)) != NULL
+        && gv_data_get_projection(GV_DATA(layer)) != NULL
+        && !EQUAL(gv_data_get_projection(GV_DATA(layer->prototype_data)),
+                  gv_data_get_projection(GV_DATA(layer))) )
+    {
+        g_warning( "gv_raster_pixel_to_view doesn't reproject yet." );
+    }
+
+    return success;
+}
+
+gint gv_raster_layer_view_to_pixel(GvRasterLayer *layer, 
+                                   double *x, double *y, double *z )
+
+{
+    int      success = TRUE;
+
+    if( success
+        && gv_data_get_projection(GV_DATA(layer->prototype_data)) != NULL
+        && gv_data_get_projection(GV_DATA(layer)) != NULL
+        && !EQUAL(gv_data_get_projection(GV_DATA(layer->prototype_data)),
+                  gv_data_get_projection(GV_DATA(layer))) )
+    {
+        g_warning( "gv_raster_pixel_to_view doesn't reproject yet." );
+    }
+
+    if( success && !layer->mesh_is_raw )
+    {
+        success = gv_raster_georef_to_pixel( layer->prototype_data, x, y, z );
+    }
+
+    return success;
+}
+
+double gv_raster_layer_pixel_size( GvRasterLayer *raster )
+
+{
+    double	x1, y1, x2, y2;
+
+    x1 = raster->prototype_data->width / 2; 
+    y1 = raster->prototype_data->height / 2;
+
+    x2 = x1 + 1.0;
+    y2 = y1 + 1.0;
+    
+    gv_raster_layer_pixel_to_view( raster, &x1, &y1, NULL );
+    gv_raster_layer_pixel_to_view( raster, &x2, &y2, NULL );
+
+    return (ABS(x2-x1) + ABS(y2-y1)) * 0.5;
+}
+
+int gv_raster_layer_get_mode( GvRasterLayer *layer )
+
+{
+    return layer->mode;
+}
+
+void gv_raster_layer_add_height( GvRasterLayer *layer, GvRaster *height_raster,
+                                 double default_height )
+{
+    gv_mesh_add_height(layer->mesh, height_raster, default_height );
+}
+
+void gv_raster_layer_clamp_height( GvRasterLayer *layer, int bclamp_min, int bclamp_max,
+                                 double min_height, double max_height )
+{
+    gv_mesh_clamp_height(layer->mesh, bclamp_min, bclamp_max, min_height, max_height );
+}
+
+#define GV_MESH_RIGHT_X_BIT 1
+#define GV_MESH_LEFT_X_BIT  2
+#define GV_MESH_TOP_Y_BIT   4
+#define GV_MESH_BOT_Y_BIT   8
+
+#define DEG2RAD         0.01745329252
+#define RAD2DEG         57.2986885
+
+static GArray *
+gv_mesh_tilelist_get_2d( GvMesh *mesh, GvViewArea *view, 
+                         GvRasterLayer *rlayer, GArray *tilelist )
+{
+    float cos_ang, sin_ang;
+    float term1, term2, term3, term4, term5, term6;
+    gint i, e, j, dimensions, mask;
+    float x, y; /* Just to make it compile */
+    GArray *verts;
+
+    cos_ang = cos( view->state.rot * DEG2RAD );
+    sin_ang = sin( view->state.rot * DEG2RAD );
+
+    term1 = (2/view->state.shape_x) *
+        view->state.linear_zoom *
+        view->state.flip_x *
+        cos_ang;
+
+    term2 = (2/view->state.shape_x) *
+        -view->state.linear_zoom *
+        view->state.flip_y *
+        sin_ang;
+
+    term3 = (2/view->state.shape_x) *
+        view->state.linear_zoom *
+        ( ( view->state.flip_x *
+            view->state.tx *
+            cos_ang ) -
+          ( view->state.flip_y *
+            view->state.ty *
+            sin_ang ) );
+
+    term4 = (2/view->state.shape_y) *
+        view->state.linear_zoom *
+        view->state.flip_x *
+        sin_ang;
+
+    term5 = (2/view->state.shape_y) *
+        view->state.linear_zoom *
+        view->state.flip_y *
+        cos_ang;
+
+    term6 = (2/view->state.shape_y) *
+        view->state.linear_zoom *
+        ( ( view->state.flip_x *
+            view->state.tx *
+            sin_ang ) +
+          ( view->state.flip_y *
+            view->state.ty *
+            cos_ang ) );
+
+    dimensions = ( 1 << mesh->detail );
+
+    for( i = 0; i < mesh->max_tiles; i++ )
+    {
+        if( ( verts = g_array_index( mesh->vertices, GArray *, i ) ) != NULL )
+        {
+            mask = 0xFF;
+
+            for( e = 0; e <= (dimensions+1); e += (dimensions+1) )
+            {
+                for( j = 0; j <= dimensions; j += dimensions )
+                {
+                    gint mask_point = 0;
+                    gfloat old_x, old_y;
+
+                    old_x = g_array_index( verts, float, 3*(e*dimensions+j) );
+                    old_y = g_array_index( verts, float, 3*(e*dimensions+j)+1 );
+
+                    x = ( term1*old_x ) + ( term2*old_y ) + term3;
+                    y = ( term4*old_x ) + ( term5*old_y ) + term6;
+
+                    if( x > 1.0 )
+                        mask_point |= GV_MESH_RIGHT_X_BIT;
+
+                    if( x < -1.0 )
+                        mask_point |= GV_MESH_LEFT_X_BIT;
+
+                    if( y > 1.0 )
+                        mask_point |= GV_MESH_TOP_Y_BIT;
+
+                    if( y < -1.0 )
+                        mask_point |= GV_MESH_BOT_Y_BIT;
+
+                    mask &= mask_point;
+
+                }
+            }
+
+            if( !mask )
+            {
+                g_array_append_val( tilelist, i );
+            }
+
+        }
+    }
+
+    return tilelist;
+}
+
+static GArray *
+gv_mesh_tilelist_get_3d( GvMesh *mesh, GvViewArea *view, 
+                         GvRasterLayer *rlayer, GArray *tilelist )
+{
+    float tile_vect[3];  /* xyz */
+    float tile_magnitude, eye_dir_magnitude, cos_angle;
+    float eye_pos[3], eye_dir[3];
+    float diag, cos_diag;
+    int   debug3d = 0;
+    int   tiles_across = rlayer->prototype_data->tiles_across;
+    int   tiles_down = rlayer->prototype_data->tiles_down;
+    int   *cornerInside, tile_i, tile_j;
+
+    if( gv_manager_get_preference(gv_get_manager(),"DEBUG3D") != NULL )
+        debug3d = atoi(gv_manager_get_preference(gv_get_manager(),"DEBUG3D"));
+
+    eye_pos[0] = view->state.eye_pos[0];
+    eye_pos[1] = view->state.eye_pos[1];
+    eye_pos[2] = view->state.eye_pos[2];
+
+    eye_dir[0] = view->state.eye_dir[0];
+    eye_dir[1] = view->state.eye_dir[1];
+    eye_dir[2] = view->state.eye_dir[2];
+
+    eye_dir_magnitude = sqrt( (eye_dir[0]) * (eye_dir[0]) + 
+                              (eye_dir[1]) * (eye_dir[1]) + 
+                              (eye_dir[2]) * (eye_dir[2]));
+    /*
+     * Compute the diagonal of the window to the y axis, and from this
+     * the cos() of the angle between the eye direction and the vector to
+     * the corner of the window. 
+     */
+    diag = sqrt(1.0 + (view->state.shape_x*view->state.shape_x)
+                /(view->state.shape_y*view->state.shape_y));
+
+    cos_diag = 1.0 / sqrt(1+diag*diag);
+    
+    if( debug3d )
+    {
+        printf( "diagonal angle = %.1f\n", 
+                acos(cos_diag) * RAD2DEG );
+    }    
+    
+    /* Allocate array of flags for each tile corner. */
+    cornerInside = (int *) g_new(int,(tiles_across+1)*(tiles_down+1));
+
+    /*
+     * Loop over each tile corner, and work out if it is inside the cone
+     * or not. 
+     */ 
+
+    for( tile_j = 0; tile_j < tiles_down+1; tile_j++ )
+    {
+        int     tile_y;
+
+        tile_y = tile_j * rlayer->prototype_data->tile_y;
+        if( tile_y > rlayer->prototype_data->height )
+            tile_y = rlayer->prototype_data->height;
+
+        for( tile_i = 0; tile_i < tiles_across+1; tile_i++ )
+        {
+            int     tile_x;
+            double  geo_x, geo_y, geo_z;
+
+            tile_x = tile_i * rlayer->prototype_data->tile_x;
+            if( tile_x > rlayer->prototype_data->width )
+                tile_x = rlayer->prototype_data->width;
+
+            geo_x = tile_x * view->state.flip_x;
+            geo_y = tile_y * view->state.flip_y;
+            geo_z = 0.0;
+                
+            gv_raster_layer_pixel_to_view(rlayer, &geo_x, &geo_y, &geo_z );
+            
+            tile_vect[0] = geo_x - eye_pos[0];
+            tile_vect[1] = geo_y - eye_pos[1];
+            tile_vect[2] = geo_z - eye_pos[2];
+
+            tile_magnitude = sqrt(tile_vect[0]*tile_vect[0] +
+                                  tile_vect[1]*tile_vect[1] +
+                                  tile_vect[2]*tile_vect[2]);
+
+            /* Dot product of tile vector with eye_direction to find 
+               cos of angle*/
+            cos_angle = (tile_vect[0] * (eye_dir[0]) +
+                         tile_vect[1] * (eye_dir[1]) +
+                         tile_vect[2] * (eye_dir[2]) ) 
+                / (tile_magnitude * eye_dir_magnitude);
+
+            cornerInside[tile_j * (tiles_across+1) + tile_i] =
+                (cos_angle >= cos_diag);
+
+            if( debug3d > 1 )
+            {
+                printf("tile (%d,%d) z %f  angle=%f ", 
+                       tile_i, tile_j, 
+                       eye_pos[2], RAD2DEG * acos(cos_angle));
+
+                if( cornerInside[tile_j*(tiles_across+1)+tile_i] )
+                {
+                    printf("\n");
+                } else {
+                    printf("not\n");
+                }
+            }
+            if( debug3d > 2)
+            {
+                printf("tile(%f,%f,%f) tile_mag %f eye_mag %f\n", 
+                       tile_vect[0], tile_vect[1], tile_vect[2], 
+                       tile_magnitude, eye_dir_magnitude);
+            }
+        }
+    }
+
+    /* 
+     * Scan through all the files, and for each one establish whether
+     * any of the corners are in the view cone.  If so, add the tile
+     * to the list of files to be drawn.
+     */
+    for( tile_j = 0; tile_j < tiles_down; tile_j++ )
+    {
+        for( tile_i = 0; tile_i < tiles_across; tile_i++ )
+        {
+            int     tile = tile_i + tile_j * tiles_across;
+            int     corner = tile_i + tile_j * (tiles_across+1);
+
+            if( cornerInside[corner]
+                || cornerInside[corner+1]
+                || cornerInside[corner+tiles_across+1]
+                || cornerInside[corner+tiles_across+2] )
+            {
+                g_array_append_val( tilelist, tile );  
+            }
+        }
+    }
+
+    g_free( cornerInside );
+
+    return tilelist;
+}
+
+GArray *
+gv_mesh_tilelist_get( GvMesh *mesh, GvViewArea *view, 
+                      GvRasterLayer *rlayer, GArray *tilelist )
+{
+    gint i;
+
+    if( !view->flag_3d)
+        gv_mesh_tilelist_get_2d( mesh, view, rlayer, tilelist );
+    else
+        gv_mesh_tilelist_get_3d( mesh, view, rlayer, tilelist );
+        
+    i = -1;
+    g_array_append_val( tilelist, i );
+
+    return tilelist;
+}
+
+#define GROW_RANGE(x,xoff,xsize) { \
+  if( (int)(x) < 0 ) (x) = 0.0; \
+  if( (int)(x) < (xoff) ) { (xsize) += (xoff) - (int)(x); (xoff) = (int)(x); } \
+  else if( (int)(x) > (xoff)+(xsize) ) { (xsize) = (int)(x) - (xoff); } }
+
+static void
+gv_raster_layer_view_extents( GvRasterLayer *rlayer, 
+                              int *xoff, int *yoff, int *xsize, int *ysize )
+
+{
+    GvRaster *raster;
+    GvViewArea   *view;
+
+    raster = rlayer->prototype_data;
+    view = GV_LAYER(rlayer)->view;
+    
+    /*
+    ** Compute a bounding rectangle.  In 3D we will just use all tiles
+    ** identified in the tile list, but for 2D we try to restrict things
+    ** more closely.
+    */
+    if( view->flag_3d )
+    {
+        *xoff = *yoff = 0;
+        *xsize = raster->width;
+        *ysize = raster->height;
+    }
+    else
+    {
+        gvgeocoord x, y;
+        double  dx, dy;
+        
+        *xsize = 1;
+        *ysize = 1;
+
+        /* upper left */
+        gv_view_area_map_pointer( view, 0.0, 0.0, &x, &y );
+        dx = x; dy = y;
+        if( gv_raster_layer_view_to_pixel( rlayer, &dx, &dy, NULL ) )
+        {
+            *xoff = MAX(0,MIN(raster->width-1,(int) dx));
+            *yoff = MAX(0,MIN(raster->height-1,(int) dy));
+        }
+        else
+        {
+            *xoff = 0;
+            *yoff = 0;
+        }
+
+        /* upper right */
+        gv_view_area_map_pointer( view, view->state.shape_x, 0.0, 
+                                  &x, &y );
+        dx = x; dy = y;
+        if( gv_raster_layer_view_to_pixel( rlayer, &dx, &dy, NULL ) )
+        {
+            GROW_RANGE(dx, *xoff, *xsize );
+            GROW_RANGE(dy, *yoff, *ysize );
+        }
+
+        /* lower right */
+        gv_view_area_map_pointer( view, 
+                                  view->state.shape_x, 
+                                  view->state.shape_y, 
+                                  &x, &y );
+        dx = x; dy = y;
+        if( gv_raster_layer_view_to_pixel( rlayer, &dx, &dy, NULL ) )
+        {
+            GROW_RANGE(dx, *xoff, *xsize );
+            GROW_RANGE(dy, *yoff, *ysize );
+        }
+
+        /* lower left */
+        gv_view_area_map_pointer( view, 0.0, view->state.shape_y, 
+                                       &x, &y );
+        dx = x; dy = y;
+        if( gv_raster_layer_view_to_pixel( rlayer, &dx, &dy, NULL ) )
+        {
+            GROW_RANGE(dx, *xoff, *xsize );
+            GROW_RANGE(dy, *yoff, *ysize );
+        }
+
+        if( *xoff + *xsize > raster->width )
+            *xsize = raster->width - *xoff;
+        if( *yoff + *ysize > raster->height )
+            *ysize = raster->height - *yoff;
+    }
+}
+
+#undef GROW_RANGE
+
+gint 
+gv_raster_layer_autoscale_view( GvRasterLayer *rlayer, int isrc,
+                                GvAutoScaleAlg alg, double alg_param, 
+                                double *out_min, double *out_max )
+
+{
+    int	sample_count, ret_val;
+    float *samples;
+    GvRaster *raster;
+    GvViewArea   *view;
+    GArray  *tile_list;
+    int xoff, yoff, xsize, ysize;
+
+    if( isrc < 0 || isrc >= rlayer->source_count )
+        return FALSE;
+
+    raster = rlayer->source_list[isrc].data;
+    if( raster == NULL )
+        return FALSE;
+
+    view = GV_LAYER(rlayer)->view;
+    
+    /*
+    ** Build the tile list for the view.  Even works in 3D!
+    */
+    tile_list = g_array_new( FALSE, FALSE, sizeof(int) );
+
+    tile_list = gv_mesh_tilelist_get( rlayer->mesh, view, rlayer, tile_list );
+
+    /*
+    ** Compute a bounding rectangle.  In 3D we will just use all tiles
+    ** identified in the tile list, but for 2D we try to restrict things
+    ** more closely.
+    */
+    gv_raster_layer_view_extents( rlayer, &xoff, &yoff, &xsize, &ysize );
+
+    /*
+    ** Collect random samples to computing scaling min/max.
+    */
+    if( gv_manager_get_preference(gv_get_manager(),"autoscale_samples") )
+    {
+        sample_count = atoi(
+            gv_manager_get_preference(gv_get_manager(),"autoscale_samples"));
+        sample_count = MAX(10,sample_count);
+    }
+    else
+        sample_count = 10000;
+
+    samples = (float *) g_new(float,sample_count);
+
+    sample_count = 
+        gv_raster_collect_random_sample(
+            raster, 10000, samples, 
+            xoff, yoff, xsize, ysize, tile_list );
+
+    /*
+    ** Perform the autoscaling.
+    */
+    ret_val = 
+        gv_raster_autoscale( raster, alg, alg_param, sample_count, samples, 
+                             out_min, out_max );
+
+    g_free( samples );
+
+    g_array_free( tile_list, TRUE );
+
+    return ret_val;
+}
+
+gint gv_raster_layer_histogram_view( GvRasterLayer *rlayer, int isrc,
+                                     double scale_min, double scale_max,
+                                     int include_out_of_range,
+                                     int bucket_count, int *histogram )
+
+{
+    GvRaster *raster;
+    GvViewArea   *view;
+    GArray  *tile_list;
+    int xoff, yoff, xsize, ysize;
+
+    if( isrc < 0 || isrc >= rlayer->source_count )
+        return 0;
+
+    raster = rlayer->source_list[isrc].data;
+    if( raster == NULL )
+        return 0;
+
+    view = GV_LAYER(rlayer)->view;
+    
+    /*
+    ** Build the tile list for the view.  Even works in 3D!
+    */
+    tile_list = g_array_new( FALSE, FALSE, sizeof(int) );
+
+    tile_list = gv_mesh_tilelist_get( rlayer->mesh, view, rlayer, tile_list );
+
+    /*
+    ** Compute a bounding rectangle.  In 3D we will just use all tiles
+    ** identified in the tile list, but for 2D we try to restrict things
+    ** more closely.
+    */
+    gv_raster_layer_view_extents( rlayer, &xoff, &yoff, &xsize, &ysize );
+
+    /*
+    ** Perform the histogram.
+    */
+    
+    return gv_raster_collect_histogram( raster, scale_min, scale_max, 
+                                        bucket_count, histogram, 
+                                        include_out_of_range,
+                                        xoff, yoff, xsize, ysize, tile_list );
+}

Added: packages/openev/branches/upstream/current/gvrasterlayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvrasterlayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterlayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,320 @@
+/******************************************************************************
+ * $Id: gvrasterlayer.h,v 1.32 2004/06/23 14:35:05 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster display layer (managed textures, redraw, etc)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterlayer.h,v $
+ * Revision 1.32  2004/06/23 14:35:05  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.31  2004/01/22 20:45:27  andrey_kiselev
+ * Added methods gv_raster_layer_nodata_set() and gv_raster_layer_nodata_get() to
+ * work with nodata_* layer properties and method gv_raster_layer_type_get() to
+ * query raster data type.
+ *
+ * Revision 1.30  2002/04/12 14:40:37  gmwalter
+ * Removed the gvmesh rescale function (not needed because of view area
+ * rescaling).
+ *
+ * Revision 1.28  2002/03/07 02:31:56  warmerda
+ * added default_height to add_height functions
+ *
+ * Revision 1.27  2001/12/13 03:29:17  warmerda
+ * avoid purging textures used in this render
+ *
+ * Revision 1.26  2001/11/28 19:23:04  warmerda
+ * Added logic to keep track if the mesh is dirty (out of date), and to
+ * refresh it before a redraw.  It is marked dirty when the prototype data
+ * emits a geotransform-changed signal.
+ *
+ * Revision 1.25  2001/10/17 16:23:52  warmerda
+ * added support for composing complex lut and pct
+ *
+ * Revision 1.24  2001/10/16 18:50:29  warmerda
+ * added autoscale and histogram functions
+ *
+ * Revision 1.23  2001/07/13 22:16:03  warmerda
+ * disard unused pc_lut_{x,y} fields
+ *
+ * Revision 1.22  2001/07/03 14:26:05  warmerda
+ * added set/get raw ability
+ *
+ * Revision 1.21  2001/01/30 19:34:29  warmerda
+ * make gv_raster_layer_purge_all_textures() public
+ *
+ * Revision 1.20  2000/08/25 20:11:52  warmerda
+ * added preliminary nodata support
+ *
+ * Revision 1.19  2000/07/18 15:04:25  warmerda
+ * added texture dump prototype
+ *
+ * Revision 1.18  2000/07/03 20:58:31  warmerda
+ * eye_pos in georef coordinates now
+ *
+ * Revision 1.17  2000/06/27 21:25:41  warmerda
+ * rewrote texture caching completely
+ *
+ * Revision 1.16  2000/06/23 12:56:18  warmerda
+ * added multiple GvRasterSource support
+ *
+ * Revision 1.15  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_RASTER_LAYER_H__
+#define __GV_RASTER_LAYER_H__
+
+#include <gtkgl/gdkgl.h>
+#include <GL/gl.h>
+#include "gvlayer.h"
+#include "gvraster.h"
+
+
+#define GV_TYPE_RASTER_LAYER            (gv_raster_layer_get_type ())
+#define GV_RASTER_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_RASTER_LAYER, GvRasterLayer))
+#define GV_RASTER_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_RASTER_LAYER, GvRasterLayerClass))
+#define GV_IS_RASTER_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_RASTER_LAYER))
+#define GV_IS_RASTER_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_CLASS_LAYER))
+
+typedef struct _GvRasterLayer       GvRasterLayer;
+typedef struct _GvRasterLayerClass  GvRasterLayerClass;
+
+/* Need to include here since we need GvRasterLayer definition */
+
+enum {
+    GV_RASTER_LAYER_BLEND_MODE_OFF = 0,
+    GV_RASTER_LAYER_BLEND_MODE_FILTER,
+    GV_RASTER_LAYER_BLEND_MODE_MULTIPLY,
+    GV_RASTER_LAYER_BLEND_MODE_ADD,
+    GV_RASTER_LAYER_BLEND_MODE_CUSTOM
+};
+
+typedef enum {
+    GV_RLM_AUTO = 0,
+    GV_RLM_SINGLE = 1,
+    GV_RLM_RGBA = 2, 
+    GV_RLM_COMPLEX = 3
+} GvRasterLayerMode;
+
+typedef struct _GvRasterLayerTexObj
+{
+    struct _GvRasterLayerTexObj *prev;
+    struct _GvRasterLayerTexObj *next;
+    GvRasterLayer      *layer;
+
+    int    texture;
+    GLuint tex_obj; /* Name of the texture object for this tile */
+    int    lod;
+    int    size;
+    int    last_render;
+} GvRasterLayerTexObj;
+
+
+struct _GvRasterLayerGLDispInfo
+{
+    /* Do we replace or modulate? */
+    gint tex_env_mode; /* GL_REPLACE and GL_MODULATE */
+    gint mag_filter;
+    gint min_filter;
+    gint s_wrap;
+    gint t_wrap;
+    GvColor fragment_color; /* Color if we modulate */
+
+    /* Is blending enabled? */
+    gint blend_enable;
+    gint blend_src, blend_dst; /* Src and dst parameters */
+
+    /* Is alpha test enabled? */
+    gint alpha_test;
+    gint alpha_test_mode;
+    GLfloat alpha_test_val;
+};
+    
+typedef struct {
+
+    GvRaster 	*data;
+    unsigned char const_value;  /* used for whole tile if data is NULL */
+    float	max, min;	/* for scaling */
+    unsigned char *lut;		/* greyscale lut (uchar*256 or NULL) */
+
+    /* lut_rgba_composed is used only in the RGBA case in the presence  */
+    /* of complex bands.  It is equivalent to the pc_lut_composed       */
+    /* used in the single complex band case, but since there are        */
+    /* potentially multiple bands, it's stored here instead.            */
+    unsigned char *lut_rgba_composed;
+
+    int         nodata_active;  /* flag indicating of nodata value meaningful*/
+    double      nodata_real;
+    double      nodata_imaginary;
+
+} GvRasterSource;
+
+#define MAX_RASTER_SOURCE 4
+
+struct _GvRasterLayer
+{
+    GvLayer layer;
+
+    GvRasterLayerMode mode;
+
+    gint      mesh_is_raw;
+    gint      mesh_is_dirty;
+    GvMesh   *mesh;
+
+    int       tile_x, tile_y;
+
+    GvRasterLayerTexObj **textures;
+
+    /* Sources list */
+
+    int       source_count;
+    GvRasterSource source_list[MAX_RASTER_SOURCE];
+
+    GvRaster *prototype_data;
+
+    /* Final color table */
+    unsigned char *pc_lut;
+    unsigned char *pc_lut_composed;
+
+    /* LUT to use in RGB/RGBA case for any complex bands found */
+    unsigned char *pc_lut_rgba_complex;
+
+    /* OpenGL display properties */
+
+    struct _GvRasterLayerGLDispInfo gl_info;
+
+    /* Texture load information */
+
+    GArray *missing_tex;
+
+    /* Current tilelist display */
+
+    GArray *tile_list;
+};
+
+struct _GvRasterLayerClass
+{
+    GvLayerClass parent_class;
+};
+
+GtkType gv_raster_layer_get_type(void);
+GtkObject *gv_raster_layer_new(int mode, 
+                               GvRaster *prototype_data, 
+                               GvProperties prop);
+
+/* Raster value setup functions */
+long gv_raster_layer_texture_clamp_set( GvRasterLayer *layer, int s_clamp, int t_clamp );
+long gv_raster_layer_zoom_set( GvRasterLayer *layer, int mag_mode, int min_mode );
+long gv_raster_layer_zoom_get( GvRasterLayer *layer, int *mag_mode, int *min_mode );
+long gv_raster_layer_alpha_set( GvRasterLayer *layer, int alpha_mode, float alpha_check_val );
+long gv_raster_layer_texture_mode_set( GvRasterLayer *layer, int texture_mode, GvColor color );
+long gv_raster_layer_blend_mode_set( GvRasterLayer *layer, int blend_mode, int sfactor, int dfactor );
+void gv_raster_layer_purge_all_textures( GvRasterLayer *layer );
+
+/* Raster value query functions */
+int gv_raster_layer_get_mode( GvRasterLayer *layer );
+long gv_raster_layer_blend_mode_get( GvRasterLayer *layer, int *blend_mode, int *sfactor, int *dfactor );
+long gv_raster_layer_alpha_get( GvRasterLayer *layer, int *alpha_mode, float *alpha_check_val );
+long gv_raster_layer_texture_mode_get( GvRasterLayer *layer, int *texture_mode, GvColor *color );
+
+/* source related */
+float gv_raster_layer_max_get( GvRasterLayer *layer, int isource );
+float gv_raster_layer_min_get( GvRasterLayer *layer, int isource );
+unsigned char *gv_raster_layer_source_get_lut( GvRasterLayer *layer, 
+                                               int isource );
+unsigned char gv_raster_layer_get_const_value( GvRasterLayer *layer, 
+                                               int isource );
+
+GvRaster *gv_raster_layer_get_data( GvRasterLayer *layer, int isource );
+
+int gv_raster_layer_set_source( GvRasterLayer *layer, int isource, 
+                                GvRaster *data, float min, float max, 
+                                unsigned char const_value, 
+                                unsigned char *lut,
+                                int nodata_active, 
+                                double nodata_real,
+                                double nodata_imaginary );
+
+int gv_raster_layer_min_set( GvRasterLayer *layer, int isource, float min );
+int gv_raster_layer_max_set( GvRasterLayer *layer, int isource, float max );
+gint gv_raster_layer_nodata_set( GvRasterLayer *layer, int isource,
+                                 double nodata_real, double nodata_imaginary );
+gint gv_raster_layer_nodata_get( GvRasterLayer *layer, int isource,
+                                 double *nodata_real,
+                                 double *nodata_imaginary );
+GDALDataType gv_raster_layer_type_get( GvRasterLayer *layer, int isource );
+    
+/* transformation */
+gint gv_raster_layer_pixel_to_view(GvRasterLayer *raster, 
+                                   double *x, double *y, double *z );
+gint gv_raster_layer_view_to_pixel(GvRasterLayer *raster, 
+                                   double *x, double *y, double *z );
+
+/* other */
+gint gv_raster_layer_autoscale_view( GvRasterLayer *rlayer, int isrc, 
+                                     GvAutoScaleAlg alg, double alg_param, 
+                                     double *out_min, double *out_max );
+
+gint gv_raster_layer_histogram_view( GvRasterLayer *rlayer, int isrc,
+                                     double scale_min, double scale_max,
+                                     int include_out_of_range,
+                                     int bucket_count, int *histogram );
+
+double gv_raster_layer_pixel_size( GvRasterLayer *layer );
+void gv_raster_layer_add_height( GvRasterLayer *layer, 
+                                 GvRaster *height_raster,
+                                 double default_height);
+void gv_raster_layer_clamp_height( GvRasterLayer *layer, 
+                                   int bclamp_min, int bclamp_max,
+                                   double min_height, double max_height );
+
+unsigned char *
+gv_raster_layer_srctile_xy_get( GvRasterLayer *layer, int isource, 
+                                int tile, int lod, int * needs_free,
+                                unsigned char *nodata_mask);
+unsigned char *gv_raster_layer_gltile_get( GvRasterLayer *layer, 
+                                           int tile, int lod,
+                                           int *format, int *type, 
+                                           int *needs_free );
+
+int gv_raster_layer_set_raw( GvRasterLayer *layer, int raw_enable );
+void gv_raster_layer_refresh_mesh( GvRasterLayer *layer );
+
+/* texture cache functions */
+void gv_raster_layer_purge_texture( GvRasterLayer *layer, int texture );
+void gv_raster_layer_create_texture( GvRasterLayer *layer, int texture,
+                                     GLuint tex_obj, int lod, int size );
+void gv_raster_layer_touch_texture( GvRasterLayer *layer, int texture );
+void gv_raster_layer_reset_texture( GvRasterLayer *layer, int texture,
+                                    int lod, int size );
+void gv_texture_cache_set_max( int );
+int gv_texture_cache_get_max();
+int gv_texture_cache_get_used();
+void gv_texture_cache_dump();
+
+GArray *gv_mesh_tilelist_get( GvMesh *mesh, GvViewArea *view,
+                              GvRasterLayer *rlayer, GArray *tilelist );
+#endif /* __GV_RASTER_LAYER_H__ */
+

Added: packages/openev/branches/upstream/current/gvrasterlut.c
===================================================================
--- packages/openev/branches/upstream/current/gvrasterlut.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterlut.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,831 @@
+/******************************************************************************
+ * $Id: gvrasterlut.c,v 1.31 2005/08/30 12:58:56 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Compute, and apply LUT to GvRaster.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterlut.c,v $
+ * Revision 1.31  2005/08/30 12:58:56  andrey_kiselev
+ * Make the return value of unsigned gv_raster_layer_lut_get().
+ *
+ * Revision 1.30  2004/06/23 14:35:05  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.29  2003/03/07 17:03:03  gmwalter
+ * Move last_complex_lut property setting down to c-level, fix magphase indeterminate
+ * phase colour setting.
+ *
+ * Revision 1.28  2003/02/26 16:43:00  warmerda
+ * don't crash on PCTs larger than 256 entries
+ *
+ * Revision 1.27  2002/05/27 19:34:02  warmerda
+ * set the center pixel to red for phase lut
+ *
+ * Revision 1.26  2001/10/17 16:23:52  warmerda
+ * added support for composing complex lut and pct
+ *
+ * Revision 1.25  2001/08/23 02:23:28  warmerda
+ * enable alpha blending if palette has any non-255 alpha values
+ *
+ * Revision 1.24  2001/07/24 21:21:45  warmerda
+ * added EV style phase colormap
+ *
+ * Revision 1.23  2001/01/30 19:34:11  warmerda
+ * added gv_raster_layer_purge_all_textures calls
+ *
+ * Revision 1.22  2000/08/25 20:10:18  warmerda
+ * modified phase to colour mapping to match EV
+ *
+ * Revision 1.21  2000/07/13 13:51:55  warmerda
+ * make 2d color luts in greyscale
+ *
+ * Revision 1.20  2000/07/07 14:29:23  srawlin
+ * removed debug print statement
+ *
+ * Revision 1.19  2000/06/27 21:26:04  warmerda
+ * removed use of invalidated
+ *
+ * Revision 1.18  2000/06/23 12:56:18  warmerda
+ * added multiple GvRasterSource support
+ *
+ * Revision 1.17  2000/06/20 14:32:06  warmerda
+ * fixed free/g_new mismatch
+ *
+ * Revision 1.16  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <gtk/gtk.h>
+#include "gvrastertypes.h"
+#include "gvrasterlut.h"
+#include "gdal.h"
+
+#ifndef M_PI
+#  define M_PI 3.14159265358979323846
+#endif
+
+static void gv_raster_layer_lut_hsv_to_rgb(  float h, float s, float v, float *r, float *g, float *b );
+static int gv_raster_layer_lut_interpolate( guchar *lut, guchar start_color[4], guint start_index, guchar end_color[4], guint end_index );
+
+
+static unsigned char *phase_lut = "\377\000\000\377\000\000\377\000\000\377\000\000\377\017\000\377\017\000\377\017\000\377\017\000\375\043\000\375\043\000\375\043\000\375\043\000\371\067\000\371\067\000\371\067\000\371\067\000\364\112\000\364\112\000\364\112\000\364\112\000\357\130\000\357\130\000\357\130\000\357\130\000\350\153\000\350\153\000\350\153\000\350\153\000\336\175\000\336\175\000\336\175\000\336\175\000\324\216\000\324\216\000\324\216\000\324\216\000\313\232\000\313\232\000\313\232\000\313\232\000\277\251\000\277\251\000\277\251\000\277\251\000\261\270\000\261\270\000\261\270\000\261\270\000\242\305\000\242\305\000\242\305\000\242\305\000\226\316\000\226\316\000\226\316\000\226\316\000\205\331\000\205\331\000\205\331\000\205\331\000\164\343\000\164\343\000\164\343\000\164\343\000\142\354\000\142\354\000\142\354\000\142\354\000\124\361\000\124\361\000\124\361\000\124\361\000\074\370\000\074\370\000\074\370\000\074\370\000\050\374\000\050\374\000\050\374\000\050\374\000\031\376\000\031\376\000\031\376\000\031\376\000\005\377\000\005\377\000\005\377\000\005\377\000\000\377\017\000\377\017\000\377\017\000\377\017\000\375\043\000\375\043\000\375\043\000\375\043\000\372\062\000\372\062\000\372\062\000\372\062\000\365\105\000\365\105\000\365\105\000\365\105\000\357\130\000\357\130\000\357\130\000\357\130\000\350\153\000\350\153\000\350\153\000\350\153\000\341\170\000\341\170\000\341\170\000\341\170\000\327\211\000\327\211\000\327\211\000\327\211\000\313\232\000\313\232\000\313\232\000\313\232\000\277\251\000\277\251\000\277\251\000\277\251\000\264\264\000\264\264\000\264\264\000\264\264\000\246\302\000\246\302\000\246\302\000\246\302\000\226\316\000\226\316\000\226\316\000\226\316\000\201\334\000\201\334\000\201\334\000\201\334\000\164\343\000\164\343\000\164\343\000\164\343\000\142\354\000\142\354\000\142\354\000\142\354\000\117\363\000\117\363\000\117\363\000\117\363\000\074\370\000\074\370\000\074\370\000\074\370\000\055\373\000\055\373\000\055\373\000\055\373\000\031\376\000\031\376\000\031\376\000\031\376\000\005\377\000\005\377\000\005\377\000\005\377\017\000\377\017\000\377\017\000\377\017\000\377\036\000\375\036\000\375\036\000\375\036\000\375\062\000\372\062\000\372\062\000\372\062\000\372\105\000\365\105\000\365\105\000\365\105\000\365\130\000\357\130\000\357\130\000\357\130\000\357\146\000\352\146\000\352\146\000\352\146\000\352\170\000\341\170\000\341\170\000\341\170\000\341\211\000\327\211\000\327\211\000\327\211\000\327\232\000\313\232\000\313\232\000\313\232\000\313\251\000\277\251\000\277\251\000\277\251\000\277\270\000\261\270\000\261\270\000\261\270\000\261\305\000\242\305\000\242\305\000\242\305\000\242\321\000\222\321\000\222\321\000\222\321\000\222\331\000\205\331\000\205\331\000\205\331\000\205\343\000\164\343\000\164\343\000\164\343\000\164\354\000\142\354\000\142\354\000\142\354\000\142\363\000\117\363\000\117\363\000\117\363\000\117\367\000\100\367\000\100\367\000\100\367\000\100\373\000\055\373\000\055\373\000\055\373\000\055\376\000\031\376\000\031\376\000\031\376\000\031\377\000\000\377\000\000\377\000\000\377\000\000";
+
+int gv_raster_layer_lut_color_wheel_new_ev( 
+    GvRasterLayer *layer, gint set_phase, gint set_magnitude )
+{
+    int i, q;
+    float f_i, f_q;
+    float magnitude, /* Calculated magnitude from I/Q data */
+	phase_angle; /* Calculated phase angle from I/Q data */
+    guchar *LUT;
+    guchar *ptr; /* This is the pointer we use to walk across the LUT table */
+
+
+    /* Allocate enough room to store the LUT */
+
+    if( ( LUT = g_new( guchar, 256 * 256 * 4 ) ) == NULL )
+    {
+	return 1;
+    } else {
+	ptr = LUT;
+	
+	for( q = 0; q < 256; q++ )
+	{
+	    for( i = 0; i < 256; i++ )
+	    {
+                int	cmap_entry;
+
+		f_i = -(( i - 127 ) / 128.0);
+		f_q = ( q - 127 ) / 128.0;
+
+                if( set_magnitude )
+                {
+                    magnitude = sqrt( f_i * f_i + f_q * f_q );
+                    magnitude = MAX(0.0,MIN(1.0,magnitude));
+                }
+                else
+                    magnitude = 1.0;
+
+                if( set_phase )
+                {
+                    phase_angle = atan2( f_q, f_i ) - M_PI*0.5;
+                    if( phase_angle < 0.0 )
+                        phase_angle += 2 * M_PI;
+
+                    cmap_entry = phase_angle * 256 / (2 * M_PI);
+                    cmap_entry = MIN(255,MAX(0,cmap_entry));
+
+                    if ((q == 127) && (i == 127))
+                    {
+                        /* We want the "indetermininate phase" LUT entry to be hardcoded 
+                          to red (0 phase). */
+
+                        *ptr++ = (int) (255*magnitude + 0.5);
+                        *ptr++ = 0;
+                        *ptr++ = 0;
+                        *ptr++ = 255;
+                    }
+                    else
+                    {
+                        *ptr++ = (int) (phase_lut[cmap_entry*3+0]*magnitude + 0.5);
+                        *ptr++ = (int) (phase_lut[cmap_entry*3+1]*magnitude + 0.5);
+                        *ptr++ = (int) (phase_lut[cmap_entry*3+2]*magnitude + 0.5);
+                        *ptr++ = 255;
+                    }
+                }
+                else
+                {
+                    *ptr++ = (int) (255 * magnitude + 0.5);
+                    *ptr++ = (int) (255 * magnitude + 0.5);
+                    *ptr++ = (int) (255 * magnitude + 0.5);
+                    *ptr++ = 255;
+                }
+	    }
+	}
+
+    }
+
+    if ( gv_raster_layer_lut_put( layer, LUT, 256 ) )
+    {
+	g_free(LUT);
+	return 1;
+    }
+
+    if (set_phase && set_magnitude )
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","magphase");
+    else if (set_magnitude)
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","magnitude");
+    else if (set_phase)
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","phase");
+    else
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","unnamed");
+
+    g_free(LUT);
+    return 0;
+}
+
+int gv_raster_layer_lut_color_wheel_new( GvRasterLayer *layer, gint h_type, gfloat h_param, gint s_type, gfloat s_param, gint v_type, gfloat v_param )
+{
+    int i, q;
+    float f_i, f_q;
+    float magnitude, /* Calculated magnitude from I/Q data */
+	phase_angle; /* Calculated phase angle from I/Q data */
+    float *h=NULL, *s=NULL, *v=NULL;
+    float r, g, b;
+    guchar *LUT;
+    guchar *ptr; /* This is the pointer we use to walk across the LUT table */
+    int    use_hsv = TRUE;
+
+
+    /* Allocate enough room to store the LUT */
+
+    if( ( LUT = g_new( guchar, 256 * 256 * 4 ) ) == NULL )
+    {
+	return 1;
+    } else {
+	/* Figure out the HSV mapping for the data -- each channel of H, S and V can be independantly specified as either
+	   Magnitude - GV_RASTER_LAYER_LUT_MAGNITUDE,
+	   Phase Angle - GV_RASTER_LAYER_LUT_PHASE_ANGLE,
+	   Scalar - GV_RASTER_LAYER_LUT_SCALAR */
+
+	switch( h_type )
+	{
+          case GV_RASTER_LAYER_LUT_MAGNITUDE:
+            h = &magnitude;
+            break;
+          case GV_RASTER_LAYER_LUT_PHASE_ANGLE:
+            h = &phase_angle;
+            break;
+          case GV_RASTER_LAYER_LUT_SCALAR:
+            h = &h_param;
+            if( h_param < 0.0 )
+                use_hsv = FALSE;
+            break;
+          case GV_RASTER_LAYER_LUT_REAL:
+            h = &f_q;
+            break;
+          case GV_RASTER_LAYER_LUT_IMAGINARY:
+            h = &f_i;
+            break;
+          default:
+            return 1;
+	}
+
+	switch( s_type )
+	{
+          case GV_RASTER_LAYER_LUT_MAGNITUDE:
+            s = &magnitude;
+            break;
+          case GV_RASTER_LAYER_LUT_PHASE_ANGLE:
+            s = &phase_angle;
+            break;
+          case GV_RASTER_LAYER_LUT_SCALAR:
+            s = &s_param;
+            break;
+          case GV_RASTER_LAYER_LUT_REAL:
+            s = &f_q;
+            break;
+          case GV_RASTER_LAYER_LUT_IMAGINARY:
+            s = &f_i;
+            break;
+          default:
+            return 1;
+	}
+	
+	switch( v_type )
+	{
+          case GV_RASTER_LAYER_LUT_MAGNITUDE:
+            v = &magnitude;
+            break;
+          case GV_RASTER_LAYER_LUT_PHASE_ANGLE:
+            v = &phase_angle;
+            break;
+          case GV_RASTER_LAYER_LUT_SCALAR:
+            v = &v_param;
+            break;
+          case GV_RASTER_LAYER_LUT_REAL:
+            v = &f_q;
+            break;
+          case GV_RASTER_LAYER_LUT_IMAGINARY:
+            v = &f_i;
+            break;
+          default:
+            return 1;
+	}
+
+	ptr = LUT;
+	
+	for( q = 0;q < 256; q++ )
+	{
+	    for( i = 0; i < 256; i++ )
+	    {
+		f_i = -(( i - 127 ) / 128.0);
+		f_q = ( q - 127 ) / 128.0;
+
+		phase_angle = (atan2( f_q, f_i ) - M_PI*0.5) / ( 2 * M_PI );
+                if( phase_angle < 0.0 )
+                    phase_angle += 1.0;
+                
+		magnitude = sqrt( f_i * f_i + f_q * f_q );
+
+                f_i = MAX(0,MIN(1,-1*f_i*0.5 + 0.5));
+                f_q = MAX(0,MIN(1,f_q*0.5 + 0.5));
+
+		if( magnitude > 1.0 )
+		    magnitude = 1.0;
+
+                if( use_hsv )
+                    gv_raster_layer_lut_hsv_to_rgb( *h, *s, *v, &r, &g, &b );
+                else
+                    r = g = b = *v;
+
+		*ptr++ = (unsigned char) ( r * 255 );
+		*ptr++ = (unsigned char) ( g * 255 );
+		*ptr++ = (unsigned char) ( b * 255 );
+		*ptr++ = 255;
+
+	    }
+	}
+    }
+
+    if ( gv_raster_layer_lut_put( layer, LUT, 256 ) )
+    {
+	g_free(LUT);
+	return 1;
+    }
+
+    if ((h_type == GV_RASTER_LAYER_LUT_SCALAR) &&
+        (s_type == GV_RASTER_LAYER_LUT_SCALAR) &&
+        (v_type == GV_RASTER_LAYER_LUT_REAL))
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","real");
+    else if ((h_type == GV_RASTER_LAYER_LUT_SCALAR) &&
+        (s_type == GV_RASTER_LAYER_LUT_SCALAR) &&
+        (v_type == GV_RASTER_LAYER_LUT_IMAGINARY))
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","imaginary");
+    else
+        gv_data_set_property(GV_DATA(layer),"last_complex_lut","unnamed");
+
+    g_free(LUT);
+    return 0;
+}
+
+int
+gv_raster_layer_lut_color_wheel_1d_new( GvRasterLayer *layer, float s, float v, float offset )
+{
+    float r, g, b, h;
+    int i, e;
+    guchar *lut;
+
+    if( ( lut = g_new( guchar, 256 * 4 ) ) != NULL )
+    {
+
+	for( e = 0, i = 0; i < 256; i++ )
+	{
+	    h = ( i / 255.0 + offset );
+	    
+	    gv_raster_layer_lut_hsv_to_rgb( h, s, v, &r, &g, &b );
+
+	    lut[e++] = (char) (r * 255.0);
+	    lut[e++] = (char) (g * 255.0);
+	    lut[e++] = (char) (b * 255.0);
+	    lut[e++] = 255;
+	}
+    }
+
+    if( gv_raster_layer_lut_put( layer, lut, 1 ) )
+    {
+	g_free( lut );
+	return 1;
+    }
+
+    g_free( lut );
+    return 0;
+}
+
+int
+gv_raster_layer_lut_interpolated_new( GvRasterLayer *layer, GvRasterLayerLutInterpolate *color_pair, int offset )
+{
+    int i;
+    guchar *lut;
+
+    if( ( lut = g_new( guchar, 256 * 4 ) ) != NULL )
+    {
+	for( i = 0; color_pair[i+1].index != -1; i++ )
+	{
+	    gv_raster_layer_lut_interpolate( lut, color_pair[i].color, color_pair[i].index+offset, color_pair[i+1].color, color_pair[i+1].index+offset );
+	}
+
+	if ( gv_raster_layer_lut_put( layer, lut, 1 ) )
+	{
+	    g_free(lut);
+	    return 1;
+	}
+        else
+            g_free(lut);
+
+    } else {
+	return 1;
+    }
+    return 0;
+}
+
+static int
+gv_raster_layer_lut_interpolate( guchar *lut, guchar start_color[4], guint start_index, guchar end_color[4], guint end_index )
+{
+    float spread = ( end_index - start_index );
+    float step_r, step_g, step_b;
+    int i, e;
+
+    g_return_val_if_fail( lut != NULL, 1 );
+
+    step_r = ( end_color[0] - start_color[0] ) / spread;
+    step_g = ( end_color[1] - start_color[1] ) / spread;
+    step_b = ( end_color[2] - start_color[2] ) / spread;
+
+    for( e = start_index * 4, i = 0; i < end_index-start_index; i++ )
+    {
+	lut[e++%1024] = start_color[0] + (guchar) ( step_r * i );
+	lut[e++%1024] = start_color[1] + (guchar) ( step_g * i );
+	lut[e++%1024] = start_color[2] + (guchar) ( step_b * i );
+	lut[e++%1024] = 255;
+    }
+
+    lut[e++%1024] = end_color[0];
+    lut[e++%1024] = end_color[1];
+    lut[e++%1024] = end_color[2];
+    lut[e%1024] = 255;
+
+    return 0;
+}
+
+/*
+** The following function creates pc_lut_compose based on taking a 2D (complex)
+** PCT in pc_lut and "precomposing" the LUT in the first raster source.  This
+** is intended to simulate "magnitude" enhancements inexpensively.
+**
+** The compose operation only does something if a 1D GvRasterSource LUT 
+** exists, and the PCT is 2D.
+*/
+
+int
+gv_raster_layer_lut_compose( GvRasterLayer *layer )
+
+{
+    unsigned char *lut;
+    int x, y, src;
+
+    if( layer->pc_lut_composed != NULL )
+    {
+        g_free( layer->pc_lut_composed );
+        layer->pc_lut_composed = NULL;
+    }
+
+    /*
+    ** Do we want to generate a composed PCT?
+    */
+    if( ( layer->mode != GV_RLM_COMPLEX 
+          || layer->pc_lut == NULL 
+          || layer->source_list[0].lut == NULL ) &&
+        ( layer->mode != GV_RLM_RGBA
+          || layer->pc_lut_rgba_complex == NULL ) )
+        return TRUE;
+
+
+    if( layer->mode == GV_RLM_COMPLEX )
+    {
+        /*
+        ** Create the composed PCT.
+        */
+        layer->pc_lut_composed = g_new( guchar, 256 * 4 * 256 );
+        lut = layer->source_list[0].lut;
+        for( x = 0; x < 256; x++ )
+        {
+            for( y = 0; y < 256; y++ )
+            {
+                float p_mag, mp_mag;
+                int   dx, dy;
+
+                dx = x - 128;
+                dy = y - 128;
+
+                /* compute magnitude scaled between 0.0 and 1.0 */
+                p_mag = sqrt((double)(dx*dx+dy*dy)) / 128.0;
+
+                if( p_mag > 1.0 )
+                    p_mag = 1.0;
+
+                /* compute modified pseudo magnitude */
+                mp_mag = lut[(int) (p_mag * 255.99)] / 255.0;
+
+                if( dx != 0 || dy != 0 )
+                {
+                    dx = (int) floor(dx * mp_mag / p_mag + 0.5);
+                    dy = (int) floor(dy * mp_mag / p_mag + 0.5);
+
+                    dx = MIN(MAX(-128,dx),127);
+                    dy = MIN(MAX(-128,dy),127);
+                }
+                
+                memcpy( layer->pc_lut_composed + (x + y*256)*4,
+                        layer->pc_lut + ((dx+128)+(dy+128)*256)*4,
+                        4 );
+            }
+        }
+    }
+    else
+    {
+        for( src = 0 ; src < layer->source_count; src++ )
+        {
+            if (layer->source_list[src].lut_rgba_composed != NULL)
+            {
+                g_free(layer->source_list[src].lut_rgba_composed);
+                layer->source_list[src].lut_rgba_composed = NULL;
+            }
+            if ( layer->source_list[src].lut != NULL )
+            {
+
+                /*
+                ** Create the composed PCT.
+                */
+                layer->source_list[src].lut_rgba_composed = \
+                                   g_new( guchar, 256 * 4 * 256 );
+                lut = layer->source_list[src].lut;
+                for( x = 0; x < 256; x++ )
+                {
+                    for( y = 0; y < 256; y++ )
+                    {
+                        float p_mag, mp_mag;
+                        int   dx, dy;
+
+                        dx = x - 128;
+                        dy = y - 128;
+
+                        /* compute magnitude scaled between 0.0 and 1.0 */
+                        p_mag = sqrt((double)(dx*dx+dy*dy)) / 128.0;
+
+                        if( p_mag > 1.0 )
+                            p_mag = 1.0;
+
+                        /* compute modified pseudo magnitude */
+                        mp_mag = lut[(int) (p_mag * 255.99)] / 255.0;
+
+                        if( dx != 0 || dy != 0 )
+                        {
+                            dx = (int) floor(dx * mp_mag / p_mag + 0.5);
+                            dy = (int) floor(dy * mp_mag / p_mag + 0.5);
+
+                            dx = MIN(MAX(-128,dx),127);
+                            dy = MIN(MAX(-128,dy),127);
+                        }
+                
+                        memcpy( layer->source_list[src].lut_rgba_composed + 
+                                (x + y*256)*4,
+                                layer->pc_lut_rgba_complex + 
+                                  ((dx+128)+(dy+128)*256)*4,
+                                4 );
+                    }
+                }
+            }
+        }
+    }
+    return TRUE;
+}
+
+int
+gv_raster_layer_lut_put( GvRasterLayer *layer, guchar *lut, gint height )
+{
+    int	i;
+
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+
+    if (layer->mode == GV_RLM_RGBA && height == 256)
+    {
+        /* Lut to apply to complex bands in rgba mode */
+        if( layer->pc_lut_rgba_complex )
+        {
+	    g_free( layer->pc_lut_rgba_complex );
+            layer->pc_lut_rgba_complex = NULL;
+        }
+
+        if( lut == NULL )
+        {
+            layer->pc_lut_rgba_complex = NULL;
+        }
+        else
+        {
+            layer->pc_lut_rgba_complex = g_new( guchar, 256 * 4 * height );
+            memcpy( layer->pc_lut_rgba_complex, lut, 256 * 4 * height );
+        }
+        /*
+        ** Turn on Alpha blending if any of the colors have a non-255 alpha
+        ** value.
+        */
+        for( i = 0; layer->pc_lut_rgba_complex != NULL && i < 256 * height; 
+             i++ )
+        {
+            if( layer->pc_lut_rgba_complex[i*4 + 3] != 255 )
+            {
+                gv_raster_layer_blend_mode_set( layer, 
+                                            GV_RASTER_LAYER_BLEND_MODE_FILTER,
+                                            0, 0 );
+                break;
+            }
+        }
+
+    }
+    else
+    {
+        if( layer->pc_lut )
+        {
+	    g_free( layer->pc_lut );
+            layer->pc_lut = NULL;
+        }
+
+        if( lut == NULL )
+        {
+            layer->pc_lut = NULL;
+        }
+        else if( (layer->mode == GV_RLM_COMPLEX && height != 256)
+                 || (layer->mode != GV_RLM_COMPLEX && height != 1) )
+        {
+            g_warning( "Attempt to apply lut of inappropriate height in" 
+                       " gv_raster_layer_lut_put()." );
+            return 1;
+        }
+        else
+        {
+            layer->pc_lut = g_new( guchar, 256 * 4 * height );
+            memcpy( layer->pc_lut, lut, 256 * 4 * height );
+        }
+        /*
+        ** Turn on Alpha blending if any of the colors have a non-255 alpha
+        ** value.
+        */
+        for( i = 0; layer->pc_lut != NULL && i < 256 * height; i++ )
+        {
+            if( layer->pc_lut[i*4 + 3] != 255 )
+            {
+                gv_raster_layer_blend_mode_set( layer, 
+                                            GV_RASTER_LAYER_BLEND_MODE_FILTER,
+                                            0, 0 );
+                break;
+            }
+        }
+
+    }
+
+
+    /*
+    ** Purge old textures, to force regeneration and notify all of change.
+    */
+    gv_raster_layer_purge_all_textures( layer );
+    gv_layer_display_change( GV_LAYER(layer), NULL );
+
+    /*
+    ** Regenerate 2D composed Complex lut if needed.
+    */
+    gv_raster_layer_lut_compose( layer );
+
+    return 0;
+}
+
+
+/* April 2004 note: modified to include rgba_complex argument     */
+/* so that real or complex lut can be selected for rgba case      */
+/* (before there was no complex or mixed real-complex rgba mode). */
+unsigned char *
+gv_raster_layer_lut_get( GvRasterLayer *layer, int *width, int *height, 
+                         int rgba_complex )
+{
+    int   width_ret=256, height_ret;
+
+    /* The argument rgba_complex indicates whether a complex or real
+     * lut should be returned in the rgba case.  If rgba_complex is 1,
+     * the complex lut will be returned.  Otherwise, the real one
+     * will be returned.  The rgba_complex argument is IGNORED if the
+     * layer mode is not RGBA.
+     */
+    g_return_val_if_fail( layer != NULL, NULL );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), NULL );
+
+    if( layer->mode == GV_RLM_COMPLEX )
+        height_ret = 256;
+    else if ( layer->mode == GV_RLM_RGBA )
+    {
+        if (rgba_complex == 1)
+            height_ret = 256;
+        else
+            height_ret = 1;
+    }
+    else 
+        height_ret = 1;
+
+    if ( layer->mode == GV_RLM_RGBA )
+    {
+        if((rgba_complex != 1) && ( layer->pc_lut == NULL ))
+        {
+            width_ret = 0;
+            height_ret = 0;
+        }
+        else if((rgba_complex == 1) && ( layer->pc_lut_rgba_complex == NULL ))
+        {
+            width_ret = 0;
+            height_ret = 0;
+        }
+    }
+    else
+    {
+        if( layer->pc_lut == NULL )
+        {
+            width_ret = 0;
+            height_ret = 0;
+        }
+    }
+
+    if( width != NULL )
+        *width = width_ret;
+
+    if( height != NULL )
+        *height = height_ret;
+    
+    if ((layer->mode == GV_RLM_RGBA) && (rgba_complex == 1))
+        return layer->pc_lut_rgba_complex;
+    else
+        return layer->pc_lut;
+}
+
+
+long
+gv_raster_layer_lut_type_get( GvRasterLayer *layer )
+{
+    g_return_val_if_fail( layer != NULL, -1 );
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), -1 );
+
+    if( layer->mode == GV_RLM_COMPLEX )
+        return GV_RASTER_LAYER_LUT_2D;
+    else if( layer->mode == GV_RLM_SINGLE )
+        return GV_RASTER_LAYER_LUT_1D;
+    else
+        return GV_RASTER_LAYER_LUT_NONE;
+}
+
+static void
+gv_raster_layer_lut_hsv_to_rgb(  float h, float s, float v, float *r, float *g, float *b )
+{
+    float f;
+    int i;
+
+    /* Find hue at max saturation */
+    h *= 6;
+    i = (int)h;
+    f = h - i;
+
+    switch(i)
+    {
+	case 0:
+	case 6:
+	    *r = 1.0;
+	    *g = f;
+	    *b = 0.0;
+	    break;
+
+	case 1:
+	    *r = 1.0 - f;
+	    *g = 1.0;
+	    *b = 0.0;
+	    break;
+
+	case 2:
+	    *r = 0.0;
+	    *g = 1.0;
+	    *b = f;
+	    break;
+
+	case 3:
+	    *r = 0.0;
+	    *g = 1.0-f;
+	    *b = 1.0;
+	    break;
+
+	case 4:
+	    *r = f;
+	    *g = 0.0;
+	    *b = 1.0;
+	    break;
+
+	case 5:
+	    *r = 1.0;
+	    *g = 0.0;
+	    *b = 1.0-f;
+	    break;
+
+    }
+
+    f = ( 0.3086 * *r + 0.6094 * *g + 0.082 * *b ) * (1.0 - s );
+    *r = f + *r * s;
+    *g = f + *g * s;
+    *b = f + *b * s;
+
+    /* Scale towards 0,0,0 using value */
+
+    *r *= v;
+    *g *= v;
+    *b *= v;
+}
+
+gint 
+gv_raster_layer_apply_gdal_color_table( GvRasterLayer *layer, 
+                                        GDALColorTableH color_table )
+
+{
+    guchar lut[256*4];
+    int    i;
+
+    memset( lut, 0, 256*4 );
+
+    if( GDALGetColorEntryCount( color_table ) > 256 )
+    {
+        CPLDebug( "OpenEV", "gv_raster_layer_apply_gdal_color_table(): "
+                  "PCT has %d entries, only using 256.",
+                  GDALGetColorEntryCount( color_table ) );
+    }
+
+    for( i = 0; i < MIN(256,GDALGetColorEntryCount( color_table )); i++ )
+    {
+        GDALColorEntry entry;
+
+        GDALGetColorEntryAsRGB( color_table, i, &entry );
+        
+        lut[i*4  ] = entry.c1;
+        lut[i*4+1] = entry.c2;
+        lut[i*4+2] = entry.c3;
+        lut[i*4+3] = entry.c4;
+    }
+
+    return gv_raster_layer_lut_put( layer, lut, 1 );
+}

Added: packages/openev/branches/upstream/current/gvrasterlut.h
===================================================================
--- packages/openev/branches/upstream/current/gvrasterlut.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrasterlut.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,87 @@
+/******************************************************************************
+ * $Id: gvrasterlut.h,v 1.15 2005/08/30 12:58:56 andrey_kiselev Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Compute, and apply LUT to GvRaster.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrasterlut.h,v $
+ * Revision 1.15  2005/08/30 12:58:56  andrey_kiselev
+ * Make the return value of unsigned gv_raster_layer_lut_get().
+ *
+ * Revision 1.14  2004/06/23 14:35:05  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.13  2001/10/17 16:23:52  warmerda
+ * added support for composing complex lut and pct
+ *
+ * Revision 1.12  2001/07/24 21:21:45  warmerda
+ * added EV style phase colormap
+ *
+ * Revision 1.11  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef _GV_RASTER_LAYER_LUT_H_
+#define _GV_RASTER_LAYER_LUT_H_
+
+#include <gtk/gtk.h>
+#include "gvrasterlayer.h"
+
+enum {
+    GV_RASTER_LAYER_LUT_MAGNITUDE,
+    GV_RASTER_LAYER_LUT_PHASE_ANGLE,
+    GV_RASTER_LAYER_LUT_SCALAR,
+    GV_RASTER_LAYER_LUT_REAL,
+    GV_RASTER_LAYER_LUT_IMAGINARY
+};
+
+enum {
+    GV_RASTER_LAYER_LUT_NONE = 0,
+    GV_RASTER_LAYER_LUT_1D,
+    GV_RASTER_LAYER_LUT_2D
+};
+
+typedef struct _GvRasterLayerLutInterpolate GvRasterLayerLutInterpolate;
+
+struct _GvRasterLayerLutInterpolate {
+    guchar color[4];
+    gint index;
+};
+
+int gv_raster_layer_lut_color_wheel_new_ev( 
+    GvRasterLayer *layer, gint set_phase, gint set_magnitude );
+int gv_raster_layer_lut_color_wheel_new( GvRasterLayer *layer, gint h_type, gfloat h_param, gint s_type, gfloat s_param, gint v_type, gfloat v_param );
+int gv_raster_layer_lut_color_wheel_1d_new( GvRasterLayer *layer, float s, float v, float offset );
+int gv_raster_layer_lut_interpolated_new( GvRasterLayer *layer, GvRasterLayerLutInterpolate *color_pair, int offset );
+int gv_raster_layer_lut_new( GvRasterLayer *layer, gint h_type, gfloat h_param, gint s_type, gfloat s_param, gint v_type, gfloat v_param );
+int gv_raster_layer_lut_put( GvRasterLayer *layer, guchar *lut, gint height );
+unsigned char *gv_raster_layer_lut_get( GvRasterLayer *layer,
+                                        int *width, int *height,
+                                        int rgba_complex);
+int gv_raster_layer_lut_compose( GvRasterLayer *layer );
+long gv_raster_layer_lut_type_get( GvRasterLayer *layer );
+int gv_raster_layer_apply_gdal_color_table( GvRasterLayer *layer,
+                                            GDALColorTableH color_table );
+
+#endif

Added: packages/openev/branches/upstream/current/gvrastersource.c
===================================================================
--- packages/openev/branches/upstream/current/gvrastersource.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrastersource.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,715 @@
+/******************************************************************************
+ * $Id: gvrastersource.c,v 1.16 2004/06/23 14:35:05 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Code related to handling of GvRasterSource on a GvRasterLayer.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrastersource.c,v $
+ * Revision 1.16  2004/06/23 14:35:05  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.15  2004/04/02 17:01:02  gmwalter
+ * Updated nodata support for complex and
+ * rgb data.
+ *
+ * Revision 1.14  2004/02/20 12:32:23  andrey_kiselev
+ * Use turn on alfa blending in gv_raster_layer_nodata_set().
+ *
+ * Revision 1.13  2004/02/18 16:55:32  andrey_kiselev
+ * Don't change the min/max levels in gv_raster_layer_set_source().
+ *
+ * Revision 1.12  2004/02/17 14:28:43  warmerda
+ * check for NULL data in layer
+ *
+ * Revision 1.11  2004/01/22 20:45:27  andrey_kiselev
+ * Added methods gv_raster_layer_nodata_set() and gv_raster_layer_nodata_get() to
+ * work with nodata_* layer properties and method gv_raster_layer_type_get() to
+ * query raster data type.
+ *
+ * Revision 1.10  2003/03/06 22:55:58  gmwalter
+ * Fix pure phase display to remove residual dependence on magnitude.
+ *
+ * Revision 1.9  2001/10/19 13:30:58  warmerda
+ * include gvrasterlut.h for compose call
+ *
+ * Revision 1.8  2001/10/17 16:23:52  warmerda
+ * added support for composing complex lut and pct
+ *
+ * Revision 1.7  2001/01/30 19:34:11  warmerda
+ * added gv_raster_layer_purge_all_textures calls
+ *
+ * Revision 1.6  2000/08/25 20:11:07  warmerda
+ * added preliminary nodata support
+ *
+ * Revision 1.5  2000/08/16 20:58:46  warmerda
+ * produce proper constant complex images
+ *
+ * Revision 1.4  2000/08/09 17:38:23  warmerda
+ * keep reference on source GvRasters
+ *
+ * Revision 1.3  2000/08/02 19:17:21  warmerda
+ * return NULL on illegal source in get_data()
+ *
+ * Revision 1.2  2000/06/27 21:26:24  warmerda
+ * removed use of invalidated
+ *
+ * Revision 1.1  2000/06/23 12:51:07  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <gtk/gtksignal.h>
+#include "gvrasterlayer.h"
+#include "gvrastertypes.h"
+#include "gvrasterlut.h"
+
+#ifndef sign
+#  define sign(a) ( (a < 0.0 )?-1.0:1.0 )
+#endif
+
+static unsigned char *
+gv_scale_tile_to_byte( void *src_data, 
+                       int tile_x, int tile_y, 
+                       float min, float max,
+                       GDALDataType gdal_type )
+
+{
+    float scale;
+    unsigned char *ret_data;
+    int   i, pixel_count = tile_x * tile_y;
+
+    if( max == min )
+        scale = 1.0;
+    else
+        scale = 255.0 / (max - min);
+
+    if( gdal_type == GDT_Byte )
+    {
+        unsigned char *raw_data = (unsigned char *) src_data;
+        float          f;
+
+        ret_data = (unsigned char *) g_malloc(pixel_count);
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            f = (raw_data[i] - min) * scale;
+            if( f < 0.0 )
+                ret_data[i] = 0;
+            else if( f > 255 )
+                ret_data[i] = 255;
+            else
+                ret_data[i] = (int) f;
+        }
+    }
+    else if( gdal_type == GDT_Float32 )
+    {
+        float 	*raw_data = (float *) src_data;
+        float    f;
+
+        ret_data = (unsigned char *) g_malloc(pixel_count);
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            f = (raw_data[i] - min) * scale;
+            if( f < 0.0 )
+                ret_data[i] = 0;
+            else if( f > 255 )
+                ret_data[i] = 255;
+            else
+                ret_data[i] = (int) f;
+        }
+    }
+    else if( gdal_type == GDT_CFloat32 )
+    {
+        float 	*raw_data = (float *) src_data;
+
+        if( max == 0.0 )
+            scale = 1.0;
+        else
+            scale = 0.5 / max;
+
+        ret_data = (unsigned char *) g_malloc(pixel_count*2);
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            float temp_i, temp_q;
+
+            temp_i = raw_data[i*2] * scale;
+            temp_q = raw_data[i*2+1] * scale;
+
+            if( fabs( temp_i ) > 0.5 )
+            {
+                temp_q *= 0.5 / fabs( temp_i );
+                temp_i = 0.5 * sign( temp_i );
+            }
+            
+            if( fabs( temp_q ) > 0.5 )
+            {
+                temp_i *= 0.5 / fabs( temp_q );
+                temp_q = 0.5 * sign( temp_q );
+            }
+
+            temp_i += 0.5;
+            temp_q += 0.5;
+
+            ret_data[i*2  ] = (int) (temp_i * 255.0);
+            ret_data[i*2+1] = (int) (temp_q * 255.0);
+        }
+    }
+    else
+    {
+        ret_data = (unsigned char *) g_malloc(pixel_count);
+
+        g_warning( "unhandled data type in gv_scale_tile_to_byte().\n" );
+        memset( ret_data, 0, pixel_count );
+    }
+
+    return ret_data;
+}
+
+static unsigned char *
+gv_scale_pure_phase_tile_to_byte( void *src_data, 
+                       int tile_x, int tile_y, 
+                       GDALDataType gdal_type )
+
+{
+    unsigned char *ret_data;
+    int   i, pixel_count = tile_x * tile_y;
+
+    if( gdal_type == GDT_CFloat32 )
+    {
+        float 	*raw_data = (float *) src_data;
+
+        ret_data = (unsigned char *) g_malloc(pixel_count*2);
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            float temp_i, temp_q;
+
+            temp_i = raw_data[i*2];
+            temp_q = raw_data[i*2+1];
+
+            if ((temp_i == 0) && (temp_q == 0))
+            {
+                ret_data[i*2]=127;
+                ret_data[i*2+1]=127;
+            }
+            else if (fabs(temp_i ) > fabs(temp_q))
+            {
+                ret_data[i*2]=(int) (0.5*(1.0+sign(temp_i))*255);
+                ret_data[i*2+1]=(int) (0.5*(1.0+temp_q/fabs(temp_i))*255);
+            }
+            else
+            {
+                ret_data[i*2]=(int) (0.5*(1.0+temp_i/fabs(temp_q))*255);
+                ret_data[i*2+1]=(int) (0.5*(1.0+sign(temp_q))*255);
+            }
+        }
+    }
+    else
+    {
+        ret_data = (unsigned char *) g_malloc(pixel_count);
+
+        g_warning( "unhandled data type in gv_scale_pure_phase_tile_to_byte().\n" );
+        memset( ret_data, 0, pixel_count );
+    }
+
+    return ret_data;
+}
+
+static void 
+gv_raster_layer_srctile_check_nodata( GvRasterSource *source, int pixels, 
+                                      unsigned char *data, 
+                                      unsigned char *nodata_mask )
+
+{
+    int       i;
+
+    if( nodata_mask == NULL || !source->nodata_active )
+        return;
+
+    /* assume all valid data in constant sources */
+    if( source->data == NULL )
+        return;
+
+    if( source->data->gdal_type == GDT_Byte )
+    {
+        unsigned char nodata = (unsigned char) source->nodata_real;
+
+        for( i = 0; i < pixels; i++ )
+        {
+            if( data[i] == nodata )
+                nodata_mask[i] = 0;
+        }
+    }
+    else if( source->data->gdal_type == GDT_Float32 )
+    {
+        float nodata = (float) source->nodata_real;
+
+        for( i = 0; i < pixels; i++ )
+        {
+            if( ((float *) data)[i] == nodata )
+                nodata_mask[i] = 0;
+        }
+    }
+    else if( source->data->gdal_type == GDT_CFloat32 )
+    {
+        float 	*f_data = (float *) data;
+
+        for( i = 0; i < pixels; i++ )
+        {
+            if( f_data[i*2] == (float) source->nodata_real
+                && f_data[i*2+1] == (float) source->nodata_imaginary )
+                nodata_mask[i] = 0;
+        }
+    }
+    else
+    {
+        g_warning( "unhandled type in gv_raster_layer_srctile_check_nodata()");
+    }
+}
+                                      
+
+unsigned char *
+gv_raster_layer_srctile_xy_get( GvRasterLayer * layer, int isource,
+                                int tile, int lod, int * needs_free,
+                                unsigned char * nodata_mask )
+
+{
+    GvRasterSource *source;
+    unsigned char * ret_data;
+    int  pixel_count;
+
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), NULL );
+    g_return_val_if_fail( layer != NULL, NULL );
+    g_return_val_if_fail( isource >= 0 
+                          && isource < layer->source_count, NULL );
+
+    source = layer->source_list + isource;
+    pixel_count = (layer->tile_x >> lod) * (layer->tile_y >> lod);
+
+    if( source->data != NULL )
+    {
+        *needs_free = FALSE;
+
+        ret_data = gv_raster_tile_get( source->data, tile, lod );
+        if( ret_data == NULL )
+            return NULL;
+
+        if( nodata_mask != NULL && source->nodata_active )
+            gv_raster_layer_srctile_check_nodata( source, pixel_count, 
+                                                  ret_data, nodata_mask );
+
+        if( source->data->gdal_type != GDT_Byte 
+            || source->min != 0.0 
+            || source->max != 255.0 )
+        {
+            *needs_free = TRUE;
+            
+            if (( layer->mode == GV_RLM_COMPLEX ) &&
+                (gv_data_get_property( GV_DATA(layer),"last_complex_lut" ) != NULL) &&
+                (strcmp(gv_data_get_property( GV_DATA(layer),"last_complex_lut" ),"phase") == 0))
+            {
+                ret_data = gv_scale_pure_phase_tile_to_byte( ret_data, 
+                                              layer->tile_x >> lod, 
+                                              layer->tile_y >> lod, 
+                                              source->data->gdal_type );
+            }
+            else if (( layer->mode == GV_RLM_RGBA ) &&
+                     (gv_data_get_property( GV_DATA(layer),"last_complex_lut" ) != NULL) &&
+                     (strcmp(gv_data_get_property( GV_DATA(layer),"last_complex_lut" ),"phase") == 0) &&
+                     (source->data->gdal_type == GDT_CFloat32) )
+            {
+                ret_data = gv_scale_pure_phase_tile_to_byte( ret_data, 
+                                              layer->tile_x >> lod, 
+                                              layer->tile_y >> lod, 
+                                              source->data->gdal_type );
+            }
+            else
+            {
+                ret_data = gv_scale_tile_to_byte( ret_data, 
+                                              layer->tile_x >> lod, 
+                                              layer->tile_y >> lod,
+                                              source->min, source->max, 
+                                              source->data->gdal_type );
+            }
+        }
+
+    }
+    else if( layer->mode == GV_RLM_COMPLEX )
+    {
+        int  i;
+
+        ret_data = (unsigned char *) g_malloc(pixel_count*2);
+        *needs_free = TRUE;
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            ret_data[i*2  ] = source->const_value;
+            ret_data[i*2+1] = 0;
+        }
+    }
+    else
+    {
+        ret_data = (unsigned char *) g_malloc(pixel_count);
+        *needs_free = TRUE;
+
+        memset( ret_data, source->const_value, pixel_count );
+    }
+
+    /* Do we have an LUT to apply? */
+    if( source->lut != NULL && layer->mode != GV_RLM_COMPLEX &&
+        ((source->data == NULL) || 
+         (source->data != NULL && source->data->gdal_type != GDT_CFloat32)))
+    {
+        int  i;
+        unsigned char *lut = source->lut;
+
+        if( ! *needs_free )
+        {
+            unsigned char *temp;
+
+            temp = (unsigned char *) g_malloc(pixel_count);
+            memcpy( temp, ret_data, pixel_count );
+            ret_data = temp;
+            *needs_free = TRUE;
+        }
+
+        for( i = 0; i < pixel_count; i++ )
+        {
+            ret_data[i] = lut[ret_data[i]];
+        }
+    }
+
+    return ret_data;
+}
+
+unsigned char *
+gv_raster_layer_source_get_lut( GvRasterLayer * layer, int isource )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), NULL );
+    g_return_val_if_fail( layer != NULL, NULL );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count,NULL);
+
+    return layer->source_list[isource].lut;
+}
+
+unsigned char 
+gv_raster_layer_get_const_value( GvRasterLayer * layer, int isource )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 0 );
+    g_return_val_if_fail( layer != NULL, 0 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 0 );
+
+    return layer->source_list[isource].const_value;
+}
+
+GvRaster *
+gv_raster_layer_get_data( GvRasterLayer * layer, int isource )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), NULL );
+    g_return_val_if_fail( layer != NULL, NULL );
+    if( isource < 0 || isource >= layer->source_count )
+        return NULL;
+    else
+        return layer->source_list[isource].data;
+}
+
+int
+gv_raster_layer_min_set( GvRasterLayer *layer, int isource, float min )
+{
+    GvRasterSource *source;
+
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 1 );
+
+    source = layer->source_list + isource;
+
+    if( min != source->min )
+    {
+        source->min = min;
+        gv_raster_layer_purge_all_textures( layer );
+        gv_layer_display_change( GV_LAYER(layer), NULL );
+    }
+
+    return 0;
+}
+
+float
+gv_raster_layer_min_get( GvRasterLayer *layer, int isource )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 1 );
+
+    return layer->source_list[isource].min;
+}
+
+int
+gv_raster_layer_max_set( GvRasterLayer *layer, int isource, float max )
+{
+    GvRasterSource *source;
+
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 1 );
+
+    source = layer->source_list + isource;
+
+    if( max != source->max )
+    {
+        source->max = max;
+        gv_raster_layer_purge_all_textures( layer );
+        gv_layer_display_change( GV_LAYER(layer), NULL );
+    }
+
+    return 0;
+}
+
+float gv_raster_layer_max_get( GvRasterLayer *layer, int isource )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 1 );
+
+    return layer->source_list[isource].max;
+}
+
+/************************************************************************/
+/*                       gv_raster_layer_nodata_set()                   */
+/************************************************************************/
+
+/**
+ * Sets NODATA value for the specified layer.
+ *
+ * @param layer Pointer to GvRasterLayer object.
+ * 
+ * @param isource Source index.
+ *
+ * @param nodata_real Real part of the variable containing NODATA value.
+ * 
+ * @param nodata_imaginary Imaginary part of the variable containing
+ * NODATA value.
+ *
+ * @return TRUE in case of success and FALSE otherwise.
+ */
+
+gint
+gv_raster_layer_nodata_set( GvRasterLayer *layer, int isource,
+                            double nodata_real, double nodata_imaginary )
+{
+    GvRasterSource *source;
+
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), FALSE );
+    g_return_val_if_fail( layer != NULL, FALSE );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count,
+                          FALSE );
+
+    source = layer->source_list + isource;
+    if( nodata_real != source->nodata_real
+        || nodata_imaginary != source->nodata_imaginary )
+    {
+        source->nodata_real = nodata_real;
+        source->nodata_imaginary = nodata_imaginary;
+        source->nodata_active = TRUE;
+        gv_raster_layer_blend_mode_set( layer, 
+                                        GV_RASTER_LAYER_BLEND_MODE_FILTER,
+                                        0, 0 );
+        gv_raster_layer_purge_all_textures( layer );
+        gv_layer_display_change( GV_LAYER(layer), NULL );
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                       gv_raster_layer_nodata_get()                   */
+/************************************************************************/
+
+/**
+ * Queries NODATA value for the specified layer.
+ *
+ * @param layer Pointer to GvRasterLayer object.
+ * 
+ * @param isource Source index.
+ *
+ * @param nodata_real Pointer to the varible which will be filled with real
+ * part of the NODATA value, may be NULL
+ * 
+ * @param nodata_imaginary Pointer to the varible which will be filled with
+ * imaginary part of the NODATA value, may be NULL.
+ *
+ * @return TRUE if specified layer has NODATA set and FALSE otherwise.
+ */
+
+gint
+gv_raster_layer_nodata_get( GvRasterLayer *layer, int isource,
+                            double *nodata_real, double *nodata_imaginary )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), FALSE );
+    g_return_val_if_fail( layer != NULL, FALSE );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count,
+                          FALSE );
+
+    if ( layer->source_list[isource].nodata_active )
+    {
+        if ( nodata_real )
+            *nodata_real = layer->source_list[isource].nodata_real;
+        if ( nodata_imaginary )
+            *nodata_imaginary = layer->source_list[isource].nodata_imaginary;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/************************************************************************/
+/*                       gv_raster_layer_type_get()                     */
+/************************************************************************/
+
+/**
+ * Queries type of the values for specified layer and source.
+ *
+ * @param layer Pointer to GvRasterLayer object.
+ * 
+ * @param isource Source index.
+ * 
+ * @return GDAL dtat type of the specified raster.
+ */
+
+GDALDataType
+gv_raster_layer_type_get( GvRasterLayer *layer, int isource )
+{
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 0 );
+    g_return_val_if_fail( layer != NULL, 0 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 0 );
+    g_return_val_if_fail( layer->source_list[isource].data != NULL, 0 );
+
+    return layer->source_list[isource].data->gdal_type;
+}
+
+int gv_raster_layer_set_source( GvRasterLayer *layer, int isource, 
+                                GvRaster *data, float min, float max,
+                                unsigned char const_value,
+                                unsigned char *lut,
+                                int nodata_active, 
+                                double nodata_real, 
+                                double nodata_imaginary )
+
+{
+    GvRasterSource *source;
+
+    g_return_val_if_fail( GV_IS_RASTER_LAYER( layer ), 1 );
+    g_return_val_if_fail( layer != NULL, 1 );
+    g_return_val_if_fail( isource >= 0 && isource < layer->source_count, 1 );
+
+    if( !nodata_active )
+    {
+        nodata_real = -1e8;
+        nodata_imaginary = 0.0;
+    }
+
+    if( data != NULL )
+    {
+        if( data->tile_x != layer->tile_x
+            || data->tile_y != layer->tile_y
+            || data->width != layer->prototype_data->width
+            || data->height != layer->prototype_data->height )
+        {
+            g_warning( "Attempt to use different GvRaster that doesn't match\n"
+                       "prototype raster in gv_raster_layer_set_source()." );
+            return 1;
+        }
+    }
+
+    source = layer->source_list + isource;
+
+    if( source->data == data 
+        && source->min == min
+        && source->max == max
+        && source->const_value == const_value
+        && !source->nodata_active == !nodata_active
+        && source->nodata_real == nodata_real
+        && source->nodata_imaginary == nodata_imaginary
+        && source->lut == NULL
+        && lut == NULL )
+        return 0;
+
+    /* Manage a reference for each source */
+
+    if( source->data != NULL )
+        gtk_object_unref( GTK_OBJECT(source->data) );
+
+    if( data != NULL )
+    {
+        gtk_object_ref( GTK_OBJECT(data) );
+        gtk_object_sink( GTK_OBJECT(data) );
+    }
+
+    source->data = data;
+    source->min = min;
+    source->max = max;
+    source->const_value = const_value;
+    source->nodata_active = nodata_active;
+    source->nodata_real = nodata_real;
+    source->nodata_imaginary = nodata_imaginary;
+    
+    if( source->lut != NULL )
+    {
+        g_free( source->lut );
+        source->lut = NULL;
+    }
+
+    if( lut != NULL )
+    {
+        source->lut = (unsigned char *) g_malloc(256);
+        memcpy( source->lut, lut, 256 );
+    }
+
+    if( source->lut_rgba_composed != NULL )
+    {
+        g_free( source->lut_rgba_composed );
+        source->lut_rgba_composed = NULL;
+    }
+        
+    gv_raster_layer_purge_all_textures( layer );
+    gv_layer_display_change( GV_LAYER(layer), NULL );
+
+    /*
+    ** Regenerate 2D composed Complex lut if needed.
+    */
+    gv_raster_layer_lut_compose( layer );
+
+    /* If we have a nodata value, we have to ensure that we have 
+       alpha blending turned on */
+    if( source->nodata_active )
+        gv_raster_layer_blend_mode_set( layer, 
+                                        GV_RASTER_LAYER_BLEND_MODE_FILTER,
+                                        0, 0 );
+
+    return 0;
+}
+

Added: packages/openev/branches/upstream/current/gvrastertypes.h
===================================================================
--- packages/openev/branches/upstream/current/gvrastertypes.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrastertypes.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,55 @@
+/******************************************************************************
+ * $Id: gvrastertypes.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Raster data types.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrastertypes.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef _GV_RASTER_TYPES_H_
+#define _GV_RASTER_TYPES_H_
+
+enum {
+    GV_RASTER_BYTE_RGBA = 0,
+    GV_RASTER_BYTE_RGB,
+    GV_RASTER_BYTE_REAL,
+    GV_RASTER_BYTE_COMPLEX,
+    GV_RASTER_SHORT_RGBA,
+    GV_RASTER_SHORT_RGB,
+    GV_RASTER_SHORT_REAL,
+    GV_RASTER_SHORT_COMPLEX,
+    GV_RASTER_INT_RGBA,
+    GV_RASTER_INT_RGB,
+    GV_RASTER_INT_REAL,
+    GV_RASTER_INT_COMPLEX,
+    GV_RASTER_FLOAT_RGBA,
+    GV_RASTER_FLOAT_RGB,
+    GV_RASTER_FLOAT_REAL,
+    GV_RASTER_FLOAT_COMPLEX
+};
+
+#endif

Added: packages/openev/branches/upstream/current/gvrecords.c
===================================================================
--- packages/openev/branches/upstream/current/gvrecords.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrecords.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,801 @@
+/******************************************************************************
+ * $Id: gvrecords.c,v 1.5 2003/08/06 22:26:03 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvRecords implementation (GvShapes like collection of records)
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrecords.c,v $
+ * Revision 1.5  2003/08/06 22:26:03  warmerda
+ * added progress monitor to gv_records load/save funcs
+ *
+ * Revision 1.4  2003/08/06 17:07:44  warmerda
+ * added support to save only selected records in gv_records_to_dbf
+ *
+ * Revision 1.3  2003/07/27 04:57:28  warmerda
+ * complete implementation
+ *
+ * Revision 1.2  2003/06/23 15:51:28  warmerda
+ * added header
+ *
+ * Revision 1.1  2003/05/23 20:43:08  warmerda
+ * New
+ *
+ */
+
+#include "gvrecords.h"
+#include "cpl_error.h"
+#include "cpl_vsi.h"
+#include "shapefil.h"
+
+static void gv_records_class_init(GvRecordsClass *klass);
+static void gv_records_init(GvRecords *points);
+static void gv_records_finalize(GtkObject *object);
+
+GtkType
+gv_records_get_type(void)
+{
+    static GtkType type = 0;
+
+    if (!type)
+    {
+	static const GtkTypeInfo records_info =
+	{
+	    "GvRecords",
+	    sizeof(GvRecords),
+	    sizeof(GvRecordsClass),
+	    (GtkClassInitFunc) gv_records_class_init,
+	    (GtkObjectInitFunc) gv_records_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	type = gtk_type_unique(gv_data_get_type(), &records_info);
+    }
+    return type;
+}
+
+static void
+gv_records_init(GvRecords *records)
+{
+    records->nRecordLength = 0;
+
+    records->nFieldCount = 0;
+    records->panFieldOffset = NULL;
+    records->panFieldWidth = NULL;
+    records->panFieldType = NULL;
+    
+    records->nRecordCount = 0;
+    
+    records->nMainDataSize = 0;
+    records->pachMainData = NULL;
+    
+    records->nUsedFieldCount = 0;
+    records->panUsedFieldList = NULL;
+}
+
+static void
+gv_records_class_init(GvRecordsClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    object_class = (GtkObjectClass*) klass;
+    data_class = (GvDataClass*) klass;
+
+    object_class->finalize = gv_records_finalize;
+}
+
+GvData *
+gv_records_new(void)
+{
+    GvData *data;
+
+    data = GV_DATA(gtk_type_new(gv_records_get_type()));
+
+    return data;
+}
+
+/************************************************************************/
+/*                     gv_records_create_records()                      */
+/*                                                                      */
+/*      Creates the requested number of new records, all initialized    */
+/*      to NULL values.  Returns the record index of the first new      */
+/*      record.                                                         */
+/************************************************************************/
+
+gint gv_records_create_records( GvRecords *psRecords, int nNewRecords )
+
+{
+    int nFirstRecord = psRecords->nRecordCount;
+
+    if( psRecords->nRecordLength * (psRecords->nRecordCount + nNewRecords) 
+        > psRecords->nMainDataSize )
+    {
+        int nNewMainSize = 
+            psRecords->nRecordLength * (psRecords->nRecordCount + nNewRecords)
+            + 100000;
+
+        psRecords->pachMainData = 
+            g_renew( char, psRecords->pachMainData, nNewMainSize );
+        psRecords->nMainDataSize = nNewMainSize;
+    }
+
+    psRecords->nRecordCount += nNewRecords;
+
+    memset( (char *) gv_records_get_raw_record_data( psRecords, nFirstRecord ),
+            GV_NULL_MARKER, nNewRecords * psRecords->nRecordLength );
+
+    return nFirstRecord;
+}
+
+/************************************************************************/
+/*                          gv_record_to_dbf()                          */
+/************************************************************************/
+
+int gv_records_to_dbf( GvRecords *psRecords, const char *pszFilename,
+                       int nSelectionCount, int *panSelectionList,
+                       GDALProgressFunc pfnProgress, void * pCBData )
+
+{
+    DBFHandle      dbf_handle;
+    int            field_index, field_count=0, shape_index, i;
+    GvProperties   *properties;
+
+    if( pfnProgress == NULL )
+        pfnProgress = GDALDummyProgress;
+
+    if( !pfnProgress( 0.0, "", pCBData ) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Try to create the named file(s).                                */
+/* -------------------------------------------------------------------- */
+    dbf_handle = DBFCreate( pszFilename );
+    if( dbf_handle == NULL )
+    {
+        g_warning( "Failed to create DBF file." );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the fields on the DBF file, if any.                      */
+/* -------------------------------------------------------------------- */
+    properties = gv_data_get_properties( GV_DATA(psRecords) );
+    for( field_index = 0; TRUE; field_index++ )
+    {
+        int  width, precision = 0, field_type;
+        char prop_name[64];
+        const char *prop_value;
+
+        sprintf( prop_name, "_field_width_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            break;
+
+        width = atoi(prop_value);
+
+        sprintf( prop_name, "_field_precision_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value != NULL )
+            precision = atoi(prop_value);
+
+        sprintf( prop_name, "_field_type_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            prop_value = "string";
+
+        if( g_strcasecmp(prop_value,"integer") == 0 )
+            field_type = FTInteger;
+        else if( g_strcasecmp(prop_value,"float") == 0 )
+            field_type = FTDouble;
+        else
+            field_type = FTString;
+
+        sprintf( prop_name, "_field_name_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            break;
+
+        DBFAddField( dbf_handle, prop_value, field_type, width, precision );
+        field_count++;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Add a dummy field if there are none.                            */
+/* -------------------------------------------------------------------- */
+    if( field_count == 0 )
+    {
+        g_warning( "No attributes to save in DBF file." );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Start writing shapes, ignoring any that don't match our         */
+/*      desired type.                                                   */
+/* -------------------------------------------------------------------- */
+    if( panSelectionList == NULL )
+        nSelectionCount = gv_records_num_records(psRecords);
+
+    for( i = 0; i < nSelectionCount; i++ )
+    {
+        if( panSelectionList != NULL )
+            shape_index = panSelectionList[i];
+        else
+            shape_index = i;
+
+        /* Write the attributes of this shape that match the DBF schema */
+        for( field_index = 0; field_index < field_count; field_index++ )
+        {
+            char field_name[32];
+            const char * field_value;
+            int  field_type;
+
+            /* FIXME: This will fail for truncated field names! */
+            field_type = DBFGetFieldInfo( dbf_handle, field_index,
+                                          field_name, NULL, NULL);
+
+            field_value = 
+                gv_records_get_raw_field_data( psRecords, shape_index,
+                                               field_index);
+
+            if( field_value == NULL )
+            {
+                DBFWriteNULLAttribute( dbf_handle, i, field_index );
+            }
+            else
+            {
+                if( field_type == FTDouble )
+                    DBFWriteDoubleAttribute( dbf_handle, i, field_index,
+                                             atof(field_value) );
+                else if( field_type == FTInteger )
+                    DBFWriteIntegerAttribute( dbf_handle, i, field_index,
+                                              atoi(field_value) );
+                else
+                    DBFWriteStringAttribute( dbf_handle, i, field_index,
+                                             field_value );
+            }
+        }
+
+        if( !pfnProgress( (i+1) / (double) nSelectionCount, "", pCBData ) )
+        {
+            DBFClose( dbf_handle );
+            VSIUnlink( pszFilename );
+            return FALSE;
+        }
+    }
+
+    DBFClose( dbf_handle );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                        gv_records_from_dbf()                         */
+/************************************************************************/
+
+GvData* gv_records_from_dbf( const char *filename,
+                             GDALProgressFunc pfnProgress, void * pCBData )
+
+{
+    DBFHandle   dbf_handle;
+    int         shape_count, shape_index, field_count = 0, field_index;
+    GvRecords   *shapes_data;
+    GvProperties *properties;
+    int         cancelled = FALSE;
+
+    if( pfnProgress == NULL )
+        pfnProgress = GDALDummyProgress;
+
+    if( !pfnProgress( 0.0, "", pCBData ) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Open the .shp and .dbf file.                                    */
+/* -------------------------------------------------------------------- */
+    dbf_handle = DBFOpen( filename, "rb" );
+    if( dbf_handle == NULL )
+    {
+        g_warning( "Invalid DBF." );
+        return NULL;
+    }
+    else
+        field_count = DBFGetFieldCount( dbf_handle );
+
+    shape_count = DBFGetRecordCount( dbf_handle );
+
+/* -------------------------------------------------------------------- */
+/*      Create shapes layer, and assign some metadata about the         */
+/*      field definitions.                                              */
+/* -------------------------------------------------------------------- */
+    shapes_data = GV_RECORDS(gv_records_new());
+    properties = gv_data_get_properties( GV_DATA(shapes_data) );
+
+    //set the filename property
+    gv_properties_set( properties, "_filename", filename );
+
+    for(field_index = 0; field_index < field_count; field_index++ )
+    {
+        char      prop_value[64];
+        int       field_type, width, precision;
+        int       rfld_type;
+
+        field_type = DBFGetFieldInfo( dbf_handle, field_index,
+                                      prop_value, &width, &precision );
+
+        if( field_type == FTInteger )
+            rfld_type = GV_RFT_INTEGER;
+        else if( field_type == FTDouble )
+            rfld_type = GV_RFT_FLOAT;
+        else 
+            rfld_type = GV_RFT_STRING;
+
+        gv_records_add_field( shapes_data, prop_value, rfld_type,
+                              width, precision );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy all the shapes, and their attributes.                      */
+/* -------------------------------------------------------------------- */
+    gv_records_create_records( shapes_data, shape_count );
+
+    for( shape_index = 0; 
+         shape_index < shape_count && !cancelled; 
+         shape_index++ )
+    {
+        for( field_index = 0; field_index < field_count; field_index++ )
+        {
+            const char  *field_value;
+            
+            if( DBFIsAttributeNULL(dbf_handle,shape_index,field_index) )
+                field_value = NULL;
+            else
+                field_value = DBFReadStringAttribute( dbf_handle, shape_index,
+                                                      field_index );
+
+            gv_records_set_raw_field_data( shapes_data, shape_index, 
+                                           field_index, field_value );
+        }
+
+        cancelled = 
+            !pfnProgress( (shape_index+1) / (double) shape_count, 
+                          "", pCBData );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    if( dbf_handle != NULL )
+        DBFClose( dbf_handle );
+
+    if( cancelled )
+    {
+        gtk_object_unref( GTK_OBJECT(shapes_data) );
+        return NULL;
+    }
+    else
+    {
+        gv_data_set_name( GV_DATA(shapes_data), filename );
+        
+        return GV_DATA(shapes_data);
+    }
+}
+
+CPL_C_START
+int CPL_DLL RECGetFieldCount( FILE *fp);
+int CPL_DLL RECGetFieldDefinition( FILE *fp, char *pszFieldName, int *pnType, 
+                                   int *pnWidth, int *pnPrecision );
+int CPL_DLL RECReadRecord( FILE *fp, char *pszRecBuf, int nRecordLength  );
+const char CPL_DLL *RECGetField( const char *pszSrc, int nStart, int nWidth );
+CPL_C_END
+
+/************************************************************************/
+/*                        gv_records_from_rec()                         */
+/************************************************************************/
+
+GvData* gv_records_from_rec( const char *filename,
+                             GDALProgressFunc pfnProgress, void * pCBData )
+
+{
+    int         record_index, field_count = 0, field_index;
+    int         *foffset, *fwidth, cancelled = FALSE;
+    GvRecords   *records_data;
+    GvProperties *properties;
+    FILE        *fpRec;
+    long        first_record_offset, all_records_length;
+    int         record_length = 0, raw_field_count, approx_record_count;
+    char        *raw_record;
+
+    if( pfnProgress == NULL )
+        pfnProgress = GDALDummyProgress;
+
+    if( !pfnProgress( 0.0, "", pCBData ) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Open the .rec file.                                             */
+/* -------------------------------------------------------------------- */
+    fpRec = VSIFOpen( filename, "rb" );
+    if( fpRec == NULL )
+    {
+        g_warning( "Unable to open requested .rec file." );
+        return NULL;
+    }
+
+    raw_field_count = RECGetFieldCount( fpRec );
+    if( raw_field_count < 1 )
+    {
+        VSIFClose( fpRec );
+        g_warning( "Unable to get field count from .rec, corrupt?" );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create shapes layer, and assign some metadata about the         */
+/*      field definitions.                                              */
+/* -------------------------------------------------------------------- */
+    foffset = (int *) g_new(int,raw_field_count);
+    fwidth = (int *) g_new(int,raw_field_count);
+
+    records_data = GV_RECORDS(gv_records_new());
+    properties = gv_data_get_properties( GV_DATA(records_data) );
+
+    //set the filename property
+    gv_properties_set( properties, "_filename", filename );
+
+    for(field_index = 0, field_count = 0, record_length = 0; 
+        field_index < raw_field_count; field_index++ )
+    {
+        char      fieldname[128];
+        int       field_type, width, precision;
+        int       rfld_type;
+
+        if( !RECGetFieldDefinition( fpRec, fieldname, &field_type, 
+                                    &width, &precision ) )
+        {
+            g_warning( "corrupt field definition line." );
+            return NULL;
+        }
+
+        if( width == 0 )
+            continue;
+
+        if( field_type == 0 /* OFTInteger */ )
+            rfld_type = GV_RFT_INTEGER;
+        else if( field_type == 2 /* OFTReal */ )
+            rfld_type = GV_RFT_FLOAT;
+        else 
+            rfld_type = GV_RFT_STRING;
+
+        gv_records_add_field( records_data, fieldname, rfld_type,
+                              width, precision );
+
+        fwidth[field_count] = width;
+        foffset[field_count++] = record_length;
+        record_length += width;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute the approximate number of records in the file.          */
+/* -------------------------------------------------------------------- */
+    first_record_offset = VSIFTell( fpRec );
+    VSIFSeek( fpRec, 0, SEEK_END );
+
+    all_records_length = VSIFTell( fpRec ) - first_record_offset;
+    VSIFSeek( fpRec, first_record_offset, SEEK_SET );
+
+    approx_record_count = all_records_length / record_length;
+
+/* -------------------------------------------------------------------- */
+/*      Copy all the shapes, and their attributes.                      */
+/* -------------------------------------------------------------------- */
+    gv_records_create_records( records_data, approx_record_count );
+    raw_record = g_new( char, record_length + 10 );
+
+    for( record_index = 0; 
+         record_index < approx_record_count && !cancelled; 
+         record_index++ )
+    {
+        if( !RECReadRecord( fpRec, raw_record, record_length ) )
+        {
+            records_data->nRecordCount = record_index;
+            break;
+        }
+
+        for( field_index = 0; field_index < field_count; field_index++ )
+        {
+            const char  *field_value = RECGetField( raw_record, 
+                                                    foffset[field_index]+1, 
+                                                    fwidth[field_index] );
+            
+            if( field_value != NULL && *field_value == '\0' )
+                field_value = NULL;
+            
+            gv_records_set_raw_field_data( records_data, record_index, 
+                                           field_index, field_value );
+        }
+
+
+        cancelled = 
+            !pfnProgress( (record_index+1) / (double) approx_record_count, 
+                          "", pCBData );
+    }
+
+    if( !cancelled )
+        cancelled = !pfnProgress( 1.0, "", pCBData );
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    g_free( foffset );
+    g_free( fwidth );
+    g_free( raw_record );
+
+    VSIFClose( fpRec );
+
+    if( !cancelled )
+    {
+        gv_data_set_name( GV_DATA(records_data), filename );
+        return GV_DATA(records_data);
+    }
+    else
+    {
+        gtk_object_unref( GTK_OBJECT(records_data) );
+        return NULL;
+    }
+}
+
+/************************************************************************/
+/*                        gv_records_finalize()                         */
+/************************************************************************/
+static void
+gv_records_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvRecords *records = GV_RECORDS(object);
+
+    CPLDebug( "OpenEV", "gv_records_finalize(%s/%p)", 
+              gv_data_get_name( GV_DATA(object) ), object );
+
+    if( records->pachMainData )
+        g_free( records->pachMainData );
+
+    if( records->papszFieldName != NULL )
+    {
+        int i;
+        for( i = 0; i < records->nFieldCount; i++ )
+            g_free( records->papszFieldName[i] );
+        g_free( records->papszFieldName );
+
+        g_free( records->panFieldOffset );
+        g_free( records->panFieldWidth );
+        g_free( records->panFieldType );
+    }
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}
+
+/************************************************************************/
+/*                   gv_records_get_raw_record_data()                   */
+/************************************************************************/
+
+const char *gv_records_get_raw_record_data( GvRecords *psRecords, 
+                                            int iRecord )
+
+{
+    if( iRecord < 0 || iRecord >= psRecords->nRecordCount )
+        return NULL;
+    else
+        return psRecords->pachMainData + psRecords->nRecordLength * iRecord;
+}
+
+/************************************************************************/
+/*                   gv_records_get_raw_field_data()                    */
+/************************************************************************/
+
+const char *gv_records_get_raw_field_data( GvRecords *psRecords, 
+                                           int iRecord, int iField )
+
+{
+    const char *pszResult;
+
+    if( iRecord < 0 || iRecord >= psRecords->nRecordCount 
+        || iField < 0 || iField >= psRecords->nFieldCount )
+        return NULL;
+
+    pszResult = psRecords->pachMainData + psRecords->nRecordLength * iRecord
+        + psRecords->panFieldOffset[iField];
+    
+    if( *pszResult == GV_NULL_MARKER )
+        return NULL;
+    else
+        return pszResult;
+}
+
+/************************************************************************/
+/*                   gv_records_set_raw_field_data()                    */
+/************************************************************************/
+
+void gv_records_set_raw_field_data( GvRecords *psRecords, 
+                                    int iRecord, int iField,
+                                    const char *pszNewValue )
+
+{
+    char *pszTarget;
+    
+    if( iRecord < 0 || iRecord >= psRecords->nRecordCount 
+        || iField < 0 || iField >= psRecords->nFieldCount )
+        return;
+
+    pszTarget = psRecords->pachMainData + psRecords->nRecordLength * iRecord
+        + psRecords->panFieldOffset[iField];
+    
+    if( pszNewValue != NULL )
+    {
+        strncpy( pszTarget, pszNewValue, psRecords->panFieldWidth[iField] );
+        pszTarget[psRecords->panFieldWidth[iField]] = '\0';
+    }
+    else
+    {
+        pszTarget[0] = GV_NULL_MARKER;
+        pszTarget[1] = '\0';
+    }
+}
+
+/************************************************************************/
+/*                       gv_records_num_records()                       */
+/************************************************************************/
+
+int gv_records_num_records( GvRecords *psRecords )
+
+{
+    return psRecords->nRecordCount;
+}
+
+/************************************************************************/
+/*                        gv_records_add_field()                        */
+/************************************************************************/
+
+gint gv_records_add_field( GvRecords *psRecords, const char *field_name,
+                           int field_type, int width, int precision )
+
+{
+    char szPropName[256], szPropValue[256];
+    GvProperties *properties;
+
+    int iField = psRecords->nFieldCount;
+
+    (void) precision;
+
+/* -------------------------------------------------------------------- */
+/*      Grow the field list array.                                      */
+/* -------------------------------------------------------------------- */
+    psRecords->nFieldCount++;
+
+    psRecords->papszFieldName = 
+        g_renew( char *, psRecords->papszFieldName, psRecords->nFieldCount );
+    psRecords->panFieldOffset = 
+        g_renew( int, psRecords->panFieldOffset, psRecords->nFieldCount );
+    psRecords->panFieldWidth = 
+        g_renew( int, psRecords->panFieldWidth, psRecords->nFieldCount );
+    psRecords->panFieldType = 
+        g_renew( int, psRecords->panFieldType, psRecords->nFieldCount );
+
+/* -------------------------------------------------------------------- */
+/*      Add the new values.                                             */
+/* -------------------------------------------------------------------- */
+    psRecords->papszFieldName[iField] = g_strdup( field_name );
+    psRecords->panFieldWidth[iField] = width;
+    psRecords->panFieldType[iField] = field_type;
+    psRecords->panFieldOffset[iField] = psRecords->nRecordLength;
+
+/* -------------------------------------------------------------------- */
+/*      Increase the record size.                                       */
+/* -------------------------------------------------------------------- */
+    psRecords->nRecordLength += width + 1;
+
+/* -------------------------------------------------------------------- */
+/*      Add the new field to the layer properties for easy access       */
+/*      from python.                                                    */
+/* -------------------------------------------------------------------- */
+    properties = gv_data_get_properties( GV_DATA(psRecords) );
+
+    sprintf( szPropName, "_field_name_%d", iField+1 );
+    gv_properties_set( properties, szPropName, field_name );
+
+    sprintf( szPropName, "_field_width_%d", iField+1 );
+    sprintf( szPropValue, "%d", width );
+    gv_properties_set( properties, szPropName, szPropValue );
+
+    sprintf( szPropName, "_field_precision_%d", iField+1 );
+    sprintf( szPropValue, "%d", precision );
+    gv_properties_set( properties, szPropName, szPropValue );
+
+    if( field_type == GV_RFT_FLOAT )
+    {
+        sprintf( szPropName, "_field_precision_%d", iField+1 );
+        sprintf( szPropValue, "%d", precision );
+        gv_properties_set( properties, szPropName, szPropValue );
+    }
+
+    sprintf( szPropName, "_field_type_%d", iField+1 );
+    if( field_type == GV_RFT_INTEGER )
+        gv_properties_set( properties, szPropName, "integer" );
+    else if( field_type == GV_RFT_FLOAT )
+        gv_properties_set( properties, szPropName, "float" );
+    else
+        gv_properties_set( properties, szPropName, "string" );
+
+/* -------------------------------------------------------------------- */
+/*      If data already exists we need to do a big reshuffle.           */
+/* -------------------------------------------------------------------- */
+    if( psRecords->nRecordCount != 0 )
+    {
+        int iRec;
+        int old_rec_length = psRecords->nRecordLength - width - 1;
+        
+        if( psRecords->nMainDataSize < 
+            psRecords->nRecordCount * psRecords->nRecordLength )
+        {
+            psRecords->nMainDataSize = 
+                psRecords->nRecordCount * psRecords->nRecordLength;
+            psRecords->pachMainData = 
+                g_renew( char, psRecords->pachMainData, 
+                         psRecords->nMainDataSize );
+        }
+
+        for( iRec = psRecords->nRecordCount-1; iRec >= 0; iRec-- )
+        {
+            char *pszNew = psRecords->pachMainData 
+                + iRec * psRecords->nRecordLength + old_rec_length;
+
+            memmove( psRecords->pachMainData + iRec * psRecords->nRecordLength,
+                     psRecords->pachMainData + iRec * old_rec_length, 
+                     old_rec_length );
+            memset( pszNew, 0, psRecords->nRecordLength - old_rec_length );
+            pszNew[0] = GV_NULL_MARKER;
+        }
+    }
+
+    return iField;
+}
+
+/************************************************************************/
+/*                   gv_records_set_used_properties()                   */
+/************************************************************************/
+
+void gv_records_set_used_properties(GvRecords *psRecords, int nFieldCount,
+                                    int *panFieldList )
+
+{
+    if( psRecords->nUsedFieldCount != 0 )
+        g_free( psRecords->panUsedFieldList );
+
+    psRecords->panUsedFieldList = g_new( int, nFieldCount );
+    memcpy( psRecords->panUsedFieldList, panFieldList, 
+            sizeof(int) * nFieldCount );
+    psRecords->nUsedFieldCount = nFieldCount;
+}
+

Added: packages/openev/branches/upstream/current/gvrecords.h
===================================================================
--- packages/openev/branches/upstream/current/gvrecords.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrecords.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * $Id: gvrecords.h,v 1.5 2003/08/06 22:26:03 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvRecords declarations.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrecords.h,v $
+ * Revision 1.5  2003/08/06 22:26:03  warmerda
+ * added progress monitor to gv_records load/save funcs
+ *
+ * Revision 1.4  2003/08/06 17:07:44  warmerda
+ * added support to save only selected records in gv_records_to_dbf
+ *
+ * Revision 1.3  2003/07/27 04:57:28  warmerda
+ * complete implementation
+ *
+ * Revision 1.2  2003/05/23 20:44:25  warmerda
+ * fixed header
+ *
+ * Revision 1.1  2003/05/23 20:43:08  warmerda
+ * New
+ *
+ */
+
+#ifndef __GV_RECORDS_H__
+#define __GV_RECORDS_H__
+
+#include "gvdata.h"
+#include "gdal.h"
+
+#define GV_TYPE_RECORDS           (gv_records_get_type ())
+#define GV_RECORDS(obj)           (GTK_CHECK_CAST ((obj), GV_TYPE_RECORDS,\
+                                                  GvRecords))
+#define GV_RECORDS_CLASS(klass)   (GTK_CHECK_CLASS_CAST((klass),GV_TYPE_RECORDS,\
+                                                       GvRecordsClass))
+#define GV_IS_RECORDS(obj)        (GTK_CHECK_TYPE ((obj), GV_TYPE_RECORDS))
+#define GV_IS_RECORDS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_SHAPES))
+
+#define GV_RFT_INTEGER  1
+#define GV_RFT_FLOAT    2
+#define GV_RFT_STRING   3
+
+#define GV_NULL_MARKER  '\01'
+
+typedef struct
+{
+    GvDataClass parent_class;
+} GvRecordsClass;
+
+typedef struct
+{
+    GvData data;
+
+    int nRecordLength;
+
+    int nFieldCount;
+    char **papszFieldName;
+    int *panFieldOffset;
+    int *panFieldWidth;
+    int *panFieldType;
+
+    int nRecordCount;
+
+    int nMainDataSize;
+    char *pachMainData;
+
+    int nUsedFieldCount;
+    int *panUsedFieldList;
+
+} GvRecords;
+
+GtkType gv_records_get_type (void);
+GvData* gv_records_new(void);
+GvData* gv_records_from_dbf(const char *, GDALProgressFunc, void * );
+int     gv_records_to_dbf(GvRecords *, const char *, int, int *,
+                          GDALProgressFunc, void * );
+GvData* gv_records_from_rec(const char *, GDALProgressFunc, void * );
+
+gint gv_records_create_records(GvRecords *, int);
+gint gv_records_num_records(GvRecords *);
+gint gv_records_add_field(GvRecords *, const char *field_name, 
+                          int field_type, int width, int precision );
+const char *gv_records_get_raw_record_data(GvRecords *, int);
+const char *gv_records_get_raw_field_data(GvRecords *, int, int);
+void gv_records_set_raw_field_data(GvRecords *, int, int, const char *);
+void gv_records_set_used_properties(GvRecords *, int nCount, int *panList);
+int *gv_records_get_used_properties(GvRecords *, int *pnCount);
+
+#endif /* ndef __GV_RECORDS_H__ */
+
+
+
+
+
+

Added: packages/openev/branches/upstream/current/gvrecttool.c
===================================================================
--- packages/openev/branches/upstream/current/gvrecttool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrecttool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,571 @@
+/******************************************************************************
+ * $Id: gvrecttool.c,v 1.7 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Rectangle editing mode in GvShapesLayer.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrecttool.c,v $
+ * Revision 1.7  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.6  2001/08/08 17:44:12  warmerda
+ * use gv_shape_type() macro
+ *
+ * Revision 1.5  2000/08/08 20:58:47  warmerda
+ * recover from layer destruction
+ *
+ * Revision 1.4  2000/08/03 18:11:51  warmerda
+ * don't create zero sized rectangles
+ *
+ * Revision 1.3  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.2  2000/07/27 17:52:09  warmerda
+ * allow edit of existing rectangles
+ *
+ * Revision 1.1  2000/07/25 23:34:03  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include "gvrecttool.h"
+#include "gvundo.h"
+#include <GL/gl.h>
+
+static void gv_rect_tool_class_init(GvRectToolClass *klass);
+static void gv_rect_tool_init(GvRectTool *tool);
+static void gv_rect_tool_draw(GvRectTool *tool);
+static void gv_rect_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_rect_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_rect_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_rect_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_rect_tool_deactivate(GvTool *tool, GvViewArea *view);
+static gint gv_rect_tool_configure(GvRectTool *tool);
+
+/* Return values for gv_roi_tool_pick_border() */
+enum
+{
+    PICK_NONE = 0,
+    PICK_CORNER_TOPLEFT,
+    PICK_CORNER_BOTTOMLEFT,
+    PICK_CORNER_BOTTOMRIGHT,
+    PICK_CORNER_TOPRIGHT,
+    PICK_SIDE_TOP,
+    PICK_SIDE_RIGHT,
+    PICK_SIDE_BOTTOM,
+    PICK_SIDE_LEFT
+};
+
+GtkType
+gv_rect_tool_get_type(void)
+{
+    static GtkType rect_tool_type = 0;
+
+    if (!rect_tool_type)
+    {
+	static const GtkTypeInfo rect_tool_info =
+	{
+	    "GvRectTool",
+	    sizeof(GvRectTool),
+	    sizeof(GvRectToolClass),
+	    (GtkClassInitFunc) gv_rect_tool_class_init,
+	    (GtkObjectInitFunc) gv_rect_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	rect_tool_type = gtk_type_unique(gv_tool_get_type(),
+					 &rect_tool_info);
+    }
+    return rect_tool_type;
+}
+
+static void
+gv_rect_tool_class_init(GvRectToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_rect_tool_deactivate;
+    tool_class->button_press = gv_rect_tool_button_press;
+    tool_class->button_release = gv_rect_tool_button_release;
+    tool_class->motion_notify = gv_rect_tool_motion_notify;
+    tool_class->key_press = gv_rect_tool_key_press;
+}
+
+static void
+gv_rect_tool_init(GvRectTool *tool)
+{
+    GV_TOOL(tool)->cursor = gdk_cursor_new(GDK_TCROSS);
+    tool->layer = NULL;
+    tool->named_layer = NULL;
+    tool->drawing = FALSE;
+}
+
+GvTool *
+gv_rect_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_RECT_TOOL));
+}
+
+static gint gv_rect_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvRectTool *tool = (GvRectTool *) data;
+
+    if( tool->layer == GV_SHAPES_LAYER(layer) )
+        gv_rect_tool_set_layer( tool, NULL );
+    
+    return 0;
+}
+
+void
+gv_rect_tool_set_layer(GvRectTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	g_warning("gv_rect_tool_set_layer(): inactive tool");
+	return;
+    }
+
+    if( layer != NULL && gv_data_is_read_only( GV_DATA(layer) ) )
+    {
+        g_warning( "gv_rect_tool_set_layer(): layer is read-only" );
+        return;
+    }
+
+    /* Disconnect from the previous layer (for draw) */
+    if (tool->layer)
+    {
+	if (tool->drawing)
+	{
+            tool->drawing = FALSE;
+	}
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gtk_signal_disconnect_by_data(GTK_OBJECT(tool->layer), (gpointer)tool);
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+ 
+    if( layer == NULL )
+        tool->layer = NULL;
+    else
+        tool->layer = GV_SHAPES_LAYER(layer);
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+	
+        /* Redraw when the layer draws */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "draw",
+					GTK_SIGNAL_FUNC(gv_rect_tool_draw),
+					GTK_OBJECT(tool));
+        /* Recover if layer destroyed */
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy", 
+            GTK_SIGNAL_FUNC(gv_rect_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+void
+gv_rect_tool_set_named_layer(GvRectTool *tool, gchar *name)
+{
+    if (tool->named_layer)
+    {
+	g_free(tool->named_layer);
+	tool->named_layer = NULL;
+    }
+    if (name)
+    {
+	tool->named_layer = g_strdup(name);	
+    }
+    /* Tool layer will be updated next time it is configured */
+}
+
+/********************************************************/
+
+static void
+gv_rect_tool_draw(GvRectTool *tool)
+{
+    if (tool->drawing)
+    {
+	/* Color is set when the layer is drawn,
+	   so we don't need to repeat it here */
+
+	glBegin(GL_LINE_LOOP);
+	glVertex2(tool->v_head.x, tool->v_head.y);
+	glVertex2(tool->v_head.x, tool->v_tail.y);
+	glVertex2(tool->v_tail.x, tool->v_tail.y);
+	glVertex2(tool->v_tail.x, tool->v_head.y);
+	glVertex2(tool->v_head.x, tool->v_head.y);
+	glEnd();
+    }
+}
+
+static void 
+gv_rect_tool_reshape( GvRectTool *r_tool, gvgeocoord x, gvgeocoord y )
+
+{
+    GvShape *shape;
+    gvgeocoord   x1, y1, x2, y2;
+
+    gv_tool_clamp_to_bounds( GV_TOOL(r_tool), &x, &y );
+
+    shape = gv_shapes_get_shape( r_tool->layer->data, r_tool->shape_id );
+    if( shape == NULL || gv_shape_get_nodes( shape, 0 ) != 5 )
+        return;
+
+    shape = gv_shape_copy( shape );
+    
+    x1 = gv_shape_get_x(shape,0,0);
+    y1 = gv_shape_get_y(shape,0,0);
+    x2 = gv_shape_get_x(shape,0,2);
+    y2 = gv_shape_get_y(shape,0,2);
+
+    if( r_tool->picked == PICK_SIDE_TOP )
+        y1 = y;
+    else if( r_tool->picked == PICK_SIDE_RIGHT )
+        x2 = x;
+    else if( r_tool->picked == PICK_SIDE_BOTTOM )
+        y2 = y;
+    else if( r_tool->picked == PICK_SIDE_LEFT )
+        x1 = x;
+    else if( r_tool->picked == PICK_CORNER_TOPLEFT )
+    {
+        x1 = x;
+        y1 = y;
+    }
+    else if( r_tool->picked == PICK_CORNER_TOPRIGHT )
+    {
+        x2 = x;
+        y1 = y;
+    }
+    else if( r_tool->picked == PICK_CORNER_BOTTOMRIGHT )
+    {
+        x2 = x;
+        y2 = y;
+    }
+    else if( r_tool->picked == PICK_CORNER_BOTTOMLEFT )
+    {
+        x1 = x;
+        y2 = y;
+    }
+
+    gv_shape_set_xyz( shape, 0, 0, x1, y1, 0 );
+    gv_shape_set_xyz( shape, 0, 1, x1, y2, 0 );
+    gv_shape_set_xyz( shape, 0, 2, x2, y2, 0 );
+    gv_shape_set_xyz( shape, 0, 3, x2, y1, 0 );
+    gv_shape_set_xyz( shape, 0, 4, x1, y1, 0 );
+
+    gv_shapes_replace_shapes( r_tool->layer->data, 1, &(r_tool->shape_id),
+                              &shape, FALSE );
+}
+
+static void
+gv_rect_tool_button_press(GvTool *r_tool, GdkEventButton *event)
+{
+    GvRectTool *tool = GV_RECT_TOOL(r_tool);
+
+    if (event->button == 1 && !tool->drawing )
+    {
+        GvNodeInfo edit_node;
+        int        before, shape_id, is_rectangle = FALSE;
+
+	if (!gv_rect_tool_configure(tool)) return;
+
+	if (gv_shape_layer_pick_shape(GV_SHAPE_LAYER(tool->layer), 
+                                      GV_TOOL(tool)->view,
+				      event->x, event->y, &shape_id))
+        {
+            GvShape *shape;
+
+            /* Is the shape a rectangle? */
+            shape = gv_shapes_get_shape( tool->layer->data, shape_id );
+            if( shape != NULL 
+                && gv_shape_type(shape) == GVSHAPE_AREA
+                && gv_shape_get_rings( shape ) == 1 
+                && gv_shape_get_nodes( shape, 0 ) == 5 )
+            {
+                gvgeocoord   x1, y1, x2, y2;
+
+                x1 = gv_shape_get_x(shape,0,0);
+                y1 = gv_shape_get_y(shape,0,0);
+                x2 = gv_shape_get_x(shape,0,2);
+                y2 = gv_shape_get_y(shape,0,2);
+
+                tool->winding = 1;
+                is_rectangle = gv_shape_get_x(shape,0,1) == x1
+                    && gv_shape_get_y(shape,0,1) == y2
+                    && gv_shape_get_x(shape,0,3) == x2
+                    && gv_shape_get_y(shape,0,3) == y1
+                    && gv_shape_get_x(shape,0,4) == x1
+                    && gv_shape_get_y(shape,0,4) == y1;
+
+                if( !is_rectangle )
+                {
+                    tool->winding = 0;
+                    is_rectangle = gv_shape_get_x(shape,0,1) == x2
+                        && gv_shape_get_y(shape,0,1) == y1
+                        && gv_shape_get_x(shape,0,3) == x1
+                        && gv_shape_get_y(shape,0,3) == y2
+                        && gv_shape_get_x(shape,0,4) == x1
+                        && gv_shape_get_y(shape,0,4) == y1;
+                }
+
+                if( is_rectangle )
+                {
+                    gv_shape_layer_clear_selection(
+                        GV_SHAPE_LAYER(tool->layer));
+		    gv_shape_layer_select_shape(
+                        GV_SHAPE_LAYER(tool->layer), shape_id);
+                }
+            }
+        }
+
+        /* Is the user selecting an existing rectangles edge/corner? */
+	if (is_rectangle 
+            && gv_shape_layer_pick_node(GV_SHAPE_LAYER(tool->layer), 
+                                        GV_TOOL(tool)->view,
+                                        event->x, event->y, &before,
+                                        &edit_node) )
+	{
+            if( tool->winding == 0 )
+            {
+                if( before )
+                    edit_node.node_id = 5 - edit_node.node_id;
+                else
+                    edit_node.node_id = 4 - edit_node.node_id;
+            }
+
+            if( before && edit_node.node_id == 1 )
+                tool->picked = PICK_SIDE_LEFT;
+            else if( before && edit_node.node_id == 2 )
+                tool->picked = PICK_SIDE_BOTTOM;
+            else if( before && edit_node.node_id == 3 )
+                tool->picked = PICK_SIDE_RIGHT;
+            else if( before && edit_node.node_id == 4 )
+                tool->picked = PICK_SIDE_TOP;
+            else if( edit_node.node_id == 0 )
+                tool->picked = PICK_CORNER_TOPLEFT;
+            else if( edit_node.node_id == 1 )
+                tool->picked = PICK_CORNER_BOTTOMLEFT;
+            else if( edit_node.node_id == 2 )
+                tool->picked = PICK_CORNER_BOTTOMRIGHT;
+            else if( edit_node.node_id == 3 )
+                tool->picked = PICK_CORNER_TOPRIGHT;
+            else if( edit_node.node_id == 4 )
+                tool->picked = PICK_CORNER_TOPLEFT;
+            else
+            {
+                g_warning( "Yikes!  What node is this?" );
+                return;
+            }
+
+            tool->reshaping = TRUE;
+            tool->shape_id = edit_node.shape_id;
+            
+            /* Close down undo.  A single operation describing the new
+               ring will be pushed to undo when drawing stops. */
+            gv_undo_close();
+            gv_undo_disable();
+
+            return;
+        }
+
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+
+	if( gv_tool_check_bounds( GV_TOOL(tool),
+                                  tool->v_tail.x, tool->v_tail.y ) )
+        {
+            /* Start a new rect */
+            tool->drawing = TRUE;
+            tool->v_head = tool->v_tail;
+        }
+    }
+}
+
+static void
+gv_rect_tool_button_release(GvTool *r_tool, GdkEventButton *event)
+{
+    GvRectTool *tool = GV_RECT_TOOL(r_tool);
+
+    if (event->button == 1 && tool->drawing )
+    {
+        GvShape *shape;
+	
+	if (!gv_rect_tool_configure(tool)) return;
+
+        /* Map pointer position to tail vertex */
+        gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+                                 &tool->v_tail.x, &tool->v_tail.y);
+	
+        gv_tool_clamp_to_bounds( GV_TOOL(tool),
+				 &tool->v_tail.x, &tool->v_tail.y );
+
+
+        if( tool->v_tail.x != tool->v_head.x 
+            && tool->v_tail.y != tool->v_head.y )
+        {
+            /* create the new rectangle */
+            shape = gv_shape_new( GVSHAPE_AREA );
+            
+            gv_shape_add_node( shape, 0, tool->v_tail.x, tool->v_tail.y, 0 );
+            gv_shape_add_node( shape, 0, tool->v_tail.x, tool->v_head.y, 0 );
+            gv_shape_add_node( shape, 0, tool->v_head.x, tool->v_head.y, 0 );
+            gv_shape_add_node( shape, 0, tool->v_head.x, tool->v_tail.y, 0 );
+            gv_shape_add_node( shape, 0, tool->v_tail.x, tool->v_tail.y, 0 ); 
+            
+            gv_shapes_layer_select_new_shape( GV_SHAPES_LAYER(tool->layer), 
+                                              shape );
+        }
+
+        tool->drawing = FALSE;
+        return;
+    }
+
+    if (event->button == 1 && tool->reshaping )
+    {
+	if (!gv_rect_tool_configure(tool)) return;
+
+        /* Reopen undo.  Push a memento describing the ring addition */
+        gv_undo_enable();
+        gv_undo_open();
+
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+        gv_rect_tool_reshape( tool, tool->v_tail.x, tool->v_tail.y );
+
+        tool->reshaping = FALSE;
+    }
+}
+
+static void
+gv_rect_tool_motion_notify(GvTool *r_tool, GdkEventMotion *event)
+{
+    GvRectTool *tool = GV_RECT_TOOL(r_tool);
+
+    if (tool->drawing)
+    {
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+
+        gv_tool_clamp_to_bounds( GV_TOOL(tool),
+				 &tool->v_tail.x, &tool->v_tail.y );
+
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+
+    if (tool->reshaping)
+    {
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+        gv_rect_tool_reshape( tool, tool->v_tail.x, tool->v_tail.y );
+    }
+}
+
+static void
+gv_rect_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvRectTool *tool = GV_RECT_TOOL(rtool);
+
+    if (!gv_rect_tool_configure(tool)) return;
+    
+    switch (event->keyval)
+    {
+	case GDK_Delete:
+	case GDK_BackSpace:
+	case GDK_Escape:
+          if( tool->drawing )
+          {
+              tool->drawing = FALSE;
+              gv_view_area_queue_draw(GV_TOOL(tool)->view);
+          }
+          if( tool->reshaping )
+          {
+              /* Reopen undo.  Push a memento describing the ring addition */
+              gv_undo_enable();
+              gv_undo_open();
+
+              tool->reshaping = FALSE;
+              gv_view_area_queue_draw(GV_TOOL(tool)->view);
+          }
+          break;
+    }
+}
+
+static void
+gv_rect_tool_deactivate(GvTool *r_tool, GvViewArea *view)
+{
+    GvRectTool *tool = GV_RECT_TOOL(r_tool);
+
+    /* Disconnect from layer */
+    if (tool->layer)
+    {
+	gv_rect_tool_set_layer(tool, NULL);
+    }
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+}
+
+static gint
+gv_rect_tool_configure(GvRectTool *tool)
+{
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	if (tool->named_layer)
+	{
+	    /* Look for named layer if given */
+	    layer = gv_view_area_get_named_layer(GV_TOOL(tool)->view,
+						 tool->named_layer);
+	}
+	else
+	{
+            layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+                                                   GV_TYPE_SHAPES_LAYER,
+                                                   FALSE);
+	}
+
+	if (!layer)
+	{
+	    g_warning("gv_rect_tool_configure(): no shapes layer in view");
+	    return FALSE;
+	}
+
+	gv_rect_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+    return tool->layer != NULL;
+}

Added: packages/openev/branches/upstream/current/gvrecttool.h
===================================================================
--- packages/openev/branches/upstream/current/gvrecttool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrecttool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,84 @@
+/******************************************************************************
+ * $Id: gvrecttool.h,v 1.3 2000/07/27 20:06:23 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Rectangle (in GvShapesLayer) editing mode.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrecttool.h,v $
+ * Revision 1.3  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.2  2000/07/27 17:52:09  warmerda
+ * allow edit of existing rectangles
+ *
+ * Revision 1.1  2000/07/25 23:34:03  warmerda
+ * New
+ *
+ */
+
+#ifndef __GV_RECT_TOOL_H__
+#define __GV_RECT_TOOL_H__
+
+#include "gvtool.h"
+#include "gvshapeslayer.h"
+
+#define GV_TYPE_RECT_TOOL            (gv_rect_tool_get_type ())
+#define GV_RECT_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_RECT_TOOL, GvRectTool))
+#define GV_RECT_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_RECT_TOOL, GvRectToolClass))
+#define GV_IS_RECT_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_RECT_TOOL))
+#define GV_IS_RECT_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_RECT_TOOL))
+
+typedef struct _GvRectTool       GvRectTool;
+typedef struct _GvRectToolClass  GvRectToolClass;
+
+struct _GvRectTool
+{
+    GvTool tool;
+
+    GvShapesLayer *layer;
+    gchar *named_layer;
+
+    guint drawing : 1;
+    GvVertex v_head, v_tail;
+
+    guint reshaping : 1;
+    int   picked;   /* which corner or edge */
+    int   shape_id; /* shapeid of rectangle being reshaped */
+    int   winding;  /* 1 = counterclockwise, 0 = clockwise */
+
+};
+
+struct _GvRectToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_rect_tool_get_type(void);
+GvTool* gv_rect_tool_new(void);
+void gv_rect_tool_set_layer(GvRectTool *tool, GvShapeLayer *layer);
+void gv_rect_tool_set_named_layer(GvRectTool *tool, gchar *name);
+
+#endif /* __GV_RECT_TOOL_H__ */
+
+
+

Added: packages/openev/branches/upstream/current/gvrenderinfo.c
===================================================================
--- packages/openev/branches/upstream/current/gvrenderinfo.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrenderinfo.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,847 @@
+/******************************************************************************
+ * $Id: gvrenderinfo.c,v 1.23 2003/06/25 16:42:18 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Parse OGRFeatureStyle rendering information and build
+ *           GvRenderPart structures.  This module is essentially associated
+ *           with the GvShapeLayer.
+ * Author:   Frank Warmerdam <warmerdam at pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrenderinfo.c,v $
+ * Revision 1.23  2003/06/25 16:42:18  warmerda
+ * gv_get_ogr_arg() made public as gv_ogrfs_get_arg().
+ * gv_split_tools() made public as gv_ogrfs_split_tools().
+ *
+ * Revision 1.22  2003/05/27 15:17:42  warmerda
+ * set pattern from pen id if available
+ *
+ * Revision 1.21  2003/05/16 18:26:32  pgs
+ * added initial code for propogating colors to sub-symbols
+ *
+ * Revision 1.20  2003/04/09 16:52:21  pgs
+ * added shadow, halo and bgcolor to LABELs
+ *
+ * Revision 1.19  2003/04/07 15:10:10  pgs
+ * added pattern support to pen objects
+ *
+ * Revision 1.18  2003/03/02 17:05:11  warmerda
+ * removed unit_vector from renderinfo args
+ *
+ * Revision 1.17  2003/02/28 16:49:06  warmerda
+ * split up renderinfo parsing to use for vector symbols
+ *
+ * Revision 1.16  2003/02/27 03:59:59  warmerda
+ * set scale_dep flag when updating renderinfo
+ *
+ * Revision 1.15  2003/02/14 20:12:43  pgs
+ * added support for line widths in PENs
+ *
+ * Revision 1.14  2002/11/15 05:04:43  warmerda
+ * added LABEL anchor point support
+ *
+ * Revision 1.13  2002/11/14 22:04:59  warmerda
+ * implement offsets for symbols
+ *
+ * Revision 1.12  2002/11/14 20:11:21  warmerda
+ * preliminary support for gvsymbolmanager from Paul
+ *
+ * Revision 1.11  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.10  2002/05/07 02:51:15  warmerda
+ * preliminary support for GVSHAPE_COLLECTION
+ *
+ * Revision 1.9  2002/03/21 15:48:51  warmerda
+ * added support for geometry specific _ogr_ogrfs_... layer properties
+ *
+ * Revision 1.8  2002/02/22 20:16:07  warmerda
+ * added brush tool support
+ *
+ * Revision 1.7  2002/02/22 19:27:16  warmerda
+ * added support for pen tools
+ *
+ * Revision 1.6  2002/01/21 20:45:39  warmerda
+ * Avoid reporting "unsupported tool" too many times.
+ *
+ * Revision 1.5  2001/08/21 21:41:24  warmerda
+ * recover if no text provided
+ *
+ * Revision 1.4  2001/05/01 19:27:09  warmerda
+ * Create an X sized box for empty labels
+ *
+ * Revision 1.3  2001/04/25 20:35:03  warmerda
+ * added proper support for descenders in label text
+ *
+ * Revision 1.2  2001/04/23 14:09:57  warmerda
+ * ensure that GVP_LAST_PART is set if the tool list is corrupt or empty
+ *
+ * Revision 1.1  2001/04/09 18:22:54  warmerda
+ * New
+ *
+ */
+
+#include "gvrenderinfo.h"
+#include "gvshapeslayer.h"
+#include "cpl_string.h"
+#include <GL/gl.h>
+
+/************************************************************************/
+/*                           gv_get_ogr_arg()                           */
+/************************************************************************/
+
+const char *
+gv_ogrfs_get_arg( const char *def, char **next_def,
+                  char **value, int *value_len )
+
+{
+    const char *ret;
+    int        vlen;
+
+    /* skip to the first argument.  It will be preceeded by '(' or ',' */
+    while( *def != '(' && *def != ',' && *def != '\0' )
+        def++;
+
+    if( *def == '\0' )
+        return NULL;
+
+    def++;
+    ret = def;
+
+    /* find start of value */
+    while( *def != ':' )
+    {
+        if( *def == '\0' )
+            return NULL;
+
+        def++;
+    }
+
+    def++;
+
+    if( *def == '"' )
+    {
+        def++;
+        if( value != NULL )
+            *value = (char *) def;
+
+        vlen = 0;
+        while( *def != '"' && *def != '\0' )
+        {
+            def++;
+            vlen++;
+        }
+        if( *def == '"' )
+            def++;
+    }
+    else
+    {
+        if( value != NULL )
+            *value = (char *) def;
+
+        vlen = 0;
+        while( *def != ')' && *def != ',' && *def != '\0' )
+        {
+            def++;
+            vlen++;
+        }
+    }
+
+    if( value_len != NULL )
+        *value_len = vlen;
+
+    if( next_def != NULL )
+        *next_def = (char *) def;
+
+    return ret;
+}
+
+/************************************************************************/
+/*                          gv_get_ogr_color()                          */
+/************************************************************************/
+
+static int
+gv_get_ogr_color( const char * sym_def, GvColor color )
+
+{
+    int      red, green, blue, alpha = 255;
+
+    if( EQUALN(sym_def,"c:",2) )
+        sym_def += 2;
+
+    if( *sym_def != '#' )
+        return FALSE;
+
+    if( sscanf( sym_def+1, "%2x%2x%2x%2x", &red, &green, &blue, &alpha ) < 3 )
+        return FALSE;
+
+    color[0] =   red / 255.0;
+    color[1] = green / 255.0;
+    color[2] =  blue / 255.0;
+    color[3] = alpha / 255.0;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                           gv_parse_label()                           */
+/************************************************************************/
+
+static guint
+gv_parse_label( GvShapesLayer *layer, GvShape *shape, const char * sym_def,
+                int *scale_dep )
+
+{
+    const char  *id;
+    char        *next, *value=NULL;
+    guint       part_index = GVP_UNINITIALIZED_PART;
+    int         value_len;
+    GvLabelRenderPart *label_info;
+
+    if( !EQUALN(sym_def,"LABEL",5) )
+    {
+        g_warning( "not a label" );
+        return part_index;
+    }
+
+    *scale_dep = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Create the new part.                                            */
+/* -------------------------------------------------------------------- */
+    part_index = gv_shape_layer_create_part( GV_SHAPE_LAYER(layer),
+                                             GvLabelPart );
+
+    label_info = (GvLabelRenderPart *)
+        gv_shape_layer_get_part( GV_SHAPE_LAYER(layer), part_index );
+
+    g_assert( label_info != NULL );
+
+    label_info->font = -1;
+
+/* -------------------------------------------------------------------- */
+/*      Parse the tool information, and apply to the renderinfo part.   */
+/* -------------------------------------------------------------------- */
+
+    while( (id = gv_ogrfs_get_arg( sym_def, &next, &value,
+                                 &value_len )) != NULL )
+    {
+        if( EQUALN(id,"c:",2) )
+        {
+            gv_get_ogr_color( value, label_info->color );
+            label_info->b_color_initialized = 1;
+        }
+        if( EQUALN(id,"b:",2) )
+        {
+            gv_get_ogr_color( value, label_info->background_color );
+            label_info->b_background_color_initialized = 1;
+        }
+        else if( EQUALN(id,"f:",2) )
+        {
+            char    *font;
+
+            font = g_strdup(value);
+            font[value_len] = '\0';
+
+            label_info->font = gv_view_area_bmfont_load(
+                GV_VIEW_AREA(GV_LAYER(layer)->view), font );
+
+            g_free( font );
+        }
+        else if( EQUALN(id,"t:",2) )
+        {
+            if( value[0] == '{' )
+            {
+                GvProperties    *properties = gv_shape_get_properties(shape);
+                char        *field_name;
+
+                field_name = g_strdup(value+1);
+                field_name[value_len-1] = '\0';
+                if( field_name[value_len-2] == '}' )
+                    field_name[value_len-2] = '\0';
+
+                label_info->text = g_strdup(
+                    gv_properties_get( properties, field_name ));
+
+                g_free( field_name );
+            }
+            else
+            {
+                label_info->text = g_malloc(value_len+1);
+                strncpy( label_info->text, value, value_len );
+                label_info->text[value_len] = '\0';
+            }
+        }
+        else if( EQUALN(id,"a:",2) )
+        {
+            label_info->angle = atof(value);
+        }
+        else if( EQUALN(id,"p:",2) )
+        {
+            label_info->anchor = atoi(value);
+            if( label_info->anchor < 1 || label_info->anchor > 9 )
+            {
+                CPLDebug( "GVRENDERINFO",
+                          "Illegal anchor position in label (%d), ignoring.",
+                          label_info->anchor );
+                label_info->anchor = GLRA_LOWER_LEFT;
+            }
+        }
+        else if( EQUALN(id,"s:",2) )
+        {
+            label_info->scale = atof(value);
+            if( label_info->scale == 0.0 )
+                label_info->scale = 1.0;
+        }
+        else if( EQUALN(id,"dx:",3) )
+        {
+            if( EQUALN(value+value_len-1,"g",1) )
+                label_info->x_offset_g = atof(value);
+            else
+                label_info->x_offset_px = atof(value);
+        }
+        else if( EQUALN(id,"dy:",3) )
+        {
+            if( EQUALN(value+value_len-1,"g",1) )
+                label_info->y_offset_g = atof(value);
+            else
+                label_info->y_offset_px = atof(value);
+        }
+        else if( EQUALN(id,"h",1) )
+        {
+            label_info->halo = TRUE;
+        }
+        else if( EQUALN(id,"sh",2) )
+        {
+            label_info->shadow = TRUE;
+        }
+
+        sym_def = next;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Default font to fixed.                                          */
+/* -------------------------------------------------------------------- */
+    if( label_info->font == -1 )
+        label_info->font = gv_view_area_bmfont_load(
+            GV_VIEW_AREA(GV_LAYER(layer)->view), "fixed" );
+
+/* -------------------------------------------------------------------- */
+/*      Did we miss out on any critical components?  If so, remove      */
+/*      this part.                                                      */
+/* -------------------------------------------------------------------- */
+    if( label_info->text == NULL || label_info->font == -1 )
+    {
+        label_info->text = g_strdup("");
+        g_warning( "missing text or font for label" );
+        /*gv_shape_layer_clear_part( GV_SHAPE_LAYER(layer), part_index );*/
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get string extents.                                             */
+/* -------------------------------------------------------------------- */
+    if( label_info->font != -1 )
+    {
+        guint   lbearing, rbearing, ascent, descent;
+        GvBMFontInfo *font_info;
+        const char *pszText;
+
+        font_info = gv_view_area_bmfont_get_info(
+            GV_VIEW_AREA(GV_LAYER(layer)->view), label_info->font );
+
+        if( label_info->text[0] == '\0' )
+            pszText = "X";
+        else
+            pszText = label_info->text;
+
+        gdk_string_extents( font_info->gdkfont, pszText,
+                            &lbearing, &rbearing, &(label_info->width),
+                            &ascent, &descent );
+
+#ifdef WIN32
+        //gdk on win32 reports the total height as the ascent
+        label_info->height = ascent - descent - 1;
+        label_info->descent = descent - 1;
+#else
+        label_info->height = ascent + descent;
+        label_info->descent = descent;
+#endif
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Adjust pixel offset to take into account the anchor point       */
+/*      (justification).                                                */
+/* -------------------------------------------------------------------- */
+    switch( label_info->anchor )
+    {
+      case GLRA_LOWER_CENTER:
+        label_info->x_offset_px -= label_info->width / 2.0;
+        break;
+
+      case GLRA_LOWER_RIGHT:
+        label_info->x_offset_px -= label_info->width;
+        break;
+
+      case GLRA_CENTER_LEFT:
+        label_info->y_offset_px += label_info->height / 2.0;
+        break;
+
+      case GLRA_CENTER_CENTER:
+        label_info->x_offset_px -= label_info->width / 2.0;
+        label_info->y_offset_px += label_info->height / 2.0;
+        break;
+
+      case GLRA_CENTER_RIGHT:
+        label_info->x_offset_px -= label_info->width;
+        label_info->y_offset_px += label_info->height / 2.0;
+        break;
+
+      case GLRA_UPPER_LEFT:
+        label_info->y_offset_px += label_info->height;
+        break;
+
+      case GLRA_UPPER_CENTER:
+        label_info->x_offset_px -= label_info->width / 2.0;
+        label_info->y_offset_px += label_info->height;
+        break;
+
+      case GLRA_UPPER_RIGHT:
+        label_info->x_offset_px -= label_info->width;
+        label_info->y_offset_px += label_info->height;
+        break;
+
+      case GLRA_LOWER_LEFT:
+      default:
+        break;
+    }
+
+    return part_index;
+}
+
+/************************************************************************/
+/*                          gv_parse_symbol()                           */
+/************************************************************************/
+
+static guint
+gv_parse_symbol( GvShapesLayer *layer, GvShape *shape, const char * sym_def,
+                 int *scale_dep )
+
+{
+    const char  *id;
+    char        *next, *value;
+    int         value_len;
+    guint       part_index = GVP_UNINITIALIZED_PART;
+    GvSymbolRenderPart *symbol_info;
+
+    if( !EQUALN(sym_def,"SYMBOL",6) )
+    {
+        g_warning( "not a symbol" );
+        return part_index;
+    }
+
+    *scale_dep = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Create the new part.                                            */
+/* -------------------------------------------------------------------- */
+    part_index = gv_shape_layer_create_part( GV_SHAPE_LAYER(layer),
+                                             GvSymbolPart );
+
+    symbol_info = (GvSymbolRenderPart *)
+        gv_shape_layer_get_part( GV_SHAPE_LAYER(layer), part_index );
+
+    g_assert( symbol_info != NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Parse the tool arguments.                                       */
+/* -------------------------------------------------------------------- */
+    while( (id = gv_ogrfs_get_arg( sym_def, &next, &value,
+                                 &value_len )) != NULL )
+    {
+        if( EQUALN(id,"c:",2) )
+        {
+            gv_get_ogr_color( value, symbol_info->color );
+            symbol_info->b_color_initialized = 1;
+        }
+        else if( EQUALN(id,"id:",3) )
+        {
+            symbol_info->symbol_id = g_malloc(value_len+1);
+            strncpy( symbol_info->symbol_id, value, value_len );
+            symbol_info->symbol_id[value_len] = '\0';
+        }
+        else if( EQUALN(id,"a:",2) )
+        {
+            symbol_info->angle = atof(value);
+        }
+        else if( EQUALN(id,"s:",2) )
+        {
+            symbol_info->scale = atof(value);
+            if( symbol_info->scale == 0.0 )
+                symbol_info->scale = 1.0;
+        }
+        else if( EQUALN(id,"dx:",3) )
+        {
+            if( EQUALN(value+value_len-1,"g",1) )
+                symbol_info->x_offset_g = atof(value);
+            else
+                symbol_info->x_offset_px = atof(value);
+        }
+        else if( EQUALN(id,"dy:",3) )
+        {
+
+            if( EQUALN(value+value_len-1,"g",1) )
+                symbol_info->y_offset_g = atof(value);
+            else
+                symbol_info->y_offset_px = atof(value);
+        }
+
+        sym_def = next;
+    }
+
+    if( symbol_info->symbol_id == NULL )
+    {
+        symbol_info->symbol_id = g_strdup("ogr-sym-0");
+    }
+
+    return part_index;
+}
+
+/************************************************************************/
+/*                            gv_parse_pen()                            */
+/************************************************************************/
+
+static guint
+gv_parse_pen( GvShapesLayer *layer, GvShape *shape, const char * sym_def,
+              int *scale_dep )
+
+{
+    const char  *id;
+    char        *next, *value;
+    int         value_len;
+    guint       part_index = GVP_UNINITIALIZED_PART;
+    GvPenRenderPart *pen_info;
+
+    if( !EQUALN(sym_def,"PEN",3) )
+    {
+        g_warning( "not a pen" );
+        return part_index;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the new part.                                            */
+/* -------------------------------------------------------------------- */
+    part_index = gv_shape_layer_create_part( GV_SHAPE_LAYER(layer),
+                                             GvPenPart );
+
+    pen_info = (GvPenRenderPart *)
+        gv_shape_layer_get_part( GV_SHAPE_LAYER(layer), part_index );
+
+    g_assert( pen_info != NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Parse the tool arguments.                                       */
+/* -------------------------------------------------------------------- */
+    while( (id = gv_ogrfs_get_arg( sym_def, &next, &value,
+                                 &value_len )) != NULL )
+    {
+        if( EQUALN(id,"c:",2) )
+        {
+            gv_get_ogr_color( value, pen_info->color );
+            pen_info->b_color_initialized = 1;
+        }
+        else if( EQUALN(id,"w:",2) )
+        {
+            pen_info->width = atof(value);
+            //TODO: should validate using
+            //glGetFloatv( GL_ALIAS_LINE_WIDTH_RANGE ) or
+            //glGetFloatv( GL_SMOOTH_LINE_WIDTH_RANGE ) but
+            //these constants aren't defined on Windows
+            if (pen_info->width < 0.0)
+                pen_info->width = 1.0;
+
+        }
+        else if (EQUALN(id, "p:", 2) )
+        {
+            //define a pattern for the pen
+            pen_info->pattern = g_malloc(value_len+1);
+            strncpy( pen_info->pattern, value, value_len );
+            pen_info->pattern[value_len] = '\0';
+
+        }
+        else if (EQUALN(id, "id:", 3) && EQUALN(value,"ogr-pen-",8) )
+        {
+            //define a pattern for the pen
+            pen_info->pattern = g_malloc(value_len+1);
+            strncpy( pen_info->pattern, value, value_len );
+            pen_info->pattern[value_len] = '\0';
+        }
+
+        sym_def = next;
+    }
+
+    if( pen_info->pattern == NULL )
+    {
+        pen_info->pattern = g_strdup("ogr-pen-0");
+    }
+
+    return part_index;
+}
+
+/************************************************************************/
+/*                           gv_parse_brush()                           */
+/************************************************************************/
+
+static guint
+gv_parse_brush( GvShapesLayer *layer, GvShape *shape, const char * sym_def,
+                int *scale_dep )
+
+{
+    const char  *id;
+    char        *next, *value;
+    int         value_len;
+    guint       part_index = GVP_UNINITIALIZED_PART;
+    GvBrushRenderPart *brush_info;
+
+    if( !EQUALN(sym_def,"BRUSH",5) )
+    {
+        g_warning( "not a brush" );
+        return part_index;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the new part.                                            */
+/* -------------------------------------------------------------------- */
+    part_index = gv_shape_layer_create_part( GV_SHAPE_LAYER(layer),
+                                             GvBrushPart );
+
+    brush_info = (GvBrushRenderPart *)
+        gv_shape_layer_get_part( GV_SHAPE_LAYER(layer), part_index );
+
+    g_assert( brush_info != NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Parse the tool arguments.                                       */
+/* -------------------------------------------------------------------- */
+    while( (id = gv_ogrfs_get_arg( sym_def, &next, &value,
+                                 &value_len )) != NULL )
+    {
+        if( EQUALN(id,"fc:",3) )
+        {
+            gv_get_ogr_color( value, brush_info->fore_color );
+            brush_info->b_fore_color_initialized = 1;
+        }
+
+        sym_def = next;
+    }
+
+    return part_index;
+}
+
+/************************************************************************/
+/*                        gv_ogrfs_split_tools()                        */
+/************************************************************************/
+
+char **gv_ogrfs_split_tools( const char *tool_list_in )
+
+{
+    int    i_src, word_start=0;
+    char   **result = NULL, *tool_list;
+
+    tool_list = g_strdup(tool_list_in);
+
+    /* split on semi-colons that are not part of quoted strings */
+
+    for( i_src = 0; tool_list[i_src] != '\0'; i_src++ )
+    {
+        if( tool_list[i_src] == '"' )
+        {
+            for( i_src++;
+                 tool_list[i_src] != '\0' && tool_list[i_src] != '"';
+                 i_src++ ) {}
+        }
+        else if( tool_list[i_src] == ';' )
+        {
+            tool_list[i_src] = '\0';
+
+            result = CSLAddString( result, tool_list + word_start );
+            word_start = i_src + 1;
+        }
+    }
+
+    result = CSLAddString( result, tool_list + word_start );
+
+    g_free( tool_list );
+
+    return result;
+}
+
+/************************************************************************/
+/*                  gv_shape_layer_build_renderinfo()                   */
+/************************************************************************/
+
+guint gv_shape_layer_build_renderinfo( GvShapeLayer *s_layer,
+                                       GvShape *shape_obj,
+                                       int *scale_dep )
+
+{
+    int     tool_index;
+    guint   base_part_index = GVP_UNINITIALIZED_PART;
+    GvShapesLayer *layer = GV_SHAPES_LAYER( s_layer );
+    const char  *ogrfs;
+    char    **tool_list;
+
+/* -------------------------------------------------------------------- */
+/*      Get the ogrfs string to apply.                                  */
+/* -------------------------------------------------------------------- */
+    ogrfs = gv_properties_get( gv_shape_get_properties(shape_obj),
+                               "_gv_ogrfs" );
+
+    if( ogrfs == NULL )
+    {
+        if( gv_shape_type(shape_obj) == GVSHAPE_POINT )
+            ogrfs = gv_properties_get(&(GV_DATA(layer)->properties),
+                                      "_gv_ogrfs_point");
+        else if( gv_shape_type(shape_obj) == GVSHAPE_LINE )
+            ogrfs = gv_properties_get(&(GV_DATA(layer)->properties),
+                                      "_gv_ogrfs_line");
+        else if( gv_shape_type(shape_obj) == GVSHAPE_AREA )
+            ogrfs = gv_properties_get(&(GV_DATA(layer)->properties),
+                                      "_gv_ogrfs_area");
+    }
+
+    if( ogrfs == NULL )
+        ogrfs = gv_properties_get(&(GV_DATA(layer)->properties),"_gv_ogrfs");
+
+    if( ogrfs == NULL )
+        return GVP_LAST_PART;
+
+/* -------------------------------------------------------------------- */
+/*      Split tool actions apart.                                       */
+/* -------------------------------------------------------------------- */
+    tool_list = gv_ogrfs_split_tools( ogrfs );
+
+    for( tool_index = 0; tool_index < CSLCount(tool_list); tool_index++ )
+    {
+        guint part_index = GVP_UNINITIALIZED_PART;
+
+        if( EQUALN(tool_list[tool_index],"LABEL",5)
+            && gv_shape_type(shape_obj) == GVSHAPE_POINT )
+        {
+            part_index =
+                gv_parse_label( layer, shape_obj,
+                                tool_list[tool_index], scale_dep );
+        }
+        else if( EQUALN(tool_list[tool_index],"SYMBOL",6)
+            && gv_shape_type(shape_obj) == GVSHAPE_POINT )
+        {
+            part_index =
+                gv_parse_symbol( layer, shape_obj,
+                                 tool_list[tool_index], scale_dep );
+        }
+        else if( EQUALN(tool_list[tool_index],"PEN",3)
+            && (gv_shape_type(shape_obj) == GVSHAPE_LINE
+                || gv_shape_type(shape_obj) == GVSHAPE_AREA
+                || gv_shape_type(shape_obj) == GVSHAPE_COLLECTION) )
+        {
+            part_index =
+                gv_parse_pen( layer, shape_obj,
+                              tool_list[tool_index], scale_dep );
+        }
+        else if( EQUALN(tool_list[tool_index],"BRUSH",5)
+                 && (gv_shape_type(shape_obj) == GVSHAPE_AREA
+                     || gv_shape_type(shape_obj) == GVSHAPE_COLLECTION) )
+        {
+            part_index =
+                gv_parse_brush( layer, shape_obj,
+                                tool_list[tool_index], scale_dep );
+        }
+        else
+        {
+            static int nReportedUnsupportedTools = 0;
+            char message[512];
+            const char *object = "unknown";
+
+            if( gv_shape_type(shape_obj) == GVSHAPE_POINT )
+                object = "point";
+            else if( gv_shape_type(shape_obj) == GVSHAPE_LINE )
+                object = "line";
+            else if( gv_shape_type(shape_obj) == GVSHAPE_AREA )
+                object = "area";
+
+            sprintf( message, "Unsupported tool (%s) for %s object.",
+                     tool_list[tool_index], object);
+
+            if( nReportedUnsupportedTools++ < 15 )
+                g_warning(message);
+            else if( nReportedUnsupportedTools++ == 15 )
+                g_warning("Unsupported tool - rest of warnings supressed.");
+
+            if( nReportedUnsupportedTools < 100 )
+                CPLDebug( "GVRENDERINFO", "%s", message );
+
+        }
+
+        if( part_index != GVP_UNINITIALIZED_PART )
+            base_part_index =
+                gv_shape_layer_chain_part( s_layer, base_part_index,
+                                           part_index);
+    }
+
+    CSLDestroy( tool_list );
+
+    return base_part_index;
+}
+
+/************************************************************************/
+/*                 gv_shape_layer_update_renderinfo()                   */
+/************************************************************************/
+
+void gv_shape_layer_update_renderinfo( GvShapeLayer *s_layer, int shape_id )
+
+{
+    GvShapesLayer *layer = GV_SHAPES_LAYER( s_layer );
+    GvShape     *shape_obj;
+    int          scale_dep = FALSE;
+    guint        base_part_index;
+
+    base_part_index = gv_shape_layer_get_first_part_index( s_layer, shape_id );
+    if( base_part_index != GVP_UNINITIALIZED_PART )
+        return;
+
+    if( !GV_IS_SHAPES_LAYER( s_layer ) )
+        return;
+
+    if( s_layer->render_index == NULL )
+        gv_shape_layer_initialize_renderindex( s_layer );
+
+    shape_obj = gv_shapes_get_shape(layer->data,shape_id);
+
+    base_part_index = gv_shape_layer_build_renderinfo( s_layer, shape_obj,
+                                                       &scale_dep );
+
+    if( base_part_index == GVP_UNINITIALIZED_PART )
+        base_part_index = GVP_LAST_PART;
+
+    g_array_index(s_layer->render_index,guint,shape_id) = base_part_index;
+
+    gv_shape_layer_set_scale_dep( s_layer, shape_id, scale_dep );
+}
+

Added: packages/openev/branches/upstream/current/gvrenderinfo.h
===================================================================
--- packages/openev/branches/upstream/current/gvrenderinfo.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrenderinfo.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,242 @@
+/******************************************************************************
+ * $Id: gvrenderinfo.h,v 1.15 2003/06/25 16:42:18 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Functions for managing per-shape rendering information in
+ *           in the GvShapeLayer.  Actual rendering of shapes using the
+ *           rendering information is still in GvShapesLayer.
+ * Author:   Frank Warmerdam <warmerdam at pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrenderinfo.h,v $
+ * Revision 1.15  2003/06/25 16:42:18  warmerda
+ * gv_get_ogr_arg() made public as gv_ogrfs_get_arg().
+ * gv_split_tools() made public as gv_ogrfs_split_tools().
+ *
+ * Revision 1.14  2003/05/16 18:26:32  pgs
+ * added initial code for propogating colors to sub-symbols
+ *
+ * Revision 1.13  2003/04/09 16:52:22  pgs
+ * added shadow, halo and bgcolor to LABELs
+ *
+ * Revision 1.12  2003/04/07 15:10:12  pgs
+ * added pattern support to pen objects
+ *
+ * Revision 1.11  2003/03/02 17:05:11  warmerda
+ * removed unit_vector from renderinfo args
+ *
+ * Revision 1.10  2003/02/28 16:49:05  warmerda
+ * split up renderinfo parsing to use for vector symbols
+ *
+ * Revision 1.9  2003/02/14 20:12:43  pgs
+ * added support for line widths in PENs
+ *
+ * Revision 1.8  2002/11/15 05:04:43  warmerda
+ * added LABEL anchor point support
+ *
+ * Revision 1.7  2002/11/14 22:05:00  warmerda
+ * implement offsets for symbols
+ *
+ * Revision 1.6  2002/11/14 20:11:21  warmerda
+ * preliminary support for gvsymbolmanager from Paul
+ *
+ * Revision 1.5  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.4  2002/02/22 20:16:07  warmerda
+ * added brush tool support
+ *
+ * Revision 1.3  2002/02/22 19:27:16  warmerda
+ * added support for pen tools
+ *
+ * Revision 1.2  2001/04/25 20:36:01  warmerda
+ * added proper support for descenders in label text
+ *
+ * Revision 1.1  2001/04/09 18:22:54  warmerda
+ * New
+ *
+ */
+
+#ifndef __GV_RENDER_INFO_H__
+#define __GV_RENDER_INFO_H__
+
+#include "gvshapeslayer.h"
+
+#define GvReservedPart  0x00
+#define GvLabelPart 0x01
+#define GvSymbolPart    0x02
+#define GvPenPart   0x03
+#define GvBrushPart 0x04
+
+#define gv_part_index_to_type(x)    ((x) & 0x07)
+#define gv_part_index_to_index(x)   ((x) >>3)
+
+/* -------------------------------------------------------------------- */
+/*      It is intended that GvRenderPart is like a subclass of the      */
+/*      more specific render infos, but for symplicity of               */
+/*      referencing fields we don't make this relationship explicit.    */
+/* -------------------------------------------------------------------- */
+
+#define GVP_UNINITIALIZED_PART  0x00
+#define GVP_LAST_PART           0x08
+
+typedef struct
+{
+  unsigned int  next_part;  /* 0 means this object is uninitialized.
+                                 * 8 means this is the last part
+                                 * Otherwise mask the GvXXXPart number
+                                 * out of the lower 3 bits, and shift the
+                                 * remainder down for the index into the table.
+                                 */
+
+} GvRenderPart;
+
+typedef struct
+{
+    /* From GvRenderPart */
+    guint         next_part;
+
+    /* Specific to GvLabelRenderPart */
+    GvColor   color;
+    /* for tracking if the color was actually initialized
+       as part of the symbol definition */
+    int           b_color_initialized;
+    GvColor   background_color;
+    /* for tracking if the color was actually initialized
+       as part of the symbol definition */
+    int           b_background_color_initialized;
+    char      *text;
+    gint      font;
+    gvgeocoord    x_offset_px;
+    gvgeocoord    y_offset_px;
+    gvgeocoord    x_offset_g;
+    gvgeocoord    y_offset_g;
+    gvgeocoord    angle;
+    gvgeocoord    scale;
+
+    int       width;
+    int       height;
+    int       descent;
+
+    int       anchor; /* GLRA_ * */
+
+    gboolean  shadow;
+    gboolean  halo;
+
+} GvLabelRenderPart;
+
+#define GLRA_LOWER_LEFT             1
+#define GLRA_LOWER_CENTER       2
+#define GLRA_LOWER_RIGHT        3
+#define GLRA_CENTER_LEFT                4
+#define GLRA_CENTER_CENTER              5
+#define GLRA_CENTER_RIGHT               6
+#define GLRA_UPPER_LEFT                 7
+#define GLRA_UPPER_CENTER               8
+#define GLRA_UPPER_RIGHT                9
+
+typedef struct
+{
+    /* From GvRenderPart */
+    guint         next_part;
+
+    /* Specific to GvSymbolRenderPart */
+    GvColor       color;
+    gchar        *symbol_id;
+    gvgeocoord    scale;
+    gvgeocoord    angle;
+
+    gvgeocoord    x_offset_g;
+    gvgeocoord    y_offset_g;
+    gvgeocoord    x_offset_px;
+    gvgeocoord    y_offset_px;
+
+    /* for tracking if the color was actually initialized
+       as part of the symbol definition */
+    int           b_color_initialized;
+
+    int           part_index; /* for vector symbols */
+
+} GvSymbolRenderPart;
+
+typedef struct
+{
+    /* From GvRenderPart */
+    guint         next_part;
+
+    /* Specific to GvPenRenderPart */
+    GvColor       color;
+    /* for tracking if the color was actually initialized
+       as part of the pen definition */
+    int           b_color_initialized;
+
+    float         width;
+    gchar        *pattern;
+} GvPenRenderPart;
+
+typedef struct
+{
+    /* From GvRenderPart */
+    guint         next_part;
+
+    /* Specific to GvBrushRenderPart */
+    GvColor   fore_color;
+    /* for tracking if the color was actually initialized
+       as part of the brush definition */
+    int           b_fore_color_initialized;
+} GvBrushRenderPart;
+
+/* -------------------------------------------------------------------- */
+/*      Functions for operating on rendering info within                */
+/*      GvShapeLayer, really an extension to GvShapeLayer API.          */
+/* -------------------------------------------------------------------- */
+GvRenderPart *gv_shape_layer_get_part( GvShapeLayer *layer, guint part_index );
+guint gv_shape_layer_get_first_part_index( GvShapeLayer *layer, gint shape_id);
+
+guint gv_shape_layer_add_part( GvShapeLayer *layer, gint shape_id,
+                               gint part_type );
+guint gv_shape_layer_create_part( GvShapeLayer *layer, gint part_type );
+guint gv_shape_layer_chain_part( GvShapeLayer *layer, gint base_part_index,
+                                 gint new_part_index );
+void gv_shape_layer_clear_shape_parts( GvShapeLayer *layer, gint shape_id );
+void gv_shape_layer_clear_part( GvShapeLayer *layer, guint part_index );
+void gv_shape_layer_clear_all_renderinfo( GvShapeLayer *layer );
+
+guint gv_shape_layer_build_renderinfo( GvShapeLayer *s_layer,
+                                       GvShape *shape_obj,
+                                       int *scale_dep );
+void gv_shape_layer_update_renderinfo( GvShapeLayer *s_layer, int shape_id );
+void gv_shape_layer_initialize_renderindex( GvShapeLayer *layer );
+
+/* -------------------------------------------------------------------- */
+/*      Helper functions for ogrfs parsing ... used in a few other      */
+/*      places.                                                         */
+/* -------------------------------------------------------------------- */
+char **gv_ogrfs_split_tools( const char *tool_list_in );
+const char *gv_ogrfs_get_arg( const char *def, char **next_def,
+                              char **value, int *value_len );
+
+
+
+#endif /*__GV_RENDER_INFO_H__ */
+
+
+

Added: packages/openev/branches/upstream/current/gvroitool.c
===================================================================
--- packages/openev/branches/upstream/current/gvroitool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvroitool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,605 @@
+/******************************************************************************
+ * $Id: gvroitool.c,v 1.11 2002/11/04 21:42:06 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Region of interest (box in raster coordinates) editing mode.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvroitool.c,v $
+ * Revision 1.11  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.10  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.9  2000/06/30 18:04:55  srawlin
+ * added ability to set ROI constraints
+ *
+ * Revision 1.8  2000/06/30 14:05:36  srawlin
+ * fixed bug where ROI selected then zoom in
+ *
+ * Revision 1.7  2000/06/29 21:18:10  srawlin
+ * Added CHANGED and CHANGING Signals
+ *
+ * Revision 1.6  2000/06/29 16:15:55  srawlin
+ * added function to create new ROI
+ *
+ * Revision 1.5  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvroitool.h"
+#include <gtk/gtksignal.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <stdio.h>
+
+#define PICK_SIZE 6.0
+
+/* Return values for gv_roi_tool_pick_border() */
+enum
+{
+    PICK_NONE = 0,
+    PICK_CORNER_TOPLEFT,
+    PICK_CORNER_BOTTOMLEFT,
+    PICK_CORNER_BOTTOMRIGHT,
+    PICK_CORNER_TOPRIGHT,
+    PICK_SIDE_TOP,
+    PICK_SIDE_RIGHT,
+    PICK_SIDE_BOTTOM,
+    PICK_SIDE_LEFT
+};
+
+/* Signals */
+enum
+{
+    ROI_CHANGED,
+    ROI_CHANGING,
+    LAST_SIGNAL
+};
+
+static void gv_roi_tool_class_init(GvRoiToolClass *klass);
+static void gv_roi_tool_init(GvRoiTool *tool);
+static void gv_roi_tool_draw(GvTool *tool);
+static void gv_roi_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_roi_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_roi_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_roi_tool_deactivate(GvTool *tool, GvViewArea *view);
+static void gv_roi_tool_start_resize(GvRoiTool *tool, gint pick, gvgeocoord pointer_x, gvgeocoord pointer_y);
+static gint gv_roi_tool_pick_border(GvRoiTool *tool, gvgeocoord x, gvgeocoord y);
+
+static guint roitool_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_roi_tool_get_type(void)
+{
+    static GtkType roi_tool_type = 0;
+
+    if (!roi_tool_type)
+    {
+	static const GtkTypeInfo roi_tool_info =
+	{
+	    "GvRoiTool",
+	    sizeof(GvRoiTool),
+	    sizeof(GvRoiToolClass),
+	    (GtkClassInitFunc) gv_roi_tool_class_init,
+	    (GtkObjectInitFunc) gv_roi_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	roi_tool_type = gtk_type_unique(gv_tool_get_type(),
+					&roi_tool_info);
+    }
+    return roi_tool_type;
+}
+
+static void
+gv_roi_tool_class_init(GvRoiToolClass *klass)
+{
+    GvToolClass *tool_class;
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+    tool_class = (GvToolClass*)klass;
+
+    roitool_signals[ROI_CHANGED] =
+	gtk_signal_new ("roi_changed",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvRoiToolClass,roi_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    roitool_signals[ROI_CHANGING] =
+	gtk_signal_new ("roi_changing",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvRoiToolClass,roi_changed),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 0 );
+
+    gtk_object_class_add_signals(object_class, roitool_signals, LAST_SIGNAL);
+    
+    klass->roi_changed = NULL;
+    klass->roi_changing = NULL;
+
+    tool_class->deactivate = gv_roi_tool_deactivate;
+    tool_class->draw = gv_roi_tool_draw;
+    tool_class->button_press = gv_roi_tool_button_press;
+    tool_class->button_release = gv_roi_tool_button_release;
+    tool_class->motion_notify = gv_roi_tool_motion_notify;
+}
+static void
+gv_roi_tool_init(GvRoiTool *tool)
+{
+    tool->pick = PICK_NONE;
+    
+    tool->roi_marked = FALSE;
+    tool->banding = FALSE;
+}
+
+GvTool *
+gv_roi_tool_new(void)
+{
+    GvTool *tool;
+
+    tool = GV_TOOL(gtk_type_new(GV_TYPE_ROI_TOOL));
+
+    
+
+    return tool;
+    
+}
+
+gint
+gv_roi_tool_get_rect(GvRoiTool *tool, GvRect *rect)
+{
+    if (!tool->roi_marked)
+    {
+	return FALSE;
+    }
+
+    rect->x = MIN(tool->v_head.x, tool->v_tail.x);
+    rect->y = MIN(tool->v_head.y, tool->v_tail.y);
+    rect->width = tool->v_tail.x - tool->v_head.x;
+    rect->height = tool->v_tail.y - tool->v_head.y;
+    rect->width = ABS(rect->width);
+    rect->height = ABS(rect->height);
+
+    return TRUE;
+}
+
+gint
+gv_roi_tool_new_rect(GvRoiTool *tool, GvRect *rect)
+{
+    /* Create new ROI */
+    tool->roi_marked = TRUE;
+    
+    tool->v_head.x = rect->x;
+    tool->v_head.y = rect->y;
+    tool->v_tail.x = rect->x + ABS(rect->width);
+    tool->v_tail.y = rect->y + ABS(rect->height);
+
+    gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                             &(tool->v_head.x), &(tool->v_head.y) );
+    gv_tool_clamp_to_bounds( GV_TOOL(tool), 
+                             &(tool->v_tail.x), &(tool->v_tail.y) );
+
+    /* Check input parameters */
+    if (rect->width <=0 || rect->height <= 0)
+    {
+        tool->roi_marked = FALSE;
+        return FALSE;
+    }
+
+    gtk_signal_emit(GTK_OBJECT(tool), 
+                    roitool_signals[ROI_CHANGED]);
+
+    gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+
+    return TRUE;
+}
+
+/**************************************************************/
+
+static void
+gv_roi_tool_button_press(GvTool *rtool, GdkEventButton *event)
+{
+    GvRoiTool *tool = GV_ROI_TOOL(rtool);
+
+    if ((event->button == 1)  && !(event->state & GDK_CONTROL_MASK)
+                              && !(event->state & GDK_SHIFT_MASK) )
+    {
+	if (tool->roi_marked)
+	{
+	    /* Check for contact with ROI border */
+	    gint pick;
+	    gvgeocoord pointer_x, pointer_y;
+
+	    pick = gv_roi_tool_pick_border(tool, event->x, event->y);
+
+	    if (pick != PICK_NONE)		
+	    {
+		/* Start ROI resize dragging operation */
+		gv_view_area_map_pointer(GV_TOOL(tool)->view,
+					 event->x, event->y,
+					 &pointer_x, &pointer_y);
+		gv_roi_tool_start_resize(tool, pick, pointer_x, pointer_y);
+		return;
+	    }
+	}
+
+	/* Set head and tail vertex to pointer position */
+	/* Map pointer position */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+	tool->v_head = tool->v_tail;
+
+        if( gv_tool_check_bounds( GV_TOOL(tool), 
+                                  tool->v_tail.x, tool->v_tail.y ) )
+        {
+            /* Begin rubber band */
+            tool->banding = TRUE;
+            tool->roi_marked = TRUE;
+
+            /* No drag offset for initial rubber banding */
+            tool->v_drag_offset.x = tool->v_drag_offset.y = 0.0;
+            tool->drag_right = tool->drag_bottom = TRUE;
+        }
+    }
+}
+
+static void
+gv_roi_tool_button_release(GvTool *rtool, GdkEventButton *event)
+{
+    GvRoiTool *tool = GV_ROI_TOOL(rtool);
+
+    if (event->button == 1 && tool->banding)
+    {
+	gvgeocoord pointer_x, pointer_y;
+	
+	/* End rubber band */
+	tool->banding = FALSE;
+
+	/* Map pointer position */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &pointer_x, &pointer_y);
+
+	/* Reposition tail vertex */
+	if (tool->drag_right)
+	{
+	    tool->v_tail.x = pointer_x + tool->v_drag_offset.x;
+	}
+	if (tool->drag_bottom)
+	{
+	    tool->v_tail.y = pointer_y + tool->v_drag_offset.y;
+	}
+
+        gv_tool_clamp_to_bounds( rtool, 
+                                 &(tool->v_tail.x), &(tool->v_tail.y) );
+
+	/* Reject empty regions */
+	if (tool->v_tail.x == tool->v_head.x ||
+	    tool->v_tail.y == tool->v_head.y)
+	{
+	    tool->roi_marked = FALSE;
+	}
+	
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+
+        gtk_signal_emit(GTK_OBJECT(tool), 
+                        roitool_signals[ROI_CHANGED]);
+    }
+}
+
+static void
+gv_roi_tool_motion_notify(GvTool *rtool, GdkEventMotion *event)
+{
+    GvRoiTool *tool = GV_ROI_TOOL(rtool);
+
+    if (tool->banding)
+    {
+	gvgeocoord pointer_x, pointer_y;
+	
+	/* Resize rubber band */
+	/* Map pointer position */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &pointer_x, &pointer_y);
+
+	/* Reposition tail vertex */
+	if (tool->drag_right)
+	{
+	    tool->v_tail.x = pointer_x + tool->v_drag_offset.x;
+	}
+	if (tool->drag_bottom)
+	{
+	    tool->v_tail.y = pointer_y + tool->v_drag_offset.y;
+	}
+
+        gv_tool_clamp_to_bounds( rtool, 
+                                 &(tool->v_tail.x), &(tool->v_tail.y) );
+
+        gtk_signal_emit(GTK_OBJECT(tool), 
+                        roitool_signals[ROI_CHANGING]);	
+
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);	
+    }
+    else if (tool->roi_marked)
+    {
+	/* Highlight roi sides to indicate the effect of a click & drag */
+	gint pick;
+
+	pick = gv_roi_tool_pick_border(tool, event->x, event->y);
+	if (pick != tool->pick)
+	{
+	    tool->pick = pick;
+	    gv_view_area_queue_draw(GV_TOOL(tool)->view);
+	}
+    }
+}
+
+static void
+gv_roi_tool_draw(GvTool *rtool)
+{
+    GvRoiTool *tool = GV_ROI_TOOL(rtool);
+
+    if (tool->roi_marked)
+    {
+	glColor3f(1.0, 0.5, 0.0);
+	glBegin(GL_LINE_LOOP);
+	glVertex2(tool->v_head.x, tool->v_head.y);
+	glVertex2(tool->v_head.x, tool->v_tail.y);
+	glVertex2(tool->v_tail.x, tool->v_tail.y);
+	glVertex2(tool->v_tail.x, tool->v_head.y);
+	glEnd();
+
+	if (tool->pick != PICK_NONE)
+	{
+	    glColor3f(1.0, 0.0, 0.0);
+	    glBegin(GL_LINES);
+	    switch (tool->pick)
+	    {
+		case PICK_CORNER_TOPLEFT:
+		    glVertex2(tool->v_tail.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_tail.y);
+		    break;
+
+		case PICK_CORNER_BOTTOMLEFT:
+		    glVertex2(tool->v_head.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_tail.y);
+		    glVertex2(tool->v_head.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_tail.y);
+		    break;
+
+		case PICK_CORNER_BOTTOMRIGHT:
+		    glVertex2(tool->v_head.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_head.y);
+		    break;
+
+		case PICK_CORNER_TOPRIGHT:
+		    glVertex2(tool->v_tail.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_head.y);
+		    glVertex2(tool->v_tail.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_head.y);
+		    break;		    
+		
+		case PICK_SIDE_TOP:
+		    glVertex2(tool->v_tail.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_head.y);
+		    break;
+
+		case PICK_SIDE_RIGHT:
+		    glVertex2(tool->v_tail.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_head.y);
+		    break;
+
+		case PICK_SIDE_BOTTOM:
+		    glVertex2(tool->v_head.x, tool->v_tail.y);
+		    glVertex2(tool->v_tail.x, tool->v_tail.y);
+		    break;
+
+		case PICK_SIDE_LEFT:
+		    glVertex2(tool->v_head.x, tool->v_head.y);
+		    glVertex2(tool->v_head.x, tool->v_tail.y);
+		    break;
+	    }
+	    glEnd();
+	}
+    }
+}
+
+static void
+gv_roi_tool_deactivate(GvTool *rtool, GvViewArea *view)
+{
+    GvRoiTool *tool = GV_ROI_TOOL(rtool);
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+
+    tool->roi_marked = FALSE;
+    tool->banding = FALSE;
+
+    gv_view_area_queue_draw(view);
+}
+
+static void
+gv_roi_tool_start_resize(GvRoiTool *tool, gint pick, gvgeocoord pointer_x,
+			 gvgeocoord pointer_y)
+{
+    gvgeocoord temp;
+#define SWAP(a,b) {temp=a; a=b; b=temp;}
+    
+    /* Reposition the head and tail vertexes so we are always dragging
+       the bottom right corner */
+    switch (pick)
+    {
+	case PICK_CORNER_TOPLEFT:
+	case PICK_SIDE_TOP:
+	case PICK_SIDE_LEFT:
+	    /* Swap head and tail */
+	    SWAP(tool->v_head.x, tool->v_tail.x);
+	    SWAP(tool->v_head.y, tool->v_tail.y);
+	    break;
+
+	case PICK_CORNER_TOPRIGHT:
+	    /* Swap y coords only */
+	    SWAP(tool->v_head.y, tool->v_tail.y);
+	    break;
+
+	case PICK_CORNER_BOTTOMLEFT:
+	    /* Swap x coords only */
+	    SWAP(tool->v_head.x, tool->v_tail.x);
+	    break;
+    }
+
+    /* Set the drag flags for side or corner dragging */
+    switch (pick)
+    {
+	case PICK_SIDE_TOP:
+	case PICK_SIDE_BOTTOM:
+	    tool->drag_right = FALSE;
+	    tool->drag_bottom = TRUE;
+	    tool->pick = PICK_SIDE_BOTTOM;
+	    break;
+
+	case PICK_SIDE_LEFT:
+	case PICK_SIDE_RIGHT:
+	    tool->drag_right = TRUE;
+	    tool->drag_bottom = FALSE;
+	    tool->pick = PICK_SIDE_RIGHT;
+	    break;
+
+	default:
+	    tool->drag_right = TRUE;
+	    tool->drag_bottom = TRUE;
+	    tool->pick = PICK_CORNER_BOTTOMRIGHT;
+	    break;
+    }
+
+    /* Set the drag offset vector */
+    tool->v_drag_offset.x = tool->v_tail.x - pointer_x;
+    tool->v_drag_offset.y = tool->v_tail.y - pointer_y;
+
+    /* Enable dragging */
+    tool->banding = TRUE;
+
+#undef SWAP    
+}
+
+static gint
+gv_roi_tool_pick_border(GvRoiTool *tool, gvgeocoord x, gvgeocoord y)
+{
+    GvViewArea *view;
+    GLuint buf[16];
+    GLint hits;
+    GLint vp[4];
+
+    /* FIXME: need to make the view area current GL context */
+
+    view = GV_TOOL(tool)->view;
+    vp[0] = vp[1]  = 0;
+    vp[2] = (GLint)view->state.shape_x;
+    vp[3] = (GLint)view->state.shape_y;
+    
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    gluPickMatrix(x, vp[3]-y, PICK_SIZE, PICK_SIZE, vp);
+    gluOrtho2D(-vp[2]/2.0, vp[2]/2.0, -vp[3]/2.0, vp[3]/2.0);
+
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    glRotate(view->state.rot, 0.0, 0.0, 1.0);
+    glScale(view->state.linear_zoom * view->state.flip_x,
+	     view->state.linear_zoom * view->state.flip_y, 1.0);
+    glTranslate(view->state.tx, view->state.ty, 0.0);
+
+    glSelectBuffer(16, buf);
+    glRenderMode(GL_SELECT);
+
+    glInitNames();
+    glPushName(-1);
+
+    /* Top */
+    glLoadName(0);
+    glBegin(GL_LINES);
+    glVertex2(tool->v_head.x, tool->v_head.y);
+    glVertex2(tool->v_tail.x, tool->v_head.y);
+    glEnd();
+
+    /* Right */
+    glLoadName(1);
+    glBegin(GL_LINES);
+    glVertex2(tool->v_tail.x, tool->v_head.y);
+    glVertex2(tool->v_tail.x, tool->v_tail.y);
+    glEnd();
+
+    /* Bottom */
+    glLoadName(2);
+    glBegin(GL_LINES);
+    glVertex2(tool->v_tail.x, tool->v_tail.y);
+    glVertex2(tool->v_head.x, tool->v_tail.y);
+    glEnd();
+
+    /* Left */
+    glLoadName(3);
+    glBegin(GL_LINES);
+    glVertex2(tool->v_head.x, tool->v_tail.y);
+    glVertex2(tool->v_head.x, tool->v_head.y);
+    glEnd();
+
+    hits = glRenderMode(GL_RENDER);
+    
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+
+    if (hits > 1)
+    {
+	/* We've picked a corner */
+	if (buf[3] == 0 && buf[7] == 1)
+	    return PICK_CORNER_TOPRIGHT;
+	if (buf[3] == 1)
+	    return PICK_CORNER_BOTTOMRIGHT;
+	if (buf[3] == 2)
+	    return PICK_CORNER_BOTTOMLEFT;
+	if (buf[3] == 0 && buf[7] == 3)
+	    return PICK_CORNER_TOPLEFT;
+    }
+    else if (hits == 1)
+    {
+	/* We've picked a side */
+	return PICK_SIDE_TOP + buf[3];
+    }
+
+    /* No hits */
+    return PICK_NONE;
+}

Added: packages/openev/branches/upstream/current/gvroitool.h
===================================================================
--- packages/openev/branches/upstream/current/gvroitool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvroitool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,87 @@
+/******************************************************************************
+ * $Id: gvroitool.h,v 1.8 2000/07/27 20:06:23 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Region of interest (box in raster coordinates) editing mode.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvroitool.h,v $
+ * Revision 1.8  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.7  2000/06/30 18:04:57  srawlin
+ * added ability to set ROI constraints
+ *
+ * Revision 1.6  2000/06/29 21:18:08  srawlin
+ * Added CHANGED and CHANGING Signals
+ *
+ * Revision 1.5  2000/06/29 16:15:57  srawlin
+ * added function to create new ROI
+ *
+ * Revision 1.4  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_ROI_TOOL_H__
+#define __GV_ROI_TOOL_H__
+
+#include "gvtypes.h"
+#include "gvtool.h"
+
+#define GV_TYPE_ROI_TOOL            (gv_roi_tool_get_type ())
+#define GV_ROI_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_ROI_TOOL, GvRoiTool))
+#define GV_ROI_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_ROI_TOOL, GvRoiToolClass))
+#define GV_IS_ROI_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_ROI_TOOL))
+#define GV_IS_ROI_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_ROI_TOOL))
+
+typedef struct _GvRoiTool       GvRoiTool;
+typedef struct _GvRoiToolClass  GvRoiToolClass;
+
+struct _GvRoiTool
+{
+    GvTool tool;
+
+    gint pick;
+    
+    gint roi_marked : 1;
+    gint banding : 1;
+    gint drag_right : 1;
+    gint drag_bottom : 1;
+    GvVertex v_head, v_tail, v_drag_offset;
+};
+
+struct _GvRoiToolClass
+{
+    GvToolClass parent_class;
+
+    void (* roi_changed)(GvRoiTool *tool);
+    void (* roi_changing)(GvRoiTool *tool);
+};
+
+GtkType gv_roi_tool_get_type(void);
+GvTool* gv_roi_tool_new(void);
+
+gint gv_roi_tool_get_rect(GvRoiTool *tool, GvRect *rect);
+gint gv_roi_tool_new_rect(GvRoiTool *tool, GvRect *rect);
+
+#endif /* __GV_ROI_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvrotatetool.c
===================================================================
--- packages/openev/branches/upstream/current/gvrotatetool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrotatetool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,756 @@
+/******************************************************************************
+ * $Id: gvrotatetool.c,v 1.2 2003/06/25 17:07:22 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Rotation and Scaling editing mode in GvShapesLayer.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrotatetool.c,v $
+ * Revision 1.2  2003/06/25 17:07:22  warmerda
+ * moved a bunch of stuff to gvshape.c
+ *
+ * Revision 1.1  2003/06/25 16:40:44  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include "gvrotatetool.h"
+#include "gvundo.h"
+#include "cpl_error.h"
+#include <GL/gl.h>
+#include <math.h>
+
+static void gv_rotate_tool_class_init(GvRotateToolClass *klass);
+static void gv_rotate_tool_init(GvRotateTool *tool);
+static void gv_rotate_tool_draw(GvRotateTool *tool);
+static void gv_rotate_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_rotate_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_rotate_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_rotate_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_rotate_tool_deactivate(GvTool *tool, GvViewArea *view);
+static gint gv_rotate_tool_configure(GvRotateTool *tool);
+
+#define HEAD_SIZE 6
+#define ARROW_SIZE 45
+
+enum
+{
+    RRMODE_DISPLAY,
+    RRMODE_ROTATE, 
+    RRMODE_SCALE,
+    RRMODE_ROTATESCALE
+};
+
+#ifndef M_PI
+#  define M_PI 3.14159265358979323846
+#endif
+
+GtkType
+gv_rotate_tool_get_type(void)
+{
+    static GtkType rotate_tool_type = 0;
+
+    if (!rotate_tool_type)
+    {
+	static const GtkTypeInfo rotate_tool_info =
+	{
+	    "GvRotateTool",
+	    sizeof(GvRotateTool),
+	    sizeof(GvRotateToolClass),
+	    (GtkClassInitFunc) gv_rotate_tool_class_init,
+	    (GtkObjectInitFunc) gv_rotate_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	rotate_tool_type = gtk_type_unique(gv_tool_get_type(),
+					 &rotate_tool_info);
+    }
+    return rotate_tool_type;
+}
+
+static void
+gv_rotate_tool_class_init(GvRotateToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_rotate_tool_deactivate;
+    tool_class->button_press = gv_rotate_tool_button_press;
+    tool_class->button_release = gv_rotate_tool_button_release;
+    tool_class->motion_notify = gv_rotate_tool_motion_notify;
+    tool_class->key_press = gv_rotate_tool_key_press;
+}
+
+static void
+gv_rotate_tool_init(GvRotateTool *tool)
+{
+    GV_TOOL(tool)->cursor = gdk_cursor_new(GDK_TCROSS);
+    tool->layer = NULL;
+    tool->named_layer = NULL;
+    tool->rrmode = RRMODE_DISPLAY;
+    tool->shape_id = -1;
+    tool->rotation = 0.0;
+    tool->scaling = 1.0;
+    tool->original = NULL;
+}
+
+GvTool *
+gv_rotate_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_ROTATE_TOOL));
+}
+
+static gint gv_rotate_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvRotateTool *tool = (GvRotateTool *) data;
+
+    if( tool->layer == GV_SHAPES_LAYER(layer) )
+        gv_rotate_tool_set_layer( tool, NULL );
+    
+    return 0;
+}
+
+/************************************************************************/
+/*                      gv_rotate_tool_terminate()                      */
+/*                                                                      */
+/*      This breaks out of a non-display mode, restores the original    */
+/*      shape and generally restores a sane state.                      */
+/************************************************************************/
+
+void 
+gv_rotate_tool_terminate( GvRotateTool *tool )
+{
+    if( tool->rrmode == RRMODE_DISPLAY )
+        return;
+
+    if( tool->original != NULL 
+        && gv_shapes_get_shape( tool->layer->data, tool->shape_id) != NULL )
+    {
+        gv_shapes_replace_shapes( tool->layer->data, 1, &(tool->shape_id), 
+                                  &(tool->original), FALSE );
+        tool->original = NULL;
+        gv_undo_enable();
+        gv_undo_open();
+    }
+    else if( tool->original != NULL )
+    {
+        gv_shape_delete( tool->original );
+        tool->original = NULL;
+        gv_undo_enable();
+        gv_undo_open();
+    }
+
+    tool->shape_id = -1;
+    tool->rrmode = RRMODE_DISPLAY;
+    gv_view_area_queue_draw(GV_TOOL(tool)->view);
+}
+
+/************************************************************************/
+/*                      gv_rotate_tool_set_layer()                      */
+/************************************************************************/
+void
+gv_rotate_tool_set_layer(GvRotateTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	g_warning("gv_rotate_tool_set_layer(): inactive tool");
+	return;
+    }
+
+    if( layer != NULL && gv_data_is_read_only( GV_DATA(layer) ) )
+    {
+        g_warning( "gv_rotate_tool_set_layer(): layer is read-only" );
+        return;
+    }
+
+    gv_rotate_tool_terminate( tool );
+    tool->shape_id = -1;
+
+    /* Disconnect from the previous layer (for draw) */
+    if (tool->layer)
+    {
+        tool->rrmode = RRMODE_DISPLAY;
+
+        /** TODO: Not sure that we need to unselect ... try to remove later */
+
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gtk_signal_disconnect_by_data(GTK_OBJECT(tool->layer), (gpointer)tool);
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+ 
+    if( layer == NULL )
+        tool->layer = NULL;
+    else
+        tool->layer = GV_SHAPES_LAYER(layer);
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+	
+        /* Redraw when the layer draws */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "draw",
+					GTK_SIGNAL_FUNC(gv_rotate_tool_draw),
+					GTK_OBJECT(tool));
+        /* Recover if layer destroyed */
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy", 
+            GTK_SIGNAL_FUNC(gv_rotate_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+/************************************************************************/
+/*                   gv_rotate_tool_set_named_layer()                   */
+/************************************************************************/
+
+void
+gv_rotate_tool_set_named_layer(GvRotateTool *tool, gchar *name)
+{
+    if (tool->named_layer)
+    {
+	g_free(tool->named_layer);
+	tool->named_layer = NULL;
+    }
+    if (name)
+    {
+	tool->named_layer = g_strdup(name);	
+    }
+    /* Tool layer will be updated next time it is configured */
+}
+
+/************************************************************************/
+/*                    gv_rotate_tool_setup_arrows()                     */
+/*                                                                      */
+/*      This function will compute the pivot location for the           */
+/*      selected shape, and the corresponding up and right vectors      */
+/*      for drawing the rotate/resize arrows.                           */
+/************************************************************************/
+
+static gint gv_rotate_tool_setup_arrows( GvRotateTool *tool )
+
+{
+    GvVertex3d pivot_3d;
+    GvShape *shape = gv_shapes_get_shape( tool->layer->data, 
+                                          tool->shape_id );
+
+    if( shape == NULL )
+    {
+        CPLDebug( "OpenEV", "gv_rotate_tool_setup_arrows(), shape==NULL!" );
+        tool->shape_id = -1;
+        return 0;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute the pivot location.                                     */
+/* -------------------------------------------------------------------- */
+    if( !gv_shape_get_center( shape, &pivot_3d ) )
+        return 0;
+
+    tool->v_pivot.x = pivot_3d.x;
+    tool->v_pivot.y = pivot_3d.y;
+
+/* -------------------------------------------------------------------- */
+/*      Compute the up vector.                                          */
+/* -------------------------------------------------------------------- */
+    gv_view_area_correct_for_transform( GV_TOOL(tool)->view, 0.0, 1.0, 
+                                        &(tool->v_up.x),
+                                        &(tool->v_up.y) );
+    gv_view_area_correct_for_transform( GV_TOOL(tool)->view, 1.0, 0.0, 
+                                        &(tool->v_right.x),
+                                        &(tool->v_right.y) );
+
+    tool->rotation = 0.0;
+    tool->scaling = 1.0;
+
+    return 1;
+}
+
+/************************************************************************/
+/*                    gv_rotate_tool_classify_hit()                     */
+/*                                                                      */
+/*      Does the mouse location in geo-coordinates hit near the head    */
+/*      of the rotate or resize tool?                                   */
+/************************************************************************/
+
+int gv_rotate_tool_classify_hit( GvRotateTool *tool, gvgeocoord x, gvgeocoord y )
+
+{
+    gvgeocoord x_focus, y_focus, x_ul, y_ul, x_lr, y_lr;
+
+    x_focus = tool->v_pivot.x + tool->v_up.x * ARROW_SIZE;
+    y_focus = tool->v_pivot.y + tool->v_up.y * ARROW_SIZE;
+
+    x_ul = x_focus - tool->v_right.x * (HEAD_SIZE*2+3) - tool->v_up.x*HEAD_SIZE;
+    y_ul = y_focus - tool->v_right.y * (HEAD_SIZE*2+3) - tool->v_up.y*HEAD_SIZE;
+    x_lr = x_focus + tool->v_right.x * (HEAD_SIZE*2+3) + tool->v_up.x*HEAD_SIZE;
+    y_lr = y_focus + tool->v_right.y * (HEAD_SIZE*2+3) + tool->v_up.y*HEAD_SIZE;
+
+    if( x >= MIN(x_ul,x_lr) && x <= MAX(x_ul,x_lr)
+        && y >= MIN(y_ul,y_lr) && y <= MAX(y_ul,y_lr) )
+    {
+        return RRMODE_ROTATE;
+    }
+
+    x_focus = tool->v_pivot.x + tool->v_right.x * ARROW_SIZE;
+    y_focus = tool->v_pivot.y + tool->v_right.y * ARROW_SIZE;
+
+    x_ul = x_focus - tool->v_right.x * HEAD_SIZE - tool->v_up.x * HEAD_SIZE;
+    y_ul = y_focus - tool->v_right.y * HEAD_SIZE - tool->v_up.y * HEAD_SIZE;
+    x_lr = x_focus + tool->v_right.x * 3 + tool->v_up.x * HEAD_SIZE;
+    y_lr = y_focus + tool->v_right.y * 3 + tool->v_up.y * HEAD_SIZE;
+
+    if( x >= MIN(x_ul,x_lr) && x <= MAX(x_ul,x_lr)
+        && y >= MIN(y_ul,y_lr) && y <= MAX(y_ul,y_lr) )
+    {
+        return RRMODE_SCALE;
+    }
+
+    return RRMODE_DISPLAY;
+}
+
+/************************************************************************/
+/*                        gv_rotate_tool_draw()                         */
+/*                                                                      */
+/*      Draw callback invoked by system after all regular layers are    */
+/*      drawn.                                                          */
+/************************************************************************/
+
+static void
+gv_rotate_tool_draw(GvRotateTool *tool)
+{
+/* -------------------------------------------------------------------- */
+/*      If we have a selected shape, we need to draw the                */
+/*      rotate/resize arrow handles.                                    */
+/* -------------------------------------------------------------------- */
+    if (tool->shape_id != -1 )
+    {
+        GvVertex v_up, v_right;
+        double rad_rot;
+
+        /*
+        ** In display mode recompute the pivot, up and right vector each
+        ** redraw. 
+        */
+        if( tool->rrmode == RRMODE_DISPLAY )
+        {
+            if( !gv_rotate_tool_setup_arrows( tool ) )
+                return;
+        }
+
+        /* 
+        ** Apply scaling and rotation.
+        */
+
+        rad_rot = (tool->rotation / 180.0) * M_PI;
+        v_up.x = tool->v_up.x*cos(rad_rot) + tool->v_up.y*sin(rad_rot);
+        v_up.y = - tool->v_up.x*sin(rad_rot) + tool->v_up.y*cos(rad_rot);
+        v_right.x = tool->v_right.x*cos(rad_rot) + tool->v_right.y*sin(rad_rot);
+        v_right.y = -tool->v_right.x*sin(rad_rot) + tool->v_right.y*cos(rad_rot);
+
+        v_up.x *= tool->scaling;
+        v_up.y *= tool->scaling;
+        v_right.x *= tool->scaling;
+        v_right.y *= tool->scaling;
+
+        glColor4f( 1.0, 0.0, 0.0, 1.0 );
+
+        if( tool->rrmode != RRMODE_SCALE )
+        {
+            /** Upward line with left/right rotate arrow heads **/
+            glBegin( GL_LINES );
+            glVertex2( tool->v_pivot.x, tool->v_pivot.y );
+            glVertex2( tool->v_pivot.x + v_up.x * ARROW_SIZE, 
+                       tool->v_pivot.y + v_up.y * ARROW_SIZE );
+
+            /* left pointing arrow */
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * ARROW_SIZE
+                       - v_right.x * HEAD_SIZE*2, 
+                       tool->v_pivot.y 
+                       + v_up.y * ARROW_SIZE
+                       - v_right.y * HEAD_SIZE*2 );
+            glVertex2( tool->v_pivot.x + v_up.x * ARROW_SIZE, 
+                       tool->v_pivot.y + v_up.y * ARROW_SIZE );
+
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * ARROW_SIZE
+                       - v_right.x * HEAD_SIZE*2, 
+                       tool->v_pivot.y 
+                       + v_up.y * ARROW_SIZE
+                       - v_right.y * HEAD_SIZE*2 );
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * (ARROW_SIZE+HEAD_SIZE)
+                       - v_right.x * HEAD_SIZE, 
+                       tool->v_pivot.y 
+                       + v_up.y * (ARROW_SIZE+HEAD_SIZE)
+                       - v_right.y * HEAD_SIZE );
+
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * ARROW_SIZE
+                       - v_right.x * HEAD_SIZE*2, 
+                       tool->v_pivot.y 
+                       + v_up.y * ARROW_SIZE
+                       - v_right.y * HEAD_SIZE*2 );
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * (ARROW_SIZE-HEAD_SIZE)
+                       - v_right.x * HEAD_SIZE, 
+                       tool->v_pivot.y 
+                       + v_up.y * (ARROW_SIZE-HEAD_SIZE)
+                       - v_right.y * HEAD_SIZE );
+
+            /* rightward  pointing arrow */
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * ARROW_SIZE
+                       + v_right.x * HEAD_SIZE*2, 
+                       tool->v_pivot.y 
+                       + v_up.y * ARROW_SIZE
+                       + v_right.y * HEAD_SIZE*2 );
+            glVertex2( tool->v_pivot.x + v_up.x * ARROW_SIZE, 
+                       tool->v_pivot.y + v_up.y * ARROW_SIZE );
+
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * ARROW_SIZE
+                       + v_right.x * HEAD_SIZE*2, 
+                       tool->v_pivot.y 
+                       + v_up.y * ARROW_SIZE
+                       + v_right.y * HEAD_SIZE*2 );
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * (ARROW_SIZE+HEAD_SIZE)
+                       + v_right.x * HEAD_SIZE, 
+                       tool->v_pivot.y 
+                       + v_up.y * (ARROW_SIZE+HEAD_SIZE)
+                       + v_right.y * HEAD_SIZE );
+
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * ARROW_SIZE
+                       + v_right.x * HEAD_SIZE*2, 
+                       tool->v_pivot.y 
+                       + v_up.y * ARROW_SIZE
+                       + v_right.y * HEAD_SIZE*2 );
+            glVertex2( tool->v_pivot.x 
+                       + v_up.x * (ARROW_SIZE-HEAD_SIZE)
+                       + v_right.x * HEAD_SIZE, 
+                       tool->v_pivot.y 
+                       + v_up.y * (ARROW_SIZE-HEAD_SIZE)
+                       + v_right.y * HEAD_SIZE );
+
+            glEnd();
+        }
+
+        if( tool->rrmode != RRMODE_ROTATE )
+        {
+
+            /** Rightward arrow for resizing. **/
+            glBegin( GL_LINES );
+            glVertex2( tool->v_pivot.x, tool->v_pivot.y );
+            glVertex2( tool->v_pivot.x + v_right.x * ARROW_SIZE, 
+                       tool->v_pivot.y + v_right.y * ARROW_SIZE );
+
+            glVertex2( tool->v_pivot.x 
+                       + v_right.x * (ARROW_SIZE-HEAD_SIZE)
+                       + v_up.x * HEAD_SIZE, 
+                       tool->v_pivot.y 
+                       + v_right.y * (ARROW_SIZE-HEAD_SIZE)
+                       + v_up.y * HEAD_SIZE );
+            glVertex2( tool->v_pivot.x + v_right.x * ARROW_SIZE, 
+                       tool->v_pivot.y + v_right.y * ARROW_SIZE );
+
+            glVertex2( tool->v_pivot.x 
+                       + v_right.x * (ARROW_SIZE-HEAD_SIZE)
+                       - v_up.x * HEAD_SIZE, 
+                       tool->v_pivot.y 
+                       + v_right.y * (ARROW_SIZE-HEAD_SIZE)
+                       - v_up.y * HEAD_SIZE );
+            glVertex2( tool->v_pivot.x + v_right.x * ARROW_SIZE, 
+                       tool->v_pivot.y + v_right.y * ARROW_SIZE );
+
+            glEnd();
+        }
+    }
+}
+
+/************************************************************************/
+/*                    gv_rotate_tool_button_press()                     */
+/************************************************************************/
+
+static void
+gv_rotate_tool_button_press(GvTool *r_tool, GdkEventButton *event)
+{
+    GvRotateTool *tool = GV_ROTATE_TOOL(r_tool);
+
+    if( event->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK) )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Have we selected an active control point on the scale/rotate    */
+/*      dohickey?                                                       */
+/* -------------------------------------------------------------------- */
+    if( tool->rrmode == RRMODE_DISPLAY && tool->shape_id != -1 )
+    {
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_head.x, &tool->v_head.y);
+
+        /*
+        ** Is this location a hit on an arrow head?
+        */
+        tool->rrmode = 
+            gv_rotate_tool_classify_hit( tool, tool->v_head.x, tool->v_head.y );
+
+        /*
+        ** Copy the original state of this shape, and disable undo till we are
+        ** done.
+        */
+        if( tool->rrmode != RRMODE_DISPLAY )
+        {
+            if( event->button != 1 )
+                tool->rrmode = RRMODE_ROTATESCALE;
+
+            tool->original = gv_shape_copy(
+                gv_shapes_get_shape( tool->layer->data, tool->shape_id ));
+
+            gv_undo_close();
+            gv_undo_disable();
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Are we selecting a shape?  Note, currently we cannot clear      */
+/*      our selection in resize/rotate mode - should we be able to?     */
+/* -------------------------------------------------------------------- */
+    if (event->button == 1 && tool->rrmode == RRMODE_DISPLAY )
+    {
+        int        shape_id;
+
+	if (!gv_rotate_tool_configure(tool)) return;
+
+	if (gv_shape_layer_pick_shape(GV_SHAPE_LAYER(tool->layer), 
+                                      GV_TOOL(tool)->view,
+				      event->x, event->y, &shape_id))
+        {
+            GvShape *shape;
+
+            /* Is the shape rotatable? */
+            shape = gv_shapes_get_shape( tool->layer->data, shape_id );
+
+            if( TRUE )
+            {
+                gv_shape_layer_clear_selection(
+                    GV_SHAPE_LAYER(tool->layer));
+                gv_shape_layer_select_shape(
+                    GV_SHAPE_LAYER(tool->layer), shape_id);
+                tool->shape_id = shape_id;
+                gv_view_area_queue_draw(GV_TOOL(tool)->view);
+            }
+        }
+        return;
+    }
+
+}
+
+/************************************************************************/
+/*                   gv_rotate_tool_button_release()                    */
+/************************************************************************/
+
+static void
+gv_rotate_tool_button_release(GvTool *r_tool, GdkEventButton *event)
+{
+    GvRotateTool *tool = GV_ROTATE_TOOL(r_tool);
+
+    if( tool->rrmode == RRMODE_DISPLAY )
+        return;
+
+    /* Put back original shape. */
+    gv_shapes_replace_shapes( tool->layer->data, 1, &(tool->shape_id), 
+                              &(tool->original), TRUE );
+    
+    /* re-enable undo */
+    gv_undo_enable();
+    gv_undo_open();
+
+    /* Apply rotation or scaling to working shape. */
+    if( tool->rrmode == RRMODE_SCALE 
+        || tool->rrmode == RRMODE_ROTATESCALE )
+        gv_shape_scale( tool->original, tool->scaling );
+
+    if( tool->rrmode == RRMODE_ROTATE 
+        || tool->rrmode == RRMODE_ROTATESCALE )
+        gv_shape_rotate( tool->original, tool->rotation );
+
+    /* Apply to the shapes object ... this will be undoable */
+    gv_shapes_replace_shapes( tool->layer->data, 1, &(tool->shape_id), 
+                              &(tool->original), TRUE );
+    tool->original = NULL;
+        
+    tool->rrmode = RRMODE_DISPLAY;
+}
+
+/************************************************************************/
+/*                    gv_rotate_tool_motion_notify()                    */
+/************************************************************************/
+static void
+gv_rotate_tool_motion_notify(GvTool *r_tool, GdkEventMotion *event)
+{
+    GvRotateTool *tool = GV_ROTATE_TOOL(r_tool);
+    GvShape  *wrk_shape;
+
+    if (tool->rrmode == RRMODE_DISPLAY )
+        return;
+
+
+    wrk_shape = gv_shape_copy(tool->original);
+
+    /* Map pointer position to tail vertex */
+    gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+                             &tool->v_tail.x, &tool->v_tail.y);
+
+    /* Compute rotation from base. */
+    if( tool->rrmode == RRMODE_ROTATE 
+        || tool->rrmode == RRMODE_ROTATESCALE )
+    {
+        double dx = tool->v_tail.x - tool->v_pivot.x;
+        double dy = tool->v_tail.y - tool->v_pivot.y;
+
+        if( fabs(dy) < fabs(dx)/1000000.0 )
+            tool->rotation = M_PI/2;
+        else
+            tool->rotation = atan(fabs(dx/dy));
+
+
+        if( dy < 0.0 && dx >= 0.0 )
+            tool->rotation = M_PI - tool->rotation;
+        else if( dy >= 0.0 && dx < 0.0 )
+            tool->rotation = -tool->rotation;
+        else if( dy < 0.0 && dx < 0.0 )
+            tool->rotation -= M_PI;
+
+
+        tool->rotation = (tool->rotation / M_PI) * 180;
+
+        gv_shape_rotate( wrk_shape, tool->rotation );
+    }
+
+    /* Compute Scaling from base. */
+    if( tool->rrmode == RRMODE_SCALE 
+        || tool->rrmode == RRMODE_ROTATESCALE )
+    {
+        double dx, dy, old_length, new_length;
+
+        dx = tool->v_tail.x - tool->v_pivot.x;
+        dy = tool->v_tail.y - tool->v_pivot.y;
+        new_length = sqrt(dx*dx + dy*dy);
+        
+        dx = tool->v_head.x - tool->v_pivot.x;
+        dy = tool->v_head.y - tool->v_pivot.y;
+        old_length = sqrt(dx*dx + dy*dy);
+
+        tool->scaling = new_length / old_length;
+
+        gv_shape_scale( wrk_shape, tool->scaling );
+    }
+    
+    /* Apply to the shapes object ... this will create an undo step */
+    gv_shapes_replace_shapes( tool->layer->data, 1, &(tool->shape_id), 
+                              &wrk_shape, FALSE );
+
+    gv_view_area_queue_draw(GV_TOOL(tool)->view);
+}
+
+/************************************************************************/
+/*                      gv_rotate_tool_key_press()                      */
+/************************************************************************/
+
+static void
+gv_rotate_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvRotateTool *tool = GV_ROTATE_TOOL(rtool);
+
+    if (!gv_rotate_tool_configure(tool)) return;
+
+    switch (event->keyval)
+    {
+	case GDK_Delete:
+	case GDK_BackSpace:
+	case GDK_Escape:
+          if( tool->rrmode != RRMODE_DISPLAY )
+          {
+              gv_rotate_tool_terminate( tool );
+          }
+          break;
+    }
+}
+
+static void
+gv_rotate_tool_deactivate(GvTool *r_tool, GvViewArea *view)
+{
+    GvRotateTool *tool = GV_ROTATE_TOOL(r_tool);
+
+    /* terminate any active modes */
+    gv_rotate_tool_terminate( tool );
+
+    /* Disconnect from layer */
+    if (tool->layer)
+    {
+	gv_rotate_tool_set_layer(tool, NULL);
+    }
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+}
+
+static gint
+gv_rotate_tool_configure(GvRotateTool *tool)
+{
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	if (tool->named_layer)
+	{
+	    /* Look for named layer if given */
+	    layer = gv_view_area_get_named_layer(GV_TOOL(tool)->view,
+						 tool->named_layer);
+	}
+	else
+	{
+            layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+                                                   GV_TYPE_SHAPES_LAYER,
+                                                   FALSE);
+	}
+
+	if (!layer)
+	{
+	    g_warning("gv_rotate_tool_configure(): no shapes layer in view");
+	    return FALSE;
+	}
+
+	gv_rotate_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+    return tool->layer != NULL;
+}

Added: packages/openev/branches/upstream/current/gvrotatetool.h
===================================================================
--- packages/openev/branches/upstream/current/gvrotatetool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvrotatetool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,82 @@
+/******************************************************************************
+ * $Id: gvrotatetool.h,v 1.1 2003/06/25 16:40:44 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Rotation and Scaling editing mode.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvrotatetool.h,v $
+ * Revision 1.1  2003/06/25 16:40:44  warmerda
+ * New
+ *
+ */
+
+#ifndef __GV_ROTATE_TOOL_H__
+#define __GV_ROTATE_TOOL_H__
+
+#include "gvtool.h"
+#include "gvshapeslayer.h"
+
+#define GV_TYPE_ROTATE_TOOL            (gv_rotate_tool_get_type ())
+#define GV_ROTATE_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_ROTATE_TOOL, GvRotateTool))
+#define GV_ROTATE_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_ROTATE_TOOL, GvRotateToolClass))
+#define GV_IS_ROTATE_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_ROTATE_TOOL))
+#define GV_IS_ROTATE_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_ROTATE_TOOL))
+
+typedef struct _GvRotateTool       GvRotateTool;
+typedef struct _GvRotateToolClass  GvRotateToolClass;
+
+struct _GvRotateTool
+{
+    GvTool tool;
+
+    GvShapesLayer *layer;
+    gchar *named_layer;
+
+    GvVertex v_pivot;
+    GvVertex v_up, v_right;
+
+    GvVertex v_head, v_tail;
+
+    int   rrmode;   /* current mode */
+    int   shape_id; /* shapeid of rectangle being reshaped */
+
+    double rotation;
+    double scaling;
+
+    GvShape *original;
+};
+
+struct _GvRotateToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_rotate_tool_get_type(void);
+GvTool* gv_rotate_tool_new(void);
+void gv_rotate_tool_set_layer(GvRotateTool *tool, GvShapeLayer *layer);
+void gv_rotate_tool_set_named_layer(GvRotateTool *tool, gchar *name);
+
+#endif /* __GV_ROTATE_TOOL_H__ */
+
+
+

Added: packages/openev/branches/upstream/current/gvselecttool.c
===================================================================
--- packages/openev/branches/upstream/current/gvselecttool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvselecttool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,445 @@
+/******************************************************************************
+ * $Id: gvselecttool.c,v 1.17 2003/09/16 15:43:11 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Shape selection tool.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvselecttool.c,v $
+ * Revision 1.17  2003/09/16 15:43:11  gmwalter
+ * Add single selection mode to select tool, checked in developer tutorials.
+ *
+ * Revision 1.16  2002/11/04 21:42:06  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.15  2002/01/18 04:53:28  warmerda
+ * fixed shift-leftclick to add/subtract from selection
+ *
+ * Revision 1.14  2001/10/23 02:43:45  pgs
+ * changed deactivate to call base class last
+ *
+ * Revision 1.13  2001/05/07 19:08:03  warmerda
+ * draw text with origin off viewport properly
+ *
+ * Revision 1.12  2001/04/09 18:16:22  warmerda
+ * honour read-only layers
+ *
+ * Revision 1.11  2000/08/23 16:48:00  warmerda
+ * don't drag select box if control/shift pressed
+ *
+ * Revision 1.10  2000/08/10 18:33:46  warmerda
+ * removed a warning
+ *
+ * Revision 1.9  2000/08/08 20:58:47  warmerda
+ * recover from layer destruction
+ *
+ * Revision 1.8  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.7  2000/06/29 14:38:58  warmerda
+ * don't emit a warning if gvselecttool configure without layers
+ *
+ * Revision 1.6  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <string.h>
+#include "gvselecttool.h"
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include <GL/gl.h>
+
+static void gv_selection_tool_class_init(GvSelectionToolClass *klass);
+static void gv_selection_tool_init(GvSelectionTool *tool);
+static void gv_selection_tool_draw(GvTool *tool);
+static void gv_selection_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_selection_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_selection_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_selection_tool_key_press(GvTool *tool, GdkEventKey *event);
+static void gv_selection_tool_deactivate(GvTool *tool, GvViewArea *view);
+static gint gv_selection_tool_configure(GvSelectionTool *tool);
+
+GtkType
+gv_selection_tool_get_type(void)
+{
+    static GtkType selection_tool_type = 0;
+
+    if (!selection_tool_type)
+    {
+	static const GtkTypeInfo selection_tool_info =
+	{
+	    "GvSelectionTool",
+	    sizeof(GvSelectionTool),
+	    sizeof(GvSelectionToolClass),
+	    (GtkClassInitFunc) gv_selection_tool_class_init,
+	    (GtkObjectInitFunc) gv_selection_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	selection_tool_type = gtk_type_unique(gv_tool_get_type(),
+					      &selection_tool_info);
+    }
+    return selection_tool_type;
+}
+
+static void
+gv_selection_tool_class_init(GvSelectionToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_selection_tool_deactivate;
+    tool_class->button_press = gv_selection_tool_button_press;
+    tool_class->button_release = gv_selection_tool_button_release;
+    tool_class->motion_notify = gv_selection_tool_motion_notify;
+    tool_class->key_press = gv_selection_tool_key_press;
+}
+static void
+gv_selection_tool_init(GvSelectionTool *tool)
+{
+    tool->layer = NULL;
+    tool->banding = FALSE;
+    tool->dragging = FALSE;
+}
+
+GvTool *
+gv_selection_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_SELECTION_TOOL));
+}
+
+static gint gv_selection_tool_layer_destroy( GtkObject *layer, gpointer data )
+
+{
+    GvSelectionTool *tool = (GvSelectionTool *) data;
+
+    if( tool->layer == GV_SHAPE_LAYER(layer) )
+        gv_selection_tool_set_layer( tool, NULL );
+
+    return 0;
+}
+
+void
+gv_selection_tool_set_layer(GvSelectionTool *tool, GvShapeLayer *layer)
+{
+    if (GV_TOOL(tool)->view == NULL)
+    {
+	return;
+    }
+
+    /* Disconnect from the previous layer (for draw) */
+    if (tool->layer)
+    {
+	gv_shape_layer_clear_selection(GV_SHAPE_LAYER(tool->layer));
+	gtk_signal_disconnect_by_data(GTK_OBJECT(tool->layer), (gpointer)tool);
+    }
+
+    tool->layer = layer;
+
+    if (layer)
+    {
+	gv_view_area_set_active_layer(GV_TOOL(tool)->view, GTK_OBJECT(layer));
+
+        /* Redraw when the layer draws */
+	gtk_signal_connect_object_after(GTK_OBJECT(layer), "draw",
+					GTK_SIGNAL_FUNC(gv_selection_tool_draw),
+					GTK_OBJECT(tool));
+
+        /* Recover if layer is destroyed */
+        gtk_signal_connect(
+            GTK_OBJECT(layer), "destroy",
+            GTK_SIGNAL_FUNC(gv_selection_tool_layer_destroy),
+            GTK_OBJECT(tool));
+    }
+}
+
+/**************************************************************/
+
+static void
+gv_selection_tool_button_press(GvTool *rtool, GdkEventButton *event)
+{
+    GvSelectionTool *tool = GV_SELECTION_TOOL(rtool);
+
+    /* ignore control corded buttons -- these are for zooming and panning */
+    if( event->state & GDK_CONTROL_MASK )
+        return;
+
+    if (event->button == 1)
+    {
+	gint shape_id;
+
+	if (!gv_selection_tool_configure(tool)) return;
+
+	if (gv_shape_layer_pick_shape(tool->layer, GV_TOOL(tool)->view,
+				      event->x, event->y, &shape_id))
+	{
+	    if ((event->state & GDK_SHIFT_MASK) &&
+                ((gv_data_get_property( GV_DATA(tool->layer),
+                 "selection_mode" ) == NULL) ||
+                (strcmp(gv_data_get_property( GV_DATA(tool->layer),
+                 "selection_mode" ),"single") != 0)))
+	    {
+		/* Shift click (de)selects without clearing other selections */
+		if (gv_shape_layer_is_selected(tool->layer, shape_id))
+		{
+		    gv_shape_layer_deselect_shape(tool->layer, shape_id);
+		}
+		else
+		{
+		    gv_shape_layer_select_shape(tool->layer, shape_id);
+		}
+	    }
+	    else if (!(event->state & GDK_SHIFT_MASK))
+	    {
+		/* Non-shift click */
+		if (!gv_shape_layer_is_selected(tool->layer, shape_id))
+		{
+		    /* Select this shape (and only this shape) */
+		    gv_shape_layer_clear_selection(tool->layer);
+		    gv_shape_layer_select_shape(tool->layer, shape_id);
+		}
+
+                if( !gv_data_is_read_only( GV_DATA(tool->layer) ) )
+                {
+                    /* Start a drag operation */
+                    tool->dragging = TRUE;
+
+                    /* Capture pointer position */
+                    gv_view_area_map_pointer(GV_TOOL(tool)->view,
+                                             event->x, event->y,
+                                             &tool->v_tail.x, &tool->v_tail.y);
+                    tool->v_head = tool->v_tail;
+
+                    /* Delay drawing the selected shapes */
+                    gv_shape_layer_draw_selected(tool->layer, GV_LATER, NULL);
+                }
+	    }
+	}
+        else if ((event->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == 0 )
+        {
+            if ((gv_data_get_property( GV_DATA(tool->layer),
+                 "selection_mode" ) != NULL) &&
+                (strcmp(gv_data_get_property( GV_DATA(tool->layer),
+                         "selection_mode" ),"single") == 0))
+            {
+  	        gv_shape_layer_clear_selection(tool->layer);
+            }
+  	    else 
+	    {
+	        /* Begin rubber band */
+	        gv_shape_layer_clear_selection(tool->layer);
+
+	        /* Capture pointer position */
+	        tool->v_tail.x = event->x - GV_TOOL(tool)->view->state.tx;
+	        tool->v_tail.y = (GV_TOOL(tool)->view->state.shape_y - 
+                                  event->y) - GV_TOOL(tool)->view->state.ty;
+	        tool->v_head = tool->v_tail;
+
+  	        tool->banding = TRUE;
+	    }
+        }
+  	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+}
+
+static void
+gv_selection_tool_button_release(GvTool *rtool, GdkEventButton *event)
+{
+    GvSelectionTool *tool = GV_SELECTION_TOOL(rtool);
+
+    if (tool->banding && event->button == 1)
+    {
+	/* End rubber band */
+	gvgeocoord ax, ay, bx, by;
+	GvRect rect;
+	GvViewAreaState *state = &GV_TOOL(tool)->view->state;
+
+	tool->banding = FALSE;
+
+	/* Get view center coords for rubber band */
+	ax = tool->v_head.x + state->tx - state->shape_x/2.0;
+	ay = tool->v_head.y + state->ty - state->shape_y/2.0;
+	bx = event->x - state->shape_x/2.0;
+	by = state->shape_y/2.0 - event->y;
+
+	rect.x = MIN(ax, bx);
+	rect.y = MIN(ay, by);
+	rect.width = ABS(ax - bx);
+	rect.height = ABS(ay - by);
+
+	/* Pick shapes in region */
+	gv_shape_layer_select_region(tool->layer, GV_TOOL(tool)->view, &rect);
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+    else if (tool->dragging && event->button == 1)
+    {
+	/* End dragging */
+	tool->dragging = FALSE;
+	gv_shape_layer_draw_selected(tool->layer, GV_ALWAYS, NULL);
+
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+
+	/* Filter out clicks that wern't really drags */
+	if (tool->v_tail.x != tool->v_head.x ||
+	    tool->v_tail.y != tool->v_head.y)
+	{
+	    /* Translate shapes */
+	    gv_shape_layer_translate_selected(tool->layer,
+					      tool->v_tail.x - tool->v_head.x,
+					      tool->v_tail.y - tool->v_head.y);
+	}
+    }
+}
+
+static void
+gv_selection_tool_motion_notify(GvTool *rtool, GdkEventMotion *event)
+{
+    GvSelectionTool *tool = GV_SELECTION_TOOL(rtool);
+
+    if (tool->banding)
+    {
+	/* Resize rubber band */
+	/* Capture pointer position to tail vertex */
+	tool->v_tail.x = event->x - GV_TOOL(tool)->view->state.tx;
+	tool->v_tail.y = (GV_TOOL(tool)->view->state.shape_y - event->y) -
+	    GV_TOOL(tool)->view->state.ty;
+
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+    else if (tool->dragging)
+    {
+	/* Drag selected shapes */
+	/* Map pointer position to tail vertex */
+	gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+				 &tool->v_tail.x, &tool->v_tail.y);
+	gv_view_area_queue_draw(GV_TOOL(tool)->view);
+    }
+}
+
+static void
+gv_selection_tool_key_press(GvTool *rtool, GdkEventKey *event)
+{
+    GvSelectionTool *tool = GV_SELECTION_TOOL(rtool);
+
+    if (!gv_selection_tool_configure(tool)) return;
+
+    switch (event->keyval)
+    {
+      case GDK_Delete:
+      case GDK_BackSpace:
+        /* Delete the currently selected lines (forces redraw) */
+        if( !gv_data_is_read_only( GV_DATA(tool->layer) ) )
+            gv_shape_layer_delete_selected(tool->layer);
+        break;
+    }
+}
+
+static void
+gv_selection_tool_draw(GvTool *rtool)
+{
+    GvSelectionTool *tool = GV_SELECTION_TOOL(rtool);
+
+    if (tool->banding)
+    {
+	/* Resolve head and tail positions in view coordinates */
+	GvViewAreaState *state = &GV_TOOL(tool)->view->state;
+
+	/* Draw the rubber band rectangle */
+	glColor3f(1.0, 0.0, 0.0);
+	glPushMatrix();
+	glLoadIdentity();
+	glTranslate(state->tx - state->shape_x/2.0,
+		     state->ty - state->shape_y/2.0, 0.0);
+	glBegin(GL_LINE_LOOP);
+	glVertex2(tool->v_head.x, tool->v_head.y);
+	glVertex2(tool->v_head.x, tool->v_tail.y);
+	glVertex2(tool->v_tail.x, tool->v_tail.y);
+	glVertex2(tool->v_tail.x, tool->v_head.y);
+	glEnd();
+	glPopMatrix();
+    }
+    else if (tool->dragging)
+    {
+	gvgeocoord dx, dy;
+
+	/* Inform layer of shape motion */
+	dx = tool->v_tail.x - tool->v_head.x;
+	dy = tool->v_tail.y - tool->v_head.y;
+	gv_shape_layer_selected_motion(tool->layer, dx, dy);
+
+	/* Draw selected objects translated */
+	glPushMatrix();
+	glTranslate(dx, dy, 0.0);
+	gv_shape_layer_draw_selected(tool->layer, GV_NOW,
+				     GV_TOOL(tool)->view);
+	glPopMatrix();
+	gv_shape_layer_selected_motion(tool->layer, 0.0, 0.0 );
+    }
+}
+
+static void
+gv_selection_tool_deactivate(GvTool *rtool, GvViewArea *view)
+{
+    GvSelectionTool *tool = GV_SELECTION_TOOL(rtool);
+
+
+    if (tool->layer)
+    {
+	if (tool->dragging)
+	{
+	    gv_shape_layer_draw_selected(tool->layer, GV_ALWAYS, NULL);
+	}
+	gv_shape_layer_clear_selection(tool->layer);
+	gv_view_area_queue_draw(view);
+        gv_selection_tool_set_layer(tool, NULL);
+    }
+    tool->banding = FALSE;
+    tool->dragging = FALSE;
+
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+}
+
+static gint
+gv_selection_tool_configure(GvSelectionTool *tool)
+{
+    /* Check that we still are working on the active layer */
+    if (!tool->layer ||	GTK_OBJECT(tool->layer) !=
+	gv_view_area_active_layer(GV_TOOL(tool)->view))
+    {
+	GtkObject *layer;
+
+	/* Attempt to find a line layer to edit */
+	layer = gv_view_area_get_layer_of_type(GV_TOOL(tool)->view,
+					       GV_TYPE_SHAPE_LAYER,
+                                               TRUE);
+	if (!layer)
+	    return FALSE;
+
+	gv_selection_tool_set_layer(tool, GV_SHAPE_LAYER(layer));
+    }
+    return TRUE;
+}

Added: packages/openev/branches/upstream/current/gvselecttool.h
===================================================================
--- packages/openev/branches/upstream/current/gvselecttool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvselecttool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * $Id: gvselecttool.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Shape selection tool.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvselecttool.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_SELECTION_TOOL_H__
+#define __GV_SELECTION_TOOL_H__
+
+#include "gvtool.h"
+#include "gvshapelayer.h"
+
+#define GV_TYPE_SELECTION_TOOL            (gv_selection_tool_get_type ())
+#define GV_SELECTION_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_SELECTION_TOOL, GvSelectionTool))
+#define GV_SELECTION_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_SELECTION_TOOL, GvSelectionToolClass))
+#define GV_IS_SELECTION_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_SELECTION_TOOL))
+#define GV_IS_SELECTION_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_SELECTION_TOOL))
+
+typedef struct _GvSelectionTool       GvSelectionTool;
+typedef struct _GvSelectionToolClass  GvSelectionToolClass;
+
+struct _GvSelectionTool
+{
+    GvTool tool;
+
+    GvShapeLayer *layer;
+    gint banding  : 1;
+    gint dragging : 1;
+    GvVertex v_head, v_tail;
+};
+
+struct _GvSelectionToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_selection_tool_get_type(void);
+GvTool* gv_selection_tool_new(void);
+void gv_selection_tool_set_layer(GvSelectionTool *tool, GvShapeLayer *layer);
+
+#endif /* __GV_SELECTION_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvshape.c
===================================================================
--- packages/openev/branches/upstream/current/gvshape.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshape.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1755 @@
+/******************************************************************************
+ * $Id: gvshape.c,v 1.21 2005/01/04 18:50:29 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvShape (point/line/area/collection vector object)
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshape.c,v $
+ * Revision 1.21  2005/01/04 18:50:29  gmwalter
+ * Checked in Aude's new gvshape function changes.
+ *
+ * Revision 1.20  2003/09/02 18:17:51  warmerda
+ * Added support for serializing collection shapes
+ *
+ * Revision 1.19  2003/08/29 20:52:43  warmerda
+ * added to/from xml translation for GvShape
+ *
+ * Revision 1.18  2003/06/26 02:46:21  pgs
+ * added define for M_PI if it is not already defined
+ *
+ * Revision 1.17  2003/06/25 17:06:06  warmerda
+ * added gv_shape_rotate(), gv_shape_scale() and related stuff
+ *
+ * Revision 1.16  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.15  2002/05/07 02:51:15  warmerda
+ * preliminary support for GVSHAPE_COLLECTION
+ *
+ * Revision 1.14  2002/03/07 18:31:56  warmerda
+ * added preliminary gv_shape_clip_to_rect() implementation
+ *
+ * Revision 1.13  2001/12/08 04:49:38  warmerda
+ * added point in polygon test
+ *
+ * Revision 1.12  2001/08/08 17:45:48  warmerda
+ * GvShape now referenced counted
+ *
+ * Revision 1.11  2000/09/11 13:54:33  warmerda
+ * fixed some bugs computing area of line features
+ *
+ * Revision 1.10  2000/09/11 13:47:24  warmerda
+ * fixed bug in computing extents of area features
+ *
+ * Revision 1.9  2000/07/14 14:51:01  warmerda
+ * fixed insert, and delete node support
+ *
+ * Revision 1.8  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+#include <assert.h>
+#include <string.h>
+#include "gvshapes.h"
+#include "gvrenderinfo.h"
+#include "cpl_string.h"
+#include "cpl_error.h"
+
+#ifndef M_PI
+#define M_PI       3.14159265358979323846
+#endif
+static int  gv_shape_count = 0;
+
+/************************************************************************/
+/*                         gv_shape_get_count()                         */
+/************************************************************************/
+
+int gv_shape_get_count()
+
+{
+    return gv_shape_count;
+}
+
+/************************************************************************/
+/*                            gv_shape_new()                            */
+/*                                                                      */
+/*      Note that the initial ref count of shapes returned by           */
+/*      gv_shape_new() is zero.  If the caller wants to retain a        */
+/*      reference to the shape (other than just adding to a             */
+/*      GvShapes, or some other operation that increments the ref       */
+/*      count) it should call gv_shape_ref().                           */
+/************************************************************************/
+
+GvShape *gv_shape_new( gint type )
+
+{
+    GvShape    *shape;
+
+    gv_shape_count++;
+
+    if( type == GVSHAPE_POINT )
+    {
+        shape = (GvShape *) g_new0( GvPointShape, 1 );
+    }
+    else if( type == GVSHAPE_LINE )
+    {
+        shape = (GvShape *) g_new0( GvLineShape, 1 );
+    }
+    else if( type == GVSHAPE_AREA )
+    {
+        shape = (GvShape *) g_new0( GvAreaShape, 1 );
+        ((GvAreaShape *) shape)->fill_objects = -1;
+    }
+    else if( type == GVSHAPE_COLLECTION )
+    {
+        shape = (GvShape *) g_new0( GvCollectionShape, 1 );
+    }
+    else
+    {
+        g_warning( "Illegal shape type in gv_shape_new()" );
+        shape = NULL;
+    }
+
+    if( shape != NULL )
+    {
+        shape->flags = type | 0;
+        shape->ref_count = 0;
+        gv_properties_init( &(shape->properties) );
+    }
+
+    return shape;
+}
+
+/************************************************************************/
+/*                            gv_shape_ref()                            */
+/************************************************************************/
+
+void gv_shape_ref( GvShape *shape )
+
+{
+    shape->ref_count++;
+}
+
+/************************************************************************/
+/*                           gv_shape_unref()                           */
+/************************************************************************/
+
+void gv_shape_unref( GvShape *shape )
+
+{
+    if( --shape->ref_count <= 0 )
+        gv_shape_delete( shape );
+}
+
+/************************************************************************/
+/*                          gv_shape_get_ref()                          */
+/************************************************************************/
+
+int gv_shape_get_ref( GvShape *shape )
+
+{
+    return shape->ref_count;
+}
+
+/************************************************************************/
+/*                          gv_shape_delete()                           */
+/************************************************************************/
+void gv_shape_delete( GvShape *shape )
+
+{
+    gv_shape_count--;
+
+    gv_properties_destroy( gv_shape_get_properties( shape ) );
+
+    switch( gv_shape_type(shape) )
+    {
+      case GVSHAPE_AREA:
+      {
+          GvAreaShape *area = (GvAreaShape *) shape;
+          int         ring;
+
+          for( ring = 0; ring < area->num_rings; ring++ )
+          {
+              g_free( area->xyz_ring_nodes[ring] );
+          }
+          if( area->xyz_ring_nodes != NULL )
+              g_free( area->xyz_ring_nodes );
+          if( area->num_ring_nodes != NULL )
+              g_free( area->num_ring_nodes );
+
+          if( area->fill )
+              g_array_free( area->fill, TRUE );
+          if( area->mode_offset )
+              g_array_free( area->mode_offset, TRUE );
+      }
+      break;
+
+      case GVSHAPE_LINE:
+      {
+          GvLineShape  *line = (GvLineShape *) shape;
+
+          if( line->xyz_nodes != NULL )
+              g_free( line->xyz_nodes );
+      }
+      break;
+
+      case GVSHAPE_COLLECTION:
+      {
+          GvCollectionShape  *col = (GvCollectionShape *) shape;
+          int                 i;
+
+          for( i = 0; i < col->geom_count; i++ )
+              gv_shape_delete( col->geom_list[i] );
+
+          if( col->geom_list != NULL )
+              g_free( col->geom_list );
+          col->geom_list = NULL;
+      }
+      break;
+
+      case GVSHAPE_POINT:
+      default:
+        break;
+    }
+
+    g_free( shape );
+}
+
+/************************************************************************/
+/*                           gv_shape_copy()                            */
+/************************************************************************/
+GvShape *gv_shape_copy( GvShape *source )
+
+{
+    GvShape   *target;
+
+    target = gv_shape_new( gv_shape_type(source) );
+
+    gv_properties_copy( &(source->properties), &(target->properties) );
+    target->flags = source->flags;
+
+    switch( gv_shape_type(source) )
+    {
+      case GVSHAPE_AREA:
+      {
+          GvAreaShape *tarea = (GvAreaShape *) target;
+          GvAreaShape *sarea = (GvAreaShape *) source;
+
+          tarea->num_rings = sarea->num_rings;
+          if( sarea->num_rings > 0 )
+          {
+              int    ring;
+
+              tarea->num_ring_nodes = g_new(int,sarea->num_rings);
+              memcpy( tarea->num_ring_nodes, sarea->num_ring_nodes,
+                      sizeof(int) * sarea->num_rings );
+
+              tarea->xyz_ring_nodes = g_new(gvgeocoord *,sarea->num_rings);
+              for( ring = 0; ring < sarea->num_rings; ring++ )
+              {
+                  tarea->xyz_ring_nodes[ring] =
+                      g_new(gvgeocoord,3*tarea->num_ring_nodes[ring]);
+                  memcpy( tarea->xyz_ring_nodes[ring],
+                          sarea->xyz_ring_nodes[ring],
+                          sizeof(gvgeocoord) * 3 * tarea->num_ring_nodes[ring] );
+              }
+          }
+      }
+      break;
+
+      case GVSHAPE_LINE:
+      {
+          int  num_nodes = ((GvLineShape *) source)->num_nodes;
+
+          ((GvLineShape *) target)->num_nodes = num_nodes;
+
+          ((GvLineShape *) target)->xyz_nodes = g_new(gvgeocoord, 3 * num_nodes);
+          memcpy( ((GvLineShape *) target)->xyz_nodes,
+                  ((GvLineShape *) source)->xyz_nodes,
+                  sizeof(gvgeocoord) * 3 * num_nodes );
+      }
+      break;
+
+      case GVSHAPE_POINT:
+        ((GvPointShape *) target)->x = ((GvPointShape *) source)->x;
+        ((GvPointShape *) target)->y = ((GvPointShape *) source)->y;
+        ((GvPointShape *) target)->z = ((GvPointShape *) source)->z;
+        break;
+
+      case GVSHAPE_COLLECTION:
+      {
+          GvCollectionShape *source_col = (GvCollectionShape *) source;
+          int i;
+
+          for( i = 0; i < source_col->geom_count; i++ )
+              gv_shape_collection_add_shape(
+                  target, gv_shape_copy( source_col->geom_list[i] ) );
+      }
+      break;
+
+      default:
+        assert(FALSE);
+    }
+
+    return target;
+}
+
+/************************************************************************/
+/*                         gv_shape_get_rings()                         */
+/************************************************************************/
+gint gv_shape_get_rings( GvShape *shape )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_AREA )
+        return ((GvAreaShape *) shape)->num_rings;
+    else
+        return 1;
+}
+
+/************************************************************************/
+/*                         gv_shape_get_nodes()                         */
+/************************************************************************/
+gint gv_shape_get_nodes( GvShape *shape, int ring )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_AREA
+        && ring >= 0 && ring < ((GvAreaShape *) shape)->num_rings )
+    {
+        return ((GvAreaShape *) shape)->num_ring_nodes[ring];
+    }
+    else if( ring == 0 && gv_shape_type(shape) == GVSHAPE_LINE )
+    {
+        return ((GvLineShape *) shape)->num_nodes;
+    }
+    else if( ring == 0 && gv_shape_type(shape) == GVSHAPE_POINT )
+        return 1;
+    else
+        return 0;
+}
+
+/************************************************************************/
+/*                          gv_shape_get_xyz()                          */
+/************************************************************************/
+gvgeocoord gv_shape_get_xyz( GvShape *shape, gint ring, gint node, gint off )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_AREA )
+    {
+        GvAreaShape *area = (GvAreaShape *) shape;
+
+        if( ring >= 0 && ring < area->num_rings
+            && node >= 0 && node < area->num_ring_nodes[ring] )
+            return area->xyz_ring_nodes[ring][node*3+off];
+        else
+            return 0.0;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_LINE )
+    {
+        GvLineShape *line = (GvLineShape *) shape;
+
+        if( ring == 0 && node >= 0 && node < line->num_nodes )
+            return line->xyz_nodes[node*3+off];
+        else
+            return 0.0;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_POINT )
+    {
+        GvPointShape *point = (GvPointShape *) shape;
+
+        if( ring != 0 || node != 0 )
+            return 0.0;
+        else if( off == 0 )
+            return point->x;
+        else if( off == 1 )
+            return point->y;
+        else if( off == 2 )
+            return point->z;
+    }
+
+    return 0.0;
+}
+
+/************************************************************************/
+/*                          gv_shape_set_xyz()                          */
+/************************************************************************/
+gint gv_shape_set_xyz( GvShape *shape, gint ring, gint node,
+                       gvgeocoord x, gvgeocoord y, gvgeocoord z )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_AREA )
+    {
+        GvAreaShape *area = (GvAreaShape *) shape;
+
+        if( ring < 0 || node < 0 )
+            return FALSE;
+
+        /* do we need to grow the ring list? */
+        if( ring >= area->num_rings )
+        {
+            area->num_ring_nodes =
+                g_renew(int, area->num_ring_nodes, ring+1 );
+            memset( area->num_ring_nodes + area->num_rings, 0,
+                    sizeof(int) * (ring + 1 - area->num_rings) );
+
+            area->xyz_ring_nodes =
+                g_renew(gvgeocoord *, area->xyz_ring_nodes, ring+1 );
+            memset( area->xyz_ring_nodes + area->num_rings, 0,
+                    sizeof(gvgeocoord *) * (ring + 1 - area->num_rings) );
+            area->num_rings = ring+1;
+        }
+
+        /* do we need to grow the list of nodes? */
+        if( area->num_ring_nodes[ring] <= node )
+        {
+            area->xyz_ring_nodes[ring] =
+                g_renew(gvgeocoord, area->xyz_ring_nodes[ring], 3*(node+1) );
+            memset( area->xyz_ring_nodes[ring] + 3*area->num_ring_nodes[ring],
+                    0, sizeof(gvgeocoord)*3*(node+1-area->num_ring_nodes[ring]) );
+            area->num_ring_nodes[ring] = node+1;
+        }
+
+        area->xyz_ring_nodes[ring][node*3  ] = x;
+        area->xyz_ring_nodes[ring][node*3+1] = y;
+        area->xyz_ring_nodes[ring][node*3+2] = z;
+
+        area->fill_objects = -1;
+
+        return TRUE;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_LINE )
+    {
+        GvLineShape *line = (GvLineShape *) shape;
+
+        if( ring != 0 || node < 0 )
+            return FALSE;
+
+        if( node >= line->num_nodes )
+        {
+            line->xyz_nodes = g_renew(gvgeocoord, line->xyz_nodes, 3*(node+1));
+
+            memset( line->xyz_nodes + line->num_nodes*3,
+                    0, (node + 1 - line->num_nodes) * 3 * sizeof(gvgeocoord) );
+
+            line->num_nodes = node+1;
+        }
+
+        line->xyz_nodes[node*3  ] = x;
+        line->xyz_nodes[node*3+1] = y;
+        line->xyz_nodes[node*3+2] = z;
+
+        return TRUE;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_POINT )
+    {
+        GvPointShape *point = (GvPointShape *) shape;
+
+        if( ring != 0 || node != 0 )
+            return FALSE;
+
+        point->x = x;
+        point->y = y;
+        point->z = z;
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/************************************************************************/
+/*                         gv_shape_add_node()                          */
+/************************************************************************/
+gint gv_shape_add_node( GvShape *shape, gint ring, gvgeocoord x, gvgeocoord y, gvgeocoord z )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_POINT )
+    {
+        gv_shape_set_xyz( shape, ring, 0, x, y, z );
+        return 0;
+    }
+    else
+    {
+        int   new_node = gv_shape_get_nodes( shape, ring );
+
+        gv_shape_set_xyz( shape, ring, new_node, x, y, z );
+
+        return new_node;
+    }
+}
+
+/************************************************************************/
+/*                        gv_shape_insert_node()                        */
+/************************************************************************/
+gint gv_shape_insert_node( GvShape *shape, gint ring, int node,
+                           gvgeocoord x, gvgeocoord y, gvgeocoord z )
+
+{
+    int     i;
+
+    if( node > gv_shape_get_nodes( shape, ring ) )
+        return -1;
+
+    for( i = gv_shape_get_nodes(shape,ring)-1; i >= node; i-- )
+    {
+        gv_shape_set_xyz( shape, ring, i+1,
+                          gv_shape_get_x( shape, ring, i ),
+                          gv_shape_get_y( shape, ring, i ),
+                          gv_shape_get_z( shape, ring, i ) );
+    }
+
+    gv_shape_set_xyz( shape, ring, node, x, y, z );
+
+    return node;
+}
+
+/************************************************************************/
+/*                        gv_shape_delete_ring()                        */
+/************************************************************************/
+gint gv_shape_delete_ring( GvShape *shape, gint ring )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_POINT && ring == 0 )
+    {
+        ((GvPointShape *) shape)->x = 0.0;
+        ((GvPointShape *) shape)->y = 0.0;
+        ((GvPointShape *) shape)->z = 0.0;
+        return TRUE;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_LINE && ring == 0 )
+    {
+        GvLineShape   *line = (GvLineShape *) shape;
+
+        line->num_nodes = 0;
+
+        return TRUE;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_AREA
+             && ring >= 0
+             && ring < gv_shape_get_rings(shape) )
+    {
+        GvAreaShape   *area = (GvAreaShape *) shape;
+
+        g_free( area->xyz_ring_nodes[ring] );
+        memmove( area->xyz_ring_nodes + ring,
+                 area->xyz_ring_nodes + ring + 1,
+                 sizeof(void*) * (area->num_rings - ring - 1) );
+        memmove( area->num_ring_nodes + ring,
+                 area->num_ring_nodes + ring + 1,
+                 sizeof(int) * (area->num_rings - ring - 1) );
+        area->fill_objects = -1;
+        area->num_rings--;
+        return TRUE;
+    }
+
+    return FALSE;
+
+}
+
+/************************************************************************/
+/*                        gv_shape_delete_node()                        */
+/************************************************************************/
+gint gv_shape_delete_node( GvShape *shape, gint ring, gint node )
+
+{
+    if( gv_shape_type(shape) == GVSHAPE_POINT && ring == 0 && node == 0 )
+    {
+        ((GvPointShape *) shape)->x = 0.0;
+        ((GvPointShape *) shape)->y = 0.0;
+        ((GvPointShape *) shape)->z = 0.0;
+        return TRUE;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_LINE && ring == 0
+             && node >= 0 && node < gv_shape_get_nodes(shape,ring) )
+    {
+        GvLineShape   *line = (GvLineShape *) shape;
+
+        memmove( line->xyz_nodes + node*3,
+                 line->xyz_nodes + (node+1)*3,
+                 sizeof(gvgeocoord) * 3 * (line->num_nodes - node - 1) );
+        line->num_nodes--;
+        return TRUE;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_AREA
+             && ring < gv_shape_get_rings(shape)
+             && node < gv_shape_get_nodes(shape,ring) )
+    {
+        GvAreaShape   *area = (GvAreaShape *) shape;
+
+        memmove( area->xyz_ring_nodes[ring] + node*3,
+                 area->xyz_ring_nodes[ring] + (node+1)*3,
+                sizeof(gvgeocoord) * 3 * (area->num_ring_nodes[ring] - node - 1) );
+        area->num_ring_nodes[ring]--;
+        area->fill_objects = -1;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/************************************************************************/
+/*                        gv_shape_get_extents()                        */
+/************************************************************************/
+void gv_shape_get_extents( GvShape *shape, GvRect *rect )
+
+{
+    rect->x = rect->y = rect->width = rect->height = 0.0;
+
+    if( gv_shape_type(shape) == GVSHAPE_POINT )
+    {
+        GvPointShape  *point = (GvPointShape *) shape;
+
+        rect->x = point->x;
+        rect->y = point->y;
+        rect->width = 0.0;
+        rect->height = 0.0;
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_LINE )
+    {
+        GvLineShape *line = (GvLineShape *) shape;
+        int         i;
+
+        if( line->num_nodes > 0 )
+        {
+            rect->x = line->xyz_nodes[0];
+            rect->y = line->xyz_nodes[1];
+            rect->width = 0.0;
+            rect->height = 0.0;
+        }
+
+        for( i = 1; i < line->num_nodes; i++ )
+        {
+            gvgeocoord     x = line->xyz_nodes[i*3  ];
+            gvgeocoord     y = line->xyz_nodes[i*3+1];
+
+            if( x < rect->x )
+            {
+                rect->width = rect->x + rect->width - x;
+                rect->x = x;
+            }
+            else if( x > rect->x + rect->width )
+            {
+                rect->width = x - rect->x;
+            }
+
+            if( y < rect->y )
+            {
+                rect->height = rect->y + rect->height - y;
+                rect->y = y;
+            }
+            else if( y > rect->y + rect->height )
+            {
+                rect->height = y - rect->y;
+            }
+        }
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_AREA )
+    {
+        GvAreaShape *area = (GvAreaShape *) shape;
+        int          ring, node, first = TRUE;
+
+        for( ring = 0; ring < area->num_rings; ring++ )
+        {
+            for( node = 0; node < area->num_ring_nodes[ring]; node++ )
+            {
+                gvgeocoord     x = area->xyz_ring_nodes[ring][node*3  ];
+                gvgeocoord     y = area->xyz_ring_nodes[ring][node*3+1];
+
+                if( first )
+                {
+                    rect->x = x;
+                    rect->width = 0;
+                    rect->y = y;
+                    rect->height = 0;
+                    first = FALSE;
+                }
+
+                if( x < rect->x )
+                {
+                    rect->width = rect->x + rect->width - x;
+                    rect->x = x;
+                }
+                else if( x > rect->x + rect->width )
+                {
+                    rect->width = x - rect->x;
+                }
+
+                if( y < rect->y )
+                {
+                    rect->height = rect->y + rect->height - y;
+                    rect->y = y;
+                }
+                else if( y > rect->y + rect->height )
+                {
+                    rect->height = y - rect->y;
+                }
+            }
+        }
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_COLLECTION )
+    {
+        GvCollectionShape *col = (GvCollectionShape *) shape;
+        int          geom;
+
+        for( geom = 0; geom < col->geom_count; geom++ )
+        {
+            if( geom == 0 )
+                gv_shape_get_extents( col->geom_list[geom], rect );
+            else
+            {
+                GvRect   this_rect;
+
+                gv_shape_get_extents( col->geom_list[geom], &this_rect );
+
+                if( this_rect.x < rect->x )
+                {
+                    rect->width += (rect->x - this_rect.x);
+                    rect->x = this_rect.x;
+                }
+                if( this_rect.y < rect->y )
+                {
+                    rect->height += (rect->y - this_rect.y);
+                    rect->y = this_rect.y;
+                }
+                if( this_rect.x + this_rect.width >
+                    rect->x + rect->width )
+                {
+                    rect->width = this_rect.x + this_rect.width - rect->x;
+                }
+                if( this_rect.y + this_rect.height >
+                    rect->y + rect->height )
+                {
+                    rect->height = this_rect.y + this_rect.height - rect->y;
+                }
+            }
+        }
+    }
+}
+
+/************************************************************************/
+/*                     gv_shape_point_in_polygon()                      */
+/*                                                                      */
+/*      Algorithm as presented by Darel R. Finley at                    */
+/*      http://freeweb.pdq.net/smokin/polygon/                          */
+/************************************************************************/
+
+gint gv_shape_point_in_polygon( GvShape *shape_poly, double x, double y )
+
+{
+    int      ring;
+    int      oddNODES = FALSE;
+    GvAreaShape *area = (GvAreaShape *) shape_poly;
+
+    if( gv_shape_type(shape_poly) != GVSHAPE_AREA )
+        return FALSE;
+
+    for( ring = 0; ring < area->num_rings; ring++ )
+    {
+        int edge;
+
+        for( edge = 0; edge < area->num_ring_nodes[ring]; edge++ )
+        {
+            int j;
+            gvgeocoord  *pt1, *pt2;
+
+            if( edge == area->num_ring_nodes[ring]-1 )
+                j = 0;
+            else
+                j = edge + 1;
+
+            pt1 = area->xyz_ring_nodes[ring] + edge * 3;
+            pt2 = area->xyz_ring_nodes[ring] + j * 3;
+
+            if( (pt1[1] < y && pt2[1] >= y)
+                || (pt2[1] < y && pt1[1] >= y) )
+            {
+                if( pt1[0] + (y-pt1[1]) / (pt2[1]-pt1[1]) * (pt2[0]-pt1[0])
+                    < x )
+                    oddNODES = !oddNODES;
+            }
+        }
+    }
+
+    return oddNODES;
+}
+
+/************************************************************************/
+/*                     gv_shape_distance_from_polygon()                      */
+/*                                                                      */
+/************************************************************************/
+gdouble gv_shape_distance_from_polygon( GvShape *shape_poly, double x, double y )
+
+{
+    int      ring;
+    int vertical, horizontal;
+    GvAreaShape *area = (GvAreaShape *) shape_poly;
+    gvgeocoord  *ptA, *ptB, ptD[2];
+    gdouble min_dist, dist, a,b,c;
+    
+    if ( gv_shape_type(shape_poly) != GVSHAPE_AREA )
+        return -1.;
+
+    /*initialize min distance*/
+    ptA = area->xyz_ring_nodes[0];    
+    min_dist = sqrt( pow((ptA[0]-x),2) + pow((ptA[1]-y),2));
+
+    for( ring = 0; ring < area->num_rings; ring++ )
+    {
+        int edge;
+
+        for( edge = 0; edge < area->num_ring_nodes[ring]; edge++ )
+        {
+            int j;
+
+            if( edge == area->num_ring_nodes[ring]-1 )
+                j = 0;
+            else
+                j = edge + 1;
+
+            ptA = area->xyz_ring_nodes[ring] + edge * 3;
+            ptB = area->xyz_ring_nodes[ring] + j * 3;
+
+	    vertical = ( fabs(ptA[0] - ptB[0]) < 1.e-10 );
+	    horizontal = ( fabs(ptA[1] - ptB[1]) < 1.e-10 );
+	    if (vertical && horizontal) 
+	      {
+		dist = sqrt( pow((ptA[0]-x),2) + pow((ptA[1]-y),2));
+		if ( dist < min_dist )
+		  min_dist  = dist;
+		continue;
+	      }
+	    else if ( vertical )
+	      {
+		ptD[0] = ptA[0];
+		ptD[1] = y;
+	      }
+	    else if ( horizontal )
+	      {
+		ptD[0] = x;
+		ptD[1] = ptA[0];
+	      }
+	    else
+	      {
+		/*Compute equation of line AB of the form y = ax + b */
+		a = (ptA[1] - ptB[1]) / ( ptA[0] - ptB[0] );
+		b = ptA[1] - a * ptA[0];
+
+		/*Given D the intersection of AB and the perpendicular to (AB) passing by C,
+		  compute equation of (CD) of the form y = -a x + c */
+		c = y + x / a;
+
+                /*Location of D*/
+		ptD[1] = (a*a * c + b) / (1. + a*a);
+		ptD[0] = (ptD[1] - b)/ a;
+
+	      }
+	    /* Make sure D is within segment AB, if not compute distance from A or B, whichever is the closet */
+	    if ( ptA[0] < ptB[0] )
+	      {
+		if ( ptD[0] < ptA[0] )
+		  {
+		    ptD[0] = ptA[0];
+		    ptD[1] = ptA[1];
+		  }
+
+		else if ( ptD[0] > ptB[0] )
+		  {
+		    ptD[0] = ptB[0];
+		    ptD[1] = ptB[1];
+		  }
+	      }
+	    else if ( ptB[0] < ptA[0] )
+	      {
+		if ( ptD[0] < ptB[0] )
+		  {
+		    ptD[0] = ptB[0];
+		    ptD[1] = ptB[1];
+		  }
+
+		else if ( ptD[0] > ptA[0] )
+		  {
+		    ptD[0] = ptA[0];
+		    ptD[1] = ptA[1];
+		  }
+	      }
+	    else if ( ptA[1] < ptB[1] )
+	      {
+		if ( ptD[1] < ptA[1] )
+		  {
+		    ptD[0] = ptA[0];
+		    ptD[1] = ptA[1];
+		  }
+		else if ( ptD[1] > ptB[1] )
+		  {
+		    ptD[0] = ptB[0];
+		    ptD[1] = ptB[1];
+		  }
+	      }
+	    else if ( ptB[1] < ptA[1] )
+	      {
+		if ( ptD[1] < ptB[1] )
+		  {
+		    ptD[0] = ptB[0];
+		    ptD[1] = ptB[1];
+		  }
+		else if ( ptD[1] > ptA[1] )
+		  {
+		    ptD[0] = ptA[0];
+		    ptD[1] = ptA[1];
+		  }
+	      }   
+
+
+
+	    dist = sqrt( pow((ptD[0]-x),2) + pow((ptD[1]-y),2));
+	    if ( dist < min_dist )
+	      min_dist  = dist;
+
+        }
+    }
+
+    return min_dist;
+}
+
+
+
+/************************************************************************/
+/*                           _gv_intersect()                            */
+/************************************************************************/
+
+static void _gv_intersect( double *x1, double *y1, double *x2, double *y2,
+                          double cut_line, int greater_flag )
+
+{
+    double  f;
+
+    if( !greater_flag && (*x1 < cut_line && *x2 < cut_line) )
+        return;
+    else if( greater_flag && (*x1 < cut_line && *x2 < cut_line) )
+        return;
+
+    if( !greater_flag )
+    {
+        *x1 = - *x1;
+        *x2 = - *x2;
+        cut_line = - cut_line;
+    }
+
+    if( *x1 < cut_line && *x2 >= cut_line )
+    {
+        f = (cut_line - *x1) / (*x2 - *x1);
+        *y1 = *y1 + (*y2 - *y1) * f;
+        *x1 = *x1 + (*x2 - *x1) * f;
+    }
+    else if( *x2 < cut_line && *x1 >= cut_line )
+    {
+        f = (cut_line - *x2) / (*x1 - *x2);
+        *y2 = *y2 + (*y1 - *y2) * f;
+        *x2 = *x2 + (*x1 - *x2) * f;
+    }
+
+    if( !greater_flag )
+    {
+        *x1 = - *x1;
+        *x2 = - *x2;
+        cut_line = - cut_line;
+    }
+}
+
+/************************************************************************/
+/*                       gv_line_intersect_rect()                       */
+/************************************************************************/
+
+int gv_line_intersect_rect( double *x1, double *y1,
+                            double *x2, double *y2,
+                            GvRect *rect )
+
+{
+    _gv_intersect( x1, y1, x2, y2, rect->x, TRUE );
+    _gv_intersect( x1, y1, x2, y2, rect->x + rect->width, FALSE );
+    _gv_intersect( y1, x1, y2, x2, rect->y, TRUE );
+    _gv_intersect( y1, x1, y2, x2, rect->y + rect->height, FALSE );
+
+    if( *x1 < rect->x || *x2 < rect->x
+        || *y1 < rect->y || *y2 < rect->y
+        || *x1 > rect->x + rect->width
+        || *x2 > rect->x + rect->width
+        || *y1 > rect->y + rect->height
+        || *y2 > rect->y + rect->height )
+        return 0;
+
+    return 1;
+}
+
+
+/************************************************************************/
+/*                       gv_shape_clip_to_rect()                        */
+/************************************************************************/
+
+GvShape *gv_shape_clip_to_rect( GvShape *shape, GvRect *rect )
+
+{
+    GvRect extents;
+    int    ring, out_ring = 0;
+    GvShape *new_shape;
+
+/* -------------------------------------------------------------------- */
+/*      Get shape extents, and check for trivial cases of complete      */
+/*      exclusion or inclusion.                                         */
+/* -------------------------------------------------------------------- */
+    gv_shape_get_extents( shape, &extents );
+
+    if( extents.height < 0.0 )
+    {
+        extents.y = extents.y + extents.height;
+        extents.height = -extents.height;
+    }
+
+    if( extents.x > rect->x + rect->width
+        || extents.y > rect->y + rect->height
+        || extents.x + extents.width < rect->x
+        || extents.y + extents.height < rect->y )
+        return NULL;
+
+    if( extents.x >= rect->x
+        && extents.x + extents.width <= rect->x + rect->width
+        && extents.y >= rect->y
+        && extents.y + extents.height <= rect->y + rect->height )
+        return gv_shape_copy( shape );
+
+    if( gv_shape_type(shape) == GVSHAPE_POINT )
+        return gv_shape_copy( shape );
+
+/* -------------------------------------------------------------------- */
+/*      Process line segment by line segment                            */
+/* -------------------------------------------------------------------- */
+    new_shape = gv_shape_new( gv_shape_type(shape) );
+    gv_properties_copy( &(shape->properties), &(new_shape->properties) );
+
+
+    for( ring = 0; ring < gv_shape_get_rings(shape); ring++ )
+    {
+        double last_x, last_y, last_z, x, y;
+        int node, last_inside, have_last_point;
+
+        last_x = x = gv_shape_get_x(shape, ring, 0);
+        last_y = y = gv_shape_get_y(shape, ring, 0);
+        last_z = gv_shape_get_z(shape, ring, 0);
+
+        if( x < rect->x || x > rect->x + rect->width
+            || y < rect->y || y > rect->y + rect->height )
+            last_inside = FALSE;
+        else
+        {
+            last_inside = TRUE;
+            gv_shape_add_node( new_shape, out_ring, last_x, last_y, last_z );
+        }
+
+        have_last_point = last_inside;
+
+        for( node = 1; node < gv_shape_get_nodes(shape,ring); node++ )
+        {
+            int this_inside, z;
+
+            x = gv_shape_get_x(shape, ring, node);
+            y = gv_shape_get_y(shape, ring, node);
+            z = gv_shape_get_z(shape,ring,node);
+
+            if( x < rect->x || x > rect->x + rect->width
+                || y < rect->y || y > rect->y + rect->height )
+                this_inside = FALSE;
+            else
+                this_inside = TRUE;
+
+            if( this_inside && last_inside )
+            {
+                last_x = x;
+                last_y = y;
+                last_z = z;
+
+                gv_shape_add_node( new_shape, out_ring, x, y, z );
+            }
+            else if( last_inside && !this_inside )
+            {
+                gv_line_intersect_rect( &last_x, &last_y, &x, &y, rect );
+                last_x = x;
+                last_y = y;
+                last_z = z;
+
+                gv_shape_add_node( new_shape, out_ring, x, y, z );
+            }
+            else if( !last_inside )
+            {
+                double x_i, y_i;
+
+                x_i = gv_shape_get_x(shape,ring,node-1);
+                y_i = gv_shape_get_y(shape,ring,node-1);
+
+                if( !gv_line_intersect_rect( &x_i, &y_i, &x, &y, rect ) )
+                {
+                    last_inside = FALSE;
+                    continue;
+                }
+
+                if( have_last_point && (x_i != last_x && y_i != last_y) )
+                {
+                    if( (x_i == rect->x && last_x == rect->x + rect->width)
+                        || (x_i == rect->x + rect->width
+                            && last_x == rect->x) )
+                    {
+                        gv_shape_add_node( new_shape, out_ring,
+                                           last_x, rect->y, z );
+                        gv_shape_add_node( new_shape, out_ring,
+                                           x_i, rect->y, z );
+                    }
+                    else if( (y_i == rect->y
+                              && last_y == rect->y + rect->height)
+                             || (y_i == rect->y + rect->height
+                                 && last_y == rect->y) )
+                    {
+                        gv_shape_add_node( new_shape, out_ring,
+                                           rect->x, last_y, z );
+                        gv_shape_add_node( new_shape, out_ring,
+                                           rect->x, y_i, z );
+                    }
+                    else if( x_i == rect->x || x_i == rect->x + rect->width )
+                        gv_shape_add_node( new_shape, out_ring,
+                                           x_i, last_y, z );
+                    else
+                        gv_shape_add_node( new_shape, out_ring,
+                                           last_x, y_i, z );
+                }
+
+                gv_shape_add_node( new_shape, out_ring, x_i, y_i, z );
+                gv_shape_add_node( new_shape, out_ring, x, y, z );
+
+                last_x = x;
+                last_y = y;
+            }
+
+            last_inside = this_inside;
+        }
+
+        if( gv_shape_get_nodes(new_shape,out_ring) > 0 )
+            out_ring++;
+    }
+
+    return new_shape;
+}
+
+/************************************************************************/
+/*                   gv_shape_collection_add_shape()                    */
+/*                                                                      */
+/*      Add a shape to a collection.  The passed shape is added         */
+/*      directly (not copied), but the reference count is incremented.  */
+/************************************************************************/
+
+void gv_shape_collection_add_shape( GvShape *col_shape, GvShape *shape )
+
+{
+    GvCollectionShape *collection = (GvCollectionShape *) col_shape;
+
+    if( gv_shape_type(col_shape) != GVSHAPE_COLLECTION )
+        return;
+
+    collection->geom_list = (GvShape **)
+        g_renew(GvShape*, collection->geom_list, collection->geom_count+1);
+
+    collection->geom_list[collection->geom_count] = shape;
+    gv_shape_ref( shape );
+
+    collection->geom_count++;
+}
+
+/************************************************************************/
+/*                   gv_shape_collection_get_count()                    */
+/************************************************************************/
+
+int gv_shape_collection_get_count( GvShape *col_shape )
+
+{
+    GvCollectionShape *collection = (GvCollectionShape *) col_shape;
+
+    if( gv_shape_type(col_shape) != GVSHAPE_COLLECTION )
+        return 0;
+    else
+        return collection->geom_count;
+}
+
+/************************************************************************/
+/*                   gv_shape_collection_get_shape()                    */
+/************************************************************************/
+
+GvShape *gv_shape_collection_get_shape( GvShape *col_shape, int shp_index )
+
+{
+    GvCollectionShape *collection = (GvCollectionShape *) col_shape;
+
+    if( gv_shape_type(col_shape) != GVSHAPE_COLLECTION )
+        return NULL;
+
+    else if( shp_index < 0 || shp_index >= collection->geom_count )
+        return NULL;
+
+    else
+        return collection->geom_list[shp_index];
+}
+
+/************************************************************************/
+/*                        gv_shape_get_center()                         */
+/************************************************************************/
+
+gint gv_shape_get_center( GvShape *shape, GvVertex3d *xyz )
+
+{
+    GvRect extent;
+
+    gv_shape_get_extents( shape, &extent );
+    xyz->x = extent.x + extent.width * 0.5;
+    xyz->y = extent.y + extent.height * 0.5;
+    xyz->z = 0.0;
+
+    return 1;
+}
+
+/************************************************************************/
+/*                     gv_shape_update_attribute()                      */
+/************************************************************************/
+
+gint gv_shape_update_attribute( GvShape *shape,
+                                const char *tool,
+                                const char *attribute,
+                                const char *update_value )
+
+{
+    const char *original_ogrfs;
+    int ret = 0, tool_index, tool_count;
+    char **tool_list;
+
+/* -------------------------------------------------------------------- */
+/*      Get the ogrfs to operate on and split it.                       */
+/* -------------------------------------------------------------------- */
+    original_ogrfs =
+        gv_properties_get( gv_shape_get_properties(shape), "_gv_ogrfs" );
+    if( original_ogrfs == NULL )
+        return 0;
+
+    tool_list = gv_ogrfs_split_tools( original_ogrfs );
+
+/* -------------------------------------------------------------------- */
+/*      Loop over tools changing them.                                  */
+/* -------------------------------------------------------------------- */
+    tool_count = CSLCount( tool_list );
+    for( tool_index = 0; tool_index < tool_count; tool_index++ )
+    {
+        const char *id;
+        char *remaining, *value, *next_remaining;
+        int value_len;
+        char *new_tool_string = NULL;
+
+        if( tool != NULL && !EQUALN(tool_list[tool_index],tool,strlen(tool)) )
+            continue;
+
+        /* Loop over args */
+
+        remaining = tool_list[tool_index];
+        while( (id = gv_ogrfs_get_arg( remaining, &next_remaining, &value,
+                                       &value_len )) != NULL )
+        {
+            if( EQUALN(id,attribute,strlen(attribute)) )
+            {
+                char *replacement_value = NULL;
+                int value_offset = value - tool_list[tool_index];
+
+                if( EQUAL(attribute,"a") )
+                {
+                    char new_value[100];
+
+                    sprintf( new_value, "%.3g", atof(update_value)+atof(value) );
+                    replacement_value = CPLStrdup( new_value );
+                }
+                else if( EQUAL(attribute,"s") )
+                {
+                    char new_value[100];
+
+                    sprintf( new_value, "%.8g", atof(update_value)*atof(value) );
+                    replacement_value = CPLStrdup( new_value );
+                }
+                else
+                    replacement_value = CPLStrdup( update_value );
+
+                new_tool_string = (char *)
+                    CPLCalloc(1,strlen(tool_list[tool_index])+strlen(replacement_value)+1);
+                strcpy( new_tool_string, tool_list[tool_index] );
+                strcpy( new_tool_string+value_offset, replacement_value );
+                strcat( new_tool_string, value + value_len );
+                break;
+            }
+            remaining = next_remaining;
+        }
+
+        /* We got to the end of the args without the attribute, add it now. */
+        if( id == NULL )
+        {
+            new_tool_string = (char *)
+                CPLCalloc(1,strlen(tool_list[tool_index])+strlen(update_value) + strlen(attribute) + 10);
+            strcpy( new_tool_string, tool_list[tool_index] );
+            sprintf( new_tool_string + strlen(new_tool_string) - 1,
+                     ",%s:%s)", attribute, update_value );
+            ret = 1;
+        }
+
+        CPLFree( tool_list[tool_index] );
+        tool_list[tool_index] = new_tool_string;
+        ret = 1;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we got a replacement, update the _gv_ogrfs attribute.        */
+/* -------------------------------------------------------------------- */
+    if( ret )
+    {
+        int sum_tool_len = 1;
+        char *new_ogrfs;
+
+        for( tool_index = 0; tool_index < tool_count; tool_index++ )
+            sum_tool_len += strlen(tool_list[tool_index]) + 1;
+
+        new_ogrfs = (char *) CPLMalloc(sum_tool_len);
+
+        for( tool_index = 0; tool_index < tool_count; tool_index++ )
+        {
+            if( tool_index == 0 )
+                strcpy( new_ogrfs, tool_list[tool_index] );
+            else
+            {
+                strcat( new_ogrfs, ";" );
+                strcat( new_ogrfs, tool_list[tool_index] );
+            }
+        }
+
+        gv_properties_set( gv_shape_get_properties(shape), "_gv_ogrfs",
+                           new_ogrfs );
+        CPLFree( new_ogrfs );
+    }
+
+    CSLDestroy( tool_list );
+
+    return ret;
+}
+
+/************************************************************************/
+/*                          gv_shape_rotate()                           */
+/************************************************************************/
+
+gint gv_shape_rotate( GvShape *shape, double angle_in_degrees )
+
+{
+    if( gv_shape_type( shape ) == GVSHAPE_POINT )
+    {
+        char angle_as_string[60];
+
+        /* ogrfs angles are counter clockwise, angle_in_degrees is clockwise*/
+        sprintf( angle_as_string, "%.3g", - angle_in_degrees );
+        return gv_shape_update_attribute( shape, "SYMBOL", "a", angle_as_string);
+    }
+    else
+    {
+        GvVertex3d pivot;
+        int ring_count, ring;
+
+        if( !gv_shape_get_center( shape, &pivot ) )
+            return 0;
+
+        ring_count = gv_shape_get_rings( shape );
+        for( ring = 0; ring < ring_count; ring++ )
+        {
+            int node_count, node;
+
+            node_count = gv_shape_get_nodes( shape, ring );
+            for( node = 0; node < node_count; node++ )
+            {
+                double new_x, new_y, rad_rot, dx, dy;
+
+                rad_rot = (angle_in_degrees / 180.0) * M_PI;
+
+                dx = gv_shape_get_x(shape,ring,node) - pivot.x;
+                dy = gv_shape_get_y(shape,ring,node) - pivot.y;
+
+                new_x = pivot.x + dx * cos(rad_rot) + dy * sin(rad_rot);
+                new_y = pivot.y - dx * sin(rad_rot) + dy * cos(rad_rot);
+
+                gv_shape_set_xyz( shape, ring, node,
+                                  new_x, new_y,
+                                  gv_shape_get_z( shape, ring, node ) );
+            }
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+/*                           gv_shape_scale()                           */
+/************************************************************************/
+
+gint gv_shape_scale( GvShape *shape, double new_scale )
+
+{
+    if( gv_shape_type( shape ) == GVSHAPE_POINT )
+    {
+        char scale_as_string[60];
+
+        sprintf( scale_as_string, "%.3g", new_scale );
+        return gv_shape_update_attribute( shape, "SYMBOL", "s", scale_as_string);
+    }
+    else
+    {
+        GvVertex3d pivot;
+        int ring_count, ring;
+
+        if( !gv_shape_get_center( shape, &pivot ) )
+            return 0;
+
+        ring_count = gv_shape_get_rings( shape );
+        for( ring = 0; ring < ring_count; ring++ )
+        {
+            int node_count, node;
+
+            node_count = gv_shape_get_nodes( shape, ring );
+            for( node = 0; node < node_count; node++ )
+            {
+                double new_x, new_y;
+
+                new_x = (gv_shape_get_x(shape,ring,node) - pivot.x) * new_scale
+                    + pivot.x;
+                new_y = (gv_shape_get_y(shape,ring,node) - pivot.y) * new_scale
+                    + pivot.y;
+
+                gv_shape_set_xyz( shape, ring, node,
+                                  new_x, new_y,
+                                  gv_shape_get_z( shape, ring, node ) );
+            }
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+/*                     gv_shape_add_ring_from_xml()                     */
+/*                                                                      */
+/*      This is just intended for local use to avoid                    */
+/*      gv_shape_from_xml_tree() from being too complex.                */
+/************************************************************************/
+
+static void gv_shape_add_ring_from_xml( GvShape *psShape, CPLXMLNode *psNode )
+
+{
+    int  ring = gv_shape_get_rings( psShape );
+
+    if( psNode == NULL || !EQUAL(psNode->pszValue,"ring") || psNode->psChild == NULL )
+        return;
+
+    if( gv_shape_type(psShape) != GVSHAPE_AREA )
+        ring = 0;
+
+/* -------------------------------------------------------------------- */
+/*      The "new" case is that the contents of the ring element are     */
+/*      one text value with a coordinate list in a similar format to    */
+/*      what GML does.                                                  */
+/* -------------------------------------------------------------------- */
+    if( psNode->psChild->psNext == NULL
+        && psNode->psChild->eType == CXT_Text )
+    {
+        char    **triplets, **coords;
+        const char *value;
+        int    node;
+
+        value = psNode->psChild->pszValue;
+        triplets = CSLTokenizeStringComplex( value, " \t\r\n",
+                                             FALSE, FALSE );
+        node = 0;
+        while ( triplets[node] )
+        {
+            coords = CSLTokenizeStringComplex( triplets[node], ",",
+                                               FALSE, FALSE );
+            if( CSLCount(coords) == 3 )
+            {
+                gv_shape_add_node( psShape, ring, atof(coords[0]),
+                                   atof(coords[1]), atof(coords[2]) );
+            }
+            else if ( CSLCount(coords) == 2 )
+            {
+                gv_shape_add_node( psShape, ring, atof(coords[0]),
+                                   atof(coords[1]), 0.0 );
+            }
+            node++;
+
+            if( coords )
+                CSLDestroy( coords );
+        }
+                
+        if( triplets )
+            CSLDestroy( triplets );
+    }
+/* -------------------------------------------------------------------- */
+/*      The "old" case is having each node as a subelement, and each    */
+/*      x, y, z ordinate as a subelement.                               */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        for( psNode = psNode->psChild; psNode != NULL; psNode = psNode->psNext)
+        {
+            if( EQUAL(psNode->pszValue,"node") )
+            {
+                double x, y, z;
+
+                x = atof(CPLGetXMLValue(psNode,"x", "0.0"));
+                y = atof(CPLGetXMLValue(psNode,"y", "0.0"));
+                z = atof(CPLGetXMLValue(psNode,"z", "0.0"));
+
+                gv_shape_add_node( psShape, ring, x, y, z );
+            }
+        }
+    }
+}
+
+/************************************************************************/
+/*                     gv_shape_property_from_xml()                     */
+/************************************************************************/
+
+static void gv_shape_property_from_xml( GvShape *psShape, 
+                                        CPLXMLNode *psProperty )
+
+{
+    const char *pszName, *pszValue;
+                
+    pszName = CPLGetXMLValue( psProperty, "name", "" );
+    pszValue = CPLGetXMLValue( psProperty, "value", NULL );
+
+    if( pszValue == NULL && psProperty->psChild != NULL
+        && psProperty->psChild->psNext != NULL 
+        && psProperty->psChild->psNext->eType == CXT_Text )
+        pszValue = psProperty->psChild->psNext->pszValue;
+                
+    if( strlen(pszName) > 0 && pszValue != NULL )
+    {
+        gv_properties_set( &(psShape->properties), 
+                           pszName, pszValue );
+    }
+}
+
+/************************************************************************/
+/*                       gv_shape_from_xml_tree()                       */
+/*                                                                      */
+/*      Convert an XML tree representation of a GvShape into an         */
+/*      instance.                                                       */
+/************************************************************************/
+
+GvShape *gv_shape_from_xml_tree( CPLXMLNode *psTree )
+
+{
+    int        nGType;
+    GvShape    *psShape;
+    static const char *pszError = "Invalid GvShape XML tree in gv_shape_from_xml_tree()";
+
+    if( psTree == NULL || psTree->eType != CXT_Element
+        || !EQUAL(psTree->pszValue,"GvShape") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, pszError );
+        return NULL;
+    }
+
+    nGType = atoi(CPLGetXMLValue( psTree, "type", "-1" ));
+    if( nGType == -1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, pszError );
+        return NULL;
+    }
+
+    psShape = gv_shape_new( nGType );
+    if( psShape == NULL )
+        return NULL;
+
+    for( psTree = psTree->psChild; psTree != NULL; psTree = psTree->psNext )
+    {
+        if( EQUAL(psTree->pszValue,"rings") )
+        {
+            CPLXMLNode *psRing;
+
+            for( psRing = psTree->psChild; psRing != NULL; 
+                 psRing = psRing->psNext )
+            {
+                if( EQUAL(psRing->pszValue,"ring") )
+                {
+                    gv_shape_add_ring_from_xml( psShape, psRing );
+                }
+            }
+        }
+        else if( EQUAL(psTree->pszValue,"Properties") )
+        {
+            CPLXMLNode *psProperty;
+
+            for( psProperty = psTree->psChild; psProperty != NULL;
+                 psProperty = psProperty->psNext )
+            {
+                gv_shape_property_from_xml( psShape, psProperty );
+            }
+        }
+        /* The old format has properties directly below the GvShape */
+        else if( EQUAL(psTree->pszValue,"Property") )
+        {
+            gv_shape_property_from_xml( psShape, psTree );
+        }
+        else if( EQUAL(psTree->pszValue,"SubShapes") )
+        {
+            CPLXMLNode *psChild;
+
+            for( psChild = psTree->psChild; psChild != NULL;
+                 psChild = psChild->psNext )
+            {
+                GvShape *psSubshape = gv_shape_from_xml_tree( psChild );
+                if( psSubshape != NULL )
+                    gv_shape_collection_add_shape( psShape, psSubshape );
+            }
+        }
+    }
+
+    return psShape;
+}
+
+/************************************************************************/
+/*                        gv_shape_to_xml_tree()                        */
+/************************************************************************/
+
+CPLXMLNode *gv_shape_to_xml_tree( GvShape *psShape )
+
+{
+    CPLXMLNode *psRoot; 
+    int iRing, nRingCount, nPropertyCount;
+    char szText[100];
+    GvProperties *psProps;
+
+    psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GvShape" );
+    
+    sprintf( szText, "%d", gv_shape_type(psShape) );
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( psRoot, CXT_Attribute, "type" ),
+        CXT_Text, szText );
+
+/* -------------------------------------------------------------------- */
+/*      Translate "rings" into coordinate lists.                        */
+/* -------------------------------------------------------------------- */
+    if( gv_shape_get_rings( psShape ) > 0 
+        && gv_shape_get_nodes( psShape, 0 ) > 0  )
+    {
+        CPLXMLNode *psRings = CPLCreateXMLNode( psRoot, CXT_Element, "rings");
+
+        nRingCount = gv_shape_get_rings( psShape );
+        for( iRing = 0; iRing < nRingCount; iRing++ )
+        {
+            char *pszCoordList;
+            int  nMaxLen, nUsed = 0, iNode;
+            int  nNodeCount = gv_shape_get_nodes( psShape, iRing );
+
+            nMaxLen = nNodeCount * 60;
+            pszCoordList = (char *) CPLMalloc(nMaxLen);
+            
+            for( iNode = 0; iNode < nNodeCount; iNode++ )
+            {
+                double x, y, z;
+
+                if( iNode > 0 )
+                    pszCoordList[nUsed++] = ' ';
+
+                x = gv_shape_get_x( psShape, iRing, iNode );
+                y = gv_shape_get_y( psShape, iRing, iNode );
+                z = gv_shape_get_z( psShape, iRing, iNode );
+
+                if( x == (int) x && y == (int) y && z == (int) z )
+                    sprintf( pszCoordList+nUsed, "%d,%d,%d", 
+                             (int) x, (int) y, (int) z );
+                else if( fabs(x) < 370 && fabs(y) < 370 )
+                    sprintf( pszCoordList+nUsed, "%.16g,%.16g,%.16g", 
+                             x, y, z );
+                else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 
+                         || fabs(z) > 100000000.0 )
+                    sprintf( pszCoordList+nUsed, "%.16g,%.16g,%.16g", 
+                             x, y, z );
+                else
+                    sprintf( pszCoordList+nUsed, "%.3f,%.3f,%.3f", x, y, z );
+                nUsed += strlen(pszCoordList+nUsed);
+            }
+
+            CPLCreateXMLElementAndValue( psRings, "ring", pszCoordList );
+            CPLFree( pszCoordList );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Translate properties.                                           */
+/* -------------------------------------------------------------------- */
+    psProps = gv_shape_get_properties(psShape);
+    nPropertyCount = gv_properties_count( psProps );
+    
+    if( nPropertyCount > 0 )
+    {
+        int iProperty;
+        CPLXMLNode *psPRoot = CPLCreateXMLNode( psRoot, CXT_Element, 
+                                                "Properties" );
+        
+        for( iProperty = 0; iProperty < nPropertyCount; iProperty++ )
+        {
+            CPLXMLNode *psProperty;
+            
+            psProperty = CPLCreateXMLNode( psPRoot, CXT_Element, "Property" );
+            
+            CPLCreateXMLNode( 
+                CPLCreateXMLNode( psProperty, CXT_Attribute, "name" ), 
+                CXT_Text, gv_properties_get_name_by_index(psProps,iProperty) );
+            CPLCreateXMLNode( psProperty, CXT_Text, 
+                    gv_properties_get_value_by_index(psProps,iProperty) );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Translate sub-shapes.                                           */
+/* -------------------------------------------------------------------- */
+    if( gv_shape_collection_get_count( psShape ) > 0 )
+    {
+        CPLXMLNode *psSubShapes = CPLCreateXMLNode( psRoot, CXT_Element, 
+                                                    "SubShapes");
+        int  iShape, nShapeCount;
+
+        nShapeCount = gv_shape_collection_get_count( psShape );
+        for( iShape = 0; iShape < nShapeCount; iShape++ )
+        {
+            CPLAddXMLChild( 
+                psSubShapes,
+                gv_shape_to_xml_tree( 
+                    gv_shape_collection_get_shape( psShape, iShape ) ) );
+        }
+    }
+
+    return psRoot;
+}

Added: packages/openev/branches/upstream/current/gvshapefile.c
===================================================================
--- packages/openev/branches/upstream/current/gvshapefile.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapefile.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,704 @@
+/******************************************************************************
+ * $Id: gvshapefile.c,v 1.21 2003/05/27 21:32:40 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Read/write link to ESRI Shapefiles from GvShapes.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapefile.c,v $
+ * Revision 1.21  2003/05/27 21:32:40  warmerda
+ * added support for reading multi-part arcs as a collection of lines
+ *
+ * Revision 1.20  2002/09/30 20:16:54  warmerda
+ * ensure area rings are forced closed on save
+ *
+ * Revision 1.19  2002/09/12 15:20:39  warmerda
+ * use DBFIsAttributeNULL() call
+ *
+ * Revision 1.18  2002/08/19 17:12:48  pgs
+ * don't set property if source value is NULL, and when writing out a dbf file
+ * make sure to write null values.
+ *
+ * Revision 1.17  2002/08/07 17:12:42  pgs
+ * added missing include for CPLDebug
+ *
+ * Revision 1.16  2002/08/02 20:53:20  warmerda
+ * dont produce g_warning() if dbf file missing
+ *
+ * Revision 1.15  2002/07/24 20:32:30  warmerda
+ * minor performance improvements
+ *
+ * Revision 1.14  2002/07/18 19:33:57  pgs
+ * added gv_shapes_to_dbf
+ *
+ * Revision 1.13  2002/07/15 18:42:10  pgs
+ * added _filename to gv_shapes that come from shape files or ogr
+ *
+ * Revision 1.12  2001/08/08 17:43:33  warmerda
+ * avoid leak of SHPObjects when reading
+ *
+ * Revision 1.11  2001/03/29 15:00:18  warmerda
+ * find first _non-NULL_ shape when saving as template
+ *
+ * Revision 1.10  2001/03/19 21:40:12  pgs
+ * use binary mode for dbfopen dummy
+ *
+ * Revision 1.9  2000/10/29 18:17:06  warmerda
+ * add dummy field when saving shapefiles with no fields
+ *
+ * Revision 1.8  2000/08/25 20:08:18  warmerda
+ * added better checking for empty shape layers
+ *
+ * Revision 1.7  2000/08/08 17:17:39  warmerda
+ * fixed return value on save
+ *
+ * Revision 1.6  2000/08/04 14:17:28  warmerda
+ * GvShapes shape ids now persistent
+ *
+ * Revision 1.5  2000/06/12 20:16:02  warmerda
+ * added shapefile write support
+ *
+ */
+
+#include "shapefil.h"
+#include "gvshapes.h"
+#include <stdlib.h>
+#include "cpl_conv.h"
+
+/************************************************************************/
+/*                      gv_shapes_from_shapefile()                      */
+/************************************************************************/
+
+GvData *gv_shapes_from_shapefile(const char *filename)
+
+{
+    SHPHandle   shp_handle;
+    DBFHandle   dbf_handle;
+    int         shape_count, shape_index, field_count = 0, field_index;
+    int         field_type;
+    GvShapes    *shapes_data;
+    GvProperties *properties;
+    char        **field_names = NULL;
+
+
+/* -------------------------------------------------------------------- */
+/*      Open the .shp and .dbf file.                                    */
+/* -------------------------------------------------------------------- */
+    shp_handle = SHPOpen( filename, "rb" );
+    dbf_handle = DBFOpen( filename, "rb" );
+    if( dbf_handle == NULL && shp_handle == NULL )
+    {
+        g_warning( "Invalid shapefile and DBF." );
+        return NULL;
+    }
+    else if( dbf_handle == NULL )
+        g_warning( "Unable to open DBF file ... continuing anyways." );
+    else
+        field_count = DBFGetFieldCount( dbf_handle );
+
+
+    if ( shp_handle != NULL )
+        SHPGetInfo( shp_handle, &shape_count, NULL, NULL, NULL );
+    else
+    {
+        CPLDebug( "OpenEV", "Unable to open shapefile, just using .dbf file.");
+        shape_count = DBFGetRecordCount( dbf_handle );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create shapes layer, and assign some metadata about the         */
+/*      field definitions.                                              */
+/* -------------------------------------------------------------------- */
+    shapes_data = GV_SHAPES(gv_shapes_new());
+    properties = gv_data_get_properties( GV_DATA(shapes_data) );
+
+    //set the filename property
+    gv_properties_set( properties, "_filename", filename );
+
+    field_names = (char **) g_new( char *, field_count+1 );
+
+    for(field_index = 0; field_index < field_count; field_index++ )
+    {
+        char      prop_value[64], prop_name[64];
+        int       field_type, width, precision;
+
+        field_type = DBFGetFieldInfo( dbf_handle, field_index,
+                                      prop_value, &width, &precision );
+
+        sprintf( prop_name, "_field_name_%d", field_index+1 );
+        gv_properties_set( properties, prop_name, prop_value );
+
+        field_names[field_index] = g_strdup( prop_value );
+
+        sprintf( prop_name, "_field_width_%d", field_index+1 );
+        sprintf( prop_value, "%d", width );
+        gv_properties_set( properties, prop_name, prop_value );
+
+        if( field_type == FTDouble )
+        {
+            sprintf( prop_name, "_field_precision_%d", field_index+1 );
+            sprintf( prop_value, "%d", precision );
+            gv_properties_set( properties, prop_name, prop_value );
+        }
+
+        sprintf( prop_name, "_field_type_%d", field_index+1 );
+        if( field_type == FTInteger )
+            gv_properties_set( properties, prop_name, "integer" );
+        else if( field_type == FTDouble )
+            gv_properties_set( properties, prop_name, "float" );
+        else
+            gv_properties_set( properties, prop_name, "string" );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy all the shapes, and their attributes.                      */
+/* -------------------------------------------------------------------- */
+
+    for( shape_index = 0; shape_index < shape_count; shape_index++ )
+    {
+        SHPObject  *src_shape;
+        GvShape    *gv_shape = NULL;
+
+        if ( shp_handle != NULL )
+        {
+            src_shape = SHPReadObject( shp_handle, shape_index );
+            if( src_shape == NULL )
+                continue;
+
+            if( src_shape->nSHPType == SHPT_POINT
+                || src_shape->nSHPType == SHPT_POINTM
+                || src_shape->nSHPType == SHPT_POINTZ )
+            {
+                gv_shape = gv_shape_new( GVSHAPE_POINT );
+                gv_shape_set_xyz( gv_shape, 0, 0,
+                                  src_shape->padfX[0],
+                                  src_shape->padfY[0],
+                                  src_shape->padfZ[0] );
+            }
+
+            else if( src_shape->nSHPType == SHPT_ARC
+                     || src_shape->nSHPType == SHPT_ARCM
+                     || src_shape->nSHPType == SHPT_ARCZ )
+            {
+                int    node;
+
+                if( src_shape->nParts == 1 )
+                {
+                    gv_shape = gv_shape_new( GVSHAPE_LINE );
+                    for( node = src_shape->nVertices-1; node >= 0; node-- )
+                        gv_shape_set_xyz( gv_shape, 0, node,
+                                          src_shape->padfX[node],
+                                          src_shape->padfY[node],
+                                          src_shape->padfZ[node] );
+                }
+                else
+                {
+                    int part;
+                    
+                    gv_shape = gv_shape_new( GVSHAPE_COLLECTION );
+
+                    for( part = 0; part < src_shape->nParts; part++ )
+                    {
+                        GvShape *line;
+                        int     node, node_count, start;
+                    
+                        start = src_shape->panPartStart[part];
+                        
+                        if( part == src_shape->nParts-1 )
+                            node_count = src_shape->nVertices - start;
+                        else
+                            node_count = src_shape->panPartStart[part+1] - start;
+                        
+                        line = gv_shape_new( GVSHAPE_LINE );
+
+                        for( node = node_count - 1; node >= 0; node-- )
+                            gv_shape_set_xyz( line, 0, node,
+                                          src_shape->padfX[node+start],
+                                          src_shape->padfY[node+start],
+                                          src_shape->padfZ[node+start] );
+                        
+                        gv_shape_collection_add_shape( gv_shape, line );
+                    }
+                }
+            }
+
+            else if( src_shape->nSHPType == SHPT_POLYGON
+                     || src_shape->nSHPType == SHPT_POLYGONM
+                     || src_shape->nSHPType == SHPT_POLYGONZ )
+            {
+                int    part;
+
+                gv_shape = gv_shape_new( GVSHAPE_AREA );
+                for( part = 0; part < src_shape->nParts; part++ )
+                {
+                    int     node, node_count, start;
+
+                    start = src_shape->panPartStart[part];
+
+                    if( part == src_shape->nParts-1 )
+                        node_count = src_shape->nVertices - start;
+                    else
+                        node_count = src_shape->panPartStart[part+1] - start;
+
+                    for( node = node_count - 1; node >= 0; node-- )
+                        gv_shape_set_xyz( gv_shape, part, node,
+                                          src_shape->padfX[node+start],
+                                          src_shape->padfY[node+start],
+                                          src_shape->padfZ[node+start] );
+                }
+            }
+
+            SHPDestroyObject( src_shape );
+        }
+        else
+            gv_shape = gv_shape_new( GVSHAPE_COLLECTION );
+
+        /* add other types later */
+
+        if( gv_shape != NULL && dbf_handle != NULL )
+        {
+            properties = gv_shape_get_properties( gv_shape );
+
+            for( field_index = 0; field_index < field_count; field_index++ )
+            {
+                const char  *field_value;
+
+                if( DBFIsAttributeNULL(dbf_handle,shape_index,field_index) )
+                    continue;
+
+                field_value = DBFReadStringAttribute( dbf_handle, shape_index,
+                                                      field_index );
+                field_type = DBFGetFieldInfo( dbf_handle, field_index,
+                                              NULL, NULL, NULL);
+
+                gv_properties_set( properties,
+                                   field_names[field_index],
+                                   field_value );
+            }
+        }
+
+        if( gv_shape != NULL )
+            gv_shapes_add_shape( shapes_data, gv_shape );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    if( dbf_handle != NULL )
+        DBFClose( dbf_handle );
+    if( shp_handle != NULL )
+        SHPClose( shp_handle );
+
+    for( field_index = 0; field_index < field_count; field_index++ )
+        g_free( field_names[field_index] );
+    g_free( field_names );
+
+    gv_data_set_name( GV_DATA(shapes_data), filename );
+
+    return GV_DATA(shapes_data);
+}
+
+/************************************************************************/
+/*                       gv_shapes_to_shapefile()                       */
+/************************************************************************/
+
+int gv_shapes_to_shapefile(const char *filename, GvData *shapes_data,
+                           int shp_type )
+
+{
+    GvShapes       *shapes = GV_SHAPES(shapes_data);
+    GvShape        *shape;
+    DBFHandle      dbf_handle;
+    SHPHandle      shp_handle;
+    int            field_index, field_count=0, shape_count, shape_index;
+    GvProperties   *properties;
+    int            max_vertices = 0, fid = -1;
+    double         *x=NULL, *y=NULL, *z=NULL;
+
+/* -------------------------------------------------------------------- */
+/*      What sort of shapefile should we write?                         */
+/* -------------------------------------------------------------------- */
+    if( shp_type == SHPT_NULL )
+    {
+        int i = 0;
+
+        shape = NULL;
+        while( i < gv_shapes_num_shapes(shapes) && shape == NULL )
+        {
+            shape = gv_shapes_get_shape(shapes, i);
+            i++;
+        }
+
+        if( shape == NULL )
+        {
+            g_warning("no shapes in layer to save ... unable to default type");
+            return FALSE;
+        }
+
+        if( gv_shape_type(shape) == GVSHAPE_POINT )
+            shp_type = SHPT_POINT;
+        else if( gv_shape_type(shape) == GVSHAPE_LINE )
+            shp_type = SHPT_ARC;
+        else
+            shp_type = SHPT_POLYGON;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to create the named file(s).                                */
+/* -------------------------------------------------------------------- */
+    dbf_handle = DBFCreate( filename );
+    if( dbf_handle == NULL )
+    {
+        g_warning( "Failed to create DBF file." );
+        return FALSE;
+    }
+
+    shp_handle = SHPCreate( filename, shp_type );
+    if( shp_handle == NULL )
+    {
+        g_warning( "Failed to create SHP file." );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the fields on the DBF file, if any.                      */
+/* -------------------------------------------------------------------- */
+    properties = gv_data_get_properties( shapes_data );
+    for( field_index = 0; TRUE; field_index++ )
+    {
+        int  width, precision = 0, field_type;
+        char prop_name[64];
+        const char *prop_value;
+
+        sprintf( prop_name, "_field_width_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            break;
+
+        width = atoi(prop_value);
+
+        sprintf( prop_name, "_field_precision_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value != NULL )
+            precision = atoi(prop_value);
+
+        sprintf( prop_name, "_field_type_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            prop_value = "string";
+
+        if( g_strcasecmp(prop_value,"integer") == 0 )
+            field_type = FTInteger;
+        else if( g_strcasecmp(prop_value,"float") == 0 )
+            field_type = FTDouble;
+        else
+            field_type = FTString;
+
+        sprintf( prop_name, "_field_name_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            break;
+
+        DBFAddField( dbf_handle, prop_value, field_type, width, precision );
+        field_count++;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Add a dummy field if there are none.                            */
+/* -------------------------------------------------------------------- */
+    if( field_count == 0 )
+    {
+        DBFAddField( dbf_handle, "FID", FTInteger, 10, 0 );
+        fid = 0;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Start writing shapes, ignoring any that don't match our         */
+/*      desired type.                                                   */
+/* -------------------------------------------------------------------- */
+    shape_count = gv_shapes_num_shapes(shapes);
+    for( shape_index = 0; shape_index < shape_count; shape_index++ )
+    {
+        SHPObject   *shp_object;
+        int         vertex_count, ring, shp_id;
+        int         *ring_start = NULL, ring_count;
+
+        shape = gv_shapes_get_shape( shapes, shape_index );
+
+        if( shape == NULL )
+            continue;
+
+        /* skip shapes not matching desired type */
+        if( (shp_type == SHPT_POINT && gv_shape_type(shape) != GVSHAPE_POINT)
+         || (shp_type == SHPT_ARC && gv_shape_type(shape) != GVSHAPE_LINE)
+         || (shp_type == SHPT_POLYGON && gv_shape_type(shape) != GVSHAPE_AREA))
+            continue;
+
+        /* count all the vertices */
+        vertex_count = 0;
+        ring_count = gv_shape_get_rings(shape);
+        for( ring = 0; ring < ring_count; ring++ )
+            vertex_count += gv_shape_get_nodes( shape, ring );
+
+        if( max_vertices < vertex_count+1 )
+        {
+            if( x != NULL )
+            {
+                free(x);
+                free(y);
+                free(z);
+            }
+
+            max_vertices = vertex_count * 2 + 50;
+            x = (double *) malloc(sizeof(double) * max_vertices);
+            y = (double *) malloc(sizeof(double) * max_vertices);
+            z = (double *) malloc(sizeof(double) * max_vertices);
+        }
+
+        vertex_count = 0;
+        for( ring = 0; ring < ring_count; ring++ )
+        {
+            int   node, node_count;
+
+            node_count = gv_shape_get_nodes( shape, ring );
+            for( node = 0; node < node_count; node++ )
+            {
+                x[vertex_count] = gv_shape_get_x(shape,ring,node);
+                y[vertex_count] = gv_shape_get_y(shape,ring,node);
+                z[vertex_count] = gv_shape_get_z(shape,ring,node);
+                vertex_count++;
+            }
+            
+            /* force closing of unclosed area rings. */
+            if( gv_shape_type(shape) == GVSHAPE_AREA 
+                && (gv_shape_get_x(shape,ring,0) 
+                       != gv_shape_get_x(shape,ring,node_count-1) 
+                    || gv_shape_get_y(shape,ring,0) 
+                       != gv_shape_get_y(shape,ring,node_count-1) ) )
+            {
+                x[vertex_count] = gv_shape_get_x(shape,ring,0);
+                y[vertex_count] = gv_shape_get_y(shape,ring,0);
+                z[vertex_count] = gv_shape_get_z(shape,ring,0);
+                vertex_count++;
+            }
+
+            if( ring_start != NULL )
+                ring_start[ring] = vertex_count;
+        }
+
+        if( gv_shape_get_rings( shape ) > 1 )
+        {
+            vertex_count = 0;
+            ring_start = (int *) malloc(sizeof(int) * ring_count);
+
+            for( ring = 0; ring < ring_count; ring++ )
+            {
+                ring_start[ring] = vertex_count;
+                vertex_count += gv_shape_get_nodes( shape, ring );
+            }
+        }
+
+        if( ring_count > 1 )
+            shp_object = SHPCreateObject( shp_type, -1, ring_count, ring_start,
+                                          NULL, vertex_count, x, y, z, NULL );
+        else
+            shp_object = SHPCreateObject( shp_type, -1, 0, NULL,
+                                          NULL, vertex_count, x, y, z, NULL );
+        if( ring_start != NULL )
+        {
+            free( ring_start );
+            ring_start = NULL;
+        }
+
+        shp_id = SHPWriteObject( shp_handle, -1, shp_object );
+        SHPDestroyObject( shp_object );
+
+        /* Write the attributes of this shape that match the DBF schema */
+        for( field_index = 0; field_index < field_count; field_index++ )
+        {
+            char field_name[32];
+            const char * field_value;
+            int  field_type;
+
+            /* FIXME: This will fail for truncated field names! */
+            field_type = DBFGetFieldInfo( dbf_handle, field_index,
+                                          field_name, NULL, NULL);
+
+            field_value = gv_properties_get( gv_shape_get_properties(shape),
+                                             field_name);
+            if( field_value == NULL )
+                field_value = "";
+
+            if( field_type == FTDouble )
+                DBFWriteDoubleAttribute( dbf_handle, shp_id, field_index,
+                                         atof(field_value) );
+            else if( field_type == FTInteger )
+                DBFWriteIntegerAttribute( dbf_handle, shp_id, field_index,
+                                          atoi(field_value) );
+            else
+                DBFWriteStringAttribute( dbf_handle, shp_id, field_index,
+                                         field_value );
+        }
+
+        if( fid != -1 )
+        {
+            DBFWriteIntegerAttribute( dbf_handle, shp_id, fid, shp_id );
+        }
+    }
+
+    if( x != NULL )
+    {
+        free( x );
+        free( y );
+        free( z );
+    }
+
+    SHPClose( shp_handle );
+    DBFClose( dbf_handle );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          gv_shapes_to_dbf()                          */
+/************************************************************************/
+
+int gv_shapes_to_dbf(const char *filename, GvData *shapes_data )
+
+{
+    GvShapes       *shapes = GV_SHAPES(shapes_data);
+    GvShape        *shape;
+    DBFHandle      dbf_handle;
+    int            field_index, field_count=0, shape_count, shape_index;
+    GvProperties   *properties;
+
+/* -------------------------------------------------------------------- */
+/*      Try to create the named file(s).                                */
+/* -------------------------------------------------------------------- */
+    dbf_handle = DBFCreate( filename );
+    if( dbf_handle == NULL )
+    {
+        g_warning( "Failed to create DBF file." );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the fields on the DBF file, if any.                      */
+/* -------------------------------------------------------------------- */
+    properties = gv_data_get_properties( shapes_data );
+    for( field_index = 0; TRUE; field_index++ )
+    {
+        int  width, precision = 0, field_type;
+        char prop_name[64];
+        const char *prop_value;
+
+        sprintf( prop_name, "_field_width_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            break;
+
+        width = atoi(prop_value);
+
+        sprintf( prop_name, "_field_precision_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value != NULL )
+            precision = atoi(prop_value);
+
+        sprintf( prop_name, "_field_type_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            prop_value = "string";
+
+        if( g_strcasecmp(prop_value,"integer") == 0 )
+            field_type = FTInteger;
+        else if( g_strcasecmp(prop_value,"float") == 0 )
+            field_type = FTDouble;
+        else
+            field_type = FTString;
+
+        sprintf( prop_name, "_field_name_%d", field_index+1 );
+        prop_value = gv_properties_get( properties, prop_name );
+        if( prop_value == NULL )
+            break;
+
+        DBFAddField( dbf_handle, prop_value, field_type, width, precision );
+        field_count++;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Add a dummy field if there are none.                            */
+/* -------------------------------------------------------------------- */
+    if( field_count == 0 )
+    {
+        g_warning( "No attributes to save in DBF file." );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Start writing shapes, ignoring any that don't match our         */
+/*      desired type.                                                   */
+/* -------------------------------------------------------------------- */
+    shape_count = gv_shapes_num_shapes(shapes);
+    for( shape_index = 0; shape_index < shape_count; shape_index++ )
+    {
+        shape = gv_shapes_get_shape( shapes, shape_index );
+
+        if( shape == NULL )
+            continue;
+
+        /* Write the attributes of this shape that match the DBF schema */
+        for( field_index = 0; field_index < field_count; field_index++ )
+        {
+            char field_name[32];
+            const char * field_value;
+            int  field_type;
+
+            /* FIXME: This will fail for truncated field names! */
+            field_type = DBFGetFieldInfo( dbf_handle, field_index,
+                                          field_name, NULL, NULL);
+
+            field_value = gv_properties_get( gv_shape_get_properties(shape),
+                                             field_name);
+            if( field_value == NULL )
+            {
+                DBFWriteNULLAttribute( dbf_handle, shape_index, field_index );
+            }
+            else
+            {
+                if( field_type == FTDouble )
+                    DBFWriteDoubleAttribute( dbf_handle, shape_index, field_index,
+                                             atof(field_value) );
+                else if( field_type == FTInteger )
+                    DBFWriteIntegerAttribute( dbf_handle, shape_index, field_index,
+                                              atoi(field_value) );
+                else
+                    DBFWriteStringAttribute( dbf_handle, shape_index, field_index,
+                                             field_value );
+            }
+        }
+
+    }
+
+    DBFClose( dbf_handle );
+
+    return TRUE;
+}

Added: packages/openev/branches/upstream/current/gvshapelayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvshapelayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapelayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1232 @@
+/******************************************************************************
+ * $Id: gvshapelayer.c,v 1.26 2003/05/16 18:26:33 pgs Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for vector display layers (eventually this will
+ *           merge with GvShapesLayer).
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapelayer.c,v $
+ * Revision 1.26  2003/05/16 18:26:33  pgs
+ * added initial code for propogating colors to sub-symbols
+ *
+ * Revision 1.25  2003/04/17 13:56:12  warmerda
+ * added more assserts
+ *
+ * Revision 1.24  2003/04/09 16:52:23  pgs
+ * added shadow, halo and bgcolor to LABELs
+ *
+ * Revision 1.23  2003/04/07 15:10:14  pgs
+ * added pattern support to pen objects
+ *
+ * Revision 1.22  2003/02/28 16:47:25  warmerda
+ * split up render part handling to support vector symbols
+ *
+ * Revision 1.21  2003/02/27 04:00:18  warmerda
+ * added scale_dep flag handling
+ *
+ * Revision 1.20  2003/02/14 20:12:43  pgs
+ * added support for line widths in PENs
+ *
+ * Revision 1.19  2002/11/15 05:04:43  warmerda
+ * added LABEL anchor point support
+ *
+ * Revision 1.18  2002/11/14 20:11:22  warmerda
+ * preliminary support for gvsymbolmanager from Paul
+ *
+ * Revision 1.17  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.16  2002/09/30 20:50:18  warmerda
+ * removed restart debug message
+ *
+ * Revision 1.15  2002/09/30 17:36:04  warmerda
+ * Increate name buffer size in gv_shape_layer_pick_node(). This fixes an odd
+ * crash on Linux/XiGraphics combo, and ensures that a hit is detected even if
+ * there are overlapping nodes as occurs with the start node in a closed area.
+ *
+ * Revision 1.14  2002/02/22 20:16:07  warmerda
+ * added brush tool support
+ *
+ * Revision 1.13  2002/02/22 19:27:16  warmerda
+ * added support for pen tools
+ *
+ * Revision 1.12  2001/11/09 16:04:53  warmerda
+ * default color to white in renderinfo
+ *
+ * Revision 1.11  2001/04/09 18:17:34  warmerda
+ * added subselection, and renderinfo support
+ *
+ * Revision 1.10  2000/08/10 20:44:27  warmerda
+ * don't try to select shapes in a zero sized rectangle
+ *
+ * Revision 1.9  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvshapelayer.h"
+#include "gvrenderinfo.h"
+#include "cpl_error.h"
+#include <gtk/gtksignal.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#define PICK_SIZE 4.0
+
+enum
+{
+    DRAW_SELECTED,
+    DELETE_SELECTED,
+    TRANSLATE_SELECTED,
+    PICK_SHAPE,
+    PICK_NODE,
+    GET_NODE,
+    MOVE_NODE,
+    INSERT_NODE,
+    DELETE_NODE,
+    NODE_MOTION,
+    SELECTION_CHANGED,
+    SUBSELECTION_CHANGED,
+    LAST_SIGNAL
+};
+
+static void gv_shape_layer_class_init(GvShapeLayerClass *klass);
+static void gv_shape_layer_init(GvShapeLayer *layer);
+static void gv_shape_layer_finalize(GtkObject *object);
+static void gv_shape_layer_selection_changed(GvShapeLayer *layer);
+static void gv_shape_layer_subselection_changed(GvShapeLayer *layer);
+
+static guint shape_layer_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_shape_layer_get_type(void)
+{
+    static GtkType shape_layer_type = 0;
+
+    if (!shape_layer_type)
+    {
+    static const GtkTypeInfo shape_layer_info =
+    {
+        "GvShapeLayer",
+        sizeof(GvShapeLayer),
+        sizeof(GvShapeLayerClass),
+        (GtkClassInitFunc) gv_shape_layer_class_init,
+        (GtkObjectInitFunc) gv_shape_layer_init,
+        /* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
+    };
+
+    shape_layer_type = gtk_type_unique(gv_layer_get_type(),
+                       &shape_layer_info);
+    }
+    return shape_layer_type;
+}
+
+static void
+gv_shape_layer_class_init(GvShapeLayerClass *klass)
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    shape_layer_signals[DRAW_SELECTED] =
+    gtk_signal_new ("draw-selected",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, draw_selected),
+            gtk_marshal_NONE__POINTER,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_POINTER);
+
+    shape_layer_signals[DELETE_SELECTED] =
+    gtk_signal_new ("delete-selected",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, delete_selected),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    shape_layer_signals[TRANSLATE_SELECTED] =
+    gtk_signal_new ("translate-selected",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass,
+                       translate_selected),
+            gtk_marshal_NONE__POINTER,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_POINTER);
+
+    shape_layer_signals[PICK_SHAPE] =
+    gtk_signal_new ("pick-shape",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, pick_shape),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    shape_layer_signals[PICK_NODE] =
+    gtk_signal_new ("pick-node",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, pick_node),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    shape_layer_signals[GET_NODE] =
+    gtk_signal_new ("get-node",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, get_node),
+            gtk_marshal_NONE__POINTER,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_POINTER);
+
+    shape_layer_signals[MOVE_NODE] =
+    gtk_signal_new ("move-node",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, move_node),
+            gtk_marshal_NONE__POINTER,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_POINTER);
+
+    shape_layer_signals[INSERT_NODE] =
+    gtk_signal_new ("insert-node",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, insert_node),
+            gtk_marshal_NONE__POINTER,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_POINTER);
+
+    shape_layer_signals[DELETE_NODE] =
+    gtk_signal_new ("delete-node",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, delete_node),
+            gtk_marshal_NONE__POINTER,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_POINTER);
+
+    shape_layer_signals[NODE_MOTION] =
+    gtk_signal_new ("node-motion",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvShapeLayerClass, node_motion),
+            gtk_marshal_NONE__INT,
+            GTK_TYPE_NONE, 1,
+            GTK_TYPE_INT);
+
+    shape_layer_signals[SELECTION_CHANGED] =
+    gtk_signal_new ("selection-changed",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET(GvShapeLayerClass,selection_changed),
+            gtk_marshal_NONE__INT,
+            GTK_TYPE_NONE, 0);
+
+    shape_layer_signals[SUBSELECTION_CHANGED] =
+    gtk_signal_new ("subselection-changed",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET(GvShapeLayerClass,
+                                          subselection_changed),
+            gtk_marshal_NONE__INT,
+            GTK_TYPE_NONE, 0);
+
+    gtk_object_class_add_signals(object_class, shape_layer_signals,
+                 LAST_SIGNAL);
+
+    object_class->finalize = gv_shape_layer_finalize;
+
+    klass->draw_selected = NULL;
+    klass->delete_selected = NULL;
+    klass->translate_selected = NULL;
+    klass->pick_shape = NULL;
+    klass->pick_node = NULL;
+    klass->get_node = NULL;
+    klass->move_node = NULL;
+    klass->insert_node = NULL;
+    klass->delete_node = NULL;
+    klass->node_motion = NULL;
+}
+
+static void
+gv_shape_layer_init(GvShapeLayer *layer)
+{
+    GvColor default_color = {1.0, 1.0, 1.0, 1.0};
+
+    layer->selected = g_array_new(FALSE, TRUE, sizeof(gint));
+    layer->flags = 0;
+    gv_color_copy(layer->color, default_color);
+
+    layer->subselection = -1;
+    layer->render_index = NULL;
+    layer->label_render = NULL;
+    layer->symbol_render = NULL;
+    layer->pen_render = NULL;
+    layer->brush_render = NULL;
+
+    layer->scale_dep_flags = NULL;
+}
+
+void
+gv_shape_layer_select_shape(GvShapeLayer *layer, gint shape_id)
+{
+    g_return_if_fail(shape_id >= 0 && shape_id < layer->selected->len);
+    if( g_array_index(layer->selected, gint, shape_id) == 0 )
+    {
+        g_array_index(layer->selected, gint, shape_id) = 1;
+        gv_shape_layer_selection_changed(layer);
+
+        gv_shape_layer_subselect_shape(layer, shape_id);
+    }
+}
+
+void
+gv_shape_layer_deselect_shape(GvShapeLayer *layer, gint shape_id)
+{
+    g_return_if_fail(shape_id >= 0 && shape_id < layer->selected->len);
+    if( g_array_index(layer->selected, gint, shape_id) != 0 )
+    {
+        if( layer->subselection == shape_id )
+            gv_shape_layer_subselect_shape(layer, -1);
+
+        g_array_index(layer->selected, gint, shape_id) = 0;
+        gv_shape_layer_selection_changed(layer);
+    }
+}
+
+void
+gv_shape_layer_clear_selection(GvShapeLayer *layer)
+{
+    gv_shape_layer_subselect_shape(layer, -1);
+
+    memset(layer->selected->data, 0, layer->selected->len * sizeof(gint));
+
+    gv_shape_layer_selection_changed(layer);
+}
+
+void
+gv_shape_layer_select_all(GvShapeLayer *layer)
+{
+    memset(layer->selected->data, 0xff, layer->selected->len * sizeof(gint));
+    gv_shape_layer_selection_changed(layer);
+}
+
+gint
+gv_shape_layer_is_selected(GvShapeLayer *layer, gint shape_id)
+{
+    g_return_val_if_fail(shape_id >= 0 && shape_id < layer->selected->len, 0);
+    return g_array_index(layer->selected, gint, shape_id);
+}
+
+gint
+gv_shape_layer_selected(GvShapeLayer *layer, gint what, void *retval)
+{
+    gint i, hit=FALSE;
+    GArray *shapes=NULL;
+
+    if (what == GV_ALL)
+    {
+        hit = FALSE;
+        shapes = (GArray*)retval;
+    }
+
+    for (i=0; i < layer->selected->len; ++i)
+    {
+        if (g_array_index(layer->selected, gint, i))
+        {
+            if (what == GV_ALL)
+            {
+                hit = TRUE;
+                g_array_append_val(shapes, i);
+            }
+            else
+            {
+                if (what == GV_FIRST) *(gint*)retval = i;
+                return TRUE;
+            }
+        }
+    }
+    return hit;
+}
+
+void
+gv_shape_layer_delete_selected(GvShapeLayer *layer)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[DELETE_SELECTED]);
+}
+
+void
+gv_shape_layer_translate_selected(GvShapeLayer *layer, gvgeocoord dx, gvgeocoord dy)
+{
+    GvVertex delta;
+
+    delta.x = dx;
+    delta.y = dy;
+    gtk_signal_emit(GTK_OBJECT(layer),
+            shape_layer_signals[TRANSLATE_SELECTED], &delta);
+}
+
+void
+gv_shape_layer_selected_motion(GvShapeLayer *layer, gvgeocoord dx, gvgeocoord dy)
+{
+    layer->selected_motion.x = dx;
+    layer->selected_motion.y = dy;
+}
+
+void
+gv_shape_layer_draw_selected(GvShapeLayer *layer, guint when, GvViewArea *view)
+{
+    switch (when)
+    {
+    case GV_ALWAYS:
+        layer->flags &= ~GV_DELAY_SELECTED;
+        break;
+
+    case GV_LATER:
+        layer->flags |= GV_DELAY_SELECTED;
+        break;
+
+    case GV_NOW:
+        gtk_signal_emit(GTK_OBJECT(layer),
+                shape_layer_signals[DRAW_SELECTED], view);
+        break;
+
+    default:
+        g_warning("gv_shape_layer_draw_selected(): invalid argument");
+        break;
+    }
+}
+
+void
+gv_shape_layer_get_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[GET_NODE], info);
+}
+
+void
+gv_shape_layer_move_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[MOVE_NODE], info);
+}
+
+void
+gv_shape_layer_insert_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[INSERT_NODE], info);
+}
+
+void
+gv_shape_layer_delete_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[DELETE_NODE], info);
+}
+
+void gv_shape_layer_node_motion(GvShapeLayer *layer, gint shape_id)
+{
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[NODE_MOTION],
+            shape_id);
+}
+
+void
+gv_shape_layer_select_region(GvShapeLayer *layer, GvViewArea *view,
+                 GvRect *rect)
+{
+    GLuint *buf;
+    GLint hits, i;
+
+    if (layer->selected->len == 0) return;
+
+    if( rect->width == 0.0 || rect->height == 0.0 )
+        return;
+
+    buf = g_new(GLuint, layer->selected->len * 4);
+    g_return_if_fail(buf);
+
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    /* Rect is in screen coordinates with origin in center of view */
+    gluOrtho2D(rect->x, rect->x + rect->width,
+               rect->y, rect->y + rect->height);
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    glRotate(view->state.rot, 0.0, 0.0, 1.0);
+    glScale(view->state.linear_zoom * view->state.flip_x,
+            view->state.linear_zoom * view->state.flip_y, 1.0);
+    glTranslate(view->state.tx, view->state.ty, 0.0);
+
+    glSelectBuffer(layer->selected->len * 4, buf);
+    glRenderMode(GL_SELECT);
+
+    glInitNames();
+    glPushName(-1);
+
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[PICK_SHAPE]);
+
+    hits = glRenderMode(GL_RENDER);
+
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+
+    layer->flags |= GV_SUPPRESS_SELCHANGED;
+    gv_shape_layer_clear_selection(layer);
+
+    for (i=0; i < hits; ++i)
+    {
+        gv_shape_layer_select_shape(layer, (gint)buf[4*i+3]);
+    }
+
+    layer->flags &= ~GV_SUPPRESS_SELCHANGED;
+
+    gv_shape_layer_selection_changed(layer);
+
+    if( hits > 0 )
+        gv_shape_layer_subselect_shape( layer, (gint) buf[3] );
+
+    g_free(buf);
+}
+
+gint
+gv_shape_layer_pick_shape(GvShapeLayer *layer, GvViewArea *view,
+              gvgeocoord x, gvgeocoord y, gint *shape_id)
+{
+    GLuint *buf;
+    GLint hits;
+    GLint vp[4];
+
+    /* FIXME: need to make the view area current GL context */
+
+    if (layer->selected->len == 0) return FALSE;
+
+    buf = g_new(GLuint, layer->selected->len * 4);
+    g_return_val_if_fail(buf, FALSE);
+
+    vp[0] = vp[1]  = 0;
+    vp[2] = (GLint)view->state.shape_x;
+    vp[3] = (GLint)view->state.shape_y;
+
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    gluPickMatrix(x, vp[3]-y, PICK_SIZE, PICK_SIZE, vp);
+    gluOrtho2D(-vp[2]/2.0, vp[2]/2.0, -vp[3]/2.0, vp[3]/2.0);
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    glRotate(view->state.rot, 0.0, 0.0, 1.0);
+    glScale(view->state.linear_zoom * view->state.flip_x,
+         view->state.linear_zoom * view->state.flip_y, 1.0);
+    glTranslate(view->state.tx, view->state.ty, 0.0);
+
+    glSelectBuffer(layer->selected->len * 4, buf);
+    glRenderMode(GL_SELECT);
+
+    glInitNames();
+    glPushName(-1);
+
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[PICK_SHAPE]);
+
+    hits = glRenderMode(GL_RENDER);
+
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+
+    if (hits > 0 && shape_id)
+    {
+    /* If there is more than one hit, we arbitrarily pick the first one */
+    *shape_id = (gint)buf[3];
+    }
+
+    g_free(buf);
+    return (hits > 0);
+}
+
+gint
+gv_shape_layer_pick_node(GvShapeLayer *layer, GvViewArea *view,
+             gvgeocoord x, gvgeocoord y, gint *before,
+             GvNodeInfo *node_info)
+{
+    GLuint *buf;
+    GLint hits;
+    GLint vp[4];
+
+    /* FIXME: need to make the view area current GL context */
+
+    if (layer->selected->len == 0) return FALSE;
+
+    buf = g_new0(GLuint, 12*3);
+    g_return_val_if_fail(buf, FALSE);
+
+    vp[0] = vp[1]  = 0;
+    vp[2] = (GLint)view->state.shape_x;
+    vp[3] = (GLint)view->state.shape_y;
+
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    gluPickMatrix(x, vp[3]-y, PICK_SIZE, PICK_SIZE, vp);
+    gluOrtho2D(-vp[2]/2.0, vp[2]/2.0, -vp[3]/2.0, vp[3]/2.0);
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    glRotate(view->state.rot, 0.0, 0.0, 1.0);
+    glScale(view->state.linear_zoom * view->state.flip_x,
+         view->state.linear_zoom * view->state.flip_y, 1.0);
+    glTranslate(view->state.tx, view->state.ty, 0.0);
+
+    glSelectBuffer(12*3, buf);
+    glRenderMode(GL_SELECT);
+
+    glInitNames();
+    glPushName(-1);
+
+    gtk_signal_emit(GTK_OBJECT(layer), shape_layer_signals[PICK_NODE]);
+
+    hits = glRenderMode(GL_RENDER);
+
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+
+    if (hits > 0 && node_info)
+    {
+    /* If there is more than one hit, we arbitrarily pick the first one */
+
+    /* Contents of the name stack is encoded as follows:
+          buf[3]  = 0 for node hit, 1 for segment hit
+          buf[4]  = ring id (always 0 for lines)
+          buf[5]  = node id
+    */
+    node_info->ring_id = (gint)buf[4];
+    node_info->node_id = (gint)buf[5];
+
+    gv_shape_layer_selected(layer, GV_FIRST, &node_info->shape_id);
+    }
+    if (hits > 0 && before)
+    {
+    *before = (gint)buf[3];
+    }
+    if( hits < 0 )
+    {
+        CPLDebug( "GvShapeLayer",
+                  "Name buffer overflow in gv_shape_layer_pick_node()" );
+    }
+
+    g_free(buf);
+    return (hits > 0);
+}
+
+void
+gv_shape_layer_set_num_shapes(GvShapeLayer *layer, gint num_shapes)
+{
+    int   old_scaledep_words, new_scaledep_words;
+
+    if (num_shapes == layer->selected->len)
+        return;
+
+    /* Do we need to grow the scaledep words array? */
+    new_scaledep_words = (num_shapes+31) / 32;
+    old_scaledep_words = (layer->selected->len+31) / 32;
+
+    if( new_scaledep_words > old_scaledep_words )
+    {
+        if( layer->scale_dep_flags != NULL )
+            layer->scale_dep_flags =
+                g_renew( gint, layer->scale_dep_flags, new_scaledep_words );
+        else
+            layer->scale_dep_flags =
+                g_new( gint, new_scaledep_words );
+
+        memset( layer->scale_dep_flags + old_scaledep_words,
+                0, (new_scaledep_words - old_scaledep_words) * 4 );
+    }
+
+    /* reset the size of the selected array */
+    g_array_set_size(layer->selected, num_shapes);
+    gv_shape_layer_clear_selection(layer);
+
+    if( layer->render_index != NULL )
+    {
+        /* new entries automatically set to zero (GVP_UNINITIALIZED_PART) */
+        g_array_set_size(layer->render_index, num_shapes);
+    }
+}
+
+void
+gv_shape_layer_set_color(GvShapeLayer *layer, GvColor color)
+{
+    gv_color_copy(layer->color, color);
+    gv_layer_display_change( GV_LAYER(layer), NULL );
+}
+
+static void
+gv_shape_layer_selection_changed(GvShapeLayer *layer)
+
+{
+    if( !(layer->flags & GV_SUPPRESS_SELCHANGED) )
+        gtk_signal_emit(GTK_OBJECT(layer),
+                        shape_layer_signals[SELECTION_CHANGED]);
+}
+
+static void
+gv_shape_layer_subselection_changed(GvShapeLayer *layer)
+
+{
+    if( !(layer->flags & GV_SUPPRESS_SELCHANGED) )
+    {
+        gtk_signal_emit(GTK_OBJECT(layer),
+                        shape_layer_signals[SUBSELECTION_CHANGED]);
+    }
+}
+
+static void
+gv_shape_layer_finalize(GtkObject *object)
+{
+    GvLayerClass *parent_class;
+    GvShapeLayer *layer;
+
+    layer = GV_SHAPE_LAYER(object);
+
+    gv_shape_layer_clear_all_renderinfo( layer );
+
+    g_array_free(layer->selected, TRUE);
+    if( layer->scale_dep_flags != NULL )
+        g_free( layer->scale_dep_flags );
+
+    parent_class = gtk_type_class(gv_layer_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+/************************************************************************/
+/*                  gv_shape_layer_get_subselection()                   */
+/************************************************************************/
+
+gint gv_shape_layer_get_subselection( GvShapeLayer *layer )
+{
+    return layer->subselection;
+}
+
+/************************************************************************/
+/*                   gv_shape_layer_subselect_shape()                   */
+/************************************************************************/
+
+void gv_shape_layer_subselect_shape( GvShapeLayer *layer, gint shape_id )
+
+{
+    if( (shape_id == -1 || gv_shape_layer_is_selected(layer, shape_id))
+        && shape_id != layer->subselection )
+    {
+        layer->subselection = shape_id;
+        gv_shape_layer_subselection_changed(layer);
+    }
+}
+
+/************************************************************************/
+/*                      gv_shape_layer_get_part()                       */
+/************************************************************************/
+
+GvRenderPart *gv_shape_layer_get_part( GvShapeLayer *layer, guint part_index )
+
+
+{
+    int     part_type, raw_index;
+
+    if( layer->render_index == NULL )
+        return NULL;
+
+    if( part_index == GVP_LAST_PART || part_index == GVP_UNINITIALIZED_PART )
+        return NULL;
+
+    raw_index = gv_part_index_to_index(part_index);
+    part_type = gv_part_index_to_type(part_index);
+
+    g_assert( raw_index >= 0 );
+    g_assert( part_type == GvLabelPart || part_type == GvSymbolPart
+              || part_type == GvPenPart || part_type == GvBrushPart );
+
+    if( part_type == GvLabelPart )
+    {
+        g_assert( raw_index < layer->label_render->len );
+
+        if( raw_index >= layer->label_render->len )
+            return NULL;
+
+        g_assert( g_array_index(layer->label_render,
+                                GvLabelRenderPart,raw_index).next_part
+                  != GVP_UNINITIALIZED_PART );
+
+        return (GvRenderPart *)
+            &(g_array_index(layer->label_render,
+                            GvLabelRenderPart,raw_index));
+    }
+    else if( part_type == GvSymbolPart )
+    {
+        g_assert( raw_index < layer->symbol_render->len );
+
+        if( raw_index >= layer->symbol_render->len )
+            return NULL;
+
+        g_assert( g_array_index(layer->symbol_render,
+                                GvSymbolRenderPart,raw_index).next_part
+                  != GVP_UNINITIALIZED_PART );
+
+        return (GvRenderPart *)
+            &(g_array_index(layer->symbol_render,
+                            GvSymbolRenderPart,raw_index));
+    }
+    else if( part_type == GvPenPart )
+    {
+        g_assert( raw_index < layer->pen_render->len );
+
+        if( raw_index >= layer->pen_render->len )
+            return NULL;
+
+        g_assert( g_array_index(layer->pen_render,
+                                GvPenRenderPart,raw_index).next_part
+                  != GVP_UNINITIALIZED_PART );
+
+        return (GvRenderPart *)
+            &(g_array_index(layer->pen_render,
+                            GvPenRenderPart,raw_index));
+    }
+    else if( part_type == GvBrushPart )
+    {
+        g_assert( raw_index < layer->brush_render->len );
+
+        if( raw_index >= layer->brush_render->len )
+            return NULL;
+
+        g_assert( g_array_index(layer->brush_render,
+                                GvBrushRenderPart,raw_index).next_part
+                  != GVP_UNINITIALIZED_PART );
+
+        return (GvRenderPart *)
+            &(g_array_index(layer->brush_render,
+                            GvBrushRenderPart,raw_index));
+    }
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                gv_shape_layer_get_first_part_index()                 */
+/************************************************************************/
+
+guint gv_shape_layer_get_first_part_index( GvShapeLayer *layer, gint shape_id)
+
+{
+    guint   part_index;
+
+    if( layer->render_index == NULL )
+        return GVP_UNINITIALIZED_PART;
+
+    g_return_val_if_fail( shape_id >= 0 && shape_id < layer->render_index->len,
+                          GVP_LAST_PART );
+
+    part_index = g_array_index(layer->render_index, guint, shape_id);
+
+    return part_index;
+}
+
+/************************************************************************/
+/*               gv_shape_layer_initialize_renderindex()                */
+/************************************************************************/
+
+void gv_shape_layer_initialize_renderindex( GvShapeLayer *layer )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Allocate render_index if it doesn't exist yet.                  */
+/* -------------------------------------------------------------------- */
+    if( layer->render_index == NULL )
+    {
+        layer->render_index = g_array_new(FALSE, TRUE, sizeof(gint));
+        g_array_set_size(layer->render_index, layer->selected->len );
+
+        layer->label_render =
+            g_array_new(FALSE, TRUE, sizeof(GvLabelRenderPart));
+        layer->symbol_render =
+            g_array_new(FALSE, TRUE, sizeof(GvSymbolRenderPart));
+        layer->pen_render =
+            g_array_new(FALSE, TRUE, sizeof(GvPenRenderPart));
+        layer->brush_render =
+            g_array_new(FALSE, TRUE, sizeof(GvBrushRenderPart));
+    }
+}
+
+/************************************************************************/
+/*                     gv_shape_layer_create_part()                     */
+/************************************************************************/
+
+guint gv_shape_layer_create_part( GvShapeLayer *layer, gint part_type )
+
+{
+    int   part_index;
+
+    g_return_val_if_fail( layer != NULL, GVP_UNINITIALIZED_PART );
+
+    if( layer->render_index == NULL )
+        gv_shape_layer_initialize_renderindex( layer );
+
+/* -------------------------------------------------------------------- */
+/*      Find the new part index in the target part type table.          */
+/* -------------------------------------------------------------------- */
+    if( part_type == GvLabelPart )
+    {
+        GvLabelRenderPart   label_info;
+
+        memset( &label_info, 0, sizeof(label_info) );
+        label_info.next_part = GVP_LAST_PART;
+        label_info.scale = 1.0;
+        label_info.color[0] = 1.0;
+        label_info.color[1] = 1.0;
+        label_info.color[2] = 1.0;
+        label_info.color[3] = 1.0;
+        label_info.b_color_initialized = 0;
+        label_info.background_color[0] = 1.0;
+        label_info.background_color[1] = 1.0;
+        label_info.background_color[2] = 1.0;
+        label_info.background_color[3] = 1.0;
+        label_info.b_background_color_initialized = 0;
+        label_info.anchor = GLRA_LOWER_LEFT;
+        label_info.halo = FALSE;
+        label_info.shadow = FALSE;
+
+        part_index = ((layer->label_render->len) << 3) | GvLabelPart;
+        g_array_append_val( layer->label_render, label_info );
+
+        return part_index;
+    }
+    else if( part_type == GvSymbolPart )
+    {
+        GvSymbolRenderPart  symbol_info;
+
+        memset( &symbol_info, 0, sizeof(symbol_info) );
+        symbol_info.next_part = GVP_LAST_PART;
+        symbol_info.scale = 1.0;
+        symbol_info.color[0] = 1.0;
+        symbol_info.color[1] = 1.0;
+        symbol_info.color[2] = 1.0;
+        symbol_info.color[3] = 1.0;
+        symbol_info.b_color_initialized = 0;
+        symbol_info.part_index = GVP_UNINITIALIZED_PART;
+
+        part_index = ((layer->symbol_render->len) << 3) | GvSymbolPart;
+        g_array_append_val( layer->symbol_render, symbol_info );
+
+        return part_index;
+    }
+    else if( part_type == GvPenPart )
+    {
+        GvPenRenderPart pen_info;
+
+        memset( &pen_info, 0, sizeof(pen_info) );
+        pen_info.next_part = GVP_LAST_PART;
+        pen_info.color[0] = 1.0;
+        pen_info.color[1] = 1.0;
+        pen_info.color[2] = 1.0;
+        pen_info.color[3] = 1.0;
+        pen_info.b_color_initialized = 0;
+        pen_info.width = 1.0;
+        pen_info.pattern = "ogr-pen-0";
+
+        part_index = ((layer->pen_render->len) << 3) | GvPenPart;
+        g_array_append_val( layer->pen_render, pen_info );
+
+        return part_index;
+    }
+    else if( part_type == GvBrushPart )
+    {
+        GvBrushRenderPart   brush_info;
+
+        memset( &brush_info, 0, sizeof(brush_info) );
+        brush_info.next_part = GVP_LAST_PART;
+        brush_info.fore_color[0] = 1.0;
+        brush_info.fore_color[1] = 1.0;
+        brush_info.fore_color[2] = 1.0;
+        brush_info.fore_color[3] = 1.0;
+        brush_info.b_fore_color_initialized = 0;
+
+        part_index = ((layer->brush_render->len) << 3) | GvBrushPart;
+        g_array_append_val( layer->brush_render, brush_info );
+
+        return part_index;
+    }
+    else
+    {
+        return GVP_UNINITIALIZED_PART;
+    }
+}
+
+/************************************************************************/
+/*                     gv_shape_layer_chain_part()                      */
+/*                                                                      */
+/*      Append this part to the the chain of which the                  */
+/*      base_part_index is the head (or at least a member).             */
+/*                                                                      */
+/*      Returns the base part index, which may new_part_index if it     */
+/*      is the first in the chain.                                      */
+/************************************************************************/
+
+guint gv_shape_layer_chain_part( GvShapeLayer *layer, gint base_part_index,
+                                 gint new_part_index )
+
+{
+    GvRenderPart *last_part;
+
+    if( base_part_index == GVP_LAST_PART
+        || base_part_index == GVP_UNINITIALIZED_PART )
+        return new_part_index;
+
+    last_part = gv_shape_layer_get_part( layer, base_part_index );
+
+    while( last_part != NULL && last_part->next_part != GVP_LAST_PART )
+        last_part = gv_shape_layer_get_part( layer, last_part->next_part );
+
+    if( last_part != NULL )
+        last_part->next_part = new_part_index;
+
+    return base_part_index;
+}
+
+/************************************************************************/
+/*                      gv_shape_layer_add_part()                       */
+/*                                                                      */
+/*      Returns GVP_UNITIALIZED_PART (0) if add fails.                  */
+/************************************************************************/
+
+guint gv_shape_layer_add_part( GvShapeLayer *layer, gint shape_id,
+                               gint part_type )
+
+{
+    guint   part_index;
+
+    g_return_val_if_fail( layer != NULL
+                          && shape_id >= 0
+                          && shape_id < layer->selected->len,
+                          GVP_UNINITIALIZED_PART );
+
+    if( layer->render_index == NULL )
+        gv_shape_layer_initialize_renderindex( layer );
+
+/* -------------------------------------------------------------------- */
+/*      A part type of GVP_LAST_PART is taken as an indication that     */
+/*      this shape should just have a GVP_LAST_PART code set for the    */
+/*      shape as a whole.  There is no render information for this      */
+/*      shape, but it is initialized.                                   */
+/* -------------------------------------------------------------------- */
+    if( part_type == GVP_LAST_PART )
+    {
+        g_array_index(layer->render_index,guint,shape_id) = GVP_LAST_PART;
+        return GVP_LAST_PART;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create and initialize the part.                                 */
+/* -------------------------------------------------------------------- */
+    part_index = gv_shape_layer_create_part( layer, part_type );
+
+    if( part_index == GVP_UNINITIALIZED_PART )
+        return part_index;
+
+/* -------------------------------------------------------------------- */
+/*      Chain this part at the end of any existing parts for this       */
+/*      shape, or if it is the first it will be set as the first part.  */
+/* -------------------------------------------------------------------- */
+    g_array_index(layer->render_index,guint,shape_id) =
+        gv_shape_layer_chain_part(
+            layer,
+            gv_shape_layer_get_first_part_index(layer, shape_id),
+            part_index );
+
+    return part_index;
+}
+
+/************************************************************************/
+/*                  gv_shape_layer_clear_shape_parts()                  */
+/************************************************************************/
+
+void gv_shape_layer_clear_shape_parts( GvShapeLayer *layer, gint shape_id )
+
+{
+    if( layer == NULL || shape_id < 0 || shape_id >= layer->selected->len
+        || layer->render_index == NULL )
+        return;
+
+    gv_shape_layer_clear_part( layer,
+        gv_shape_layer_get_first_part_index( layer, shape_id ) );
+
+    g_array_index( layer->render_index, guint, shape_id )
+        = GVP_UNINITIALIZED_PART;
+}
+
+
+/************************************************************************/
+/*                     gv_shape_layer_clear_part()                      */
+/*                                                                      */
+/*      De-initialized the indicated part, and any parts linked         */
+/*      after it.  Returns parts to GVP_UNINITIALIZED state.            */
+/************************************************************************/
+
+void gv_shape_layer_clear_part( GvShapeLayer *layer, guint part_index )
+
+{
+    int         part_type = gv_part_index_to_type(part_index);
+    GvRenderPart    *part_info;
+
+    part_info = gv_shape_layer_get_part( layer, part_index );
+    if( part_info == NULL )
+        return;
+
+    if( part_info->next_part != GVP_LAST_PART )
+        gv_shape_layer_clear_part( layer, part_info->next_part );
+
+    if( part_type == GvLabelPart )
+    {
+        GvLabelRenderPart *label_info = (GvLabelRenderPart *) part_info;
+
+        if( label_info->text != NULL )
+            g_free( label_info->text );
+    }
+
+    if( part_type == GvSymbolPart )
+    {
+        GvSymbolRenderPart *symbol_info = (GvSymbolRenderPart *) part_info;
+
+        if( symbol_info->symbol_id != NULL )
+            g_free( symbol_info->symbol_id );
+    }
+
+    part_info->next_part = GVP_UNINITIALIZED_PART;
+}
+
+/************************************************************************/
+/*                gv_shape_layer_clear_all_renderinfo()                 */
+/*                                                                      */
+/*      Quickly wipe all rendering info, and recover all memory         */
+/*      associated with keeping rendering info.                         */
+/************************************************************************/
+
+void gv_shape_layer_clear_all_renderinfo( GvShapeLayer *layer )
+
+{
+    int     i;
+
+    /*
+     * When the render info changes, it is possible for the scale dependentness
+     * of things to change to, so clear the flags will be be rebuilt next
+     * time the features are drawn.
+     */
+    if( layer->scale_dep_flags != NULL
+        && layer->selected != NULL )
+        memset( layer->scale_dep_flags, 0,
+                ((layer->selected->len+31) / 32) * 4 );
+
+    if( layer->render_index == NULL )
+        return;
+
+    g_array_free( layer->render_index, TRUE );
+    layer->render_index = NULL;
+
+    for( i=0; i<layer->symbol_render->len; i++ )
+    {
+        GvSymbolRenderPart *symbol_info;
+        symbol_info = &(g_array_index(layer->symbol_render,
+                                      GvSymbolRenderPart, i));
+        if (symbol_info->next_part != GVP_UNINITIALIZED_PART)
+            if (symbol_info->symbol_id != NULL)
+                g_free( symbol_info->symbol_id );
+    }
+
+    g_array_free( layer->symbol_render, TRUE );
+    layer->symbol_render = NULL;
+
+    for( i = 0; i < layer->label_render->len; i++ )
+    {
+        GvLabelRenderPart  *label_info;
+
+        label_info = &(g_array_index(layer->label_render,GvLabelRenderPart,i));
+        if( label_info->next_part != GVP_UNINITIALIZED_PART )
+        {
+            if( label_info->text != NULL )
+                g_free( label_info->text );
+        }
+    }
+
+    g_array_free( layer->label_render, TRUE );
+    layer->label_render = NULL;
+
+    g_array_free( layer->pen_render, TRUE );
+    layer->pen_render = NULL;
+
+    g_array_free( layer->brush_render, TRUE );
+    layer->pen_render = NULL;
+
+}
+
+/************************************************************************/
+/*                    gv_shape_layer_get_scale_dep()                    */
+/************************************************************************/
+
+int gv_shape_layer_get_scale_dep( GvShapeLayer *layer, gint shape_id )
+
+{
+    if( layer->scale_dep_flags == NULL )
+        return 0;
+
+    if( shape_id < 0 || shape_id >= layer->selected->len )
+        return 0;
+
+    return layer->scale_dep_flags[shape_id >> 5] & (1 << (shape_id & 0x1f) );
+}
+
+/************************************************************************/
+/*                    gv_shape_layer_set_scale_dep()                    */
+/************************************************************************/
+
+void gv_shape_layer_set_scale_dep( GvShapeLayer *layer, gint shape_id,
+                                   int scale_dep )
+
+{
+    if( layer->scale_dep_flags == NULL )
+        return;
+
+    if( shape_id < 0 || shape_id >= layer->selected->len )
+        return;
+
+    if( scale_dep )
+        layer->scale_dep_flags[shape_id >> 5] |= (1 << (shape_id & 0x1f) );
+    else
+        layer->scale_dep_flags[shape_id >> 5] &= ~(1 << (shape_id & 0x1f) );
+}
+

Added: packages/openev/branches/upstream/current/gvshapelayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvshapelayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapelayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,139 @@
+/******************************************************************************
+ * $Id: gvshapelayer.h,v 1.11 2003/02/27 04:00:19 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for vector display layers (eventually this will
+ *           merge with GvShapesLayer).
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapelayer.h,v $
+ * Revision 1.11  2003/02/27 04:00:19  warmerda
+ * added scale_dep flag handling
+ *
+ * Revision 1.10  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.9  2002/02/22 20:16:07  warmerda
+ * added brush tool support
+ *
+ * Revision 1.8  2002/02/22 19:27:16  warmerda
+ * added support for pen tools
+ *
+ * Revision 1.7  2001/04/09 18:17:34  warmerda
+ * added subselection, and renderinfo support
+ *
+ * Revision 1.6  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_SHAPE_LAYER_H__
+#define __GV_SHAPE_LAYER_H__
+
+#include <gdk/gdk.h>
+#include "gvlayer.h"
+#include "gvviewarea.h"
+
+#define GV_TYPE_SHAPE_LAYER            (gv_shape_layer_get_type ())
+#define GV_SHAPE_LAYER(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_SHAPE_LAYER, GvShapeLayer))
+#define GV_SHAPE_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_SHAPE_LAYER, GvShapeLayerClass))
+#define GV_IS_SHAPE_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_SHAPE_LAYER))
+#define GV_IS_SHAPE_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_SHAPE_LAYER))
+
+/* Access macro for selected buffer (for use by subclass) */
+#define GV_SHAPE_LAYER_SELBUF(layer) \
+     ((gint*)(GV_SHAPE_LAYER(layer)->selected->data))
+
+typedef struct _GvShapeLayer       GvShapeLayer;
+typedef struct _GvShapeLayerClass  GvShapeLayerClass;
+
+struct _GvShapeLayer
+{
+    GvLayer layer;
+    GArray *selected;
+    gint    subselection;
+    guint flags;
+    GvColor color;
+    GvVertex selected_motion;
+
+    /* All related to keeping feature rendering information */
+    GArray  *render_index;
+
+    GArray  *label_render;
+    GArray  *symbol_render;
+    GArray  *pen_render;
+    GArray  *brush_render;
+
+    gint    *scale_dep_flags;
+};
+
+struct _GvShapeLayerClass
+{
+    GvLayerClass parent_class;
+
+    void (* draw_selected)      (GvShapeLayer *layer, GvViewArea *view);
+    void (* delete_selected)    (GvShapeLayer *layer);
+    void (* translate_selected) (GvShapeLayer *layer, GvVertex *delta);
+    void (* pick_shape)         (GvShapeLayer *layer);
+    void (* pick_node)          (GvShapeLayer *layer);
+    void (* get_node)           (GvShapeLayer *layer, GvNodeInfo *info);
+    void (* move_node)          (GvShapeLayer *layer, GvNodeInfo *info);
+    void (* insert_node)        (GvShapeLayer *layer, GvNodeInfo *info);
+    void (* delete_node)        (GvShapeLayer *layer, GvNodeInfo *info);
+    void (* node_motion)        (GvShapeLayer *layer, gint shape_id);
+    void (* selection_changed)  (GvShapeLayer *layer);
+    void (* subselection_changed)(GvShapeLayer *layer);
+};
+
+GtkType    gv_shape_layer_get_type (void);
+
+void gv_shape_layer_select_shape(GvShapeLayer *layer, gint shape_id);
+void gv_shape_layer_deselect_shape(GvShapeLayer *layer, gint shape_id);
+void gv_shape_layer_clear_selection(GvShapeLayer *layer);
+void gv_shape_layer_select_all(GvShapeLayer *layer);
+void gv_shape_layer_draw_selected(GvShapeLayer *layer, guint when, GvViewArea *view);
+gint gv_shape_layer_is_selected(GvShapeLayer *layer, gint shape_id);
+gint gv_shape_layer_selected(GvShapeLayer *layer, gint what, void *retval);
+void gv_shape_layer_delete_selected(GvShapeLayer *layer);
+void gv_shape_layer_translate_selected(GvShapeLayer *layer, gvgeocoord dx, gvgeocoord dy);
+void gv_shape_layer_selected_motion(GvShapeLayer *layer, gvgeocoord dx, gvgeocoord dy);
+
+void gv_shape_layer_subselect_shape(GvShapeLayer *layer, gint shape_id);
+gint gv_shape_layer_get_subselection(GvShapeLayer *layer);
+
+void gv_shape_layer_set_scale_dep(GvShapeLayer *layer, gint shape_id, int dep);
+int  gv_shape_layer_get_scale_dep(GvShapeLayer *layer, gint shape_id );
+
+void gv_shape_layer_get_node          (GvShapeLayer *layer, GvNodeInfo *info);
+void gv_shape_layer_move_node         (GvShapeLayer *layer, GvNodeInfo *info);
+void gv_shape_layer_insert_node       (GvShapeLayer *layer, GvNodeInfo *info);
+void gv_shape_layer_delete_node       (GvShapeLayer *layer, GvNodeInfo *info);
+void gv_shape_layer_node_motion       (GvShapeLayer *layer, gint shape_id);
+
+void gv_shape_layer_select_region     (GvShapeLayer *layer, GvViewArea *view, GvRect *rect);
+gint gv_shape_layer_pick_shape        (GvShapeLayer *layer, GvViewArea *view, gvgeocoord x, gvgeocoord y, gint *shape_id);
+gint gv_shape_layer_pick_node         (GvShapeLayer *layer, GvViewArea *view, gvgeocoord x, gvgeocoord y, gint *before, GvNodeInfo *node_info);
+
+void gv_shape_layer_set_num_shapes(GvShapeLayer *layer, gint num_shapes);
+void gv_shape_layer_set_color(GvShapeLayer *layer, GvColor color);
+
+#endif /*__GV_SHAPE_LAYER_H__ */

Added: packages/openev/branches/upstream/current/gvshapes.c
===================================================================
--- packages/openev/branches/upstream/current/gvshapes.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapes.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,730 @@
+/******************************************************************************
+ * $Id: gvshapes.c,v 1.20 2005/01/14 16:51:51 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Vector shape container class.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapes.c,v $
+ * Revision 1.20  2005/01/14 16:51:51  gmwalter
+ * Checked in Aude's gv_shapes_add_shape_last function
+ * (allows shapes to be added without repeating
+ * indices if others have been deleted).
+ *
+ * Revision 1.19  2004/02/20 10:40:36  andrey_kiselev
+ * Use gv_raster_get_nodata() instead of GDALGetNoDataValue().
+ *
+ * Revision 1.18  2003/02/27 03:55:06  warmerda
+ * improved debug calls
+ *
+ * Revision 1.17  2003/01/06 21:20:03  warmerda
+ * added gv_shapes_from_ogr_layer
+ *
+ * Revision 1.16  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.15  2002/03/07 02:31:56  warmerda
+ * added default_height to add_height functions
+ *
+ * Revision 1.14  2001/08/08 17:45:48  warmerda
+ * GvShape now referenced counted
+ *
+ * Revision 1.13  2001/08/07 20:48:05  warmerda
+ * report the creation of GvShapes
+ *
+ * Revision 1.12  2001/03/29 03:39:33  warmerda
+ * improved add_height() to look around a bit when facing nodata values
+ *
+ * Revision 1.11  2001/01/26 13:57:13  warmerda
+ * fixed serious bugs in add_height code (x/y transformed)
+ *
+ * Revision 1.10  2001/01/18 16:48:14  warmerda
+ * added gv_shapes_add_height() and wrappers
+ *
+ * Revision 1.9  2000/08/08 20:09:41  warmerda
+ * added finalize debug statement
+ *
+ * Revision 1.8  2000/08/04 18:38:00  warmerda
+ * make some operations safer with illegal shapeids
+ *
+ * Revision 1.7  2000/08/04 14:14:12  warmerda
+ * GvShapes shape ids now persistent
+ *
+ * Revision 1.6  2000/07/14 14:51:01  warmerda
+ * fixed insert, and delete node support
+ *
+ * Revision 1.5  2000/07/13 19:08:37  warmerda
+ * added coping optional for gv_shapes_replace_shapes
+ *
+ * Revision 1.4  2000/06/28 13:10:42  warmerda
+ * added preliminary OGR support
+ *
+ * Revision 1.3  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gextra.h"
+#include "gvshapes.h"
+#include "gvraster.h"
+#include "cpl_error.h"
+
+typedef struct _GvShapesMemento GvShapesMemento;
+
+struct _GvShapesMemento
+{
+    GvDataMemento base;
+    GArray *ids;
+    GPtrArray *shapes;
+};
+
+static void gv_shapes_class_init(GvShapesClass *klass);
+static void gv_shapes_init(GvShapes *points);
+static void gv_shapes_get_memento(GvData *points, gpointer info, 
+                                  GvDataMemento **memento);
+static void gv_shapes_set_memento(GvData *points, GvDataMemento *memento);
+static void gv_shapes_del_memento(GvData *points, GvDataMemento *memento);
+static void gv_shapes_changed(GvData *points, gpointer data);
+static void gv_shapes_finalize(GtkObject *object);
+
+GtkType
+gv_shapes_get_type(void)
+{
+    static GtkType type = 0;
+
+    if (!type)
+    {
+	static const GtkTypeInfo shapes_info =
+	{
+	    "GvShapes",
+	    sizeof(GvShapes),
+	    sizeof(GvShapesClass),
+	    (GtkClassInitFunc) gv_shapes_class_init,
+	    (GtkObjectInitFunc) gv_shapes_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	type = gtk_type_unique(gv_data_get_type(), &shapes_info);
+    }
+    return type;
+}
+
+static void
+gv_shapes_init(GvShapes *shapes)
+{
+    shapes->shapes = g_ptr_array_new();
+    shapes->extents_valid = FALSE;
+    shapes->actual_num_shapes = 0;
+}
+
+static void
+gv_shapes_class_init(GvShapesClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvDataClass *data_class;
+
+    object_class = (GtkObjectClass*) klass;
+    data_class = (GvDataClass*) klass;
+
+    object_class->finalize = gv_shapes_finalize;
+    
+    data_class->changed = gv_shapes_changed;
+    data_class->get_memento = gv_shapes_get_memento;
+    data_class->set_memento = gv_shapes_set_memento;
+    data_class->del_memento = gv_shapes_del_memento;
+}
+
+GvData *
+gv_shapes_new(void)
+{
+    GvData *data;
+
+    data = GV_DATA(gtk_type_new(gv_shapes_get_type()));
+
+    CPLDebug( "OpenEV", "gv_shapes_new(%p)", data );
+
+    return data;
+}
+
+gint
+gv_shapes_add_shape(GvShapes *shapes, GvShape *new_shape)
+
+{
+    int  shape_id;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+    /* Identify where to put it, reuse old holes if available */
+    if( shapes->shapes->len != shapes->actual_num_shapes )
+    {
+        for( shape_id=0; shape_id < shapes->shapes->len; shape_id++ )
+        {
+            if( g_ptr_array_index(shapes->shapes, shape_id) == NULL )
+                break;
+        }
+    }
+    else
+    {
+        shape_id = shapes->shapes->len;
+    }
+
+    /* Make notification of impending change */
+    change_info.shape_id = &shape_id;
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    /* apply update */
+    if( shape_id == shapes->shapes->len )
+        g_ptr_array_add(shapes->shapes, new_shape );
+    else
+        g_ptr_array_index(shapes->shapes, shape_id) = new_shape;
+
+    gv_shape_ref( new_shape );
+    shapes->actual_num_shapes++;
+
+    /* notify of completed change */
+    gv_data_changed(GV_DATA(shapes), &change_info);
+    
+    return shape_id;
+}
+
+/* same as gv_shape_add_shape but do not fill the holes, always append shape at the end*/
+gint
+gv_shapes_add_shape_last(GvShapes *shapes, GvShape *new_shape)
+
+{
+    int  shape_id;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 1, NULL};
+
+
+    shape_id = shapes->shapes->len;
+
+    /* Make notification of impending change */
+    change_info.shape_id = &shape_id;
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    /* apply update */
+    if( shape_id == shapes->shapes->len )
+        g_ptr_array_add(shapes->shapes, new_shape );
+    else
+        g_ptr_array_index(shapes->shapes, shape_id) = new_shape;
+
+    gv_shape_ref( new_shape );
+    shapes->actual_num_shapes++;
+
+    /* notify of completed change */
+    gv_data_changed(GV_DATA(shapes), &change_info);
+    
+    return shape_id;
+}
+
+void
+gv_shapes_delete_shapes(GvShapes *shapes, gint num_shapes, gint *id_list)
+{
+    GvShapeChangeInfo change_info = {GV_CHANGE_DELETE, 0, NULL};
+    GvShape  *shape;
+    gint     i;
+
+    change_info.num_shapes = num_shapes;
+    change_info.shape_id = id_list;
+
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    for( i = 0; i < num_shapes; i++ )
+    {
+        if( id_list[i] < 0 || id_list[i] >= shapes->shapes->len )
+            shape = NULL;
+        else
+            shape = g_ptr_array_index(shapes->shapes, id_list[i]);
+
+        if( shape != NULL )
+        {
+            g_ptr_array_index(shapes->shapes,id_list[i]) = NULL;
+            gv_shape_unref(shape);
+            shapes->actual_num_shapes--;
+        }
+    }
+
+    /* Boil NULLs off the end of the list */
+    while( shapes->shapes->len > 0 
+           && g_ptr_array_index(shapes->shapes, 
+                                shapes->shapes->len-1) == NULL )
+    {
+        g_ptr_array_remove_index_fast( shapes->shapes, 
+                                       shapes->shapes->len-1 );
+    }
+
+    gv_data_changed(GV_DATA(shapes), &change_info);
+}
+
+void
+gv_shapes_translate_shapes(GvShapes *shapes, gint num_shapes, gint *id_list,
+			   gvgeocoord dx, gvgeocoord dy)
+{
+    GvShape *shape;
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_shapes;
+    change_info.shape_id = id_list;
+
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    for (i=0; i < num_shapes; ++i)
+    {
+        int    ring;
+
+        shape = gv_shapes_get_shape(shapes,id_list[i]);
+        if( shape == NULL )
+            continue;
+        
+        for( ring = gv_shape_get_rings(shape)-1; ring >= 0; ring-- )
+        {
+            int    node;
+
+            for( node = gv_shape_get_nodes(shape,ring)-1; 
+                 node >= 0; node-- )
+            {
+                gv_shape_set_xyz( shape, ring, node, 
+                                  gv_shape_get_x(shape, ring, node) + dx,
+                                  gv_shape_get_y(shape, ring, node) + dy,
+                                  gv_shape_get_z(shape, ring, node) );
+            }
+        }
+    }
+    gv_data_changed(GV_DATA(shapes), &change_info);
+}
+
+void
+gv_shapes_get_extents(GvShapes *shapes, GvRect *rect)
+{
+    if (!shapes->extents_valid)
+    {
+	gint i, num_shapes, valid_shapes = 0;
+	GvVertex vmax, vmin;
+
+	vmin.x = vmin.y = GV_MAXFLOAT;
+	vmax.x = vmax.y = -GV_MAXFLOAT;
+
+	num_shapes = gv_shapes_num_shapes(shapes);
+	for (i=0; i < num_shapes; ++i)
+	{
+            GvRect   rect;
+            GvShape *shape = gv_shapes_get_shape(shapes,i);
+
+            if( shape == NULL )
+                continue;
+
+            gv_shape_get_extents( shape, &rect );
+
+            if( rect.x != 0 || rect.y != 0 
+                || rect.width != 0 || rect.height != 0 )
+            {
+                valid_shapes++;
+                vmin.x = MIN(vmin.x,rect.x);
+                vmax.x = MAX(vmax.x,rect.x+rect.width);
+                vmin.y = MIN(vmin.y,rect.y);
+                vmax.y = MAX(vmax.y,rect.y+rect.height);
+            }
+	}
+
+	if (valid_shapes == 0)
+	{
+	    shapes->extents.x = 0;
+	    shapes->extents.y = 0;
+	    shapes->extents.width = 0;
+	    shapes->extents.height = 0;
+	}
+	else
+	{
+	    shapes->extents.x = vmin.x;
+	    shapes->extents.y = vmin.y;
+	    shapes->extents.width = vmax.x - vmin.x;
+	    shapes->extents.height = vmax.y - vmin.y;
+	}
+	shapes->extents_valid = TRUE;
+    }
+
+    *rect = shapes->extents;
+}
+
+void
+gv_shapes_replace_shapes(GvShapes *shapes, gint num_shapes, gint *shape_id,
+			 GvShape **shps, int make_copy)
+{
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+
+    change_info.num_shapes = num_shapes;
+    change_info.shape_id = shape_id;
+
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    for (i=0; i < num_shapes; ++i)
+    {
+        GvShape	*shape;
+
+        if( shape_id[i] < 0 || shape_id[i] >= shapes->shapes->len )
+            continue;
+        else if( gv_shapes_get_shape(shapes, shape_id[i]) != NULL )
+            gv_shape_unref( gv_shapes_get_shape(shapes, shape_id[i]) );
+        else
+            g_warning( "Missing shape in gv_shapes_replace_shapes()" );
+
+        if( make_copy )
+            shape = gv_shape_copy(shps[i]);
+        else
+            shape = shps[i];
+
+        gv_shape_ref( shape );
+        g_ptr_array_index(shapes->shapes, shape_id[i]) = shape;
+    }
+
+    gv_data_changed(GV_DATA(shapes), &change_info);
+}
+
+static void
+gv_shapes_insert_shapes(GvShapes *shapes, gint num_shapes, gint *shape_ids,
+			GvShape **shps)
+{
+    gint i;
+    GvShapeChangeInfo change_info = {GV_CHANGE_ADD, 0, NULL};
+
+    change_info.num_shapes = num_shapes;
+    change_info.shape_id = shape_ids;
+
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    for (i=0; i < num_shapes; ++i)
+    {
+        int id = shape_ids[i];
+
+        if( id >= shapes->shapes->len )
+        {
+            int  old_length = shapes->shapes->len;
+
+            g_ptr_array_set_size( shapes->shapes, id+1 );
+            while( old_length < id )
+            {
+                g_ptr_array_index(shapes->shapes, old_length) = NULL;
+                old_length++;
+            }
+
+            gv_shape_ref( shps[i] );
+            g_ptr_array_index( shapes->shapes, id ) = shps[i];
+
+            shapes->actual_num_shapes++;
+        }
+        else if( g_ptr_array_index( shapes->shapes, id ) == NULL )
+        {
+            gv_shape_ref( shps[i] );
+            g_ptr_array_index( shapes->shapes, id ) = shps[i];
+            shapes->actual_num_shapes++;
+        }
+        else
+        {
+            g_warning( "gv_shapes_insert_shapes(): target shape_id not NULL!");
+        }
+    }
+
+    gv_data_changed(GV_DATA(shapes), &change_info);
+}
+
+static void
+gv_shapes_get_memento(GvData *gv_data, gpointer data,
+		      GvDataMemento **memento)
+{
+    GvShapes	*shapes = GV_SHAPES(gv_data);
+    GvShapesMemento *mem;
+    GvShapeChangeInfo *info = (GvShapeChangeInfo *) data;
+    int i;
+
+    mem = g_new(GvShapesMemento, 1);
+    mem->base.data = GV_DATA(shapes);
+    mem->base.type = info->change_type;
+
+    mem->ids = g_array_new(FALSE, FALSE, sizeof(gint));
+    g_array_append_vals(mem->ids, info->shape_id, info->num_shapes);
+
+    /* Grab in ascending order */
+    if (info->num_shapes > 1)
+    {
+	g_sort_type(mem->ids->data, gint, mem->ids->len);
+    }
+
+    if (info->change_type == GV_CHANGE_ADD)
+    {
+	mem->shapes = NULL;
+    }
+    else
+    {
+	mem->shapes = g_ptr_array_new();
+	for (i=0; i < info->num_shapes; ++i)
+	{
+            GvShape    *shape = gv_shapes_get_shape(shapes,info->shape_id[i]);
+
+            shape = gv_shape_copy( shape );
+            gv_shape_ref( shape );
+            g_ptr_array_add(mem->shapes, shape );
+	}
+    }
+
+    *memento = (GvDataMemento*)mem;
+}
+
+static void
+gv_shapes_set_memento(GvData *gv_data, GvDataMemento *data_memento)
+{
+    GvShapes	*shapes = GV_SHAPES(gv_data);
+    GvShapesMemento *memento = (GvShapesMemento *) data_memento;
+
+    switch (memento->base.type)
+    {
+	case GV_CHANGE_ADD:
+	    gv_shapes_delete_shapes(shapes, memento->ids->len,
+				    (gint*)memento->ids->data);
+	    break;
+
+	case GV_CHANGE_REPLACE:
+	    gv_shapes_replace_shapes(shapes, memento->ids->len,
+				     (gint*)memento->ids->data,
+				     (GvShape **)memento->shapes->pdata,
+                                     TRUE);
+	    break;
+
+	case GV_CHANGE_DELETE:
+	    gv_shapes_insert_shapes(shapes, memento->ids->len,
+				     (gint*)memento->ids->data,
+				     (GvShape **)memento->shapes->pdata);
+	    break;
+    }
+
+    gv_shapes_del_memento((GvData *) shapes, (GvDataMemento *) memento);
+}
+
+static void
+gv_shapes_del_memento(GvData *gv_data, GvDataMemento *data_memento)
+{
+    GvShapesMemento *memento = (GvShapesMemento *) data_memento;
+
+    if (memento->shapes)
+    {
+        int  i;
+
+        for (i=0; i < memento->shapes->len; ++i)
+        {
+            gv_shape_unref(g_ptr_array_index(memento->shapes, i));
+        }
+        g_ptr_array_free(memento->shapes, TRUE);
+
+    }
+    g_array_free(memento->ids, TRUE);
+    g_free(memento);
+}
+
+static void
+gv_shapes_changed(GvData *gv_data, gpointer data)
+{
+    GvShapes	*shapes = GV_SHAPES(gv_data);
+
+    shapes->extents_valid = FALSE;
+}
+
+static void
+gv_shapes_finalize(GtkObject *object)
+{
+    GvDataClass *parent_class;
+    GvShapes *shapes = GV_SHAPES(object);
+    int          i;
+
+    CPLDebug( "OpenEV", "gv_shapes_finalize(%s/%p)", 
+              gv_data_get_name( GV_DATA(object) ), object );
+
+    for( i = 0; i < gv_shapes_num_shapes(shapes); i++ )
+    {
+        if( gv_shapes_get_shape(shapes, i) != NULL )
+            gv_shape_unref( gv_shapes_get_shape(shapes, i) );
+    }
+
+    g_ptr_array_free(shapes->shapes,TRUE);
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_data_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}
+
+#ifndef HAVE_OGR
+GvData *gv_shapes_from_ogr(const char *filename, int iLayer)
+{
+    CPLDebug( "OpenEV", 
+              "gv_shapes_from_ogr(%s) called, but OGR not configured",
+              filename );
+    return NULL;
+}
+
+GvData *gv_shapes_from_ogr_layer(void *)
+{
+    CPLDebug( "OpenEV", "gv_shapes_from_ogr_layer() called, "
+              "but OGR not configured" );
+    return NULL;
+}
+#endif
+
+void
+gv_shapes_add_height( GvShapes *shapes, GvData *raster_data, double offset,
+                      double default_height )
+{
+    int success, i, num_shapes;
+    double x, y, z, last_z, imaginary, nodata_value;
+    GvRaster *raster = GV_RASTER(raster_data);
+    GvShapeChangeInfo change_info = {GV_CHANGE_REPLACE, 0, NULL};
+    int	   *id_list;
+
+    /*
+     * Notify of impending change.
+     */
+    num_shapes = gv_shapes_num_shapes(shapes);
+    id_list = g_new( int, num_shapes );
+
+    change_info.num_shapes = 0;
+    change_info.shape_id = id_list;
+
+    for (i=0; i < num_shapes; i++)
+    {
+        if( gv_shapes_get_shape(shapes,i) != NULL )
+            id_list[change_info.num_shapes++] = i;
+    }
+
+    gv_data_changing(GV_DATA(shapes), &change_info);
+
+    /*
+     * Establish the "nodata" value.
+     */
+    success = gv_raster_get_nodata( raster, &nodata_value );
+    if( !success )
+        nodata_value = -1e8;
+
+    /*
+     * Loop over all shapes, applying height. 
+     */
+    for (i=0; i < num_shapes; i++)
+    {
+        GvShape *shape = gv_shapes_get_shape(shapes,i);
+        int 	ring, ring_count = gv_shape_get_rings( shape );
+        
+        if( shape == NULL )
+            continue;
+
+        last_z = default_height;
+
+        for( ring = 0; ring < ring_count; ring++ )
+        {
+            int	node, node_count = gv_shape_get_nodes( shape, ring );
+
+            for( node = 0; node < node_count; node++ )
+            {
+                double	x_orig, y_orig;
+
+                /* get xy in image space */
+                x_orig = x = gv_shape_get_x( shape, ring, node );
+                y_orig = y = gv_shape_get_y( shape, ring, node );
+                z = 0.0;
+
+                if (!gv_raster_georef_to_pixel(raster, &x, &y, &z))
+                {
+                    fprintf(stderr, "ERROR raster_georef_to_pixel failed!!!\n");
+                    break;
+                }
+
+                if( x > -1.0 && x < 0.0 )
+                    x = 0.0;
+                if( y > -1.0 && y < 0.0 )
+                    y = 0.0;
+                if( x >= raster->width && x < raster->width+1 )
+                    x = raster->width - 0.01;
+                if( y >= raster->height && y < raster->height+1 )
+                    y = raster->height - 0.01;
+
+                /* Check if mesh xy values outside of height raster 
+                   - leave as 0 */
+                if( x >= 0.0 && x < raster->width
+                    && y >= 0.0 && y < raster->height )
+                {
+                    if (!gv_raster_get_sample(raster, x, y, &z, &imaginary))
+                    {
+                        fprintf(stderr, 
+                                "ERROR raster_get_sample failed for (x y z) %f %f\n", 
+                                x, y);
+                    }
+                    else
+                    {
+                        if( z == nodata_value && x > 0 )
+                            gv_raster_get_sample(raster, x-1, y, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && x < raster->width-1 )
+                            gv_raster_get_sample(raster, x+1, y, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && y > 0 )
+                            gv_raster_get_sample(raster, x, y-1, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && y < raster->height-1 )
+                            gv_raster_get_sample(raster, x, y+1, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && x > 1 )
+                            gv_raster_get_sample(raster, x-2, y, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && x < raster->width-2 )
+                            gv_raster_get_sample(raster, x+2, y, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && y > 1 )
+                            gv_raster_get_sample(raster, x, y-2, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value && y < raster->height-2 )
+                            gv_raster_get_sample(raster, x, y+2, &z, 
+                                                 &imaginary);
+
+                        if( z == nodata_value )
+                            z = last_z;
+                        else
+                            z += offset;
+
+                        last_z = z;
+                    }
+                }
+
+                gv_shape_set_xyz( shape, ring, node, x_orig, y_orig, z );
+            }
+        }
+    }
+
+    /* notify of completed change */
+    gv_data_changed(GV_DATA(shapes), &change_info);
+
+    g_free( id_list);
+}

Added: packages/openev/branches/upstream/current/gvshapes.h
===================================================================
--- packages/openev/branches/upstream/current/gvshapes.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapes.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,254 @@
+/******************************************************************************
+ * $Id: gvshapes.h,v 1.26 2005/01/14 16:51:51 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Definitions of GvShape (single object) and GvShapes (data
+ *           container).
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapes.h,v $
+ * Revision 1.26  2005/01/14 16:51:51  gmwalter
+ * Checked in Aude's gv_shapes_add_shape_last function
+ * (allows shapes to be added without repeating
+ * indices if others have been deleted).
+ *
+ * Revision 1.25  2005/01/04 18:50:31  gmwalter
+ * Checked in Aude's new gvshape function changes.
+ *
+ * Revision 1.24  2003/08/29 20:52:43  warmerda
+ * added to/from xml translation for GvShape
+ *
+ * Revision 1.23  2003/06/25 17:06:06  warmerda
+ * added gv_shape_rotate(), gv_shape_scale() and related stuff
+ *
+ * Revision 1.22  2003/01/06 21:20:03  warmerda
+ * added gv_shapes_from_ogr_layer
+ *
+ * Revision 1.21  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.20  2002/07/18 19:33:57  pgs
+ * added gv_shapes_to_dbf
+ *
+ * Revision 1.19  2002/05/07 02:51:15  warmerda
+ * preliminary support for GVSHAPE_COLLECTION
+ *
+ * Revision 1.18  2002/03/07 18:31:56  warmerda
+ * added preliminary gv_shape_clip_to_rect() implementation
+ *
+ * Revision 1.17  2002/03/07 02:31:56  warmerda
+ * added default_height to add_height functions
+ *
+ * Revision 1.16  2001/12/08 04:49:38  warmerda
+ * added point in polygon test
+ *
+ * Revision 1.15  2001/08/08 17:45:48  warmerda
+ * GvShape now referenced counted
+ *
+ * Revision 1.14  2001/01/18 16:48:14  warmerda
+ * added gv_shapes_add_height() and wrappers
+ *
+ * Revision 1.13  2000/08/04 14:14:12  warmerda
+ * GvShapes shape ids now persistent
+ *
+ * Revision 1.12  2000/07/14 14:51:01  warmerda
+ * fixed insert, and delete node support
+ *
+ * Revision 1.11  2000/07/13 19:08:37  warmerda
+ * added coping optional for gv_shapes_replace_shapes
+ *
+ * Revision 1.10  2000/06/28 13:10:42  warmerda
+ * added preliminary OGR support
+ *
+ * Revision 1.9  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_SHAPES_H__
+#define __GV_SHAPES_H__
+
+#include "gvdata.h"
+#include "cpl_minixml.h"
+
+#define GV_TYPE_SHAPES           (gv_shapes_get_type ())
+#define GV_SHAPES(obj)           (GTK_CHECK_CAST ((obj), GV_TYPE_SHAPES,\
+                                                  GvShapes))
+#define GV_SHAPES_CLASS(klass)   (GTK_CHECK_CLASS_CAST((klass),GV_TYPE_SHAPES,\
+                                                       GvShapesClass))
+#define GV_IS_SHAPES(obj)        (GTK_CHECK_TYPE ((obj), GV_TYPE_SHAPES))
+#define GV_IS_SHAPES_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_SHAPES))
+
+#define GVSHAPE_POINT      1
+#define GVSHAPE_LINE       2
+#define GVSHAPE_AREA       3
+#define GVSHAPE_COLLECTION 4
+
+#define GVSF_TYPE_MASK      0x07
+#define GVSF_CUSTOM_RENDER  0x08
+
+typedef struct
+{
+    guint     flags;
+    guint     ref_count;
+    GvProperties properties;
+} GvShape;
+
+typedef struct
+{
+    guint     flags;
+    guint     ref_count;
+    GvProperties properties;
+    gvgeocoord     x;
+    gvgeocoord     y;
+    gvgeocoord     z;
+} GvPointShape;
+
+typedef struct
+{
+    guint     flags;
+    guint     ref_count;
+    GvProperties properties;
+    int       num_nodes;
+    gvgeocoord     *xyz_nodes;
+} GvLineShape;
+
+typedef struct
+{
+    guint     flags;
+    guint     ref_count;
+    GvProperties properties;
+    int       num_rings;
+    int       *num_ring_nodes;
+    gvgeocoord     **xyz_ring_nodes;
+
+    /* tesselation information */
+    gint      fill_objects; /* -1 is untesselated, -2 is `do not tesselate' */
+    GArray    *mode_offset;
+    GArray    *fill;
+} GvAreaShape;
+
+typedef struct
+{
+    guint     flags;
+    guint     ref_count;
+    GvProperties properties;
+    int       geom_count;
+    GvShape   **geom_list;
+} GvCollectionShape;
+
+typedef struct
+{
+    GvData data;
+
+    GPtrArray *shapes;
+    int    actual_num_shapes;  /* not including NULLs in GPtrArray */
+
+    GvRect extents;
+    guint extents_valid : 1;
+} GvShapes;
+
+typedef struct
+{
+    GvDataClass parent_class;
+} GvShapesClass;
+
+GvShape* gv_shape_new( gint type );
+GvShape* gv_shape_from_xml_tree( CPLXMLNode * );
+CPLXMLNode *gv_shape_to_xml_tree( GvShape *psShape );
+
+#define gv_shape_get_properties( shape ) (&(shape)->properties)
+#define gv_shape_type( shape ) ((shape)->flags & GVSF_TYPE_MASK)
+
+void     gv_shape_unref( GvShape *shape );
+void     gv_shape_ref( GvShape *shape );
+int      gv_shape_get_ref( GvShape *shape );
+void     gv_shape_delete( GvShape *shape );
+GvShape* gv_shape_copy( GvShape *shape );
+gint     gv_shape_get_rings( GvShape *shape );
+gint     gv_shape_get_nodes( GvShape *shape, gint ring );
+gvgeocoord  gv_shape_get_xyz( GvShape *shape, gint ring, gint node, gint off );
+void     gv_shape_get_extents( GvShape *shape, GvRect *rect );
+
+#define gv_shape_get_x(shape,ring,node) gv_shape_get_xyz(shape,ring,node,0)
+#define gv_shape_get_y(shape,ring,node) gv_shape_get_xyz(shape,ring,node,1)
+#define gv_shape_get_z(shape,ring,node) gv_shape_get_xyz(shape,ring,node,2)
+
+gint     gv_shape_set_xyz( GvShape *shape, gint ring, gint node,
+                           gvgeocoord x, gvgeocoord y, gvgeocoord z );
+gint     gv_shape_add_node( GvShape *shape, gint ring,
+                             gvgeocoord x, gvgeocoord y, gvgeocoord z );
+gint     gv_shape_insert_node( GvShape *shape, gint ring, int node,
+                               gvgeocoord x, gvgeocoord y, gvgeocoord z );
+gint     gv_shape_delete_node( GvShape *shape, gint ring, gint node );
+gint     gv_shape_delete_ring( GvShape *shape, gint ring );
+gint     gv_shape_point_in_polygon( GvShape *shape_poly, double x, double y );
+gdouble gv_shape_distance_from_polygon( GvShape *shape_poly, double x, double y );
+GvShape *gv_shape_clip_to_rect( GvShape *shape, GvRect *rect );
+
+void     gv_shape_collection_add_shape( GvShape *collection, GvShape *shape );
+GvShape *gv_shape_collection_get_shape( GvShape *collection, int shp_index );
+int      gv_shape_collection_get_count( GvShape *collection );
+
+GtkType  gv_shapes_get_type (void);
+
+gint     gv_area_shape_tessellate(GvAreaShape *area);
+
+int      gv_shape_get_count(void);
+gint     gv_shape_update_attribute( GvShape *shape, const char *tool, 
+                                    const char *attribute, 
+                                    const char *update_value );
+gint gv_shape_get_center( GvShape *shape, GvVertex3d *xyz );
+gint gv_shape_rotate( GvShape *shape, double angle_in_degrees );
+gint gv_shape_scale( GvShape *shape, double new_scale );
+
+GvData* gv_shapes_new(void);
+GvData* gv_shapes_from_shapefile(const char *);
+GvData* gv_shapes_from_ogr(const char *,int);
+GvData* gv_shapes_from_ogr_layer(void *);
+
+void    gv_shapes_add_height(GvShapes *shapes, GvData *raster, double offset,
+                             double default_height );
+int gv_shapes_to_shapefile(const char *,GvData *, int);
+int gv_shapes_to_dbf(const char *,GvData * );
+void gv_shapes_get_extents(GvShapes *shapes, GvRect *rect);
+gint gv_shapes_add_shape(GvShapes *shapes, GvShape *shape);
+gint gv_shapes_add_shape_last(GvShapes *shapes, GvShape *shape);
+void gv_shapes_replace_shapes(GvShapes *shapes, gint num_shapes,
+                              gint *shape_id, GvShape **shps, int make_copy);
+void gv_shapes_delete_shapes(GvShapes *shapes, gint num_shapes, gint*shapeids);
+void gv_shapes_translate_shapes(GvShapes *shapes, gint num_shapes,
+                                gint *shapeids, gvgeocoord dx, gvgeocoord dy );
+
+#define gv_shapes_num_shapes(adata) \
+     (adata->shapes->len)
+
+#define gv_shapes_get_shape(adata,id) \
+     ((GvShape*)g_ptr_array_index(adata->shapes, id))
+
+#ifdef HAVE_OGR
+#define gv_have_ogr_support() 1
+#else
+#define gv_have_ogr_support() 0
+#endif
+
+#endif /*__GV_SHAPES_H__ */

Added: packages/openev/branches/upstream/current/gvshapeslayer.c
===================================================================
--- packages/openev/branches/upstream/current/gvshapeslayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapeslayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2526 @@
+/******************************************************************************
+ * $Id: gvshapeslayer.c,v 1.73 2004/04/08 18:03:18 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer for vector shapes.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapeslayer.c,v $
+ * Revision 1.73  2004/04/08 18:03:18  gmwalter
+ * Fix for line node editing, and for area
+ * editing in the case where the first
+ * and last nodes are different but share
+ * either the x or y coordinate.
+ *
+ * Revision 1.72  2004/03/22 19:21:15  gmwalter
+ * Fixed initialization problem.
+ *
+ * Revision 1.71  2004/03/08 18:23:43  gmwalter
+ * Make sure layer's _point_size property is used.
+ *
+ * Revision 1.70  2004/02/12 22:11:21  gmwalter
+ * Update selected shape display in Atlantis
+ * build case.
+ *
+ * Revision 1.69  2004/01/21 01:13:44  sduclos
+ * fix typo
+ *
+ * Revision 1.68  2004/01/20 16:13:01  warmerda
+ * added render plugin support for S52 viewer
+ *
+ * Revision 1.67  2003/09/12 17:35:42  warmerda
+ * Added logic to aggregate selection boxes in the drawinfo.selection_box
+ * rectangle when draw_mode == NORMAL_GET_BOX.  This is intended to allow
+ * us to draw a selection box around a complex shapes consisting of multiple
+ * parts offset from the reference point.  In the past we only drew the
+ * selection around the first part in such cases.  This seems to work though
+ * the testing isn't ... extensive.
+ *
+ * Revision 1.66  2003/09/02 17:22:26  warmerda
+ * added per-layer symbol manager support
+ *
+ * Revision 1.65  2003/08/27 20:03:05  warmerda
+ * Added geo2screen_works to drawinfo structure.  It is set to false for text
+ * drawn within symbols since symbol rescaling via glScale() will mess up
+ * the bmfont_draw() logic for ensuring text is drawn "on screen".  True
+ * otherwise.  It is passed on to gv_view_area_bmfont_draw().
+ *
+ * Revision 1.64  2003/07/03 16:12:05  pgs
+ * fixed bug in y offset of symbols (typo in var name)
+ *
+ * Revision 1.63  2003/05/16 21:31:26  warmerda
+ * added support for passing default color down to components of a symbol
+ *
+ * Revision 1.62  2003/05/16 18:26:33  pgs
+ * added initial code for propogating colors to sub-symbols
+ *
+ * Revision 1.61  2003/05/16 17:42:39  warmerda
+ * fix up pixel offsets for sub-symbols
+ *
+ * Revision 1.60  2003/05/08 19:51:05  warmerda
+ * Fixed node picking for gv_shapes_layer_draw_pen().
+ * Draw selection boxes as squares instead of diamonds.
+ */
+
+#include "gvshapeslayer.h"
+#include "gvutils.h"
+#include "gvrenderinfo.h"
+#include "gvsymbolmanager.h"
+#include "gvmanager.h"
+#include "cpl_error.h"
+#include <GL/gl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <gtk/gtksignal.h>
+#include <gmodule.h>
+
+#define DEFAULT_POINT_SIZE 6
+#define DEFAULT_LINE_WIDTH 1.0
+
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+
+#ifndef PI
+#define PI  3.1415927
+#endif
+
+static void gv_shapes_layer_class_init(GvShapesLayerClass *klass);
+static void gv_shapes_layer_init(GvShapesLayer *layer);
+static void gv_shapes_layer_display_change(GvLayer *data,gpointer change_info);
+static void gv_shapes_layer_selection_change(GvLayer *data,
+                                             gpointer change_info);
+static void gv_shapes_layer_extents(GvLayer *layer, GvRect *rect);
+static void gv_shapes_layer_data_change(GvData *layer, gpointer change_info);
+static void gv_shapes_layer_delete_selected(GvShapeLayer *layer);
+static void gv_shapes_layer_translate_selected(GvShapeLayer *layer,
+                                               GvVertex *delta);
+static void gv_shapes_layer_pick_shape(GvShapeLayer *layer);
+static void gv_shapes_layer_pick_node(GvShapeLayer *rlayer);
+static void gv_shapes_layer_get_node(GvShapeLayer *layer, GvNodeInfo *info);
+static void gv_shapes_layer_move_node(GvShapeLayer *layer, GvNodeInfo *info);
+static void gv_shapes_layer_insert_node(GvShapeLayer *layer, GvNodeInfo *info);
+static void gv_shapes_layer_delete_node(GvShapeLayer *layer, GvNodeInfo *info);
+static void gv_shapes_layer_node_motion(GvShapeLayer *layer, gint area_id);
+static void gv_shapes_layer_finalize(GtkObject *layer );
+static void gv_shapes_layer_destroy( GtkObject *object );
+
+/************************************************************************/
+/*                      gv_shapes_layer_get_type()                      */
+/************************************************************************/
+GtkType
+gv_shapes_layer_get_type(void)
+{
+    static GtkType shapes_layer_type = 0;
+
+    if (!shapes_layer_type)
+    {
+    static const GtkTypeInfo shapes_layer_info =
+    {
+        "GvShapesLayer",
+        sizeof(GvShapesLayer),
+        sizeof(GvShapesLayerClass),
+        (GtkClassInitFunc) gv_shapes_layer_class_init,
+        (GtkObjectInitFunc) gv_shapes_layer_init,
+        /* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
+    };
+
+    shapes_layer_type = gtk_type_unique(gv_shape_layer_get_type(),
+                                            &shapes_layer_info);
+    }
+    return shapes_layer_type;
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_class_init()                     */
+/************************************************************************/
+static void
+gv_shapes_layer_class_init(GvShapesLayerClass *klass)
+{
+    GvDataClass *data_class;
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    data_class = (GvDataClass*) klass;
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    data_class->changed = gv_shapes_layer_data_change;
+
+    layer_class->draw = gv_shapes_layer_draw;
+    layer_class->extents_request = gv_shapes_layer_extents;
+
+    shape_layer_class->draw_selected = gv_shapes_layer_draw_selected;
+    shape_layer_class->delete_selected = gv_shapes_layer_delete_selected;
+    shape_layer_class->translate_selected = gv_shapes_layer_translate_selected;
+    shape_layer_class->pick_shape = gv_shapes_layer_pick_shape;
+    shape_layer_class->pick_node = gv_shapes_layer_pick_node;
+    shape_layer_class->get_node = gv_shapes_layer_get_node;
+    shape_layer_class->move_node = gv_shapes_layer_move_node;
+    shape_layer_class->insert_node = gv_shapes_layer_insert_node;
+    shape_layer_class->delete_node = gv_shapes_layer_delete_node;
+    shape_layer_class->node_motion = gv_shapes_layer_node_motion;
+
+    ((GtkObjectClass *) klass)->destroy = gv_shapes_layer_destroy;
+    ((GtkObjectClass *) klass)->finalize = gv_shapes_layer_finalize;
+}
+
+/************************************************************************/
+/*                        gv_shapes_layer_init()                        */
+/************************************************************************/
+static void
+gv_shapes_layer_init(GvShapesLayer *layer)
+{
+    GvColor default_shapes_color = {0.5, 1.0, 0.5, 1.0};
+
+    /* 0 is always an invalid display list number.  */
+    layer->display_list = 0;
+
+    layer->data = NULL;
+    layer->symbol_manager = NULL;
+
+    gv_color_copy(GV_SHAPE_LAYER(layer)->color, default_shapes_color);
+
+    gv_properties_set( &(GV_DATA(layer)->properties), "_point_color",
+                       "0.5 1.0 0.5 1.0" );
+    gv_properties_set( &(GV_DATA(layer)->properties), "_line_color",
+                       "0.5 1.0 0.5 1.0" );
+    gv_properties_set( &(GV_DATA(layer)->properties), "_line_width",
+                       "1.0" );
+    gv_properties_set( &(GV_DATA(layer)->properties), "_area_edge_color",
+                       "0.5 1.0 0.5 1.0" );
+    gv_properties_set( &(GV_DATA(layer)->properties), "_area_edge_width",
+                       "1.0" );
+    gv_properties_set( &(GV_DATA(layer)->properties), "_area_fill_color",
+                       "0.5 1.0 0.5 0.5" );
+
+    gtk_signal_connect_object(GTK_OBJECT(layer), "display-change",
+                  GTK_SIGNAL_FUNC(gv_shapes_layer_display_change),
+                  GTK_OBJECT(layer));
+    gtk_signal_connect_object(GTK_OBJECT(layer), "selection-changed",
+                  GTK_SIGNAL_FUNC(gv_shapes_layer_selection_change),
+                  GTK_OBJECT(layer));
+}
+
+/************************************************************************/
+/*                         gv_shape_layer_new()                         */
+/************************************************************************/
+GtkObject *
+gv_shapes_layer_new(GvShapes *data)
+{
+    GvShapesLayer *layer = GV_SHAPES_LAYER(gtk_type_new(
+    gv_shapes_layer_get_type()));
+
+    if( data != NULL )
+        gv_shapes_layer_set_data( layer, data );
+    else
+        gv_shapes_layer_set_data( layer, GV_SHAPES(gv_shapes_new()) );
+
+    return GTK_OBJECT(layer);
+}
+
+/************************************************************************/
+/*                      gv_shape_layer_set_data()                       */
+/************************************************************************/
+void gv_shapes_layer_set_data( GvShapesLayer *layer,
+                               GvShapes *data )
+
+{
+    layer->data = data;
+    gv_data_set_parent(GV_DATA(layer), GV_DATA(layer->data));
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+                  gv_shapes_num_shapes(layer->data));
+
+#ifdef GV_USE_RENDER_PLUGIN
+    {
+        const char   *prop_name  = "_ogr_driver_name";
+        GvProperties *properties = gv_data_get_properties(GV_DATA(data));
+        const char   *prop_value = gv_properties_get(properties, prop_name);
+
+        if (g_module_supported() && (NULL != prop_value)) {
+            gchar* (*get_drv_name) ();
+            gchar   *plugin = g_module_build_path("/usr/local/lib", "S52");
+           GModule *module = g_module_open(plugin, G_MODULE_BIND_LAZY);
+
+            if (NULL == module)
+                printf("module error:%s\n", g_module_error());
+            else {
+                if (g_module_symbol(module, prop_name, (gpointer) &get_drv_name)) {
+                    void (*layer_init) (GvShapesLayer *layer);
+                    gchar *drv_name = get_drv_name();
+
+                    if (0 == strcmp(prop_value, drv_name)) {
+                        if (g_module_symbol(module, "_layer_init", 
+                                            (gpointer) & layer_init))
+                            layer_init(layer);
+                        else
+                            printf("module error:%s\n", g_module_error());
+                    }
+                } else
+                    printf("module error:%s\n", g_module_error());
+            }
+           g_free(plugin);
+        }
+    }
+#endif
+
+}
+
+/************************************************************************/
+/*                 gv_shapes_layer_get_symbol_manager()                 */
+/************************************************************************/
+
+GtkObject *gv_shapes_layer_get_symbol_manager( GvShapesLayer *layer, 
+                                               int ok_to_create )
+
+{
+    if( ok_to_create && layer->symbol_manager == NULL )
+        layer->symbol_manager = GTK_OBJECT(gv_symbol_manager_new());
+
+    return layer->symbol_manager;
+}
+
+/************************************************************************/
+/*                  gv_shapes_layer_select_new_shape()                  */
+/************************************************************************/
+
+gint gv_shapes_layer_select_new_shape( GvShapesLayer *layer, GvShape * shape )
+
+{
+    gint id;
+
+    id = gv_shapes_add_shape(layer->data, shape);
+
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+                  gv_shapes_num_shapes(layer->data));
+    gv_shape_layer_select_shape(GV_SHAPE_LAYER(layer), id);
+
+    return id;
+}
+
+/************************************************************************/
+/*                   gv_shapes_layer_override_color()                   */
+/************************************************************************/
+
+void
+gv_shapes_layer_override_color( GvShape * shape, GvColor color,
+                                const char * property_name )
+
+{
+    GvProperties *properties = gv_shape_get_properties(shape);
+
+    if( properties != NULL )
+    {
+        const char * user_color;
+
+        user_color = gv_properties_get( properties, property_name);
+
+        if( user_color != NULL )
+        {
+            gv_set_color_from_string( color, user_color,
+                                      color[0], color[1],
+                                      color[2], color[3] );
+        }
+    }
+}
+
+/************************************************************************/
+/*                   gv_shapes_layer_get_draw_info()                    */
+/************************************************************************/
+
+void
+gv_shapes_layer_get_draw_info(GvViewArea *view, GvShapesLayer *layer,
+                              GvShapeDrawInfo *drawinfo )
+
+{
+    GvColor  def_color;
+
+    gv_color_copy(def_color, GV_SHAPE_LAYER(layer)->color);
+    gv_color_copy(drawinfo->color, def_color );
+
+    gv_set_color_from_string(
+        drawinfo->point_color,
+        gv_properties_get( &(GV_DATA(layer)->properties), "_point_color"),
+        def_color[0], def_color[1], def_color[2], def_color[3]);
+
+    gv_set_color_from_string(
+        drawinfo->line_color,
+        gv_properties_get( &(GV_DATA(layer)->properties), "_line_color"),
+        def_color[0], def_color[1], def_color[2], def_color[3]);
+
+    if( gv_properties_get(&(GV_DATA(layer)->properties),"_line_width") != NULL)
+        drawinfo->line_width =
+          atof(gv_properties_get(&(GV_DATA(layer)->properties),"_line_width"));
+    else
+        drawinfo->line_width = DEFAULT_LINE_WIDTH;
+
+    gv_set_color_from_string(
+        drawinfo->area_edge_color,
+        gv_properties_get( &(GV_DATA(layer)->properties), "_area_edge_color"),
+        def_color[0], def_color[1], def_color[2], def_color[3]);
+
+    if( gv_properties_get(&(GV_DATA(layer)->properties),"_area_edge_width") != NULL)
+        drawinfo->area_edge_width =
+          atof(gv_properties_get(&(GV_DATA(layer)->properties),"_area_edge_width"));
+    else
+        drawinfo->area_edge_width = DEFAULT_LINE_WIDTH;
+
+    gv_set_color_from_string(
+        drawinfo->area_fill_color,
+        gv_properties_get( &(GV_DATA(layer)->properties), "_area_fill_color"),
+        def_color[0], def_color[1], def_color[2], 0.6);
+
+    if( gv_properties_get(&(GV_DATA(layer)->properties),"_point_size") != NULL)
+        drawinfo->point_size =
+          atof(gv_properties_get(&(GV_DATA(layer)->properties),"_point_size"));
+    else
+        drawinfo->point_size = DEFAULT_POINT_SIZE;
+
+    /*
+     * Transform a 1 pixel "right" vector into geo-space.  We can compose
+     * a variety of deltas from this for selection boxes, crosshair sizes
+     * and so forth.
+     */
+    drawinfo->dx = 1.0;
+    drawinfo->dy = 0.0;
+    gv_view_area_correct_for_transform(view, drawinfo->dx, drawinfo->dy,
+                                       &(drawinfo->dx), &(drawinfo->dy) );
+    drawinfo->dunit =
+        sqrt( drawinfo->dx * drawinfo->dx + drawinfo->dy * drawinfo->dy );
+    drawinfo->dpixel = drawinfo->dunit;
+    drawinfo->geo2screen_works = TRUE;
+
+    drawinfo->box_set = FALSE;
+}
+
+/************************************************************************/
+/*                gv_draw_info_aggregate_select_region()                */
+/*                                                                      */
+/*      Add the passed point to the current selection_box stored in     */
+/*      the drawinfo.                                                   */
+/************************************************************************/
+
+void gv_draw_info_aggregate_select_region( GvShapeDrawInfo *drawinfo,
+                                           double x, double y )
+
+{
+    if( !drawinfo->box_set )
+    {
+        drawinfo->box_set = TRUE;
+        drawinfo->selection_box.x = x;
+        drawinfo->selection_box.y = y;
+        drawinfo->selection_box.width = 0;
+        drawinfo->selection_box.height = 0;
+    }
+    else
+    {
+        if( x < drawinfo->selection_box.x )
+        {
+            drawinfo->selection_box.width += drawinfo->selection_box.x - x;
+            drawinfo->selection_box.x = x;
+        }
+        else if( x > drawinfo->selection_box.x+drawinfo->selection_box.width )
+        {
+            drawinfo->selection_box.width = x - drawinfo->selection_box.x;
+        }
+
+        if( y < drawinfo->selection_box.y )
+        {
+            drawinfo->selection_box.height += drawinfo->selection_box.y - y;
+            drawinfo->selection_box.y = y;
+        }
+        else if( y > drawinfo->selection_box.y+drawinfo->selection_box.height )
+        {
+            drawinfo->selection_box.height = y - drawinfo->selection_box.y;
+        }
+    }
+}
+
+/************************************************************************/
+/*                       gv_draw_info_grow_box()                        */
+/*                                                                      */
+/*      Expand the "selection box" by the indicated factor around       */
+/*      its center.  This is used before drawing it to move it out a    */
+/*      bit from the object it encloses.  Normally the growth factor    */
+/*      would be 1.2.                                                   */
+/************************************************************************/
+
+static void gv_draw_info_grow_box( GvShapeDrawInfo *drawinfo, double factor )
+
+{
+    double center_x, center_y;
+
+    center_x = drawinfo->selection_box.x + drawinfo->selection_box.width * 0.5;
+    center_y = drawinfo->selection_box.y + drawinfo->selection_box.height *0.5;
+    
+    drawinfo->selection_box.width *= factor;
+    drawinfo->selection_box.height *= factor;
+
+    /* Don't let the width or height of a selection box be too much smaller
+       than the other dimension. */
+    
+    if( drawinfo->selection_box.width < drawinfo->selection_box.height/4.0 )
+        drawinfo->selection_box.width = drawinfo->selection_box.height/4.0;
+
+    if( drawinfo->selection_box.height < drawinfo->selection_box.width/4.0 )
+        drawinfo->selection_box.height = drawinfo->selection_box.width/4.0;
+
+    drawinfo->selection_box.x = center_x - drawinfo->selection_box.width * 0.5;
+    drawinfo->selection_box.y = center_y - drawinfo->selection_box.height *0.5;
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_draw_label()                     */
+/************************************************************************/
+
+static void
+gv_shapes_layer_draw_label( GvViewArea *view, GvShapesLayer *layer,
+                            GvShape *shape, GvShapeDrawInfo *drawinfo,
+                            gvgeocoord x, gvgeocoord y,
+                            GvLabelRenderPart *label_info,
+                            gv_draw_mode draw_mode )
+
+{
+    gvgeocoord x_offset; /* for shadow and halo effects */
+    gvgeocoord y_offset;
+/* -------------------------------------------------------------------- */
+/*      Apply any offsets.                                              */
+/* -------------------------------------------------------------------- */
+    x += label_info->x_offset_g;
+    y += label_info->y_offset_g;
+
+    if( label_info->x_offset_px != 0.0 || label_info->y_offset_px != 0.0 )
+    {
+        x += label_info->x_offset_px * drawinfo->dpixel;
+        y -= label_info->y_offset_px * drawinfo->dpixel;
+    }
+
+    /* draw text effects first */
+    if (label_info->halo || label_info->shadow)
+    {
+        x_offset = drawinfo->dpixel;
+        y_offset = drawinfo->dpixel;
+
+        if (label_info->halo)
+        {
+            glColor4fv(label_info->background_color);
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x - x_offset, y - y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x - x_offset, y ,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x - x_offset, y + y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x, y + y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x + x_offset, y + y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x + x_offset, y,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x + x_offset, y - y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x, y - y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+        }
+
+        if (label_info->shadow)
+        {
+            glColor4fv(label_info->background_color);
+            gv_view_area_bmfont_draw( view, label_info->font,
+                                      x + x_offset, y + y_offset,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+        }
+    }
+
+    if (label_info->b_color_initialized)
+        glColor4fv( label_info->color );
+    else
+        glColor4fv( drawinfo->color );
+
+/* -------------------------------------------------------------------- */
+/*      Draw the text itself.                                           */
+/* -------------------------------------------------------------------- */
+    if( draw_mode != PICKING )
+    {
+        /* When dragging text, there is an extra translation applied
+           which gv_view_Area_bmfont_draw() isn't able to account for
+           while trying to keep the RasterPos in the frustrum.  Restore
+           the original translation value, and offset the x,y passed to
+           the bmfont drawer. */
+
+        if( draw_mode == SELECTED
+            && (GV_SHAPE_LAYER(layer)->selected_motion.x != 0
+                || GV_SHAPE_LAYER(layer)->selected_motion.y != 0) )
+        {
+            glTranslate(-GV_SHAPE_LAYER(layer)->selected_motion.x,
+                         -GV_SHAPE_LAYER(layer)->selected_motion.y,
+                         0.0 );
+            gv_view_area_bmfont_draw(
+                view, label_info->font,
+                x + GV_SHAPE_LAYER(layer)->selected_motion.x,
+                y + GV_SHAPE_LAYER(layer)->selected_motion.y,
+                label_info->text, 
+                !drawinfo->geo2screen_works );
+            glTranslate(GV_SHAPE_LAYER(layer)->selected_motion.x,
+                         GV_SHAPE_LAYER(layer)->selected_motion.y,
+                         0.0 );
+        }
+        else
+        {
+            gv_view_area_bmfont_draw( view, label_info->font, x, y,
+                                      label_info->text, 
+                                      !drawinfo->geo2screen_works );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Draw the selection box, or a filled polygon depending on mode.  */
+/* -------------------------------------------------------------------- */
+    if( draw_mode != NORMAL )
+    {
+        gvgeocoord  ll_px, ll_py, ur_px, ur_py, ur_geox, ur_geoy;
+        gvgeocoord      ul_geox, ul_geoy, lr_geox, lr_geoy;
+
+        gv_view_area_inverse_map_pointer( view, x, y, &ll_px, &ll_py );
+        ll_px -= 2;
+        ll_py += 2 + label_info->descent;
+        ur_px = ll_px + label_info->width + 4;
+        ur_py = ll_py - label_info->height - 4;
+        gv_view_area_map_pointer( view, ur_px, ur_py, &ur_geox, &ur_geoy );
+        gv_view_area_map_pointer( view, ll_px, ll_py, &x, &y );
+        gv_view_area_map_pointer( view, ll_px, ur_py, &ul_geox, &ul_geoy );
+        gv_view_area_map_pointer( view, ur_px, ll_py, &lr_geox, &lr_geoy );
+
+        if( draw_mode == NORMAL_GET_BOX )
+        {
+            gv_draw_info_aggregate_select_region( drawinfo, x, y );
+            gv_draw_info_aggregate_select_region( drawinfo, ur_geox, ur_geoy );
+        }
+        else if( draw_mode == SELECTED )
+        {
+            /* Draw box around text */
+            glBegin(GL_LINE_LOOP);
+            glVertex3(x, y, 0.0);
+            glVertex3(ul_geox, ul_geoy, 0.0);
+            glVertex3(ur_geox, ur_geoy, 0.0);
+            glVertex3(lr_geox, lr_geoy, 0.0);
+            glEnd();
+        }
+        else /* picking ... draw filled box */
+        {
+            glBegin(GL_POLYGON);
+            glVertex3(x, y, 0.0);
+            glVertex3(ul_geox, ul_geoy, 0.0);
+            glVertex3(ur_geox, ur_geoy, 0.0);
+            glVertex3(lr_geox, lr_geoy, 0.0);
+            glVertex3(x, y, 0.0);
+            glEnd();
+
+        }
+    }
+}
+
+/************************************************************************/
+/*                    gv_shapes_layer_draw_symbol()                     */
+/************************************************************************/
+
+static void
+gv_shapes_layer_draw_symbol( GvViewArea *view, GvShapesLayer *layer,
+                             GvShape *shape, GvShapeDrawInfo *drawinfo,
+                             gvgeocoord x, gvgeocoord y, gvgeocoord z,
+                             GvSymbolRenderPart *symbol_info,
+                             int part_index, gv_draw_mode draw_mode )
+
+{
+    guint sym_id = -1;
+    GvSymbolObj *poSymbol = NULL;
+
+    if( EQUALN(symbol_info->symbol_id, "ogr-sym-", 8) )
+    {
+        sym_id = atoi(symbol_info->symbol_id + 8);
+    }
+    else
+    {
+        if( layer->symbol_manager != NULL 
+            && gv_symbol_manager_has_symbol( 
+                GV_SYMBOL_MANAGER(layer->symbol_manager), 
+                symbol_info->symbol_id ) )
+            poSymbol = gv_symbol_manager_get_symbol( 
+                GV_SYMBOL_MANAGER(layer->symbol_manager),
+                symbol_info->symbol_id );
+        else
+            poSymbol = gv_symbol_manager_get_symbol( gv_get_symbol_manager(),
+                                                     symbol_info->symbol_id );
+
+        if (poSymbol == NULL)
+            CPLDebug( "OpenEV", "poSymbol is NULL !!! " );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Render raster symbol.                                           */
+/* -------------------------------------------------------------------- */
+    if( poSymbol != NULL && poSymbol->type == GV_SYMBOL_RASTER )
+    {
+        gvgeocoord bx, by;
+
+        /* Compute the lower left corner of the raster assuming it is to be
+           centered on the point before offsets */
+        bx = x + symbol_info->x_offset_g;
+        by = y + symbol_info->y_offset_g;
+
+        bx = bx + (symbol_info->x_offset_px - poSymbol->width/2)
+            * drawinfo->dpixel;
+        by = by - (symbol_info->y_offset_px - poSymbol->height/2)
+            * drawinfo->dpixel;
+
+        /* Draw an outline slightly larger than the raster to indicate
+           selection.  Draw a filled polygon the same size as the raster
+           when picking */
+
+        if( draw_mode != NORMAL )
+        {
+            gvgeocoord geo_width, geo_height, mult_fac = 1.0, box_x, box_y;
+
+            if( draw_mode == SELECTED )
+                mult_fac = 1.2;
+
+            geo_width = poSymbol->width * mult_fac * drawinfo->dpixel;
+            geo_height = poSymbol->height * mult_fac * drawinfo->dpixel;
+
+            box_x = x + symbol_info->x_offset_g - geo_width/2.0
+                + symbol_info->x_offset_px * drawinfo->dpixel;
+            box_y = y + symbol_info->y_offset_g - geo_height/2.0
+                - symbol_info->y_offset_px * drawinfo->dpixel;
+
+            if( draw_mode == NORMAL_GET_BOX )
+            {
+                gv_draw_info_aggregate_select_region( drawinfo, box_x, box_y);
+                gv_draw_info_aggregate_select_region( drawinfo, 
+                                                      box_x + geo_width,
+                                                      box_y + geo_height );
+            }
+            else
+            {
+                /* Draw box around symbol */
+                glBegin( draw_mode == SELECTED ? GL_LINE_LOOP : GL_POLYGON );
+                glVertex3(box_x,box_y,0.0);
+                glVertex3(box_x+geo_width,box_y,0.0);
+                glVertex3(box_x+geo_width,box_y+geo_height,0.0);
+                glVertex3(box_x,box_y+geo_height,0.0);
+                glVertex3(box_x,box_y,0.0);
+                glEnd();
+            }
+        }
+
+        /* Draw the raster (as long as we aren't picking) */
+        if( draw_mode != PICKING )
+        {
+            glRasterPos3d( bx, by, z );
+            glPixelZoom(1.0,-1.0);
+            glDrawPixels( poSymbol->width, poSymbol->height, GL_RGBA,
+                          GL_UNSIGNED_INT_8_8_8_8_REV, poSymbol->buffer );
+        }
+
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Render vector symbols.                                          */
+/* -------------------------------------------------------------------- */
+    else if( sym_id != -1
+             || (poSymbol != NULL && poSymbol->type == GV_SYMBOL_VECTOR) )
+    {
+        gvgeocoord cx, cy;
+        gvgeocoord base_vector = drawinfo->dunit * drawinfo->point_size;
+
+        if (symbol_info->b_color_initialized)
+            glColor4fv( symbol_info->color );
+        else
+            glColor4fv( drawinfo->color );
+
+        /* Perform any required pixel or georeferenced translation of the
+           center point */
+
+        cx = x + symbol_info->x_offset_g;
+        cy = y + symbol_info->y_offset_g;
+
+        if(symbol_info->x_offset_px != 0.0 || symbol_info->y_offset_px != 0.0)
+        {
+            cx = cx + symbol_info->x_offset_px * drawinfo->dpixel;
+            cy = cy - symbol_info->y_offset_px * drawinfo->dpixel;
+        }
+
+        glTranslate( cx, cy, z );
+
+        if( symbol_info->scale != 1.0 )
+            glScale( symbol_info->scale,
+                     symbol_info->scale,
+                     symbol_info->scale );
+
+        if( symbol_info->angle != 0.0 )
+            glRotate( symbol_info->angle, 0.0, 0.0, 1.0 );
+
+        switch( sym_id )
+        {
+          case -1: /* general vector symbol */
+          {
+              GvShape *shape_obj = (GvShape *) poSymbol->buffer;
+              gvgeocoord base_scale;
+              GvShapeDrawInfo sub_drawinfo;
+
+              /* Initialize renderinfo */
+              if( symbol_info->part_index == GVP_UNINITIALIZED_PART )
+              {
+                  int scale_dep = FALSE;
+                  int sub_part_index;
+
+                  sub_part_index =
+                      gv_shape_layer_build_renderinfo( GV_SHAPE_LAYER(layer),
+                                                       shape_obj,
+                                                       &scale_dep );
+
+                  /* symbol_info may have moved, so re-fetch based on index */
+                  symbol_info = (GvSymbolRenderPart *)
+                      gv_shape_layer_get_part( GV_SHAPE_LAYER(layer),
+                                               part_index );
+                  symbol_info->part_index = sub_part_index;
+                  if( symbol_info->part_index == GVP_UNINITIALIZED_PART )
+                      symbol_info->part_index = GVP_LAST_PART;
+              }
+
+              memcpy( &sub_drawinfo, drawinfo, sizeof(GvShapeDrawInfo) );
+              sub_drawinfo.dx = 1.0;
+              sub_drawinfo.dy = 0.0;
+              sub_drawinfo.dunit = 1.0;
+              sub_drawinfo.dpixel *= 1/(drawinfo->dunit * symbol_info->scale);
+              sub_drawinfo.geo2screen_works = FALSE;
+              sub_drawinfo.box_set = FALSE;
+
+              if (symbol_info->b_color_initialized)
+                  gv_color_copy( sub_drawinfo.color, symbol_info->color );
+
+              base_scale = drawinfo->dunit;
+
+              glScale( base_scale, base_scale, base_scale );
+
+              gv_shapes_layer_draw_shape( view, layer, symbol_info->part_index,
+                                          shape_obj,
+                                          draw_mode == SELECTED ? NORMAL_GET_BOX : draw_mode,
+                                          &sub_drawinfo );
+
+              if( draw_mode == SELECTED && sub_drawinfo.box_set )
+              {
+                  draw_mode = NORMAL; /* disable later selection drawing */
+
+                  gv_draw_info_grow_box( &sub_drawinfo, 1.2 );
+                  
+                  glBegin(GL_LINE_LOOP);
+                  glVertex2( 
+                      sub_drawinfo.selection_box.x, 
+                      sub_drawinfo.selection_box.y );
+                  glVertex2( 
+                      sub_drawinfo.selection_box.x + sub_drawinfo.selection_box.width, 
+                      sub_drawinfo.selection_box.y );
+                  glVertex2( 
+                      sub_drawinfo.selection_box.x + sub_drawinfo.selection_box.width, 
+                      sub_drawinfo.selection_box.y + sub_drawinfo.selection_box.height);
+                  glVertex2( 
+                      sub_drawinfo.selection_box.x, 
+                      sub_drawinfo.selection_box.y + sub_drawinfo.selection_box.height);
+                  glEnd();
+              }
+              else if( draw_mode == NORMAL_GET_BOX )
+              {
+                  /* We should transform the box in sub_drawinfo and move
+                     it into drawinto ... but thats hard so I am deferring
+                     it till needed. */
+              }
+              glScale( 1.0/base_scale, 1.0/base_scale, 1.0/base_scale );
+          }
+          break;
+
+          case 0: /* cross */
+            glBegin(GL_LINES);
+            glVertex3(-base_vector, 0,0.0);
+            glVertex3( base_vector, 0,0.0);
+            glVertex3( 0, -base_vector,0.0);
+            glVertex3( 0, base_vector,0.0);
+            glEnd();
+            break;
+
+          case 1: /* X */
+            glBegin(GL_LINES);
+            glVertex3(-base_vector, -base_vector,0.0);
+            glVertex3( base_vector, base_vector,0.0);
+            glVertex3( -base_vector, base_vector,0.0);
+            glVertex3( base_vector, -base_vector,0.0);
+            glEnd();
+            break;
+
+          case 2: /* unfilled circle */
+            glBegin(GL_LINE_LOOP);
+            glVertex3(base_vector*0.0,base_vector*1.0,0.0);
+            glVertex3(base_vector*0.342020148171,
+                       base_vector*0.939692619022,0.0);
+            glVertex3(base_vector*0.642787617587,
+                       base_vector*0.76604443649,0.0);
+            glVertex3(base_vector*0.866025411519,
+                       base_vector*0.499999986603,0.0);
+            glVertex3(base_vector*0.984807756594,
+                       base_vector*0.173648157354,0.0);
+            glVertex3(base_vector*0.984807748535,
+                       base_vector*-0.173648203059,0.0);
+            glVertex3(base_vector*0.866025388314,
+                       base_vector*-0.500000026795,0.0);
+            glVertex3(base_vector*0.642787582035,
+                       base_vector*-0.766044466322,0.0);
+            glVertex3(base_vector*0.34202010456,
+                       base_vector*-0.939692634895,0.0);
+            glVertex3(base_vector*0,
+                       base_vector*-1.0,0.0);
+            glVertex3(base_vector*-0.342020191783,
+                       base_vector*-0.93969260314,0.0);
+            glVertex3(base_vector*-0.642787653139,
+                       base_vector*-0.76604440665,0.0);
+            glVertex3(base_vector*-0.866025434725,
+                       base_vector*-0.49999994641,0.0);
+            glVertex3(base_vector*-0.984807764653,
+                       base_vector*-0.17364811164,0.0);
+            glVertex3(base_vector*-0.984807740476,
+                       base_vector*0.173648248764,0.0);
+            glVertex3(base_vector*-0.866025365109,
+                       base_vector*0.500000066987,0.0);
+            glVertex3(base_vector*-0.642787546482,
+                       base_vector*0.766044496153,0.0);
+            glVertex3(base_vector*-0.342020060949,
+                       base_vector*0.939692650769,0.0);
+            glVertex3(base_vector*9.28204133326e-08,
+                       base_vector*1.0,0.0);
+            glEnd();
+            break;
+
+          case 3: /* filled circle */
+            glBegin(GL_POLYGON);
+            glVertex3(base_vector*0.0,base_vector*1.0,0.0);
+            glVertex3(base_vector*0.342020148171,
+                       base_vector*0.939692619022,0.0);
+            glVertex3(base_vector*0.642787617587,
+                       base_vector*0.76604443649,0.0);
+            glVertex3(base_vector*0.866025411519,
+                       base_vector*0.499999986603,0.0);
+            glVertex3(base_vector*0.984807756594,
+                       base_vector*0.173648157354,0.0);
+            glVertex3(base_vector*0.984807748535,
+                       base_vector*-0.173648203059,0.0);
+            glVertex3(base_vector*0.866025388314,
+                       base_vector*-0.500000026795,0.0);
+            glVertex3(base_vector*0.642787582035,
+                       base_vector*-0.766044466322,0.0);
+            glVertex3(base_vector*0.34202010456,
+                       base_vector*-0.939692634895,0.0);
+            glVertex3(base_vector*0,base_vector*-1.0,0.0);
+            glVertex3(base_vector*-0.342020191783,
+                       base_vector*-0.93969260314,0.0);
+            glVertex3(base_vector*-0.642787653139,
+                       base_vector*-0.76604440665,0.0);
+            glVertex3(base_vector*-0.866025434725,
+                       base_vector*-0.49999994641,0.0);
+            glVertex3(base_vector*-0.984807764653,
+                       base_vector*-0.17364811164,0.0);
+            glVertex3(base_vector*-0.984807740476,
+                       base_vector*0.173648248764,0.0);
+            glVertex3(base_vector*-0.866025365109,
+                       base_vector*0.500000066987,0.0);
+            glVertex3(base_vector*-0.642787546482,
+                       base_vector*0.766044496153,0.0);
+            glVertex3(base_vector*-0.342020060949,
+                       base_vector*0.939692650769,0.0);
+            glVertex3(base_vector*9.28204133326e-08,base_vector*1.0,0.0);
+            glEnd();
+            break;
+
+          case 4: /* square */
+            glBegin( GL_LINE_LOOP );
+            glVertex3( -base_vector, -base_vector ,0.0);
+            glVertex3( -base_vector, base_vector ,0.0);
+            glVertex3( base_vector, base_vector ,0.0);
+            glVertex3( base_vector, -base_vector ,0.0);
+            glVertex3( -base_vector, -base_vector ,0.0);
+            glEnd();
+            break;
+
+          case 5: /* filled square */
+            glBegin( GL_POLYGON );
+            glVertex3( -base_vector, -base_vector ,0.0);
+            glVertex3( -base_vector, base_vector ,0.0);
+            glVertex3( base_vector, base_vector ,0.0);
+            glVertex3( base_vector, -base_vector ,0.0);
+            glVertex3( -base_vector, -base_vector ,0.0);
+            glEnd();
+            break;
+
+          case 6: /* triangle */
+            glBegin(GL_LINE_LOOP);
+            glVertex3( 0, base_vector ,0.0);
+            glVertex3( -base_vector*0.866, -base_vector*0.5,0.0);
+            glVertex3(  base_vector*0.866, -base_vector*0.5,0.0);
+            glVertex3( 0, base_vector ,0.0);
+            glEnd();
+            break;
+
+          case 7: /* filled triangle */
+            glBegin(GL_POLYGON);
+            glVertex3( 0, base_vector ,0.0);
+            glVertex3(  base_vector*0.866, -base_vector*0.5,0.0);
+            glVertex3( -base_vector*0.866, -base_vector*0.5,0.0);
+            glVertex3( 0, base_vector ,0.0);
+            glEnd();
+            break;
+
+          case 8: /* star */
+            glBegin( GL_LINE_LOOP );
+            glVertex3(0.0,base_vector*1.0,0.0);
+            glVertex3(base_vector*0.587785222255,
+                       base_vector*-0.809017016198,0.0);
+            glVertex3(base_vector*-0.951056493349,
+                       base_vector*0.309017064997,0.0);
+            glVertex3(base_vector*0.951056522032,
+                       base_vector*0.309016976719,0.0);
+            glVertex3(base_vector*-0.587785297348,
+                       base_vector*-0.80901696164,0.0);
+            glVertex3(0.0,base_vector*1.0,0.0);
+            glEnd();
+            break;
+
+          case 9: /* filled star */
+            glBegin( GL_POLYGON );
+            glVertex3(base_vector * -0.195928, base_vector *  0.269672,0.0);
+            glVertex3(base_vector *  0.000000, base_vector *  1.000000,0.0);
+            glVertex3(base_vector *  0.195928, base_vector *  0.269672,0.0);
+            glEnd();
+            glBegin( GL_POLYGON );
+            glVertex3(base_vector *  0.195928, base_vector *  0.269672,0.0);
+            glVertex3(base_vector *  0.951057, base_vector *  0.309017,0.0);
+            glVertex3(base_vector *  0.317019, base_vector * -0.103006,0.0);
+            glEnd();
+            glBegin( GL_POLYGON );
+            glVertex3(base_vector *  0.317019, base_vector * -0.103006,0.0);
+            glVertex3(base_vector *  0.587785, base_vector * -0.809017,0.0);
+            glVertex3(base_vector *  0.000000, base_vector * -0.333333,0.0);
+            glEnd();
+            glBegin( GL_POLYGON );
+            glVertex3(base_vector *  0.000000, base_vector * -0.333333,0.0);
+            glVertex3(base_vector * -0.587785, base_vector * -0.809017,0.0);
+            glVertex3(base_vector * -0.317019, base_vector * -0.103006,0.0);
+            glEnd();
+            glBegin( GL_POLYGON );
+            glVertex3(base_vector * -0.317019, base_vector * -0.103006,0.0);
+            glVertex3(base_vector * -0.951057, base_vector *  0.309017,0.0);
+            glVertex3(base_vector * -0.195928, base_vector *  0.269672,0.0);
+            glEnd();
+            glBegin( GL_POLYGON );
+            glVertex3(base_vector *  0.195928, base_vector *  0.269672,0.0);
+            glVertex3(base_vector *  0.317019, base_vector * -0.103006,0.0);
+            glVertex3(base_vector *  0.000000, base_vector * -0.333333,0.0);
+            glVertex3(base_vector * -0.317019, base_vector * -0.103006,0.0);
+            glVertex3(base_vector * -0.195928, base_vector *  0.269672,0.0);
+            glEnd();
+
+          case 10: /* vertical bar */
+            glBegin(GL_LINES);
+            glVertex3( 0, 0 ,0.0);
+            glVertex3( 0, base_vector ,0.0);
+            glEnd();
+            break;
+        }
+
+        if( symbol_info->angle != 0.0 )
+            glRotatef( -symbol_info->angle,0.0,0.0, 1.0 );
+
+        if( draw_mode == SELECTED )
+        {
+            float   bx = base_vector * 1.2;
+            
+            /* Draw box around crosshairs */
+            glBegin(GL_LINE_LOOP);
+            glVertex3(-bx, -bx,0.0);
+            glVertex3( bx, -bx,0.0);
+            glVertex3( bx,  bx,0.0);
+            glVertex3(-bx,  bx,0.0);
+            glEnd();
+        }
+        else if( draw_mode == NORMAL_GET_BOX )
+        {
+            float   bx = base_vector;
+
+            /* We do not currently take symbol rotation angle into account. */
+
+            gv_draw_info_aggregate_select_region( drawinfo, 
+                                                  cx - bx*symbol_info->scale,
+                                                  cy - bx*symbol_info->scale );
+            gv_draw_info_aggregate_select_region( drawinfo, 
+                                                  cx + bx*symbol_info->scale,
+                                                  cy + bx*symbol_info->scale );
+        }
+
+        if( symbol_info->scale != 1.0 )
+        {
+            float one_over_scale;
+
+            one_over_scale = 1/symbol_info->scale;
+            glScale( one_over_scale, one_over_scale, one_over_scale );
+        }
+
+        glTranslate( -cx, -cy, -z );
+    }
+    else
+    {
+        CPLDebug( "OpenEV", "symbol not understood" );
+    }
+}
+
+/************************************************************************/
+/*                      gv_shapes_layer_draw_pen()                      */
+/************************************************************************/
+
+static void
+gv_shapes_layer_draw_pen( GvViewArea *view, GvShape *shape,
+                          GvShapeDrawInfo *drawinfo,
+                          GvPenRenderPart *pen_info,
+                          gv_draw_mode draw_mode )
+
+{
+    double box_vector = drawinfo->dunit*(DEFAULT_POINT_SIZE+pen_info->width/2-1);
+    guint pen_id = -1;
+
+    GLint factor = 1;
+    GLushort stipple = 0xFFFF;
+
+    if (pen_info->b_color_initialized)
+        glColor4fv( pen_info->color );
+    else
+        glColor4fv( drawinfo->color );
+
+    glLineWidth( pen_info->width );
+
+    if( draw_mode != PICKING
+        && EQUALN(pen_info->pattern, "ogr-pen-", 8) )
+    {
+        pen_id = atoi(pen_info->pattern + 8);
+    }
+
+    /*
+    A note about pen stippling.  According to the OpenGL spec,
+    the stipple pattern is a 16 bit number used as a mask.  Any
+    bit that is set is visible in the output, any bit not set
+    ends up not visible.  Factor is a linear multiplication of
+    the stipple pattern so a factor of 2 gives 32 bits but each
+    bit controls two pixels instead of 1.  Another important
+    piece of information: stippling is used from the lowest bit
+    to the highest bit (right to left as you look at the number).
+
+    0xFFFF is all bits on
+    0x0000 is all bits off
+    0x0F0F is a dash pattern with four on/four off
+
+    All patterns must have a repeat of 16.
+    */
+
+    if (pen_id != -1)
+    {
+        switch( pen_id )
+        {
+            case 0: /* default pen, don't use stippling */
+                pen_id = -1;
+                break;
+            case 1: /* invisible pen */
+                factor = 1;
+                stipple = 0x0000;
+                break;
+            case 2: /* dash */
+                factor = 1;
+                stipple = 0x0F0F;
+                break;
+            case 3: /* short dash */
+                factor = 1;
+                stipple = 0x0F0F;
+                break;
+            case 4: /* long dash */
+                factor = 3;
+                stipple = 0x0F0F;
+                break;
+            case 5: /* dot */
+                factor = 1;
+                stipple = 0x3333;
+                break;
+            case 6: /* dash-dot */
+                factor = 2;
+                stipple = 0x2727;
+                break;
+            case 7: /* dash-dot-dot */
+                factor = 2;
+                stipple = 0x333F;
+                break;
+            case 8: /*alternate pixels */
+                factor = 1;
+                stipple = 0x5555;
+                break;
+            default: /* default to pen 0 which is no stipple */
+                pen_id = -1;
+        }
+    }
+
+    if (pen_id != -1)
+    {
+        // On some ATI OpenGL drivers (eg Xpert2000) we need
+        // to set line width to 1 to get stipples working.
+        // glLineWidth( 1.0 );
+
+        glEnable( GL_LINE_STIPPLE );
+        glLineStipple( factor, stipple );
+    }
+
+    if( gv_shape_type(shape) == GVSHAPE_LINE )
+    {
+        GvLineShape   *line = (GvLineShape *) shape;
+        int j;
+
+        glVertexPointer( 3, GL_GEOCOORD, 0, line->xyz_nodes );
+        glDrawArrays( GL_LINE_STRIP, 0, line->num_nodes );
+
+        //revert line width for drawing the boxes
+        glLineWidth( 1.0 );
+        glLineStipple( 1, 0xffff );
+
+        /* Draw small box around each point, in picking mode we fill it. */
+        if( draw_mode == PICKING || draw_mode == SELECTED )
+        {
+            for (j=0; j < line->num_nodes; ++j)
+            {
+                gvgeocoord x, y;
+                
+                x = line->xyz_nodes[j*3];
+                y = line->xyz_nodes[j*3+1];
+                
+                if( draw_mode == PICKING )
+                    glBegin(GL_POLYGON);
+                else
+                    glBegin(GL_LINE_LOOP);
+                glVertex2(x-box_vector, y-box_vector);
+                glVertex2(x+box_vector, y-box_vector);
+                glVertex2(x+box_vector, y+box_vector);
+                glVertex2(x-box_vector, y+box_vector);
+                glEnd();
+            }
+        }
+        else if( draw_mode == NORMAL_GET_BOX )
+        {
+            int j;
+            for (j=0; j < line->num_nodes; ++j)
+            {
+                gv_draw_info_aggregate_select_region( drawinfo, 
+                                                      line->xyz_nodes[j*3],
+                                                      line->xyz_nodes[j*3+1] );
+            }
+        }
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_AREA )
+    {
+        GvAreaShape   *area = (GvAreaShape *) shape;
+        int           ring, j;
+
+        for( ring = 0; ring < area->num_rings; ring++ )
+        {
+            if( area->num_ring_nodes[ring] > 1 )
+            {
+                glVertexPointer( 3, GL_GEOCOORD, 0,
+                                 area->xyz_ring_nodes[ring] );
+                glDrawArrays( GL_LINE_STRIP, 0,
+                              area->num_ring_nodes[ring] );
+            }
+        }
+        /* revert to 1 pixel lines for the boxes */
+        glLineWidth( 1.0 );
+        glLineStipple( 1, 0xffff );
+
+        /* selection/picking per-node work */
+        for( ring = 0;
+             ring < area->num_rings && draw_mode != NORMAL;
+             ring++ )
+        {
+            for (j=0; j < area->num_ring_nodes[ring]; ++j)
+            {
+                gvgeocoord x, y;
+
+                x = area->xyz_ring_nodes[ring][j*3];
+                y = area->xyz_ring_nodes[ring][j*3+1];
+
+                if( draw_mode == NORMAL_GET_BOX )
+                {
+                    gv_draw_info_aggregate_select_region( drawinfo, x, y );
+                }
+                else
+                {
+                    if( draw_mode == PICKING )
+                        glBegin(GL_POLYGON);
+                    else
+                        glBegin(GL_LINE_LOOP);
+                    glVertex2(x-box_vector, y-box_vector);
+                    glVertex2(x+box_vector, y-box_vector);
+                    glVertex2(x+box_vector, y+box_vector);
+                    glVertex2(x-box_vector, y+box_vector);
+                    glEnd();
+                }
+            }
+        }
+    }
+    else if( gv_shape_type(shape) == GVSHAPE_COLLECTION )
+    {
+        int  i, count = gv_shape_collection_get_count( shape );
+
+        for( i = 0; i < count; i++ )
+            gv_shapes_layer_draw_pen( view,
+                                      gv_shape_collection_get_shape(shape,i),
+                                      drawinfo, pen_info, draw_mode );
+    }
+    else
+    {
+        g_warning( "GvShapesLayer: pen tool on point ignored." );
+    }
+
+    if (pen_id != -1)
+        glDisable( GL_LINE_STIPPLE );
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_draw_brush()                     */
+/************************************************************************/
+
+static void
+gv_shapes_layer_draw_brush( GvViewArea *view, GvShape *shape,
+                            GvShapeDrawInfo *drawinfo,
+                            GvBrushRenderPart *brush_info,
+                            gv_draw_mode draw_mode )
+
+{
+
+    GvAreaShape   *area = (GvAreaShape *) shape;
+    int            fill_object, ring;
+    double box_vector = drawinfo->dunit * DEFAULT_POINT_SIZE;
+
+    if( gv_shape_type(shape) != GVSHAPE_AREA )
+    {
+        if( gv_shape_type(shape) == GVSHAPE_COLLECTION )
+        {
+            int  i, count = gv_shape_collection_get_count( shape );
+
+            for( i = 0; i < count; i++ )
+                gv_shapes_layer_draw_brush(
+                    view, gv_shape_collection_get_shape(shape,i),
+                    drawinfo, brush_info, draw_mode );
+        }
+        else
+        {
+            static int bWarningIssued = FALSE;
+
+            if( !bWarningIssued )
+            {
+                bWarningIssued = TRUE;
+                g_warning( "GvShapesLayer: BRUSH tool on non-area ignored." );
+            }
+        }
+
+        return;
+    }
+
+    if (brush_info->b_fore_color_initialized)
+        glColor4fv( brush_info->fore_color );
+    else
+        glColor4fv( drawinfo->color );
+
+    if( area->fill_objects == -1 )
+    {
+        gv_area_shape_tessellate( area );
+    }
+
+    if( area->fill != NULL && area->fill_objects > 0 )
+    {
+        glVertexPointer(3, GL_GEOCOORD, 0, area->fill->data );
+
+        for( fill_object = 0;
+             fill_object < area->fill_objects;
+             fill_object++ )
+        {
+            int f_offset=g_array_index(area->mode_offset,gint,
+                                       fill_object*2+1);
+            int f_mode = g_array_index(area->mode_offset,gint,
+                                       fill_object*2);
+            int f_len;
+
+            if( fill_object == area->fill_objects-1 )
+                f_len = area->fill->len - f_offset;
+            else
+                f_len = g_array_index(area->mode_offset,gint,
+                                      fill_object*2+3)
+                    - f_offset;
+
+            glDrawArrays(f_mode, f_offset, f_len);
+        }
+    }
+
+    /* Draw small box around each point in selected mode */
+    for( ring = 0;
+         ring < area->num_rings && draw_mode == SELECTED;
+         ring++ )
+    {
+        int j;
+
+        for (j=0; j < area->num_ring_nodes[ring]; ++j)
+        {
+            gvgeocoord x, y;
+
+            x = area->xyz_ring_nodes[ring][j*3];
+            y = area->xyz_ring_nodes[ring][j*3+1];
+
+            glBegin(GL_LINE_LOOP);
+            glVertex2(x-box_vector, y-box_vector);
+            glVertex2(x+box_vector, y-box_vector);
+            glVertex2(x+box_vector, y+box_vector);
+            glVertex2(x-box_vector, y+box_vector);
+            glEnd();
+        }
+    }
+
+    if( draw_mode == NORMAL_GET_BOX )
+    {
+        int j;
+
+        for (j=0; j < area->num_ring_nodes[ring]; ++j)
+        {
+            gvgeocoord x, y;
+
+            x = area->xyz_ring_nodes[ring][j*3];
+            y = area->xyz_ring_nodes[ring][j*3+1];
+
+            gv_draw_info_aggregate_select_region( drawinfo, x, y );
+        }
+    }
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_draw_shape()                     */
+/************************************************************************/
+
+void gv_shapes_layer_draw_shape( GvViewArea *view, GvShapesLayer *layer,
+                                 int part_index, GvShape *shape_obj,
+                                 gv_draw_mode draw_mode,
+                                 GvShapeDrawInfo *drawinfo )
+{
+    gvgeocoord x, y, z;
+    GvShapeDrawInfo sAltDrawInfo;
+
+/* -------------------------------------------------------------------- */
+/*      If we weren't provided with layer drawing defaults, compute     */
+/*      some now.                                                       */
+/* -------------------------------------------------------------------- */
+    if( drawinfo == NULL )
+    {
+        gv_shapes_layer_get_draw_info( view, layer, &sAltDrawInfo );
+        drawinfo = &sAltDrawInfo;
+    }
+
+    if( part_index != GVP_LAST_PART )
+    {
+        int      part_draw_mode = draw_mode;
+
+        while( part_index != GVP_LAST_PART )
+        {
+            GvRenderPart    *part_info;
+            int      part_type, next_part_index;
+
+            part_type = gv_part_index_to_type( part_index );
+            part_info = gv_shape_layer_get_part( GV_SHAPE_LAYER(layer),
+                                                 part_index );
+            next_part_index = part_info->next_part;
+
+            /* if there are multiple parts, we need to aggregate 
+               selection boxes. */
+
+            if( part_draw_mode == SELECTED 
+                && gv_shape_type(shape_obj) == GVSHAPE_POINT
+                && next_part_index != GVP_LAST_PART )
+                part_draw_mode = NORMAL_GET_BOX;
+
+
+            g_assert( part_info != NULL );
+
+            if( part_type == GvLabelPart
+                && gv_shape_type(shape_obj) == GVSHAPE_POINT )
+            {
+                x = ((GvPointShape *) shape_obj)->x;
+                y = ((GvPointShape *) shape_obj)->y;
+                z = ((GvPointShape *) shape_obj)->z;
+
+                gv_shapes_layer_draw_label( view, layer, shape_obj, drawinfo,
+                                            x, y,
+                                            (GvLabelRenderPart *) part_info,
+                                            part_draw_mode );
+            }
+            else if( part_type == GvSymbolPart
+                     && gv_shape_type(shape_obj) == GVSHAPE_POINT )
+            {
+                x = ((GvPointShape *) shape_obj)->x;
+                y = ((GvPointShape *) shape_obj)->y;
+                z = ((GvPointShape *) shape_obj)->z;
+
+                gv_shapes_layer_draw_symbol( view, layer, shape_obj, drawinfo,
+                                             x, y, z,
+                                             (GvSymbolRenderPart *) part_info,
+                                             part_index, part_draw_mode );
+            }
+            else if( part_type == GvPenPart )
+            {
+                gv_shapes_layer_draw_pen( view, shape_obj, drawinfo,
+                                          (GvPenRenderPart *) part_info,
+                                          part_draw_mode );
+            }
+            else if( part_type == GvBrushPart )
+            {
+                gv_shapes_layer_draw_brush(view, shape_obj, drawinfo,
+                                           (GvBrushRenderPart *) part_info,
+                                           part_draw_mode );
+            }
+
+            part_index = next_part_index;
+
+            /* We only draw the first part with a selection box. */
+            if( part_draw_mode == SELECTED )
+                part_draw_mode = NORMAL;
+        }
+
+        /* do we need to draw the selection box now? */
+        if( part_draw_mode == NORMAL_GET_BOX && draw_mode == SELECTED )
+        {
+            gv_draw_info_grow_box( drawinfo, 1.2 );
+
+            /* draw box around cross hair */
+            glBegin(GL_LINE_LOOP);
+            glVertex2( 
+                drawinfo->selection_box.x, 
+                drawinfo->selection_box.y );
+            glVertex2( 
+                drawinfo->selection_box.x + drawinfo->selection_box.width, 
+                drawinfo->selection_box.y );
+            glVertex2( 
+                drawinfo->selection_box.x + drawinfo->selection_box.width, 
+                drawinfo->selection_box.y + drawinfo->selection_box.height);
+            glVertex2( 
+                drawinfo->selection_box.x, 
+                drawinfo->selection_box.y + drawinfo->selection_box.height);
+            glEnd();
+        }
+    }
+
+    else if( gv_shape_type(shape_obj) == GVSHAPE_POINT )
+    {
+        if( draw_mode != PICKING )
+        {
+            GvColor      point_color;
+            double       dx = drawinfo->dx * drawinfo->point_size;
+            double       dy = drawinfo->dy * drawinfo->point_size;
+
+            gv_color_copy( point_color, drawinfo->point_color );
+
+            gv_shapes_layer_override_color( shape_obj, point_color,
+                                            "_gv_color" );
+            glColor4fv(point_color);
+
+            x = ((GvPointShape *) shape_obj)->x;
+            y = ((GvPointShape *) shape_obj)->y;
+            z = ((GvPointShape *) shape_obj)->z;
+
+            /* default to simple cross */
+            glBegin(GL_LINES);
+            glVertex3(x-dx, y-dy, z);
+            glVertex3(x+dx, y+dy, z);
+            glVertex3(x+dy, y-dx, z);
+            glVertex3(x-dy, y+dx, z);
+            glEnd();
+        }
+
+        if( draw_mode == SELECTED || draw_mode == PICKING )
+        {
+            double delta = drawinfo->dunit *(drawinfo->point_size+2);
+
+            x = ((GvPointShape *) shape_obj)->x;
+            y = ((GvPointShape *) shape_obj)->y;
+            z = ((GvPointShape *) shape_obj)->z;
+
+            /* draw box around cross hair */
+            if( draw_mode == SELECTED )
+                glBegin(GL_LINE_LOOP);
+            else
+                glBegin(GL_POLYGON);
+
+            glVertex3(x-delta, y-delta, z);
+            glVertex3(x+delta, y-delta, z);
+            glVertex3(x+delta, y+delta, z);
+            glVertex3(x-delta, y+delta, z);
+            glEnd();
+        }
+        else if( draw_mode == NORMAL_GET_BOX )
+        {
+            double delta = drawinfo->dunit *(drawinfo->point_size+2);
+
+            x = ((GvPointShape *) shape_obj)->x;
+            y = ((GvPointShape *) shape_obj)->y;
+
+            gv_draw_info_aggregate_select_region( drawinfo, 
+                                                  x - delta, y - delta );
+            gv_draw_info_aggregate_select_region( drawinfo, 
+                                                  x + delta, y + delta );
+        }
+    }
+
+    else if( gv_shape_type(shape_obj) == GVSHAPE_LINE )
+    {
+        GvLineShape   *line = (GvLineShape *) shape_obj;
+
+        if( draw_mode != PICKING )
+        {
+            GvColor       color;
+
+            gv_color_copy( color, drawinfo->line_color );
+
+            gv_shapes_layer_override_color( shape_obj, color, "_gv_color" );
+            glColor4fv(color);
+        }
+
+        glLineWidth( drawinfo->line_width );
+
+        glVertexPointer( 3, GL_GEOCOORD, 0, line->xyz_nodes );
+        glDrawArrays( GL_LINE_STRIP, 0, line->num_nodes );
+
+        glLineWidth( DEFAULT_LINE_WIDTH );
+
+        if( draw_mode == SELECTED || draw_mode == PICKING )
+        {
+#ifdef ATLANTIS_BUILD
+                /* Atlantis builds: polygon selection draws small
+                   diamonds around nodes instead of large squares */
+                double delta = drawinfo->dunit
+                    * (DEFAULT_POINT_SIZE + drawinfo->line_width - 1 ) * 0.5;
+
+#else
+            double delta = drawinfo->dunit
+                * (DEFAULT_POINT_SIZE + drawinfo->line_width - 1 );
+#endif
+            int j;
+
+            /* Draw small box around each point */
+            for (j=0; j < line->num_nodes; ++j)
+            {
+                x = line->xyz_nodes[j*3];
+                y = line->xyz_nodes[j*3+1];
+
+                if( draw_mode == SELECTED )
+                    glBegin(GL_LINE_LOOP);
+                else
+                    glBegin(GL_POLYGON);
+#ifdef ATLANTIS_BUILD
+                glVertex2(x, y-delta);
+                glVertex2(x-delta, y);
+                glVertex2(x, y+delta);
+                glVertex2(x+delta, y);
+
+#else                        
+                glVertex2(x-delta, y-delta);
+                glVertex2(x+delta, y-delta);
+                glVertex2(x+delta, y+delta);
+                glVertex2(x-delta, y+delta);
+#endif
+                glEnd();
+            }
+        }
+        else if( draw_mode == NORMAL_GET_BOX )
+        {
+            int j;
+            for (j=0; j < line->num_nodes; ++j)
+            {
+                x = line->xyz_nodes[j*3];
+                y = line->xyz_nodes[j*3+1];
+
+            }
+            gv_draw_info_aggregate_select_region( drawinfo, x, y );
+        }
+    }
+
+    else if( gv_shape_type(shape_obj) == GVSHAPE_AREA )
+    {
+        GvAreaShape   *area = (GvAreaShape *) shape_obj;
+        int            want_fill = drawinfo->area_fill_color[3] > 0.001;
+        int            fill_object, ring;
+        int            gl_line_mode = GL_LINE_STRIP;
+        GvColor        color;
+
+
+        if( want_fill && area->fill_objects == -1 )
+            gv_area_shape_tessellate( area );
+
+        if( area->fill != NULL && area->fill_objects > 0 && want_fill )
+        {
+            /* set fill color */
+            if( draw_mode != PICKING )
+            {
+                gv_color_copy( color, drawinfo->area_fill_color );
+
+                gv_shapes_layer_override_color( shape_obj, color,
+                                                "_gv_fill_color" );
+                glColor4fv(color);
+            }
+
+            glVertexPointer(3, GL_GEOCOORD, 0, area->fill->data );
+            gl_line_mode = GL_LINE_LOOP;
+        }
+
+        for( fill_object = 0;
+             fill_object < area->fill_objects && want_fill;
+             fill_object++ )
+        {
+            int f_offset=g_array_index(area->mode_offset,gint,
+                                       fill_object*2+1);
+            int f_mode = g_array_index(area->mode_offset,gint,
+                                       fill_object*2);
+            int f_len;
+
+            if( fill_object == area->fill_objects-1 )
+                f_len = area->fill->len - f_offset;
+            else
+                f_len = g_array_index(area->mode_offset,gint,
+                                      fill_object*2+3)
+                    - f_offset;
+
+            glDrawArrays(f_mode, f_offset, f_len);
+        }
+
+        /* Get edge color */
+        gv_color_copy( color, drawinfo->area_edge_color );
+        gv_shapes_layer_override_color( shape_obj, color, "_gv_color" );
+
+        /* only draw the edge if it's not transparent */
+        if (color[3] > 0.001 || draw_mode != NORMAL )
+        {
+            glLineWidth( drawinfo->area_edge_width );
+
+            if( draw_mode != PICKING )
+                glColor4fv(color);
+
+            for( ring = 0; ring < area->num_rings; ring++ )
+            {
+                if( area->num_ring_nodes[ring] > 1 )
+                {
+                    glVertexPointer( 3, GL_GEOCOORD, 0,
+                                     area->xyz_ring_nodes[ring] );
+                    glDrawArrays( gl_line_mode, 0,
+                                  area->num_ring_nodes[ring] );
+                }
+            }
+            glLineWidth( DEFAULT_LINE_WIDTH );
+
+            if( draw_mode == SELECTED || draw_mode == PICKING )
+            {
+#ifdef ATLANTIS_BUILD
+                /* Atlantis builds: polygon selection draws small
+                   diamonds around nodes instead of large squares */
+                double delta = drawinfo->dunit
+                    * (DEFAULT_POINT_SIZE + drawinfo->line_width - 1 ) * 0.5;
+
+#else
+                double delta = drawinfo->dunit
+                    * (DEFAULT_POINT_SIZE + drawinfo->line_width - 1 );
+#endif
+                int    j;
+
+                for( ring = 0; ring < area->num_rings; ring++ )
+                {
+                    /* Draw small box around each point */
+                    if( draw_mode == SELECTED )
+                        glColor4f(color[0], color[1], color[2], 1.0);
+
+                    for (j=0; j < area->num_ring_nodes[ring]; ++j)
+                    {
+                        x = area->xyz_ring_nodes[ring][j*3];
+                        y = area->xyz_ring_nodes[ring][j*3+1];
+
+                        if( draw_mode == NORMAL_GET_BOX )
+                            gv_draw_info_aggregate_select_region( drawinfo, 
+                                                                  x, y );
+                        else
+                        {
+                            if( draw_mode == SELECTED )
+                                glBegin(GL_LINE_LOOP);
+                            else
+                                glBegin(GL_POLYGON);
+
+#ifdef ATLANTIS_BUILD
+                            glVertex2(x, y-delta);
+                            glVertex2(x-delta, y);
+                            glVertex2(x, y+delta);
+                            glVertex2(x+delta, y);
+
+#else                            
+                            glVertex2(x-delta, y-delta);
+                            glVertex2(x+delta, y-delta);
+                            glVertex2(x+delta, y+delta);
+                            glVertex2(x-delta, y+delta);
+#endif
+                            glEnd();
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else if( gv_shape_type(shape_obj) == GVSHAPE_COLLECTION )
+    {
+        int  i_sub, count = gv_shape_collection_get_count( shape_obj );
+
+        for( i_sub = 0; i_sub < count; i_sub++ )
+        {
+            GvShape *sub_shape
+                = gv_shape_collection_get_shape(shape_obj, i_sub);
+
+            gv_shapes_layer_draw_shape( view, layer, GVP_LAST_PART,
+                                        sub_shape, draw_mode, drawinfo );
+        }
+    }
+}
+
+/************************************************************************/
+/*                        gv_shapes_layer_draw()                        */
+/************************************************************************/
+void
+gv_shapes_layer_draw(GvLayer *r_layer, GvViewArea *view)
+{
+    GvShapesLayer *layer = GV_SHAPES_LAYER(r_layer);
+    gint i, shape_count;
+    gint *selected, presentation;
+    GvShapeDrawInfo   drawinfo;
+    gint bAntialiased, in_list_count = 0, out_list_count = 0;
+    const char * pszAntialiased;
+#ifdef ATLANTIS_BUILD
+    int          bDisplayListsEnabled = 0; /* display lists unreliable */ 
+#else
+    int          bDisplayListsEnabled =
+        gv_manager_get_preference(gv_get_manager(),"display_lists") == NULL
+        || EQUAL(gv_manager_get_preference(gv_get_manager(),
+                                           "display_lists"),"ON");
+#endif
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    shape_count = gv_shapes_num_shapes(layer->data);
+
+/* -------------------------------------------------------------------- */
+/*      Setup layer wide drawing state.                                 */
+/* -------------------------------------------------------------------- */
+    gv_shapes_layer_get_draw_info( view, layer, &drawinfo );
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_BLEND);
+
+    //this enables anti-aliasing for lines, points and polygons
+    pszAntialiased = gv_properties_get( &(GV_DATA(layer)->properties),
+                                        "_gl_antialias");
+
+    //the property can be NULL or 0 to turn it off, otherwise it is on
+    if (pszAntialiased == NULL )
+    {
+        bAntialiased = FALSE;
+    }
+    else if (strcmp(pszAntialiased, "0") == 0)
+    {
+        bAntialiased = FALSE;
+    }
+    else
+    {
+        bAntialiased = TRUE;
+    }
+
+    if (bAntialiased)
+        glEnable( GL_LINE_SMOOTH );
+
+/* -------------------------------------------------------------------- */
+/*      Draw all shapes that are not scale dependent, potentially       */
+/*      using a display list.                                           */
+/* -------------------------------------------------------------------- */
+    if( glIsList( layer->display_list ) )
+    {
+        glCallList( layer->display_list );
+    }
+    else if( bDisplayListsEnabled )
+    {
+        layer->display_list = glGenLists( 1 );
+        glNewList( layer->display_list, GL_COMPILE_AND_EXECUTE );
+
+        for (i=0; i < shape_count; ++i)
+        {
+            GvShape *shape_obj;
+            guint    part_index;
+
+            if (selected[i] && !presentation)
+                continue;
+
+            shape_obj = gv_shapes_get_shape(layer->data,i);
+            if( shape_obj == NULL )
+                continue;
+
+            /*
+             * We need to ensure the parts are initialized as this sets the
+             * scale dependent flag we need to test.
+             */
+            part_index =
+                gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i );
+            if( part_index == GVP_UNINITIALIZED_PART )
+            {
+                gv_shape_layer_update_renderinfo( GV_SHAPE_LAYER(layer), i );
+                part_index =
+                    gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i);
+                g_assert( part_index != GVP_UNINITIALIZED_PART );
+            }
+
+            /* Cross hairs are scale dependent drawing. */
+            if( part_index == GVP_LAST_PART
+                && gv_shape_type(shape_obj) == GVSHAPE_POINT )
+                gv_shape_layer_set_scale_dep( GV_SHAPE_LAYER(layer),
+                                              i, TRUE );
+
+            /*
+             * Only draw this shape as part of the display list if it is
+             * not scale dependent.
+             */
+            if( !gv_shape_layer_get_scale_dep( GV_SHAPE_LAYER(layer), i ) )
+            {
+                in_list_count++;
+                drawinfo.box_set = FALSE;
+                gv_shapes_layer_draw_shape( view, layer, part_index, shape_obj,
+                                            NORMAL, &drawinfo );
+            }
+        }
+
+        glEndList();
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Draw all scale dependent shapes.                                */
+/* -------------------------------------------------------------------- */
+    for (i=0; i < shape_count; ++i)
+    {
+        GvShape *shape_obj;
+        guint    part_index;
+
+        if (selected[i] && !presentation)
+            continue;
+
+        shape_obj = gv_shapes_get_shape(layer->data,i);
+        if( shape_obj == NULL )
+            continue;
+
+        part_index =
+            gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i );
+
+        if( part_index == GVP_UNINITIALIZED_PART )
+        {
+            gv_shape_layer_update_renderinfo( GV_SHAPE_LAYER(layer), i );
+            part_index =
+                gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i);
+            g_assert( part_index != GVP_UNINITIALIZED_PART );
+        }
+
+        if( !bDisplayListsEnabled
+            || gv_shape_layer_get_scale_dep( GV_SHAPE_LAYER(layer), i ) )
+        {
+            out_list_count++;
+            drawinfo.box_set = FALSE;
+            gv_shapes_layer_draw_shape( view, layer, part_index, shape_obj,
+                                        NORMAL, &drawinfo );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Restore drawing state.                                          */
+/* -------------------------------------------------------------------- */
+    glDisable(GL_BLEND);
+    glDisableClientState(GL_VERTEX_ARRAY);
+
+    if (bAntialiased)
+        glDisable( GL_LINE_SMOOTH );
+
+
+    if( in_list_count > 0 )
+    {
+        CPLDebug( "OpenEV", "GvShapesLayer: %d in display list, %d not.",
+                  in_list_count, out_list_count );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we need to draw selected shapes now?                         */
+/* -------------------------------------------------------------------- */
+    if( gv_shape_layer_selected( GV_SHAPE_LAYER(layer), GV_FIRST, &i )
+        && !GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+        gv_shapes_layer_draw_selected(GV_SHAPE_LAYER(layer), view);
+    }
+}
+
+/************************************************************************/
+/*                   gv_shapes_layer_draw_selected()                    */
+/************************************************************************/
+
+void
+gv_shapes_layer_draw_selected(GvShapeLayer *r_layer, GvViewArea *view)
+{
+    GvShapesLayer *layer = GV_SHAPES_LAYER(r_layer);
+    gint i, shape_count;
+    gint *selected;
+    GvShapeDrawInfo   drawinfo;
+
+    gv_shapes_layer_get_draw_info( view, layer, &drawinfo );
+
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+    shape_count = gv_shapes_num_shapes(layer->data);
+
+    /*
+      ideal box size for around point cross hairs.
+
+    bx = by = drawinfo.point_size + 2;
+    gv_view_area_correct_for_transform(view, bx, by, &bx, &by);
+    */
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_BLEND);
+    for (i=0; i < shape_count; ++i)
+    {
+        GvShape     *shape_obj;
+        guint       part_index;
+
+        /* skip unselected shapes */
+        if (!selected[i])
+            continue;
+
+        /* fetch the shape itself */
+        shape_obj = gv_shapes_get_shape(layer->data, i);
+        if( shape_obj == NULL )
+            continue;
+
+        /* get information on first part, ogrfs driven. */
+        part_index =
+            gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i );
+        if( part_index == GVP_UNINITIALIZED_PART )
+        {
+            gv_shape_layer_update_renderinfo( GV_SHAPE_LAYER(layer), i );
+            part_index =
+                gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i);
+            g_assert( part_index != GVP_UNINITIALIZED_PART );
+        }
+
+        /* draw the shape in selected mode. */
+        drawinfo.box_set = FALSE;
+        gv_shapes_layer_draw_shape( view, layer, part_index, shape_obj,
+                                    SELECTED, &drawinfo );
+    }
+    glDisable(GL_BLEND);
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_pick_shape()                     */
+/************************************************************************/
+static void
+gv_shapes_layer_pick_shape(GvShapeLayer *r_layer)
+{
+    GvShapesLayer *layer = GV_SHAPES_LAYER(r_layer);
+    gint i, shape_count;
+    GvShapeDrawInfo   drawinfo;
+    GvViewArea   *view;
+
+    view = GV_VIEW_AREA(GV_LAYER(r_layer)->view);
+    gv_shapes_layer_get_draw_info( view, layer, &drawinfo );
+
+    if (!gv_layer_is_visible(GV_LAYER(layer))) return;
+    shape_count = gv_shapes_num_shapes(layer->data);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    for (i=0; i < shape_count; ++i)
+    {
+        GvShape * shape_obj = gv_shapes_get_shape(layer->data, i);
+        guint   part_index;
+
+        if( shape_obj == NULL )
+            continue;
+
+        /* get information on first part, ogrfs driven. */
+        part_index =
+            gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i );
+        if( part_index == GVP_UNINITIALIZED_PART )
+        {
+            gv_shape_layer_update_renderinfo( GV_SHAPE_LAYER(layer), i );
+            part_index =
+                gv_shape_layer_get_first_part_index( GV_SHAPE_LAYER(layer), i);
+            g_assert( part_index != GVP_UNINITIALIZED_PART );
+        }
+
+        /* Load the "ID" of the shape being drawn. */
+        glLoadName(i);
+
+        /* Draw it in PICKING mode */
+        drawinfo.box_set = FALSE;
+        gv_shapes_layer_draw_shape( view, layer, part_index, shape_obj,
+                                    PICKING, &drawinfo );
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_pick_node()                      */
+/************************************************************************/
+static void
+gv_shapes_layer_pick_node(GvShapeLayer *rlayer)
+{
+    GvShape *shape;
+    gint sel, i, r;
+    GvShapeDrawInfo drawinfo;
+    double dx, dy, dsize;
+
+    if (!gv_layer_is_visible(GV_LAYER(rlayer))) return;
+
+    if (!gv_shape_layer_selected(rlayer, GV_FIRST, &sel))
+        return;
+
+    shape = gv_shapes_get_shape(GV_SHAPES_LAYER(rlayer)->data, sel);
+    if( shape == NULL )
+        return;
+
+    /* How big should node be drawn?  We want this to match the size
+       they are normally drawn visibly */
+
+    gv_shapes_layer_get_draw_info( GV_LAYER(rlayer)->view,
+                                   GV_SHAPES_LAYER(rlayer), &drawinfo );
+
+    dsize = drawinfo.dunit;
+    dx = dsize * (drawinfo.point_size+2);
+    dy = dsize * (drawinfo.point_size+2);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+
+    /* Nodes first */
+    glLoadName(0);
+    glPushName(-1);
+    glPointSize(2.0);
+    for (r=0; r < gv_shape_get_rings(shape); ++r)
+    {
+        int num_nodes = gv_shape_get_nodes(shape,r);
+
+        glLoadName(r);
+        glPushName(-1);
+
+        for (i=0; i < num_nodes; ++i)
+        {
+            double x, y, z;
+
+            x = gv_shape_get_x(shape,r,i);
+            y = gv_shape_get_y(shape,r,i);
+            z = gv_shape_get_z(shape,r,i);
+
+            glLoadName(i);
+
+            glBegin(GL_POLYGON);
+            glVertex3(x-dx, y-dy, z);
+            glVertex3(x+dy, y-dx, z);
+            glVertex3(x+dx, y+dy, z);
+            glVertex3(x-dy, y+dx, z);
+            glVertex3(x-dx, y-dy, z);
+            glEnd();
+
+        }
+        glPopName(); /* node id */
+    }
+    glPopName(); /* ring id */
+
+    /* Segments next */
+    glLoadName(1);
+    glPushName(-1);
+    for (r=0; r <  gv_shape_get_rings(shape); ++r)
+    {
+        int num_nodes = gv_shape_get_nodes(shape,r);
+
+        glLoadName(r);
+
+        /* First segment connects last vertex to first vertex:
+           "before" vertex zero as long as the line wouldn't be degenerate. */
+
+        /* April, 2004 bug fix: the last vertex-first vertex segment
+         * should not be drawn for line shapes, as this can cause problems
+         * with editing if another segment of the line is contained within
+         * the segment, eg:
+         *      N6----------N7
+         *      |           |
+         * N1   N5--N4      N8
+         * |        |
+         * N2-------N3
+         * clicking to create a new node on the N4-N5 segment would
+         * actually result in a node being inserted before N1.
+         */
+        glPushName(0);
+        if( (gv_shape_type(shape) != GVSHAPE_LINE) &&
+            (gv_shape_get_x(shape,r,num_nodes-1) != gv_shape_get_x(shape,r,0)
+         || gv_shape_get_y(shape,r,num_nodes-1) != gv_shape_get_y(shape,r,0)))
+        {
+            glBegin(GL_LINES);
+            glVertex3( gv_shape_get_x(shape,r,num_nodes-1),
+                       gv_shape_get_y(shape,r,num_nodes-1),
+                       gv_shape_get_z(shape,r,num_nodes-1));
+            glVertex3( gv_shape_get_x(shape,r,0),
+                       gv_shape_get_y(shape,r,0),
+                       gv_shape_get_z(shape,r,0));
+            glEnd();
+        }
+
+        for (i=1; i < num_nodes; ++i)
+        {
+            glLoadName(i);
+            glBegin(GL_LINES);
+            glVertex3( gv_shape_get_x(shape,r,i),
+                       gv_shape_get_y(shape,r,i),
+                       gv_shape_get_z(shape,r,i));
+            glVertex3( gv_shape_get_x(shape,r,i-1),
+                       gv_shape_get_y(shape,r,i-1),
+                       gv_shape_get_z(shape,r,i-1));
+            glEnd();
+        }
+        glPopName(); /* node id */
+    }
+    glPopName(); /* ring id */
+    glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+/************************************************************************/
+/*                      gv_shapes_layer_get_node()                      */
+/************************************************************************/
+static void
+gv_shapes_layer_get_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    GvShape *shape;
+
+    shape = gv_shapes_get_shape( GV_SHAPES_LAYER(layer)->data,
+                                 info->shape_id );
+    if( shape == NULL )
+        return;
+
+    if( gv_shape_type(shape) == GVSHAPE_POINT )
+        info->vertex = (GvVertex *) &((GvPointShape *) shape)->x;
+    else if( gv_shape_type(shape) == GVSHAPE_LINE )
+    {
+        GvLineShape *line = (GvLineShape *) shape;
+
+        info->vertex = (GvVertex *) (line->xyz_nodes + 3*info->node_id);
+    }
+
+    else if( gv_shape_type(shape) == GVSHAPE_AREA )
+    {
+        GvAreaShape *area = (GvAreaShape *) shape;
+        gvgeocoord       *ring = area->xyz_ring_nodes[info->ring_id];
+
+        info->vertex = (GvVertex *) (ring + 3*info->node_id);
+    }
+}
+
+/************************************************************************/
+/*                     gv_shapes_layer_move_node()                      */
+/************************************************************************/
+static void
+gv_shapes_layer_move_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    GvShape *shape;
+
+    shape = gv_shapes_get_shape( GV_SHAPES_LAYER(layer)->data,
+                                 info->shape_id );
+    if( shape == NULL )
+        return;
+
+    shape = gv_shape_copy( shape );
+
+    gv_shape_set_xyz( shape, info->ring_id, info->node_id,
+                      info->vertex->x,
+                      info->vertex->y,
+                      0.0 );
+
+    gv_shapes_replace_shapes( GV_SHAPES_LAYER(layer)->data,
+                              1, &(info->shape_id),
+                              &shape, FALSE );
+}
+
+/************************************************************************/
+/*                    gv_shapes_layer_insert_node()                     */
+/************************************************************************/
+static void
+gv_shapes_layer_insert_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    GvShape *shape;
+
+    shape = gv_shapes_get_shape( GV_SHAPES_LAYER(layer)->data,
+                                 info->shape_id );
+    if( shape == NULL )
+        return;
+
+    shape = gv_shape_copy( shape );
+
+    gv_shape_insert_node( shape, info->ring_id, info->node_id,
+                          info->vertex->x, info->vertex->y, 0.0 );
+
+    gv_shapes_replace_shapes( GV_SHAPES_LAYER(layer)->data,
+                              1, &(info->shape_id),
+                              &shape, FALSE );
+}
+
+/************************************************************************/
+/*                    gv_shapes_layer_delete_node()                     */
+/************************************************************************/
+static void
+gv_shapes_layer_delete_node(GvShapeLayer *layer, GvNodeInfo *info)
+{
+    GvShape *shape;
+
+    shape = gv_shapes_get_shape( GV_SHAPES_LAYER(layer)->data,
+                                 info->shape_id );
+    if( shape == NULL )
+        return;
+
+    shape = gv_shape_copy( shape );
+
+    gv_shape_delete_node( shape, info->ring_id, info->node_id );
+
+    /* delete the ring, if it is now empty */
+    if( gv_shape_get_nodes( shape, info->ring_id ) == 0 )
+        gv_shape_delete_ring( shape, info->ring_id );
+
+    /* Delete the shape if it is now empty */
+    if( gv_shape_get_rings( shape ) == 0 )
+    {
+        gv_shape_layer_clear_selection( layer );
+        gv_shapes_delete_shapes( GV_SHAPES_LAYER(layer)->data,
+                                 1, &(info->shape_id) );
+        gv_shape_delete( shape );
+    }
+    else
+    {
+        gv_shapes_replace_shapes( GV_SHAPES_LAYER(layer)->data,
+                                  1, &(info->shape_id),
+                                  &shape, FALSE );
+    }
+}
+
+/************************************************************************/
+/*                    gv_shapes_layer_node_motion()                     */
+/************************************************************************/
+static void
+gv_shapes_layer_node_motion(GvShapeLayer *layer, gint area_id)
+{
+    GvShape *shape;
+
+    shape = gv_shapes_get_shape( GV_SHAPES_LAYER(layer)->data, area_id );
+    if( shape == NULL )
+        return;
+
+    if( gv_shape_type(shape) == GVSHAPE_AREA )
+    {
+        ((GvAreaShape *) shape)->fill_objects = -2;
+    }
+}
+
+/************************************************************************/
+/*                  gv_shapes_layer_delete_selected()                   */
+/************************************************************************/
+static void
+gv_shapes_layer_delete_selected(GvShapeLayer *layer)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+    gv_shapes_delete_shapes(GV_SHAPES_LAYER(layer)->data,
+                                sel->len, (gint*)sel->data);
+        gv_shape_layer_clear_selection(layer);
+    }
+    g_array_free(sel, TRUE);
+}
+
+/************************************************************************/
+/*                 gv_shapes_layer_translate_selected()                 */
+/************************************************************************/
+static void
+gv_shapes_layer_translate_selected(GvShapeLayer *layer, GvVertex *delta)
+{
+    GArray *sel;
+
+    sel = g_array_new(FALSE, FALSE, sizeof(gint));
+    if (gv_shape_layer_selected(GV_SHAPE_LAYER(layer), GV_ALL, sel))
+    {
+    /* This will force a selection clear */
+    gv_shapes_translate_shapes(GV_SHAPES_LAYER(layer)->data,
+                                   sel->len, (gint*)sel->data,
+                   delta->x, delta->y);
+    }
+    g_array_free(sel, TRUE);
+}
+
+/************************************************************************/
+/*                      gv_shapes_layer_extents()                       */
+/************************************************************************/
+static void
+gv_shapes_layer_extents(GvLayer *layer, GvRect *rect)
+{
+    gv_shapes_get_extents(GV_SHAPES_LAYER(layer)->data, rect);
+}
+
+/************************************************************************/
+/*                    gv_shapes_layer_data_change()                     */
+/************************************************************************/
+static void
+gv_shapes_layer_data_change(GvData *data, gpointer change_info)
+{
+    GvShapesLayer  *layer = GV_SHAPES_LAYER(data);
+
+    gv_shape_layer_clear_all_renderinfo( GV_SHAPE_LAYER(data) );
+
+    /* Reset the selected array to reflect the data length */
+    gv_shape_layer_set_num_shapes(GV_SHAPE_LAYER(layer),
+                  gv_shapes_num_shapes(layer->data));
+
+    /* clear the display list if the data changes, this will cause a new
+       list to be created the next time it is drawn */
+    if (glIsList(layer->display_list))
+    {
+        glDeleteLists( layer->display_list, 1 );
+        layer->display_list = 0;
+    }
+
+}
+
+/************************************************************************/
+/*                  gv_shapes_layer_selection_change()                  */
+/************************************************************************/
+static void gv_shapes_layer_selection_change(GvLayer *data,
+                                             gpointer change_info )
+
+{
+    /* If the selection changes, we need to clear the display list. */
+
+    if (glIsList(GV_SHAPES_LAYER(data)->display_list))
+    {
+        glDeleteLists( GV_SHAPES_LAYER(data)->display_list, 1 );
+        GV_SHAPES_LAYER(data)->display_list = 0;
+    }
+}
+
+/************************************************************************/
+/*                   gv_shapes_layer_display_change()                   */
+/************************************************************************/
+static void
+gv_shapes_layer_display_change(GvLayer *data, gpointer change_info)
+
+{
+    gv_shape_layer_clear_all_renderinfo( GV_SHAPE_LAYER(data) );
+
+    /* clear the display list if the data changes, this will cause a new
+       list to be created the next time it is drawn */
+    if (glIsList(GV_SHAPES_LAYER(data)->display_list))
+    {
+        glDeleteLists( GV_SHAPES_LAYER(data)->display_list, 1 );
+        GV_SHAPES_LAYER(data)->display_list = 0;
+    }
+}
+
+/************************************************************************/
+/*                      gv_shapes_layer_destroy()                       */
+/************************************************************************/
+
+static void gv_shapes_layer_destroy( GtkObject *gtk_object )
+
+{
+    GvShapeLayerClass *parent_class;
+    GvShapesLayer *slayer = GV_SHAPES_LAYER(gtk_object);
+
+    CPLDebug( "OpenEV", "gv_shapes_layer_destroy(%s)", 
+              gv_data_get_name(GV_DATA(slayer)) );
+
+    if( slayer->symbol_manager != NULL )
+    {
+        gtk_object_unref( GTK_OBJECT(slayer->symbol_manager) );
+        slayer->symbol_manager = NULL;
+    }
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_shape_layer_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(gtk_object);         
+}
+
+/************************************************************************/
+/*                      gv_shapes_layer_finalize()                      */
+/************************************************************************/
+static void
+gv_shapes_layer_finalize(GtkObject *object)
+
+{
+    GvShapeLayerClass *parent_class;
+    GvShapesLayer *layer = GV_SHAPES_LAYER(object);
+
+    CPLDebug( "OpenEV", "gv_shapes_layer_finalize(%s)",
+              gv_data_get_name( GV_DATA(object) ) );
+
+    /* delete the display list if it is valid */
+    if (glIsList(layer->display_list))
+        glDeleteLists( layer->display_list, 1 );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_shape_layer_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);
+}

Added: packages/openev/branches/upstream/current/gvshapeslayer.h
===================================================================
--- packages/openev/branches/upstream/current/gvshapeslayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvshapeslayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,161 @@
+/******************************************************************************
+ * $Id: gvshapeslayer.h,v 1.18 2003/09/12 17:35:43 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Display layer for vector shapes.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvshapeslayer.h,v $
+ * Revision 1.18  2003/09/12 17:35:43  warmerda
+ * Added logic to aggregate selection boxes in the drawinfo.selection_box
+ * rectangle when draw_mode == NORMAL_GET_BOX.  This is intended to allow
+ * us to draw a selection box around a complex shapes consisting of multiple
+ * parts offset from the reference point.  In the past we only drew the
+ * selection around the first part in such cases.  This seems to work though
+ * the testing isn't ... extensive.
+ *
+ * Revision 1.17  2003/09/02 17:22:27  warmerda
+ * added per-layer symbol manager support
+ *
+ * Revision 1.16  2003/08/27 20:03:05  warmerda
+ * Added geo2screen_works to drawinfo structure.  It is set to false for text
+ * drawn within symbols since symbol rescaling via glScale() will mess up
+ * the bmfont_draw() logic for ensuring text is drawn "on screen".  True
+ * otherwise.  It is passed on to gv_view_area_bmfont_draw().
+ *
+ * Revision 1.15  2003/05/16 21:31:26  warmerda
+ * added support for passing default color down to components of a symbol
+ *
+ * Revision 1.14  2003/05/16 17:42:40  warmerda
+ * fix up pixel offsets for sub-symbols
+ *
+ * Revision 1.13  2003/02/28 16:49:42  warmerda
+ * preliminary support for symbols rendered from GvShapes
+ *
+ * Revision 1.12  2003/02/27 04:08:57  warmerda
+ * added view to gv_shapes_layer_get_draw_info
+ *
+ * Revision 1.11  2003/02/14 22:55:57  pgs
+ * added support for _line_width and _area_edge_width
+ *
+ * Revision 1.10  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.9  2002/09/27 18:16:21  pgs
+ * added display list support and antialiasing support for line layers
+ *
+ * Revision 1.8  2001/04/09 18:22:22  warmerda
+ * use renderinfo for ogrfs, added text selection box
+ *
+ * Revision 1.7  2001/03/21 22:40:18  warmerda
+ * allow setting _gv_ogrfs on layer, and add support for text from fields
+ *
+ * Revision 1.6  2000/08/14 15:08:08  warmerda
+ * make gv_shapes_layer_override_color public
+ *
+ * Revision 1.5  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_SHAPES_LAYER_H__
+#define __GV_SHAPES_LAYER_H__
+
+#include "gvshapelayer.h"
+#include "gvshapes.h"
+
+#define GV_TYPE_SHAPES_LAYER           (gv_shapes_layer_get_type ())
+#define GV_SHAPES_LAYER(obj)           (GTK_CHECK_CAST ((obj), GV_TYPE_SHAPES_LAYER, GvShapesLayer))
+#define GV_SHAPES_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_SHAPES_LAYER, GvShapesLayerClass))
+#define GV_IS_SHAPES_LAYER(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_SHAPES_LAYER))
+#define GV_IS_SHAPES_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_SHAPES_LAYER))
+
+typedef struct _GvShapesLayer       GvShapesLayer;
+typedef struct _GvShapesLayerClass  GvShapesLayerClass;
+
+struct _GvShapesLayer
+{
+    GvShapeLayer shape_layer;
+
+    GvShapes *data;
+    gint edit_ring;
+    gint display_list;
+
+    GtkObject *symbol_manager;
+};
+
+struct _GvShapesLayerClass
+{
+    GvShapeLayerClass parent_class;
+};
+
+typedef struct
+{
+    GvColor  color;
+    GvColor  point_color;
+    GvColor  line_color;
+    float line_width;
+    GvColor  area_fill_color;
+    GvColor  area_edge_color;
+    float area_edge_width;
+    gvgeocoord   point_size;
+    gvgeocoord   dx, dy, dunit, dpixel;
+    int      geo2screen_works; /* this doesn't work if extra GL scalings
+                                  are in effect for sub-symbols */
+
+    char     ogrfs[2048];
+    
+    /* used to aggregate and return selection box extents. */
+    int      box_set;
+    GvRect   selection_box;
+} GvShapeDrawInfo;
+
+typedef enum
+{
+    NORMAL,
+    SELECTED,
+    PICKING,
+    NORMAL_GET_BOX
+} gv_draw_mode;
+
+void gv_draw_info_aggregate_select_region( GvShapeDrawInfo *, 
+                                           double x, double y );
+
+GtkType gv_shapes_layer_get_type(void);
+GtkObject* gv_shapes_layer_new(GvShapes *data);
+gint gv_shapes_layer_select_new_shape( GvShapesLayer *, GvShape * );
+void gv_shapes_layer_set_data( GvShapesLayer *, GvShapes * );
+void gv_shapes_layer_draw_selected(GvShapeLayer *layer, GvViewArea *view);
+void gv_shapes_layer_draw(GvLayer *layer, GvViewArea *view);
+void gv_shapes_layer_get_draw_info(GvViewArea *view, GvShapesLayer *, 
+                                   GvShapeDrawInfo * );
+void gv_shapes_layer_override_color( GvShape *, GvColor, const char * );
+
+void gv_shapes_layer_update_renderinfo( GvShapesLayer *, int shape_id );
+GtkObject *gv_shapes_layer_get_symbol_manager( GvShapesLayer *layer, 
+                                               int ok_to_create );
+
+void gv_shapes_layer_draw_shape( GvViewArea *view, GvShapesLayer *layer,
+                                 int part_index, GvShape *shape_obj, 
+                                 gv_draw_mode draw_mode,
+                                 GvShapeDrawInfo *drawinfo );
+#endif /* __GV_SHAPES_LAYER_H__ */

Added: packages/openev/branches/upstream/current/gvskirt.c
===================================================================
--- packages/openev/branches/upstream/current/gvskirt.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvskirt.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,367 @@
+/******************************************************************************
+ * $Id: gvskirt.c,v 1.6 2002/10/07 06:08:07 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Code to generate 3D skirts around a raster layer.  The skirts
+ *           are generated as GvShapesLayer.  This feature is initially
+ *           implemented for use in CIETmap.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, DM Solutions Group (www.dmsolutions.ca)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvskirt.c,v $
+ * Revision 1.6  2002/10/07 06:08:07  warmerda
+ * Avoid unused variable warning.
+ *
+ * Revision 1.5  2002/09/27 18:17:18  pgs
+ * modified skirt building to remove white lines.
+ *
+ * Revision 1.4  2002/09/12 15:34:41  warmerda
+ * use odd modulus for facet counter
+ *
+ * Revision 1.3  2002/09/11 19:15:37  warmerda
+ * changed way of deciding on white facets to avoid angular dependencies
+ *
+ * Revision 1.2  2001/07/13 22:14:18  warmerda
+ * completed implementation
+ *
+ * Revision 1.1  2001/07/09 20:22:04  warmerda
+ * New
+ *
+ */
+
+#include "gvshapes.h"
+#include "gvrasterlayer.h"
+#include "cpl_error.h"
+#include "gvshapeslayer.h"
+
+static void add_edge( GvShapes *shapes, GvRasterLayer *dem_layer,
+                      double base_z,
+                      double x1, double y1, double z1,
+                      double x2, double y2, double z2 )
+
+{
+    GvRaster    *dem = dem_layer->prototype_data;
+    GvShape *facet;
+    GvColor color;
+    GvProperties *prop;
+    char    color_prop[128];
+    float   z1_mesh = 0, z2_mesh = 0;
+    int     success, i;
+    double  x_geo, y_geo;
+    //static int  facet_counter = 0;
+    float alpha = 0.8;
+
+    facet = gv_shape_new( GVSHAPE_AREA );
+
+/* -------------------------------------------------------------------- */
+/*      Get sample value from the mesh.                                 */
+/* -------------------------------------------------------------------- */
+    x_geo = x1;
+    y_geo = y1;
+    gv_raster_pixel_to_georef( dem, &x_geo, &y_geo, NULL );
+
+    z1_mesh = gv_mesh_get_height( dem_layer->mesh, x_geo, y_geo, &success );
+    if( !success )
+        z1_mesh = z1;
+
+    x_geo = x2;
+    y_geo = y2;
+    gv_raster_pixel_to_georef( dem, &x_geo, &y_geo, NULL );
+
+    z2_mesh = gv_mesh_get_height( dem_layer->mesh, x_geo, y_geo, &success );
+    if( !success )
+        z2_mesh = z2;
+
+/* -------------------------------------------------------------------- */
+/*      Setup the color of the facet.  We set some to white to          */
+/*      provide a stripped effect.  The rest are set to the color of    */
+/*      the raster at that point.                                       */
+/* -------------------------------------------------------------------- */
+/*    if( ((int) (x1+y1)) % 20 >= 0 && ((int) (x1+y1)) % 20 <= 2 ) */
+
+    //use a transparent skirt instead
+    /*
+    if( facet_counter++ > 18 ) //facet_counter == 19 ) //facet_counter == 18 || facet_counter == 19)
+    {
+        if (facet_counter == 19)
+            facet_counter = 0;
+        color[0] = 1.0;
+        color[1] = 1.0;
+        color[2] = 1.0;
+        color[3] = alpha;
+    }
+    else
+    */
+    {
+        GvRasterSource *src = dem_layer->source_list + 0;
+        int  r_class;
+
+        r_class = (((((z1+z2)/2.0) - src->min)/ (src->max-src->min))*255);
+        r_class = MAX(0,MIN(255,r_class));
+
+        if( src->lut )
+            r_class = src->lut[r_class];
+
+        if( dem_layer->pc_lut != NULL )
+        {
+            color[0] = dem_layer->pc_lut[r_class * 4    ] / 255.0;
+            color[1] = dem_layer->pc_lut[r_class * 4 + 1] / 255.0;
+            color[2] = dem_layer->pc_lut[r_class * 4 + 2] / 255.0;
+            //color[3] = dem_layer->pc_lut[r_class * 4 + 3] / 255.0;
+            color[3] = alpha;
+        }
+        else
+        {
+            color[0] = color[1] = color[2] = r_class / 255.0;
+            color[3] = alpha;
+        }
+    }
+
+    for (i=0;i<3;i++)
+        color[i] = color[i] + (1.0 - color[3]) * (1.0 - color[i]);
+    color[3] = 1.0;
+
+    prop = gv_shape_get_properties( facet );
+
+    sprintf( color_prop, "%.3f %.3f %.3f %.3f", color[0], color[1],
+                                                color[2], color[3] );
+    gv_properties_set( prop, "_gv_fill_color", color_prop );
+    //disable edges to speed up drawing
+    gv_properties_set( prop, "_gv_color", "0.0 0.0 0.0 0.0" );
+
+/* -------------------------------------------------------------------- */
+/*      Create a polygon for the facet.                                 */
+/* -------------------------------------------------------------------- */
+    gv_raster_pixel_to_georef( dem, &x1, &y1, NULL );
+    gv_raster_pixel_to_georef( dem, &x2, &y2, NULL );
+
+    gv_shape_add_node( facet, 0, x1, y1, z1_mesh );
+    gv_shape_add_node( facet, 0, x1, y1, base_z );
+    gv_shape_add_node( facet, 0, x2, y2, base_z );
+    gv_shape_add_node( facet, 0, x2, y2, z2_mesh );
+    gv_shape_add_node( facet, 0, x1, y1, z1_mesh );
+
+/* -------------------------------------------------------------------- */
+/*      Add the shape to the shapes container.                          */
+/* -------------------------------------------------------------------- */
+    gv_shapes_add_shape( shapes, facet );
+}
+
+
+/************************************************************************/
+/*                           gv_build_skirt()                           */
+/************************************************************************/
+GvLayer *gv_build_skirt( GvRasterLayer *dem_layer, double base_z )
+
+{
+    GvShapes    *shapes;
+    int     x, y;
+    GvRaster    *dem = dem_layer->prototype_data;
+    double  no_data = 1000000;
+    float   *this_line, *next_line;
+
+    this_line = g_new(float,dem->width+2);
+    next_line = g_new(float,dem->width+2);
+
+    if( dem_layer->source_list[0].nodata_active )
+        no_data = dem_layer->source_list[0].nodata_real;
+
+    for( x = 0; x < dem->width+2; x++)
+        next_line[x] = no_data;
+
+    shapes = GV_SHAPES(gv_shapes_new());
+
+    for( y = -1; y < dem->height; y++ )
+    {
+        memcpy( this_line, next_line, sizeof(float) * (dem->width+2) );
+
+        if( y+1 < dem->height )
+        {
+            GDALRasterIO( dem->gdal_band, GF_Read,
+                          0, y+1, dem->width, 1,
+                          next_line + 1, dem->width, 1, GDT_Float32, 0, 0 );
+        }
+        else
+        {
+            for( x = 0; x < dem->width+2; x++)
+                next_line[x] = no_data;
+        }
+
+        for( x = -1; x < dem->width; x++ )
+        {
+            double  pix_00, pix_10, pix_01, pix_11;
+            int     nd_00, nd_10, nd_01, nd_11;
+
+            /*
+            ** Fetch the four local pixels.
+            */
+
+            pix_00 = this_line[x+1];
+            pix_10 = this_line[x+2];
+            pix_01 = next_line[x+1];
+            pix_11 = next_line[x+2];
+
+            nd_00 = (pix_00 == no_data);
+            nd_01 = (pix_01 == no_data);
+            nd_10 = (pix_10 == no_data);
+            nd_11 = (pix_11 == no_data);
+
+            if( (nd_00 == nd_01) && (nd_00 == nd_10) && (nd_00 == nd_11) )
+                continue;
+#ifndef notdef
+            if( !nd_00 && nd_01 )
+            {
+                add_edge( shapes, dem_layer, base_z,
+                          x, y+1, pix_00,
+                          x+1, y+1, pix_00);
+            }
+            if( nd_00 && !nd_01 )
+            {
+                add_edge( shapes, dem_layer, base_z,
+                          x+1, y+1, pix_01,
+                          x, y+1, pix_01);
+            }
+            if( !nd_00 && nd_10 )
+            {
+                add_edge( shapes, dem_layer, base_z,
+                          x+1, y+1, pix_00,
+                          x+1, y, pix_00);
+            }
+            if( nd_00 && !nd_10 )
+            {
+                add_edge( shapes, dem_layer, base_z,
+                          x+1, y, pix_10,
+                          x+1, y+1, pix_10);
+            }
+#endif
+#ifdef notdef
+            if( nd_00 && nd_01 && !nd_10 && !nd_11 )
+            {
+                /* vertical edge. */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y, pix_10,
+                          x+0.5, y+1, pix_11 );
+            }
+            else if( !nd_00 && !nd_01 && nd_10 && nd_11 )
+            {
+                /* vertical edge, reversed. */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y+1, pix_00,
+                          x+0.5, y, pix_01 );
+            }
+            else if( nd_00 && nd_10 && !nd_01 && !nd_11 )
+            {
+                /* horizontal edge. */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x, y+0.5, pix_01,
+                          x+1.0, y+0.5, pix_11 );
+            }
+            else if( !nd_00 && !nd_10 && nd_01 && nd_11 )
+            {
+                /* horizontal edge, reversed. */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+1.0, y+0.5, pix_00,
+                          x, y+0.5, pix_10 );
+            }
+            else if( nd_00 && !nd_10 && !nd_01 && !nd_11 )
+            {
+                /* diag, top left missing, reversed */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y, pix_10,
+                          x, y+0.5, pix_01 );
+            }
+            else if( !nd_00 && nd_10 && nd_01 && nd_11 )
+            {
+                /* diag, top left present */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y, pix_00,
+                          x, y+0.5, pix_00 );
+            }
+            else if( !nd_00 && !nd_10 && nd_01 && !nd_11 )
+            {
+                /* diag, bottom left missing */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y+1, pix_11,
+                          x, y+0.5, pix_00 );
+            }
+            else if( nd_00 && nd_10 && !nd_01 && nd_11 )
+            {
+                /* diag, bottom left present */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y+1, pix_01,
+                          x, y+0.5, pix_01 );
+            }
+            else if( !nd_00 && nd_10 && !nd_01 && !nd_11 )
+            {
+                /* diag, top right missing */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y, pix_00,
+                          x+1, y+0.5, pix_11 );
+            }
+            else if( nd_00 && !nd_10 && nd_01 && nd_11 )
+            {
+                /* diag, top right present */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+0.5, y, pix_10,
+                          x+1, y+0.5, pix_10 );
+            }
+            else if( !nd_10 && !nd_10 && !nd_01 && nd_11 )
+            {
+                /* diag, bottom right missing */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+1, y+0.5, pix_10,
+                          x+0.5, y+1, pix_01 );
+            }
+            else if( nd_00 && nd_10 && nd_01 && !nd_11 )
+            {
+                /* diag, bottom right present */
+
+                add_edge( shapes, dem_layer, base_z,
+                          x+1, y+0.5, pix_11,
+                          x+0.5, y+1, pix_11 );
+            }
+            else
+            {
+                printf( "%d,%d,%d,%d\n", nd_00, nd_10, nd_01, nd_11 );
+            }
+#endif
+        }
+    }
+
+    g_free( this_line );
+    g_free( next_line );
+
+/* -------------------------------------------------------------------- */
+/*      Turn this into a layer.                                         */
+/* -------------------------------------------------------------------- */
+    return GV_LAYER(gv_shapes_layer_new(shapes));
+}

Added: packages/openev/branches/upstream/current/gvsymbolmanager.c
===================================================================
--- packages/openev/branches/upstream/current/gvsymbolmanager.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvsymbolmanager.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,706 @@
+/******************************************************************************
+ * $Id: gvsymbolmanager.c,v 1.12 2004/02/16 17:08:12 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  manage file-based symbols
+ * Author:   Paul Spencer (pgs at magma.ca)
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Paul Spencer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvsymbolmanager.c,v $
+ * Revision 1.12  2004/02/16 17:08:12  warmerda
+ * support OPENEVHOME as well as OPENEV_HOME
+ *
+ * Revision 1.11  2003/11/07 18:27:08  warmerda
+ * use dummy sumbol when load fails
+ *
+ * Revision 1.10  2003/09/02 17:25:08  warmerda
+ * Added _has_symbol(), and _get_names() methods.  Use outside GvShape
+ * serialize/deserialize code.  Don't store symbols with absolute paths in
+ * the hash.
+ *
+ * Revision 1.9  2003/08/29 20:52:58  warmerda
+ * use gv_shape_from_xml_tree
+ *
+ * Revision 1.8  2003/05/16 17:50:34  warmerda
+ * moved debug statement to avoid output everytime a loaded symbol is drawn
+ *
+ * Revision 1.7  2003/04/09 21:32:48  andrey_kiselev
+ * Couple of memory leaks fixed.
+ *
+ * Revision 1.6  2003/04/09 13:14:24  andrey_kiselev
+ * Memory leak fixed.
+ *
+ * Revision 1.5  2003/04/08 11:56:32  andrey_kiselev
+ * Implemented gv_symbol_manager_save_vector_symbol() function.
+ *
+ * Revision 1.4  2003/04/07 20:09:04  andrey_kiselev
+ * Path to simbols directory now could be configured with "symbols_dir"
+ * GvManager property.
+ *
+ * Revision 1.3  2003/04/07 15:52:49  andrey_kiselev
+ * gv_symbol_manager_get_symbol() now able to load and inject vector symbols.
+ *
+ * Revision 1.2  2003/02/28 16:46:45  warmerda
+ * added partial support for vector symbols
+ *
+ * Revision 1.1  2002/11/14 20:10:41  warmerda
+ * New
+ */
+
+#include "gvsymbolmanager.h"
+#include "gvmanager.h"
+#include "gdal.h"
+#include "cpl_conv.h"
+#include "cpl_string.h"
+#include "cpl_minixml.h"
+#include <gtk/gtksignal.h>
+#include <stdio.h>
+
+/* signals (none for now?) */
+enum { LAST_SIGNAL };
+
+GByte *     gdal_to_rgba( GDALDatasetH hDS );
+gboolean    finalize_symbol(gpointer key, gpointer value, gpointer user_data);
+
+static void gv_symbol_manager_init( GvSymbolManager *manager );
+static void gv_symbol_manager_class_init( GvSymbolManagerClass *klass );
+static void gv_symbol_manager_finalize( GtkObject *manager );
+
+/* static void gv_symbol_manager_signals[LAST_SIGNAL] = { 0 }; */
+
+/************************************************************************/
+/*                    gv_symbol_manager_class_init()                    */
+/************************************************************************/
+GtkType 
+gv_symbol_manager_get_type(void)
+{
+    static GtkType symbol_manager_type = 0;
+
+    if (!symbol_manager_type)
+    {
+        static const GtkTypeInfo symbol_manager_info =
+        {
+            "GvSymbolManager",
+            sizeof(GvSymbolManager),
+            sizeof(GvSymbolManagerClass),
+            (GtkClassInitFunc) gv_symbol_manager_class_init,
+            (GtkObjectInitFunc) gv_symbol_manager_init,
+            /* reserved_1 */ NULL,
+            /* reserved_2 */ NULL,
+            (GtkClassInitFunc) NULL,
+        };
+        symbol_manager_type = gtk_type_unique( gtk_object_get_type(),
+                                               &symbol_manager_info );
+    }
+    return symbol_manager_type;
+}
+
+/************************************************************************/
+/*                    gv_symbol_manager_class_init()                    */
+/************************************************************************/
+static void 
+gv_symbol_manager_class_init( GvSymbolManagerClass *klass )
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    ((GtkObjectClass *) klass)->finalize = gv_symbol_manager_finalize;
+}
+
+/************************************************************************/
+/*                       gv_symbol_manager_init()                       */
+/************************************************************************/
+static void 
+gv_symbol_manager_init( GvSymbolManager *manager )
+{
+    manager->symbol_cache = g_hash_table_new( g_str_hash, g_str_equal );
+
+    //GDALAllRegister();
+}
+
+/************************************************************************/
+/*                         gv_symbol_manager_new()                      */
+/*                                                                      */
+/*      construct a new instance of the symbol manager.  This           */
+/*      function is private.  Call gv_get_symbol_manager()              */
+/************************************************************************/
+GvSymbolManager *gv_symbol_manager_new()
+{
+    return GV_SYMBOL_MANAGER(gtk_type_new(GV_TYPE_SYMBOL_MANAGER));
+}
+
+/************************************************************************/
+/*                         gv_get_symbol_manager()                      */
+/*                                                                      */
+/*      return a reference to the symbol manager object.  This          */
+/*      implements the singleton pattern so that only one symbol        */
+/*      manager is created.                                             */
+/************************************************************************/
+GvSymbolManager *
+gv_get_symbol_manager()
+{
+    static GvSymbolManager *main_symbol_manager = NULL;
+
+    if ( main_symbol_manager == NULL )
+    {
+        main_symbol_manager = gv_symbol_manager_new();
+    }
+
+    return main_symbol_manager;
+}
+
+/************************************************************************/
+/*                    gv_symbol_manager_has_symbol()                    */
+/************************************************************************/
+
+int gv_symbol_manager_has_symbol( GvSymbolManager *manager, 
+                                  const char *name )
+
+{
+    g_return_val_if_fail( manager != NULL, 0 );
+
+    return g_hash_table_lookup( manager->symbol_cache, name ) != NULL;
+}
+
+/************************************************************************/
+/*                      gv_symbol_manager_get_symbol()                  */
+/*                                                                      */
+/*      manager - a gvsymbolmanager instance (use gv_get_symbol_manager)*/
+/*      pszFilename - the name of the symbol to read in                 */
+/*                                                                      */
+/*      This returns a texture 'name' that can be used with             */
+/*      glBindTexture if the symbol is successfully loaded and          */
+/*      converted to a texture or 0 on failure.  Symbols are cached     */
+/*      using the absolute path to the symbol file so that only one     */
+/*      copy of the symbol should ever be loaded.  This could be        */
+/*      circumvented by having symlinks or .. in the path but in        */
+/*      most real cases this won't happen.                              */
+/*                                                                      */
+/*      The maximum cache size is set in the xxxxxxx property.  When    */
+/*      the symbol cache is full, textures are released on a Least      */
+/*      Recently Used basis.  The texture can still be accessed         */
+/*      because the symbol name will persist in the cache.              */
+/************************************************************************/
+GvSymbolObj *
+gv_symbol_manager_get_symbol(GvSymbolManager *manager, const char *symbol_name)
+{
+    gchar   *pszOpenEVHome = NULL;
+    gchar   *pszSymbolsDir = NULL;
+    gchar   *pszAbsolutePath = NULL;
+    gchar   *pszPathSeparator = NULL;
+    GDALDatasetH hDataset;
+    GvSymbolObj *poSymbol;
+    CPLXMLNode  *xml_shape = NULL;
+    GByte *rgba_buffer;
+
+/* -------------------------------------------------------------------- */
+/*      Lookup the symbol in the hash table, and return it if found.    */
+/* -------------------------------------------------------------------- */
+    poSymbol = g_hash_table_lookup( manager->symbol_cache, symbol_name );
+    if( poSymbol != NULL )
+        return poSymbol;
+
+/* -------------------------------------------------------------------- */
+/*      We didn't already have it, so try to find a file to load it     */
+/*      from.                                                           */
+/* -------------------------------------------------------------------- */
+#ifndef WIN32
+    pszPathSeparator = "/";
+#else
+    pszPathSeparator = "\\";
+#endif /* WIN32 */
+
+    /* validate inputs */
+    g_return_val_if_fail( manager != NULL, 0 );
+    g_return_val_if_fail( symbol_name != NULL, 0 );
+
+    /* get an absolute path */
+    if ( !g_path_is_absolute( symbol_name ) )
+    {
+        /* check configuration option first */
+        pszSymbolsDir = g_strdup( gv_manager_get_preference( gv_get_manager(),
+                                                             "symbols_dir" ) );
+
+        /* if not configured check $OPENEV_HOME */
+        if ( !pszSymbolsDir )
+        {
+            pszOpenEVHome = g_getenv( "OPENEV_HOME" );
+            if( pszOpenEVHome == NULL )
+                pszOpenEVHome = g_getenv( "OPENEVHOME" );
+            if ( pszOpenEVHome )
+                pszSymbolsDir = g_strjoin( pszPathSeparator, pszOpenEVHome,
+                                           "symbols", NULL );
+        }
+        
+        /* get current directory as last resort */
+        if ( !pszSymbolsDir )
+            pszSymbolsDir = g_get_current_dir();
+
+        pszAbsolutePath = g_strjoin( pszPathSeparator, pszSymbolsDir, 
+                                     symbol_name, NULL );
+        g_free( pszSymbolsDir );
+    }
+    else
+        pszAbsolutePath = g_strdup( symbol_name );
+
+/* -------------------------------------------------------------------- */
+/*      pszAbsolutePath contains a newly allocated string that is       */
+/*      suitable for using as a key in the hash table.  If a texture    */
+/*      is found in the hash table then this string needs to be         */
+/*      freed.  If one isn't found then the string is used in the       */
+/*      hash table and should be freed when the associated hash         */
+/*      table entry is released                                         */
+/* -------------------------------------------------------------------- */
+    CPLDebug( "OpenEV", 
+              "gv_symbol_manager_get_symbol(%s) ... need to load.", 
+                  pszAbsolutePath );
+    
+    /* 
+     * validate path by opening with GDAL and looking for an error 
+     * Disable CPL error handler to supress error reporting
+     */
+    
+    CPLErrorReset();
+    CPLPushErrorHandler( CPLQuietErrorHandler );
+    hDataset = GDALOpen( pszAbsolutePath, GA_ReadOnly );
+    CPLPopErrorHandler();
+    
+    if ( hDataset )
+    {
+        rgba_buffer = gdal_to_rgba( hDataset );
+        
+        if ( rgba_buffer )
+        {
+            gv_symbol_manager_inject_raster_symbol( 
+                manager, symbol_name,
+                GDALGetRasterXSize( hDataset ),
+                GDALGetRasterYSize( hDataset ),
+                rgba_buffer );
+            CPLFree( rgba_buffer );
+        }
+        
+        GDALClose( hDataset );
+    }
+    /* probably we have vector symbol? */
+    else if ( ( xml_shape = CPLParseXMLFile( pszAbsolutePath ) ) )
+    {
+        GvShape     *shape;
+        shape = gv_shape_from_xml_tree( xml_shape );
+        
+        CPLDestroyXMLNode( xml_shape );
+        
+        if( shape != NULL )
+            gv_symbol_manager_inject_vector_symbol( manager, symbol_name,
+                                                    shape );
+        else
+        {
+            CPLDebug( "OpenEV", 
+                      "Failed to instantiate GvSahpe from file %s, using simple point.", 
+                      pszAbsolutePath );
+        
+            shape = gv_shape_new( GVSHAPE_POINT );
+            gv_symbol_manager_inject_vector_symbol( manager, symbol_name,
+                                                    shape );
+        }
+    }
+    else
+    {
+        GvShape *shape;
+
+        CPLDebug( "OpenEV", "Failed to open file %s, using simple point.", 
+                  pszAbsolutePath );
+        
+        shape = gv_shape_new( GVSHAPE_POINT );
+        gv_symbol_manager_inject_vector_symbol( manager, symbol_name,
+                                                shape );
+    }
+    
+    /* look up in the hash table again */
+    poSymbol = g_hash_table_lookup(manager->symbol_cache, symbol_name );
+
+    g_free( pszAbsolutePath );
+
+    return poSymbol;
+}
+
+/************************************************************************/
+/*                            finalize_symbol()                         */
+/*                                                                      */
+/*      finalize a symbol object's hash table entry by freeing          */
+/*      allocated memory.  It is (relatively) safe to free the key      */
+/*      since we are sure we allocated the memory for it when           */
+/*      inserting into the hashtable.                                   */
+/************************************************************************/
+
+gboolean
+finalize_symbol( gpointer key, gpointer value, gpointer user_data )
+{
+    GvSymbolObj *poSymbol = (GvSymbolObj *) value;
+
+    g_free( key );
+
+    if (poSymbol->type == GV_SYMBOL_RASTER)
+        g_free( poSymbol->buffer );
+    else if( poSymbol->type == GV_SYMBOL_VECTOR )
+        gv_shape_unref( (GvShape *) poSymbol->buffer );
+
+    g_free( poSymbol );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                       gv_symbol_manager_finalize()                   */
+/*                                                                      */
+/*      clean up the symbol manager storage and free associated memory. */
+/************************************************************************/
+static void
+gv_symbol_manager_finalize( GtkObject *object)
+{
+    GvSymbolManagerClass *parent_class;
+    GvSymbolManager *manager = GV_SYMBOL_MANAGER(object);
+
+    CPLDebug( "OpenEV", "gv_symbol_manager_finalize(%p)", object );
+
+    g_hash_table_foreach_remove(manager->symbol_cache, finalize_symbol, NULL);
+
+    g_hash_table_destroy( manager->symbol_cache );
+    
+    /* parent class destructor */
+    parent_class = gtk_type_class( gtk_object_get_type() );
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+/************************************************************************/
+/*                   gv_symbol_manager_eject_symbol()                   */
+/*                                                                      */
+/*      Remove and deallocate one (named) symbol from the symbol        */
+/*      manager cache.                                                  */
+/************************************************************************/
+
+int gv_symbol_manager_eject_symbol( GvSymbolManager *manager, 
+                                    const char *symbol_name )
+
+{
+    CPLDebug( "OpenEV", 
+              "gv_symbol_manager_eject_symbol() not yet impelemented." );
+    return FALSE;
+}
+
+/************************************************************************/
+/*               gv_symbol_manager_inject_raster_symbol()               */
+/*                                                                      */
+/*      Create a GvSymbolObject corresponding to the pass size and      */
+/*      raster info, and inject it into the symbol manager with the     */
+/*      indicated name.                                                 */
+/************************************************************************/
+
+void gv_symbol_manager_inject_raster_symbol( GvSymbolManager *manager, 
+                                             const char *symbol_name, 
+                                             int width, int height, 
+                                             void *rgba_buffer )
+
+{
+    GvSymbolObj *symbol;
+
+    /* allocate a new symbol object */
+    symbol = g_new0( GvSymbolObj, 1 );
+
+    symbol->type = GV_SYMBOL_RASTER;
+    
+    symbol->width = width;
+    symbol->height = height;
+    
+    /* initialize the foreground and background colors */
+    symbol->foreground[0] = 0.0;
+    symbol->foreground[1] = 0.0;
+    symbol->foreground[2] = 0.0;
+    symbol->foreground[3] = 1.0;
+    symbol->background[0] = 1.0;
+    symbol->background[1] = 1.0;
+    symbol->background[2] = 1.0;
+    symbol->background[3] = 0.0;
+
+    /* copy the raster buffer. */
+    symbol->buffer = (GByte *) CPLMalloc(width*height*4);
+    memcpy( symbol->buffer, rgba_buffer, width*height*4 );
+
+    /* insert new symbol into symbol manager */
+    g_hash_table_insert( manager->symbol_cache, g_strdup(symbol_name), symbol);
+
+    CPLDebug("OpenEV", "inject_raster_symbol(%s, %dx%d)", 
+             symbol_name, width, height );
+}
+
+/************************************************************************/
+/*               gv_symbol_manager_inject_vector_symbol()               */
+/*                                                                      */
+/*      Create a GvSymbolObject corresponding to the passed             */
+/*      GvShape.  An internal copy of the shape is made.                */
+/************************************************************************/
+
+void gv_symbol_manager_inject_vector_symbol( GvSymbolManager *manager, 
+                                             const char *symbol_name, 
+                                             GvShape *shape )
+
+{
+    GvSymbolObj *symbol;
+
+    /* allocate a new symbol object */
+    symbol = g_new0( GvSymbolObj, 1 );
+
+    symbol->type = GV_SYMBOL_VECTOR;
+    
+    /* take a reference to the shape */
+    symbol->buffer = shape;
+    gv_shape_ref( shape );
+    
+    /* insert new symbol into symbol manager */
+    g_hash_table_insert( manager->symbol_cache, g_strdup(symbol_name), symbol );
+
+    CPLDebug("OpenEV", "inject_vector_symbol(%s)", symbol_name );
+}
+
+/************************************************************************/
+/*               gv_symbol_manager_save_vector_symbol()                 */
+/*                                                                      */
+/*      Save an existing and loaded vector simbol under new name.       */
+/************************************************************************/
+
+int gv_symbol_manager_save_vector_symbol( GvSymbolManager *manager, 
+                                          const char *symbol_name, 
+                                          const char *new_name )
+
+{
+    GvSymbolObj *symbol;
+
+    /* allocate a new symbol object */
+    symbol = g_hash_table_lookup( manager->symbol_cache, symbol_name );
+
+    CPLDebug("OpenEV", "save_vector_symbol(%s->%s)", symbol_name, new_name );
+
+    if ( symbol && symbol->type == GV_SYMBOL_VECTOR )
+    {
+        GvShape     *shape;
+        CPLXMLNode  *xml_shape;
+
+        shape = (GvShape *)symbol->buffer;
+        xml_shape = gv_shape_to_xml_tree( shape );
+
+        if ( CPLSerializeXMLTreeToFile( xml_shape, new_name ) )
+        {
+            CPLDestroyXMLNode( xml_shape );
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+
+}
+
+/************************************************************************/
+/*                             gdal_to_rgba()                           */
+/*                                                                      */
+/*      convert a GDAL dataset into an RGBA buffer.  Provided by        */
+/*      Frank Warmerdam :)                                              */
+/************************************************************************/
+GByte *
+gdal_to_rgba( GDALDatasetH hDS )
+{
+    int  nXSize, nYSize;
+    GByte *pabyRGBABuf = NULL;
+
+    /* validation of input parameters */
+    g_return_val_if_fail( hDS != NULL, NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Allocate RGBA Raster buffer.                                    */
+/* -------------------------------------------------------------------- */
+
+    nXSize = GDALGetRasterXSize( hDS );
+    nYSize = GDALGetRasterYSize( hDS );
+    CPLDebug( "OpenEV", "creating buffer of (%d,%d)", nXSize, nYSize );
+    pabyRGBABuf = (GByte *) CPLMalloc( nXSize * nYSize * 4 );
+
+/* -------------------------------------------------------------------- */
+/*      Handle case where source is already presumed to be RGBA.        */
+/* -------------------------------------------------------------------- */
+    if( GDALGetRasterCount(hDS) == 4 )
+    {
+        GDALRasterIO( GDALGetRasterBand( hDS, 1 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+0, nXSize, nYSize, GDT_Byte, 
+                      4, nXSize * 4 );
+                      
+        GDALRasterIO( GDALGetRasterBand( hDS, 2 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+1, nXSize, nYSize, GDT_Byte, 4, 
+                      nXSize * 4 );
+                      
+        GDALRasterIO( GDALGetRasterBand( hDS, 3 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+2, nXSize, nYSize, GDT_Byte, 4, 
+                      nXSize * 4 );
+                      
+        GDALRasterIO( GDALGetRasterBand( hDS, 4 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+3, nXSize, nYSize, GDT_Byte, 4, 
+                      nXSize * 4 );
+    }
+/* -------------------------------------------------------------------- */
+/*      Source is RGB.  Set Alpha to 255.                               */
+/* -------------------------------------------------------------------- */
+    else if( GDALGetRasterCount(hDS) == 3 )
+    {
+        memset( pabyRGBABuf, 255, 4 * nXSize * nYSize );
+        
+        GDALRasterIO( GDALGetRasterBand( hDS, 1 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+0, nXSize, nYSize, GDT_Byte, 
+                      4, nXSize * 4 );
+                      
+        GDALRasterIO( GDALGetRasterBand( hDS, 2 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+1, nXSize, nYSize, GDT_Byte, 4, 
+                      nXSize * 4 );
+                      
+        GDALRasterIO( GDALGetRasterBand( hDS, 3 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+2, nXSize, nYSize, GDT_Byte, 4, 
+                      nXSize * 4 );
+    }
+/* -------------------------------------------------------------------- */
+/*      Source is pseudocolored.  Load and then convert to RGBA.        */
+/* -------------------------------------------------------------------- */
+    else if( GDALGetRasterCount(hDS) == 1 
+             && GDALGetRasterColorTable( GDALGetRasterBand( hDS, 1 )) != NULL )
+    {
+        int        i;
+        GDALColorTableH hTable;
+        GByte      abyPCT[1024];
+
+        /* Load color table, and produce 256 entry table to RGBA. */
+        hTable = GDALGetRasterColorTable( GDALGetRasterBand( hDS, 1 ) );
+
+        for( i = 0; i < MIN(256,GDALGetColorEntryCount( hTable )); i++ )
+        {
+            GDALColorEntry sEntry;
+
+            GDALGetColorEntryAsRGB( hTable, i, &sEntry );
+            abyPCT[i*4+0] = sEntry.c1;
+            abyPCT[i*4+1] = sEntry.c2;
+            abyPCT[i*4+2] = sEntry.c3;
+            abyPCT[i*4+3] = sEntry.c4;
+        }
+
+        /* Fill in any missing colors with greyscale. */
+        for( i = GDALGetColorEntryCount( hTable ); i < 256; i++ )
+        {
+            abyPCT[i*4+0] = i;
+            abyPCT[i*4+1] = i;
+            abyPCT[i*4+2] = i;
+            abyPCT[i*4+3] = 255;
+        }
+
+        /* Read indexed raster */
+        GDALRasterIO( GDALGetRasterBand( hDS, 1 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+0, nXSize, nYSize, GDT_Byte, 
+                      4, nXSize * 4 );
+
+        /* Convert to RGBA using palette. */
+        for( i = nXSize * nYSize - 1; i >= 0; i-- )
+        {
+            memcpy( pabyRGBABuf + i*4, 
+                    abyPCT + pabyRGBABuf[i*4]*4, 
+                    4 );
+        }
+    }
+/* -------------------------------------------------------------------- */
+/*      Source band is greyscale.  Load it into Red, Green and Blue.    */
+/* -------------------------------------------------------------------- */
+    else if( GDALGetRasterCount(hDS) == 1 )
+    {
+        memset( pabyRGBABuf, 255, 4 * nXSize * nYSize );
+        
+        GDALRasterIO( GDALGetRasterBand( hDS, 1 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+0, nXSize, nYSize, GDT_Byte, 
+                      4, nXSize * 4 );
+        GDALRasterIO( GDALGetRasterBand( hDS, 1 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+1, nXSize, nYSize, GDT_Byte, 
+                      4, nXSize * 4 );
+        GDALRasterIO( GDALGetRasterBand( hDS, 1 ), GF_Read, 
+                      0, 0, nXSize, nYSize, 
+                      pabyRGBABuf+2, nXSize, nYSize, GDT_Byte, 
+                      4, nXSize * 4 );
+    }
+
+    return pabyRGBABuf;
+}
+
+/************************************************************************/
+/*                        gv_sm_name_collector()                        */
+/*                                                                      */
+/*      Callback function used by gv_symbol_manager_get_names().        */
+/************************************************************************/
+
+typedef struct {
+    char **name_list;
+    int  count_so_far;
+    int  count_max;
+} name_collector_info;
+
+static void gv_sm_name_collector( gpointer key, gpointer value, 
+                                  gpointer user_data )
+
+{
+    name_collector_info *nci = (name_collector_info *) user_data;
+
+    nci->name_list[nci->count_so_far++] = (char*) key;
+}
+
+/************************************************************************/
+/*                    gv_symbol_manager_get_names()                     */
+/*                                                                      */
+/*      Fetch a list of all symbol names on this symbol manager.        */
+/*      The list should be freed with g_free() when no longer           */
+/*      needed.  The strings it contains are internal.                  */
+/************************************************************************/
+
+char **gv_symbol_manager_get_names( GvSymbolManager *manager )
+
+{
+    name_collector_info nci;
+
+    nci.count_so_far = 0;
+    nci.count_max = g_hash_table_size( manager->symbol_cache );
+    nci.name_list = g_new( char *, nci.count_max+1 );
+    nci.name_list[nci.count_max] = NULL;
+    
+    g_hash_table_foreach( manager->symbol_cache, gv_sm_name_collector, 
+                          &nci );
+
+    return nci.name_list;
+}
+

Added: packages/openev/branches/upstream/current/gvsymbolmanager.h
===================================================================
--- packages/openev/branches/upstream/current/gvsymbolmanager.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvsymbolmanager.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * $Id: gvsymbolmanager.h,v 1.5 2003/09/02 17:25:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  manage file-based symbols
+ * Author:   Paul Spencer (pgs at magma.ca)
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Paul Spencer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvsymbolmanager.h,v $
+ * Revision 1.5  2003/09/02 17:25:08  warmerda
+ * Added _has_symbol(), and _get_names() methods.  Use outside GvShape
+ * serialize/deserialize code.  Don't store symbols with absolute paths in
+ * the hash.
+ *
+ * Revision 1.4  2003/04/08 11:56:32  andrey_kiselev
+ * Implemented gv_symbol_manager_save_vector_symbol() function.
+ *
+ * Revision 1.3  2003/02/28 16:46:46  warmerda
+ * added partial support for vector symbols
+ *
+ * Revision 1.2  2003/01/08 03:25:40  warmerda
+ * fiddle with include files for win build
+ *
+ * Revision 1.1  2002/11/14 20:10:41  warmerda
+ * New
+ *
+ *
+ */
+
+#ifndef __GV_SYMBOL_H__
+#define __GV_SYMBOL_H__
+
+#include <gtkgl/gdkgl.h>
+#include <GL/gl.h>
+#include <gtk/gtkobject.h>
+#include "gvtypes.h"
+#include "gvshapes.h"
+
+
+#define GV_TYPE_SYMBOL_MANAGER       (gv_symbol_manager_get_type ())
+#define GV_SYMBOL_MANAGER(obj)       (GTK_CHECK_CAST ((obj), GV_TYPE_SYMBOL_MANAGER, GvSymbolManager))
+#define GV_SYMBOL_MANAGER_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_SYMBOL_MANAGER, GvSymbolManagerClass))
+#define GV_IS_SYMBOL_MANAGER(obj)           (GTK_CHECK_TYPE ((obj), GV_TYPE_SYMBOL_MANAGER))
+#define GV_IS_SYMBOL_MANAGER_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GV_SYMBOL_TYPE_MANAGER))
+
+enum { GV_SYMBOL_RASTER = 0, GV_SYMBOL_VECTOR = 1 };
+
+typedef struct _GvSymbolManager         GvSymbolManager;
+typedef struct _GvSymbolManagerClass    GvSymbolManagerClass;
+
+typedef struct _GvSymbolObj
+{
+    guint        type;           /* GV_SYMBOL_RASTER or GV_SYMBOL_VECTOR */
+
+    /* for rasters */
+    GvColor     foreground;     /* foreground color */
+    GvColor     background;     /* background color */
+    guint       width;          /* width of the symbol */
+    guint       height;         /* height of the symbol */
+
+    /* type specific storage (RGBA image or GvShape *) */
+    void *      buffer;         
+} GvSymbolObj;
+
+struct _GvSymbolManager
+{
+    GtkObject object;
+    GHashTable *symbol_cache;
+};
+
+struct _GvSymbolManagerClass
+{
+    GtkObjectClass parent_class;
+};
+
+GtkType          gv_symbol_manager_get_type  ( void );
+GvSymbolManager* gv_symbol_manager_new       ( void );
+GvSymbolManager* gv_get_symbol_manager       ( void );
+
+int           gv_symbol_manager_has_symbol( GvSymbolManager *manager, 
+                                            const char *name );
+
+GvSymbolObj*  gv_symbol_manager_get_symbol( GvSymbolManager *manager,
+                                            const char  *pszFilename );
+
+void gv_symbol_manager_inject_raster_symbol( GvSymbolManager *manager, 
+                                             const char *symbol_name, 
+                                             int width, int height, 
+                                             void *rgba_buffer );
+void gv_symbol_manager_inject_vector_symbol( GvSymbolManager *manager, 
+                                             const char *symbol_name, 
+                                             GvShape *shape );
+int  gv_symbol_manager_eject_symbol( GvSymbolManager *manager, 
+                                     const char *symbol_name );
+int gv_symbol_manager_save_vector_symbol( GvSymbolManager *manager, 
+                                          const char *symbol_name, 
+                                          const char *new_name );
+
+char **gv_symbol_manager_get_names( GvSymbolManager *manager );
+
+
+#endif /* __GV_SYMBOL_H__ */

Added: packages/openev/branches/upstream/current/gvtess.c
===================================================================
--- packages/openev/branches/upstream/current/gvtess.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtess.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,272 @@
+/******************************************************************************
+ * $Id: gvtess.c,v 1.12 2002/12/10 02:57:33 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Tesselation for GvAreas (will be discarded with GvAreas)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtess.c,v $
+ * Revision 1.12  2002/12/10 02:57:33  sduclos
+ * update tess callback cast for WIN_CALLBACK
+ *
+ * Revision 1.11  2002/11/05 18:56:25  sduclos
+ * fix gcc warning
+ *
+ * Revision 1.10  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.9  2001/05/01 21:48:01  warmerda
+ * fixed Mesa3.3 compatibility fixes
+ *
+ * Revision 1.8  2001/04/24 16:22:12  warmerda
+ * fixed mesa3.3 incompatibility
+ *
+ * Revision 1.7  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvtess.h"
+
+/* gtkgl.h is to satisfy some Windows definition requirements */
+#include <gtkgl/gdkgl.h>
+
+#include <GL/glu.h>
+
+/* Backward compatibility with glu ver. 1.1 */
+#if !defined(GLU_VERSION_1_2) 
+
+#if !defined(GLU_TESS_BEGIN)
+#define GLU_TESS_BEGIN  GLU_BEGIN
+#define GLU_TESS_END    GLU_END
+#define GLU_TESS_VERTEX GLU_VERTEX
+#define GLU_TESS_ERROR  GLU_ERROR
+#endif
+
+#define gluTessBeginPolygon(tess,data) gluBeginPolygon(tess)
+#define gluTessBeginContour(tess)      gluNextContour(tess, GLU_UNKNOWN)
+#define gluTessEndContour(tess)
+#define gluTessEndPolygon(tess)        gluEndPolygon(tess)
+
+typedef GLUtriangulatorObj GLUtesselator;
+#endif /* GLU_VERSION_1_2 */
+
+enum { GV_CCW, GV_CW };
+
+static GvArea *area;
+
+#ifdef WIN32
+#  define WIN_CALLBACK FAR PASCAL
+#else
+#  define WIN_CALLBACK
+#endif
+
+static void WIN_CALLBACK tess_begin(GLenum type);
+static void WIN_CALLBACK tess_end(void);
+static void WIN_CALLBACK tess_vertex(void *data);
+static void WIN_CALLBACK tess_error(GLenum err);
+static gint check_ring_lengths(void);
+static void check_winding(void);
+static gint find_winding(GArray *array);
+static void reverse_array(GArray *array);
+
+gint
+gv_area_tessellate(GvArea *in_area)
+{
+    typedef void (*f);
+    static GLUtesselator *tess = NULL;
+    GArray *ring;
+    GvVertex *v;
+    int i, j;
+    GLdouble coords[3];
+
+    
+    if (!tess)
+    {
+	tess = gluNewTess();
+	g_return_val_if_fail(tess, FALSE);
+
+	gluTessCallback(tess, GLU_TESS_BEGIN, (f) tess_begin);
+	gluTessCallback(tess, GLU_TESS_END,   (f) tess_end);
+	gluTessCallback(tess, GLU_TESS_VERTEX,(f) tess_vertex);
+	gluTessCallback(tess, GLU_TESS_ERROR, (f) tess_error);
+    }
+
+    /* Global is available to all tess callbacks */
+    area = in_area;
+
+    /* Check for short ring lengths */
+    if (!check_ring_lengths()) return FALSE;
+    
+    /* Fix ring winding before tesselation */
+    check_winding();
+    
+    area->fill_objects = 0;
+    if (area->fill)
+    {
+	g_array_set_size(area->fill, 0);
+	g_array_set_size(area->mode_offset, 0);
+    }
+    else
+    {
+	area->fill = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+	area->mode_offset = 
+	  g_array_new(FALSE, FALSE, sizeof(gint));
+    }
+
+    coords[2] = 0.0;
+
+    gluTessBeginPolygon(tess, NULL);
+    for (i=0; i < area->rings->len; ++i)
+    {
+	ring = gv_areas_get_ring(area, i);
+	
+	gluTessBeginContour(tess);
+	for (j=0; j < ring->len; ++j)
+	{
+	    v = &g_array_index(ring, GvVertex, j);
+	    coords[0] = (GLdouble)v->x;
+	    coords[1] = (GLdouble)v->y;
+	    
+	    gluTessVertex(tess, coords, v);
+	}
+	gluTessEndContour(tess);
+    }
+    gluTessEndPolygon(tess);
+
+    return (area->fill_objects > 0 
+	    && g_array_index(area->mode_offset,gint,0) != GV_TESS_NONE);
+}
+
+static void WIN_CALLBACK
+tess_begin(GLenum type)
+{
+    area->fill_objects++;
+    g_array_append_val(area->mode_offset, type );
+    g_array_append_val(area->mode_offset, area->fill->len );
+}
+
+static void WIN_CALLBACK
+tess_end(void)
+{
+}
+
+static void WIN_CALLBACK
+tess_vertex(void *data)
+{
+  /* for some unknown reason, we somes get called with a NULL 
+     if the object doesn't tesselate properly (with libMesa). */
+
+  if( data != NULL )
+    g_array_append_vals(area->fill, data, 1);
+  else
+    tess_error( 0 );
+}
+
+static void WIN_CALLBACK
+tess_error(GLenum err)
+{
+  GLenum bad_fill_mode = GV_TESS_NONE;
+  gint   offset = 0;
+
+  g_array_set_size(area->fill, 0);
+  g_array_set_size(area->mode_offset, 0);
+
+  area->fill_objects = 1;
+  g_array_append_val(area->mode_offset, bad_fill_mode );
+  g_array_append_val(area->mode_offset, offset );
+}
+
+static gint
+check_ring_lengths(void)
+{
+    int r, rings;
+
+    rings = gv_areas_num_rings(area);
+    
+    for (r=0; r < rings; ++r)
+    {
+	if (gv_areas_get_ring(area, r)->len < 3) return FALSE;
+    }
+    return TRUE;
+}
+
+static void
+check_winding(void)
+{
+    int r, rings, winding;
+    GArray *ring;
+
+    rings = gv_areas_num_rings(area);
+    
+    for (r=0; r < rings; ++r)
+    {
+	ring = gv_areas_get_ring(area, r);
+	winding = find_winding(ring);
+	if ((r == 0 && winding == GV_CW) || (r > 0 && winding == GV_CCW))
+	{
+	    reverse_array(ring);
+	}
+    }
+}
+
+/* According to the comp.graphics.algorthms FAQ, item 2.07, the
+ * orientation (winding) of a simple polygon can be determined by
+ * looking at the sign of the following series:
+ *    sum_{i=0}^{n-1} (x_i y_{i+1} - y_i x_{i+1})
+ * A positive result means a counter clockwise winding.
+ */ 
+static gint
+find_winding(GArray *array)
+{
+    GvVertex *a, *b;
+    gvgeocoord sum;
+    int i;
+
+    sum = 0.0;
+    a = &g_array_index(array, GvVertex, 0);
+    b = a+1;
+    for (i=1; i < array->len; ++i, ++a, ++b)
+    {
+	sum += a->x * b->y - a->y * b->x;
+    }
+    b = &g_array_index(array, GvVertex, 0);
+    sum += a->x * b->y - a->y * b->x;
+
+    return (sum < 0.0 ? GV_CW : GV_CCW);
+}
+
+static void
+reverse_array(GArray *array)
+{
+    GvVertex temp;
+    int i;
+
+    for (i=0; i < array->len/2; ++i)
+    { 
+	temp = g_array_index(array, GvVertex, i);
+	g_array_index(array, GvVertex, i) = g_array_index(array, GvVertex,
+							  array->len - i - 1);
+	g_array_index(array, GvVertex, array->len - i - 1) = temp;
+    }
+}
+

Added: packages/openev/branches/upstream/current/gvtess.h
===================================================================
--- packages/openev/branches/upstream/current/gvtess.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtess.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * $Id: gvtess.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Tesselation for GvAreas (will be discarded with GvAreas)
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtess.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_TESS_H__
+#define __GV_TESS_H__
+
+#include "gvareas.h"
+
+gint gv_area_tessellate(GvArea *area);
+
+#endif /* __GV_TESS_H__ */

Added: packages/openev/branches/upstream/current/gvtessshape.c
===================================================================
--- packages/openev/branches/upstream/current/gvtessshape.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtessshape.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,288 @@
+/******************************************************************************
+ * $Id: gvtessshape.c,v 1.11 2003/02/25 19:41:23 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Tesselation for GvAreaShape.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtessshape.c,v $
+ * Revision 1.11  2003/02/25 19:41:23  warmerda
+ * avoid warning
+ *
+ * Revision 1.10  2002/12/10 02:57:34  sduclos
+ * update tess callback cast for WIN_CALLBACK
+ *
+ * Revision 1.9  2002/11/05 18:56:26  sduclos
+ * fix gcc warning
+ *
+ * Revision 1.8  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.7  2001/05/01 21:48:01  warmerda
+ * fixed Mesa3.3 compatibility fixes
+ *
+ * Revision 1.6  2001/04/24 16:22:12  warmerda
+ * fixed mesa3.3 incompatibility
+ *
+ * Revision 1.5  2000/07/13 22:25:43  warmerda
+ * detect tesselations w/1 fill_object and no vertices, and reset to 0 objects
+ *
+ * Revision 1.4  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvshapes.h"
+
+/* gtkgl.h is to satisfy some Windows definition requirements */
+#include <gtkgl/gdkgl.h>
+#include <GL/glu.h>
+#include <string.h>
+
+/* Backward compatibility with glu ver. 1.1 */
+#if !defined(GLU_VERSION_1_2)
+
+#if !defined(GLU_TESS_BEGIN)
+#define GLU_TESS_BEGIN  GLU_BEGIN
+#define GLU_TESS_END    GLU_END
+#define GLU_TESS_VERTEX GLU_VERTEX
+#define GLU_TESS_ERROR  GLU_ERROR
+#endif
+
+#define gluTessBeginPolygon(tess,data) gluBeginPolygon(tess)
+#define gluTessBeginContour(tess)      gluNextContour(tess, GLU_UNKNOWN)
+#define gluTessEndContour(tess)
+#define gluTessEndPolygon(tess)        gluEndPolygon(tess)
+
+typedef GLUtriangulatorObj GLUtesselator;
+#endif /* GLU_VERSION_1_2 */
+
+enum { GV_CCW, GV_CW };
+
+static GvAreaShape *area;
+
+#ifdef WIN32
+#  define WIN_CALLBACK FAR PASCAL
+#else
+#  define WIN_CALLBACK
+#endif
+
+static void WIN_CALLBACK tess_begin(GLenum type);
+static void WIN_CALLBACK tess_end(void);
+static void WIN_CALLBACK tess_vertex(void *data);
+static void WIN_CALLBACK tess_error(GLenum err);
+static gint check_ring_lengths(void);
+static void check_winding(void);
+static gint find_winding(gvgeocoord *, int);
+static void reverse_array(gvgeocoord *, int);
+
+gint
+gv_area_shape_tessellate(GvAreaShape *in_area)
+{
+    typedef void (*f);
+    static GLUtesselator *tess = NULL;
+    int i, j;
+    GLdouble coords[3];
+
+    /* A fill_objects value of -2 is a special flag meaning don't tesselate
+       or fill because we are in the midst of editing. */
+    if( in_area->fill_objects == -2 )
+        return FALSE;
+    
+    if (!tess)
+    {
+	tess = gluNewTess();
+	g_return_val_if_fail(tess, FALSE);
+
+	gluTessCallback(tess, GLU_TESS_BEGIN, (f) tess_begin);
+	gluTessCallback(tess, GLU_TESS_END,   (f) tess_end);
+	gluTessCallback(tess, GLU_TESS_VERTEX,(f) tess_vertex);
+	gluTessCallback(tess, GLU_TESS_ERROR, (f) tess_error);
+    }
+
+    /* Global is available to all tess callbacks */
+    area = in_area;
+
+    /* Check for short ring lengths, or unclosed rings */
+    if (!check_ring_lengths()) return FALSE;
+    
+    /* Fix ring winding before tesselation */
+    check_winding();
+    
+    area->fill_objects = 0;
+    if (area->fill)
+    {
+	g_array_set_size(area->fill, 0);
+	g_array_set_size(area->mode_offset, 0);
+    }
+    else
+    {
+	area->fill = g_array_new(FALSE, FALSE, sizeof(GvVertex3d));
+	area->mode_offset = 
+	  g_array_new(FALSE, FALSE, sizeof(gint));
+    }
+
+    gluTessBeginPolygon(tess, NULL);
+    for (i=0; i < gv_shape_get_rings((GvShape *) area); ++i)
+    {
+        int     node_count;
+        gvgeocoord   *xyz_nodes;
+
+	node_count = area->num_ring_nodes[i];
+        xyz_nodes = area->xyz_ring_nodes[i];
+	
+	gluTessBeginContour(tess);
+	for (j=0; j < node_count; ++j)
+	{
+            coords[0] = xyz_nodes[j*3+0];
+            coords[1] = xyz_nodes[j*3+1];
+            coords[2] = xyz_nodes[j*3+2];
+	    
+	    gluTessVertex(tess, coords, xyz_nodes + j*3);
+	}
+	gluTessEndContour(tess);
+    }
+    gluTessEndPolygon(tess);
+
+    if( area->fill->len == 0 )
+        area->fill_objects = 0;
+
+    return (area->fill_objects > 0 
+	    && g_array_index(area->mode_offset,gint,0) != GV_TESS_NONE);
+}
+
+static void WIN_CALLBACK
+tess_begin(GLenum type)
+{
+    area->fill_objects++;
+    g_array_append_val(area->mode_offset, type );
+    g_array_append_val(area->mode_offset, area->fill->len );
+}
+
+static void WIN_CALLBACK
+tess_end(void)
+{
+}
+
+static void WIN_CALLBACK
+tess_vertex(void *data)
+{
+  /* for some unknown reason, we somes get called with a NULL 
+     if the object doesn't tesselate properly (with libMesa). */
+
+  if( data != NULL )
+    g_array_append_vals(area->fill, data, 1);
+  else
+    tess_error( 0 );
+}
+
+static void WIN_CALLBACK
+tess_error(GLenum err)
+{
+  GLenum bad_fill_mode = GV_TESS_NONE;
+  gint   offset = 0;
+
+  g_array_set_size(area->fill, 0);
+  g_array_set_size(area->mode_offset, 0);
+
+  area->fill_objects = 1;
+  g_array_append_val(area->mode_offset, bad_fill_mode );
+  g_array_append_val(area->mode_offset, offset );
+}
+
+static gint
+check_ring_lengths(void)
+{
+    int r;
+
+    for (r=0; r < area->num_rings; ++r)
+    {
+        int nodes = area->num_ring_nodes[r];
+
+	if ( nodes < 3) 
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+check_winding(void)
+{
+    int r, winding;
+
+    for (r=0; r < area->num_rings; ++r)
+    {
+	winding = find_winding(area->xyz_ring_nodes[r], 
+                               area->num_ring_nodes[r]);
+
+	if ((r == 0 && winding == GV_CW) || (r > 0 && winding == GV_CCW))
+	{
+	    reverse_array(area->xyz_ring_nodes[r], 
+                          area->num_ring_nodes[r]);
+	}
+    }
+}
+
+/* According to the comp.graphics.algorthms FAQ, item 2.07, the
+ * orientation (winding) of a simple polygon can be determined by
+ * looking at the sign of the following series:
+ *    sum_{i=0}^{n-1} (x_i y_{i+1} - y_i x_{i+1})
+ * A positive result means a counter clockwise winding.
+ */ 
+static gint
+find_winding(gvgeocoord *xyz_nodes, int node_count)
+{
+    gvgeocoord *a, *b;
+    gvgeocoord sum;
+    int i;
+
+    sum = 0.0;
+    a = xyz_nodes + 0;
+    b = xyz_nodes + 3;
+    for (i=1; i < node_count; ++i, a += 3, b += 3)
+    {
+	sum += a[0] * b[1] - a[1] * b[0];
+    }
+    b = xyz_nodes;
+    sum += a[0] * b[1] - a[1] * b[0];
+
+    return (sum < 0.0 ? GV_CW : GV_CCW);
+}
+
+static void
+reverse_array(gvgeocoord *xyz_nodes, int node_count)
+{
+    int i;
+    gvgeocoord   xyz_temp[3];
+
+    for (i=0; i < node_count/2; ++i)
+    { 
+        memcpy( xyz_temp, xyz_nodes+i*3, 
+                sizeof(gvgeocoord)*3 );
+        memcpy( xyz_nodes+i*3, xyz_nodes+(node_count-i-1)*3, 
+                sizeof(gvgeocoord)*3);
+        memcpy( xyz_nodes+(node_count-i-1)*3, xyz_temp, 
+                sizeof(gvgeocoord)*3);
+    }
+}
+

Added: packages/openev/branches/upstream/current/gvtexturecache.c
===================================================================
--- packages/openev/branches/upstream/current/gvtexturecache.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtexturecache.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,236 @@
+/******************************************************************************
+ * $Id: gvtexturecache.c,v 1.4 2001/12/13 03:29:17 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GvRasterLayer texture caching.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtexturecache.c,v $
+ * Revision 1.4  2001/12/13 03:29:17  warmerda
+ * avoid purging textures used in this render
+ *
+ * Revision 1.3  2000/07/18 14:52:06  warmerda
+ * added texture dump, touch reset textures
+ *
+ * Revision 1.2  2000/07/03 12:47:46  warmerda
+ * upped default to 16M again
+ *
+ * Revision 1.1  2000/06/27 21:25:53  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include "gvrasterlayer.h"
+
+static gint gv_cache_max = 16 * 1024 * 1024;
+static gint gv_cache_used = 0;
+
+static GvRasterLayerTexObj *lru_head = NULL;
+static GvRasterLayerTexObj *lru_tail = NULL;
+
+static int gv_purge_texture_lru();
+
+void gv_texture_cache_dump()
+
+{
+    GvRasterLayerTexObj *link;
+
+    printf( " -- texture lru dump -- (used=%d of %d)\n",
+            gv_cache_used, gv_cache_max );
+    for( link = lru_head; link != NULL; link = link->next )
+    {
+        if( link->next != NULL )
+        {
+            assert( link->next->prev == link );
+        }
+
+        printf( "%-20.20s tex_obj=%d tile=%d lod=%d, size=%d\n",
+                gv_data_get_name(GV_DATA(link->layer)),
+                link->tex_obj, link->texture, link->lod, link->size );
+    }
+}
+
+int gv_texture_cache_get_max()
+
+{
+    return gv_cache_max;
+}
+
+void gv_texture_cache_set_max( int new_max )
+
+{
+    gv_cache_max = new_max;
+
+    /* free space to ensure we stay below our limit */
+    while( gv_cache_used > gv_cache_max && gv_purge_texture_lru() ) {}
+}
+
+int gv_texture_cache_get_used()
+
+{
+    return gv_cache_used;
+}
+
+void gv_raster_layer_create_texture( GvRasterLayer *layer, int texture,
+                                     GLuint tex_obj, int lod, int size )
+
+{
+    GvRasterLayerTexObj  *tex;
+
+    /* If it already exists, blow it away - this shouldn't happen */
+    if( layer->textures[texture] != NULL )
+        gv_raster_layer_purge_texture( layer, texture );
+
+    tex = g_new0( GvRasterLayerTexObj, 1 );
+    tex->lod = lod;
+    tex->size = size;
+    tex->tex_obj = tex_obj;
+    tex->layer = layer;
+    tex->texture = texture;
+
+    /* account for texture memory used */
+    gv_cache_used += tex->size;
+
+    /* free space to ensure we stay below our limit */
+    while( gv_cache_used > gv_cache_max && gv_purge_texture_lru() ) {}
+
+    /* attach to layer */
+    layer->textures[texture] = tex;
+
+    /* put into the LRU list */
+    gv_raster_layer_touch_texture( layer, texture );
+}
+
+void gv_raster_layer_reset_texture( GvRasterLayer *layer, int texture, 
+                                    int lod, int size )
+
+{
+    gv_cache_used -= layer->textures[texture]->size;
+    
+    layer->textures[texture]->size = size;
+    layer->textures[texture]->lod = lod;
+
+    gv_cache_used += layer->textures[texture]->size;
+    gv_raster_layer_touch_texture( layer, texture );
+
+    /* purge other textures so that will stay under the texture memory limit */
+    while(gv_cache_used > gv_cache_max && gv_purge_texture_lru()) {}
+}
+
+void gv_raster_layer_purge_texture( GvRasterLayer *layer, int texture )
+
+{
+    GvRasterLayerTexObj  *tex = layer->textures[texture];
+
+    if( tex == NULL )
+        return;
+
+    gv_cache_used -= tex->size;
+    glDeleteTextures( 1, &(tex->tex_obj) );
+
+    if( tex->prev != NULL )
+        tex->prev->next = tex->next;
+
+    if( tex->next != NULL )
+        tex->next->prev = tex->prev;
+    
+    if( lru_head == tex )
+        lru_head = tex->next;
+
+    if( lru_tail == tex )
+        lru_tail = tex->prev;
+
+    g_free( layer->textures[texture] );
+    layer->textures[texture] = NULL;
+}
+
+void gv_raster_layer_touch_texture( GvRasterLayer *layer, int texture )
+
+{
+    GvRasterLayerTexObj  *tex = layer->textures[texture];
+
+    if( tex == NULL )
+        return;
+
+    /* Remove from current location in LRU list (if in it) */
+    if( tex->prev != NULL )
+        tex->prev->next = tex->next;
+
+    if( tex->next != NULL )
+        tex->next->prev = tex->prev;
+    
+    if( lru_head == tex )
+        lru_head = tex->next;
+
+    if( lru_tail == tex )
+        lru_tail = tex->prev;
+
+    tex->prev = tex->next = NULL;
+
+    /* Add at tail of list */
+    if( lru_tail == NULL )
+    {
+        lru_tail = lru_head = tex;
+    }
+    else
+    {
+        assert( lru_head != NULL );
+        assert( lru_tail->next == NULL );
+
+        lru_tail->next = tex;
+        tex->prev = lru_tail;
+        lru_tail = tex;
+    }
+
+    /* mark it with the current render count */
+    tex->last_render = gv_get_render_counter();
+}    
+
+/*
+ * Purge the least recently used texture (at the head of LRU list). 
+ */
+static int gv_purge_texture_lru()
+
+{
+    if( lru_head == NULL )
+        return FALSE;
+
+    if( lru_head->last_render == gv_get_render_counter() )
+    {
+        static int bReported = FALSE;
+
+        if( !bReported )
+        {
+            CPLDebug( "gvrasterlayer", "short circuit purge within render" );
+            bReported = TRUE;
+        }
+        
+        return FALSE;
+    }
+    
+    gv_raster_layer_purge_texture( lru_head->layer, 
+                                   lru_head->texture );
+        
+    return TRUE;
+}

Added: packages/openev/branches/upstream/current/gvtool.c
===================================================================
--- packages/openev/branches/upstream/current/gvtool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,359 @@
+/******************************************************************************
+ * $Id: gvtool.c,v 1.11 2005/01/17 18:37:43 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for editing mode tools.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtool.c,v $
+ * Revision 1.11  2005/01/17 18:37:43  gmwalter
+ * Add ability to reset tool cursor type.
+ *
+ * Revision 1.10  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.9  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.8  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvtool.h"
+#include <gtk/gtksignal.h>
+
+enum
+{
+    ACTIVATE,
+    DEACTIVATE,
+    DRAW,
+    BUTTON_PRESS,
+    BUTTON_RELEASE,
+    MOTION_NOTIFY,
+    KEY_PRESS,
+    ENTER_NOTIFY,
+    LEAVE_NOTIFY,
+    LAST_SIGNAL
+};
+
+static void gv_tool_class_init(GvToolClass *klass);
+static void gv_tool_init(GvTool *tool);
+static void gv_tool_real_activate(GvTool *tool, GvViewArea *area);
+static void gv_tool_real_deactivate(GvTool *tool, GvViewArea *area);
+
+static guint tool_signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gv_tool_get_type(void)
+{
+    static GtkType tool_type = 0;
+
+    if (!tool_type)
+    {
+	static const GtkTypeInfo tool_info =
+	{
+	    "GvTool",
+	    sizeof(GvTool),
+	    sizeof(GvToolClass),
+	    (GtkClassInitFunc) gv_tool_class_init,
+	    (GtkObjectInitFunc) gv_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	tool_type = gtk_type_unique(gtk_object_get_type(),
+				    &tool_info);
+    }
+    return tool_type;
+}
+
+static void
+gv_tool_class_init(GvToolClass *klass)
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    tool_signals[ACTIVATE] =
+	gtk_signal_new ("activate",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, activate),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    tool_signals[DEACTIVATE] =
+	gtk_signal_new ("deactivate",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, deactivate),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);    
+
+    tool_signals[DRAW] =
+	gtk_signal_new ("draw",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, draw),
+			gtk_marshal_NONE__NONE,
+			GTK_TYPE_NONE, 0);    
+	
+    tool_signals[BUTTON_PRESS] =
+	gtk_signal_new ("button-press",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, button_press),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    tool_signals[BUTTON_RELEASE] =
+	gtk_signal_new ("button-release",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, button_release),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    tool_signals[MOTION_NOTIFY] =
+	gtk_signal_new ("motion-notify",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, motion_notify),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);
+
+    tool_signals[KEY_PRESS] =
+	gtk_signal_new ("key-press",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, key_press),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);    
+
+    tool_signals[ENTER_NOTIFY] =
+	gtk_signal_new ("enter-notify",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, enter_notify),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);    
+
+    tool_signals[LEAVE_NOTIFY] =
+	gtk_signal_new ("leave-notify",
+			GTK_RUN_FIRST,
+			object_class->type,
+			GTK_SIGNAL_OFFSET (GvToolClass, leave_notify),
+			gtk_marshal_NONE__POINTER,
+			GTK_TYPE_NONE, 1,
+			GTK_TYPE_POINTER);    
+
+    gtk_object_class_add_signals(object_class, tool_signals, LAST_SIGNAL);
+    
+    klass->activate = gv_tool_real_activate;
+    klass->deactivate = gv_tool_real_deactivate;
+    klass->draw = NULL;
+    klass->button_press = NULL;
+    klass->button_release = NULL;
+    klass->motion_notify = NULL;
+    klass->key_press = NULL;
+    klass->enter_notify = NULL;
+    klass->leave_notify = NULL;
+}
+
+static void
+gv_tool_init(GvTool *tool)
+{
+    tool->view = NULL;
+    tool->cursor = NULL;
+    tool->bounded = 0;
+}
+
+void
+gv_tool_activate(GvTool *tool, GvViewArea *view)
+{
+    gtk_signal_emit(GTK_OBJECT(tool), tool_signals[ACTIVATE], view);
+}
+
+void
+gv_tool_deactivate(GvTool *tool, GvViewArea *view)
+{
+    gtk_signal_emit(GTK_OBJECT(tool), tool_signals[DEACTIVATE], view);
+}
+
+/**************************************************************/
+
+static void
+gv_tool_real_activate(GvTool *tool, GvViewArea *view)
+{
+    GvToolClass *klass;
+    
+    if (tool->view)
+    {
+	g_warning("gv_tool_activate(): tool %s is already active on a view",
+		  gtk_type_name(GTK_OBJECT_TYPE(tool)));
+	return;
+    }
+
+    tool->view = view;
+    gtk_object_ref(GTK_OBJECT(view));
+    klass = (GvToolClass*)gtk_type_class(GTK_OBJECT_TYPE(tool));
+
+    /* This could be done through an indirect function call which
+       emits a tool signal.  Probably better but more overhead... */
+
+    if (klass->draw)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "gldraw",
+				  GTK_SIGNAL_FUNC(klass->draw),
+				  GTK_OBJECT(tool));	
+    }
+    if (klass->button_press)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "button-press-event",
+				  GTK_SIGNAL_FUNC(klass->button_press),
+				  GTK_OBJECT(tool));
+    }
+    if (klass->button_release)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "button-release-event",
+				  GTK_SIGNAL_FUNC(klass->button_release),
+				  GTK_OBJECT(tool));
+    }
+    if (klass->motion_notify)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "motion-notify-event",
+				  GTK_SIGNAL_FUNC(klass->motion_notify),
+				  GTK_OBJECT(tool));
+    }
+    if (klass->key_press)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "key-press-event",
+				  GTK_SIGNAL_FUNC(klass->key_press),
+				  GTK_OBJECT(tool));
+    }
+    if (klass->enter_notify)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "enter-notify-event",
+				  GTK_SIGNAL_FUNC(klass->enter_notify),
+				  GTK_OBJECT(tool));
+    }
+    if (klass->leave_notify)
+    {
+	gtk_signal_connect_object(GTK_OBJECT(view), "leave-notify-event",
+				  GTK_SIGNAL_FUNC(klass->leave_notify),
+				  GTK_OBJECT(tool));
+    }
+
+    /* Install cursor for this tool */
+    if (GTK_WIDGET_REALIZED(GTK_WIDGET(view)))
+    {
+	gdk_window_set_cursor(GTK_WIDGET(view)->window, tool->cursor);
+    }
+}
+
+static void
+gv_tool_real_deactivate(GvTool *tool, GvViewArea *view)
+{
+    g_return_if_fail(tool->view == view);
+
+    gtk_signal_disconnect_by_data(GTK_OBJECT(tool->view), (gpointer)tool);
+    tool->view = NULL;
+    gtk_object_unref(GTK_OBJECT(view));
+}
+
+GvViewArea *gv_tool_get_view(GvTool *tool)
+
+{
+    return tool->view;
+}
+
+gint
+gv_tool_set_boundary(GvTool *tool, GvRect *rect)
+{
+    if( rect == NULL )
+    {
+        tool->bounded = FALSE;
+        return TRUE;
+    }
+
+    /* check rect is valid */
+    if (rect->width <= 0 || rect->height <= 0)
+    {
+        return FALSE;
+    }
+
+    tool->bounded = TRUE;
+    tool->boundary = *rect;
+
+    return TRUE;
+}
+
+void gv_tool_clamp_to_bounds( GvTool *tool, gvgeocoord *x, gvgeocoord *y )
+
+{
+    if( tool->bounded )
+    {
+        *x = MAX(MIN(*x,tool->boundary.x+tool->boundary.width),
+                tool->boundary.x);
+        *y = MAX(MIN(*y,tool->boundary.y+tool->boundary.height),
+                tool->boundary.y);
+    }
+}
+
+gint gv_tool_check_bounds( GvTool *tool, gvgeocoord x, gvgeocoord y )
+
+{
+    if( !tool->bounded )
+        return TRUE;
+
+    if( x < tool->boundary.x || x > tool->boundary.x + tool->boundary.width )
+        return FALSE;
+
+    if( y < tool->boundary.y || y > tool->boundary.y + tool->boundary.height )
+        return FALSE;
+
+    return TRUE;
+}
+
+void
+gv_tool_set_cursor(GvTool *tool, gint cursor_type)
+{
+    if (tool->cursor != NULL)
+        gdk_cursor_destroy(tool->cursor);
+
+    tool->cursor = gdk_cursor_new(cursor_type);
+
+    if ((tool->view != NULL) && (GTK_WIDGET_REALIZED(GTK_WIDGET(tool->view))))
+    {
+	gdk_window_set_cursor(GTK_WIDGET(tool->view)->window, tool->cursor);
+    }
+
+}

Added: packages/openev/branches/upstream/current/gvtool.h
===================================================================
--- packages/openev/branches/upstream/current/gvtool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,99 @@
+/******************************************************************************
+ * $Id: gvtool.h,v 1.8 2005/01/17 18:37:43 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Base class for editing mode tools.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtool.h,v $
+ * Revision 1.8  2005/01/17 18:37:43  gmwalter
+ * Add ability to reset tool cursor type.
+ *
+ * Revision 1.7  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.6  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.5  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_TOOL_H__
+#define __GV_TOOL_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkobject.h>
+#include "gvviewarea.h"
+#include "gvtypes.h"
+
+#define GV_TYPE_TOOL            (gv_tool_get_type ())
+#define GV_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_TOOL, GvTool))
+#define GV_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_TOOL, GvToolClass))
+#define GV_IS_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_TOOL))
+#define GV_IS_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_TOOL))
+
+/* Shortcuts to parent class functions */
+#define GV_TOOL_ACTIVATE(t,v)  (*((GvToolClass*)gtk_type_class(GV_TYPE_TOOL))->activate)(GV_TOOL(t),v)
+#define GV_TOOL_DEACTIVATE(t,v)  (*((GvToolClass*)gtk_type_class(GV_TYPE_TOOL))->deactivate)(GV_TOOL(t),v)
+
+typedef struct _GvTool       GvTool;
+typedef struct _GvToolClass  GvToolClass;
+
+struct _GvTool
+{
+    GtkObject object;
+
+    GvViewArea *view;
+    GdkCursor *cursor;
+
+    GvRect boundary;      /* Constraints of where ROI can be dragged out */
+    gint bounded : 1;     /* Boolean, have boundary constraints on ROI be set*/
+};
+
+struct _GvToolClass
+{
+    GtkObjectClass parent_class;
+
+    void (* activate)       (GvTool *tool, GvViewArea *view);
+    void (* deactivate)     (GvTool *tool, GvViewArea *view);
+    void (* draw)           (GvTool *tool);
+    void (* button_press)   (GvTool *tool, GdkEventButton *event);
+    void (* button_release) (GvTool *tool, GdkEventButton *event);
+    void (* motion_notify)  (GvTool *tool, GdkEventMotion *event);
+    void (* key_press)      (GvTool *tool, GdkEventKey *event);
+    void (* enter_notify)   (GvTool *tool, GdkEventCrossing *event);
+    void (* leave_notify)   (GvTool *tool, GdkEventCrossing *event);
+};
+
+GtkType gv_tool_get_type(void);
+
+void gv_tool_activate(GvTool *tool, GvViewArea *view);
+void gv_tool_deactivate(GvTool *tool, GvViewArea *view);
+GvViewArea *gv_tool_get_view(GvTool *tool);
+gint gv_tool_set_boundary(GvTool *tool, GvRect *rect);
+void gv_tool_clamp_to_bounds( GvTool *tool, gvgeocoord *x, gvgeocoord *y );
+gint gv_tool_check_bounds( GvTool *tool, gvgeocoord x, gvgeocoord y );
+void gv_tool_set_cursor(GvTool *tool, gint cursor_type);
+
+#endif /* __GV_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvtoolbox.c
===================================================================
--- packages/openev/branches/upstream/current/gvtoolbox.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtoolbox.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,280 @@
+/******************************************************************************
+ * $Id: gvtoolbox.c,v 1.8 2000/08/03 18:39:49 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Container for available editing tools, manages which is active.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtoolbox.c,v $
+ * Revision 1.8  2000/08/03 18:39:49  warmerda
+ * activate a tool on key-press, or button-press but not focus-enter
+ *
+ * Revision 1.7  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.6  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvtoolbox.h"
+#include <gtk/gtksignal.h>
+
+static void gv_toolbox_class_init(GvToolboxClass *klass);
+static void gv_toolbox_init(GvToolbox *toolbox);
+static void gv_toolbox_activate(GvTool *toolbox, GvViewArea *view);
+static void gv_toolbox_deactivate(GvTool *toolbox, GvViewArea *view);
+static void gv_toolbox_view_event(GvViewArea *view, GdkEvent *event, GvToolbox *toolbox);
+static void gv_toolbox_switch_to_view(GvToolbox *toolbox, GvViewArea *view);
+static void gv_toolbox_destroy(GtkObject *object);
+static void gv_toolbox_finalize(GtkObject *object);
+
+GtkType
+gv_toolbox_get_type(void)
+{
+    static GtkType toolbox_type = 0;
+
+    if (!toolbox_type)
+    {
+	static const GtkTypeInfo toolbox_info =
+	{
+	    "GvToolbox",
+	    sizeof(GvToolbox),
+	    sizeof(GvToolboxClass),
+	    (GtkClassInitFunc) gv_toolbox_class_init,
+	    (GtkObjectInitFunc) gv_toolbox_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	toolbox_type = gtk_type_unique(gv_tool_get_type(),
+				       &toolbox_info);
+    }
+    return toolbox_type;
+}
+
+static void
+gv_toolbox_class_init(GvToolboxClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvToolClass *tool_class;
+
+    object_class = (GtkObjectClass*)klass;    
+    tool_class = (GvToolClass*)klass;
+
+    object_class->destroy = gv_toolbox_destroy;
+    object_class->finalize = gv_toolbox_finalize;
+    
+    tool_class->activate = gv_toolbox_activate;
+    tool_class->deactivate = gv_toolbox_deactivate;
+}
+
+static void
+gv_toolbox_init(GvToolbox *toolbox)
+{
+    toolbox->tools = g_hash_table_new(g_str_hash, g_str_equal);
+    toolbox->active_tool = NULL;
+    toolbox->views = NULL;
+}
+
+GvTool *
+gv_toolbox_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_TOOLBOX));
+}
+
+void
+gv_toolbox_add_tool(GvToolbox *toolbox, gchar *name, GvTool *tool)
+{
+    if (g_hash_table_lookup(toolbox->tools, name))
+    {
+	g_warning("gv_toolbox_add_tool(): tool %s already exists", name);
+	return;
+    }
+
+    gtk_object_ref(GTK_OBJECT(tool));
+    gtk_object_sink(GTK_OBJECT(tool));
+    g_hash_table_insert(toolbox->tools, g_strdup(name), (gpointer)tool);
+
+    if( GV_TOOL(toolbox)->bounded ) 
+        gv_tool_set_boundary( tool, &(GV_TOOL(toolbox)->boundary) );
+}
+
+void
+gv_toolbox_activate_tool(GvToolbox *toolbox, gchar *tool_name)
+{
+    GvTool *tool;
+
+    if (tool_name)
+    {
+	tool = (GvTool*)g_hash_table_lookup(toolbox->tools, tool_name);
+	if (!tool)
+	{
+	    g_warning("gv_toolbox_activate_tool(): no tool %s in toolbox",
+		      tool_name);
+	    return;
+	}
+    }
+    else
+    {
+	tool = NULL;
+    }
+
+    /* Check if tool is already active */
+    if (tool == toolbox->active_tool) return;
+
+    /* Deactivate currently active tool */
+    if (toolbox->active_tool && GV_TOOL(toolbox)->view)
+    {
+	gv_tool_deactivate(toolbox->active_tool, GV_TOOL(toolbox)->view);
+    }
+
+    /* Activate new tool */
+    toolbox->active_tool = tool;
+    if (toolbox->active_tool && GV_TOOL(toolbox)->view)
+    {
+	gv_tool_activate(toolbox->active_tool, GV_TOOL(toolbox)->view);
+    }
+}
+
+/*******************************************************/
+
+static void
+gv_toolbox_activate(GvTool *tool, GvViewArea *view)
+{
+    GvToolbox *toolbox = GV_TOOLBOX( tool );
+
+    if (!g_list_find(toolbox->views, view))
+    {
+        gtk_object_ref(GTK_OBJECT(view));
+        toolbox->views = g_list_append(toolbox->views, (gpointer)view);
+
+        gtk_signal_connect(GTK_OBJECT(view), "button-press-event",
+                           GTK_SIGNAL_FUNC(gv_toolbox_view_event),
+                           (gpointer)toolbox);
+        gtk_signal_connect(GTK_OBJECT(view), "key-press-event",
+                           GTK_SIGNAL_FUNC(gv_toolbox_view_event),
+                           (gpointer)toolbox);
+    }
+
+    gv_toolbox_switch_to_view( toolbox, view );
+}
+
+static void
+gv_toolbox_deactivate(GvTool *tool, GvViewArea *view)
+{
+    GvToolbox *toolbox = GV_TOOLBOX( tool );
+
+    if (!g_list_find(toolbox->views, view))
+    {
+	g_warning("gv_toolbox_activate(): view not active");
+	return;
+    }
+
+    if (view == GV_TOOL(toolbox)->view)
+    {
+	if (toolbox->active_tool)
+	{
+	    gv_tool_deactivate(toolbox->active_tool, view);
+	}
+	GV_TOOL(toolbox)->view = NULL;
+    }
+
+    gtk_signal_disconnect_by_data(GTK_OBJECT(view), (gpointer)toolbox);
+    toolbox->views = g_list_remove(toolbox->views, view);
+    gtk_object_unref(GTK_OBJECT(view));    
+}
+
+static void
+gv_toolbox_view_event(GvViewArea *view, GdkEvent *event, GvToolbox *toolbox)
+{
+    /* Check whether this view is the active view */
+    if (view != GV_TOOL(toolbox)->view)
+    {
+        gv_tool_activate( GV_TOOL(toolbox), view );
+    }
+}
+
+static void
+gv_toolbox_switch_to_view(GvToolbox *toolbox, GvViewArea *view)
+{
+    /* Deactivate the active tool */
+    if (toolbox->active_tool && GV_TOOL(toolbox)->view)
+    {
+	gv_tool_deactivate(toolbox->active_tool, GV_TOOL(toolbox)->view);
+    }
+
+    /* Switch views */
+    GV_TOOL(toolbox)->view = view;
+
+    /* Reactivate the active tool */
+    if (toolbox->active_tool && view)
+    {
+	gv_tool_activate(toolbox->active_tool, view);
+    }
+}
+
+static gboolean
+unref_object_foreach(gpointer key, gpointer value, gpointer data)
+{
+    gtk_object_unref((GtkObject*)value);
+    return TRUE;
+}
+
+static void
+gv_toolbox_destroy(GtkObject *object)
+{
+    GvToolClass *parent_class;
+    GvToolbox *toolbox;    
+
+    toolbox = GV_TOOLBOX(object);
+
+    /* Remove all views */
+    while (toolbox->views)
+    {
+	GvViewArea *view = (GvViewArea*)toolbox->views->data;
+	gv_tool_deactivate(GV_TOOL(toolbox), view);
+    }
+
+    /* Remove all tools */
+    g_hash_table_foreach_remove(toolbox->tools, unref_object_foreach, NULL);
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_tool_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(object);         
+}
+
+static void
+gv_toolbox_finalize(GtkObject *object)
+{
+    GvToolClass *parent_class;
+    GvToolbox *toolbox;    
+
+    toolbox = GV_TOOLBOX(object);
+
+    g_hash_table_destroy(toolbox->tools);
+    
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_tool_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);         
+}

Added: packages/openev/branches/upstream/current/gvtoolbox.h
===================================================================
--- packages/openev/branches/upstream/current/gvtoolbox.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtoolbox.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * $Id: gvtoolbox.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Container for available editing tools, manages which is active.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtoolbox.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_TOOLBOX_H__
+#define __GV_TOOLBOX_H__
+
+#include "gvtool.h"
+
+#define GV_TYPE_TOOLBOX            (gv_toolbox_get_type ())
+#define GV_TOOLBOX(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_TOOLBOX, GvToolbox))
+#define GV_TOOLBOX_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_TOOLBOX, GvToolboxClass))
+#define GV_IS_TOOLBOX(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_TOOLBOX))
+#define GV_IS_TOOLBOX_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_TOOLBOX))
+
+typedef struct _GvToolbox       GvToolbox;
+typedef struct _GvToolboxClass  GvToolboxClass;
+
+struct _GvToolbox
+{
+    GvTool tool;
+
+    GHashTable *tools;
+    GvTool *active_tool;
+    GList *views;
+};
+
+struct _GvToolboxClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_toolbox_get_type(void);
+GvTool* gv_toolbox_new(void);
+
+void gv_toolbox_add_tool(GvToolbox *toolbox, gchar *name, GvTool *tool);
+void gv_toolbox_activate_tool(GvToolbox *toolbox, gchar *tool_name);
+
+#endif /* __GV_TOOLBOX_H__ */

Added: packages/openev/branches/upstream/current/gvtracktool.c
===================================================================
--- packages/openev/branches/upstream/current/gvtracktool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtracktool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,147 @@
+/******************************************************************************
+ * $Id: gvtracktool.c,v 1.8 2002/11/04 21:42:07 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Tracking display of raster values and position of cursor.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtracktool.c,v $
+ * Revision 1.8  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.7  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvtracktool.h"
+#include "gvrasterlayer.h"
+#include "gvutils.h"
+#include "gvmanager.h"
+
+#include <gtk/gtklabel.h>
+
+static void gv_track_tool_class_init(GvTrackToolClass *klass);
+static void gv_track_tool_init(GvTrackTool *tool);
+static void gv_track_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_track_tool_leave_notify(GvTool *tool, GdkEventCrossing *event);
+static void gv_track_tool_destroy(GtkObject *object);
+
+GtkType
+gv_track_tool_get_type(void)
+{
+    static GtkType track_tool_type = 0;
+
+    if (!track_tool_type)
+    {
+	static const GtkTypeInfo track_tool_info =
+	{
+	    "GvTrackTool",
+	    sizeof(GvTrackTool),
+	    sizeof(GvTrackToolClass),
+	    (GtkClassInitFunc) gv_track_tool_class_init,
+	    (GtkObjectInitFunc) gv_track_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	track_tool_type = gtk_type_unique(gv_tool_get_type(),
+					  &track_tool_info);
+    }
+    return track_tool_type;
+}
+
+static void
+gv_track_tool_class_init(GvTrackToolClass *klass)
+{
+    GtkObjectClass *object_class;
+    GvToolClass *tool_class;
+
+    object_class = (GtkObjectClass*)klass;
+    tool_class = (GvToolClass*)klass;
+
+    object_class->destroy = gv_track_tool_destroy;
+    
+    tool_class->motion_notify = gv_track_tool_motion_notify;
+    tool_class->leave_notify = gv_track_tool_leave_notify;
+}
+
+static void
+gv_track_tool_init(GvTrackTool *tool)
+{
+    tool->label = NULL;
+}
+
+GvTool *
+gv_track_tool_new(GtkObject *label)
+{
+    GvTrackTool *tool;
+
+    g_return_val_if_fail(GTK_IS_LABEL(label), NULL);
+
+    tool = GV_TRACK_TOOL(gtk_type_new(GV_TYPE_TRACK_TOOL));
+    tool->label = label;
+    gtk_object_ref(label);
+
+    return GV_TOOL(tool);
+}
+
+/********************************************************/
+
+static void
+gv_track_tool_motion_notify(GvTool *tool_in, GdkEventMotion *event)
+{
+    GvTrackTool *tool = GV_TRACK_TOOL(tool_in);
+    gvgeocoord geo_x, geo_y;
+    const char  *text;
+    GvProperties *properties = gv_manager_get_preferences( gv_get_manager() );
+
+    gv_view_area_map_pointer(GV_TOOL(tool)->view, event->x, event->y,
+			     &geo_x, &geo_y);
+
+    text = gv_format_point_query( GV_TOOL(tool)->view, 
+                                  properties, geo_x, geo_y );
+    gtk_label_set_text(GTK_LABEL(tool->label), text);
+}
+
+static void
+gv_track_tool_leave_notify(GvTool *tool, GdkEventCrossing *event)
+{
+    gtk_label_set_text(GTK_LABEL(GV_TRACK_TOOL(tool)->label), "");
+}
+
+static void
+gv_track_tool_destroy(GtkObject *object)
+{
+    GvToolClass *parent_class;
+    GvTrackTool *tool;
+
+    tool = GV_TRACK_TOOL(object);
+
+    gtk_object_unref(tool->label);
+    tool->label = NULL;
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gv_tool_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(object);         
+}

Added: packages/openev/branches/upstream/current/gvtracktool.h
===================================================================
--- packages/openev/branches/upstream/current/gvtracktool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtracktool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * $Id: gvtracktool.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Tracking display of raster values and position of cursor.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtracktool.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_TRACK_TOOL_H__
+#define __GV_TRACK_TOOL_H__
+
+#include "gvtool.h"
+
+#define GV_TYPE_TRACK_TOOL            (gv_track_tool_get_type ())
+#define GV_TRACK_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_TRACK_TOOL, GvTrackTool))
+#define GV_TRACK_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_TRACK_TOOL, GvTrackToolClass))
+#define GV_IS_TRACK_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_TRACK_TOOL))
+#define GV_IS_TRACK_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_TRACK_TOOL))
+
+typedef struct _GvTrackTool       GvTrackTool;
+typedef struct _GvTrackToolClass  GvTrackToolClass;
+
+struct _GvTrackTool
+{
+    GvTool tool;
+
+    GtkObject *label;
+};
+
+struct _GvTrackToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_track_tool_get_type(void);
+GvTool* gv_track_tool_new(GtkObject *label);
+
+#endif /* __GV_TRACK_TOOL_H__ */

Added: packages/openev/branches/upstream/current/gvtypes.h
===================================================================
--- packages/openev/branches/upstream/current/gvtypes.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvtypes.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,155 @@
+/******************************************************************************
+ * $Id: gvtypes.h,v 1.15 2004/03/05 23:40:12 sduclos Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Assortment of OpenEV types.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvtypes.h,v $
+ * Revision 1.15  2004/03/05 23:40:12  sduclos
+ * remove commentd define GV_USE_DOUBLE.. , remove TAB
+ *
+ * Revision 1.14  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.13  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_TYPES_H__
+#define __GV_TYPES_H__
+
+#include <glib.h>
+#include "gv_config.h"
+
+#define GV_NONE    0x000
+#define GV_ALWAYS  0x001
+#define GV_LATER   0x002
+#define GV_NOW     0x003
+#define GV_FIRST   0x004
+#define GV_ALL     0x005
+
+/* GvShapeLayer.flags */
+#define GV_DELAY_SELECTED  0x0001
+#define GV_SUPPRESS_SELCHANGED 0x0002
+
+#define GV_TESS_NONE 0
+#define GV_TESS_LOCK -1
+
+#define GV_CHANGE_ADD      0x001
+#define GV_CHANGE_REPLACE  0x002
+#define GV_CHANGE_DELETE   0x003
+
+#define GV_MINFLOAT G_MINFLOAT
+#define GV_MAXFLOAT G_MAXFLOAT
+
+
+#ifdef GV_USE_DOUBLE_PRECISION_COORD
+#
+#   define glVertex2   glVertex2d
+#   define glVertex2v  glVertex2dv
+#   define glVertex3   glVertex3d
+#   define glScale     glScaled
+#   define glRotate    glRotated
+#   define glTranslate glTranslated
+#
+#   define GL_GEOCOORD GL_DOUBLE
+#   define GLgeocoord  GLdouble
+#
+#else
+#
+#   define glVertex2   glVertex2f
+#   define glVertex2v  glVertex2fv
+#   define glVertex3   glVertex3f
+#   define glScale     glScalef
+#   define glRotate    glRotatef
+#   define glTranslate glTranslatef
+#
+#   define GL_GEOCOORD GL_FLOAT
+#   define GLgeocoord  GLfloat
+#
+#endif
+
+// SD Note: sizeof(GLgeocoord) == sizeof(gvgeocoord)
+#ifdef GV_USE_DOUBLE_PRECISION_COORD
+typedef double gvgeocoord;
+#else
+typedef float  gvgeocoord;
+#endif
+
+typedef float gvfloat;
+
+typedef float GvColor[4];  /* RGBA, range [0-1] */
+
+typedef struct _GvRect GvRect;
+typedef struct _GvVertex GvVertex;
+typedef struct _GvVertex3d GvVertex3d;
+typedef struct _GvNodeInfo GvNodeInfo;
+typedef struct _GvShapeChangeInfo GvShapeChangeInfo;
+
+struct _GvRect
+{
+    gvgeocoord x, y, width, height;
+};
+
+struct _GvVertex
+{
+    gvgeocoord x, y;
+};
+
+struct _GvVertex3d
+{
+    gvgeocoord x, y, z;
+};
+
+struct _GvNodeInfo
+{
+    gint shape_id;
+    gint ring_id;
+    gint node_id;
+    GvVertex *vertex;
+};
+
+struct _GvShapeChangeInfo
+{
+    gint change_type;
+    gint num_shapes;
+    gint *shape_id;
+};
+
+typedef struct _GvRasterChangeInfo
+{
+    gint  change_type;
+    gint  x_off;
+    gint  y_off;
+    gint  width;
+    gint  height;
+} GvRasterChangeInfo;
+
+/* FIXME: Where is a good place for this? */
+#define gv_color_copy(d,s)  {d[0]=s[0];d[1]=s[1];d[2]=s[2];d[3]=s[3];}
+
+#endif /* __GV_TYPES_H__ */
+
+
+

Added: packages/openev/branches/upstream/current/gvundo.c
===================================================================
--- packages/openev/branches/upstream/current/gvundo.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvundo.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,248 @@
+/******************************************************************************
+ * $Id: gvundo.c,v 1.6 2002/09/30 20:50:32 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Generic undo management.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvundo.c,v $
+ * Revision 1.6  2002/09/30 20:50:32  warmerda
+ * fixed serious bug in gv_undo_end_group
+ *
+ * Revision 1.5  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvundo.h"
+#include "gextra.h"
+#include <gtk/gtksignal.h>
+
+#define DEFAULT_STACK_MAX   64
+
+typedef struct _GvUndo GvUndo;
+
+struct _GvUndo
+{
+    GList *undo_stack;
+    gint undo_stack_count;
+    gint stack_max;
+    gint undo_enabled : 1;
+    gint undo_open : 1;
+    gint group_locked : 1;
+    gint next_group;
+};
+
+static void gv_undo_data_changing(GvData *data, gpointer change_info);
+static void gv_undo_data_destroy(GvData *data);
+
+static GvUndo *undo = NULL;
+
+static void
+gv_undo_init(void)
+{
+    undo = g_new(GvUndo, 1);
+    undo->undo_stack = NULL;
+    undo->undo_stack_count = 0;
+    undo->stack_max = DEFAULT_STACK_MAX;
+    undo->undo_enabled = TRUE;
+    undo->undo_open = TRUE;
+    undo->next_group = 1;
+    undo->group_locked = FALSE;
+}
+
+void
+gv_undo_register_data(GvData *data)
+{
+    if (!undo) gv_undo_init();
+
+    gtk_signal_connect(GTK_OBJECT(data), "changing",
+		       GTK_SIGNAL_FUNC(gv_undo_data_changing), NULL);
+
+    gtk_signal_connect(GTK_OBJECT(data), "destroy",
+		       GTK_SIGNAL_FUNC(gv_undo_data_destroy), NULL);
+}
+
+void
+gv_undo_open(void)
+{
+    if (!undo) gv_undo_init();
+    undo->undo_open = TRUE;
+}
+
+void
+gv_undo_close(void)
+{
+    if (!undo) gv_undo_init();
+    undo->undo_open = FALSE;
+}
+
+void
+gv_undo_push(GvDataMemento *memento)
+{
+    if (!undo) gv_undo_init();
+
+    /* set group id */
+    memento->group = undo->next_group;
+    if( !undo->group_locked )
+        undo->next_group++;
+
+    /* push on undo stack */
+    undo->undo_stack = g_list_push(undo->undo_stack, memento);
+
+    /* discard oldest undo step if we are at the limit */
+    if (++undo->undo_stack_count > undo->stack_max)
+    {
+	GList *remove;
+	GvDataMemento *memento;
+
+	remove = g_list_last(undo->undo_stack);
+	memento = (GvDataMemento*)remove->data;
+	undo->undo_stack = g_list_remove_link(undo->undo_stack, remove);
+	undo->undo_stack_count--;
+
+	gv_data_del_memento(memento->data, memento);
+    }
+}
+
+void
+gv_undo_pop(void)
+{
+    GvDataMemento *memento;
+    gint open;
+    gint pop_group;
+
+    if (!undo) gv_undo_init();
+    if (!undo->undo_enabled || undo->undo_stack_count == 0) return;
+
+    if( undo->group_locked )
+        gv_undo_end_group( undo->next_group );
+
+    /* pop all the undo's of a group */
+    pop_group = ((GvDataMemento *) undo->undo_stack->data)->group;
+    while( undo->undo_stack_count > 0 
+           && ((GvDataMemento *) undo->undo_stack->data)->group == pop_group )
+    {
+        memento = (GvDataMemento*)undo->undo_stack->data;
+        undo->undo_stack = g_list_pop(undo->undo_stack);
+        undo->undo_stack_count--;
+
+        /* Close the undo object temporarily while undoing */
+        open = undo->undo_open;
+        undo->undo_open = FALSE;
+        gv_data_set_memento(memento->data, memento);
+        undo->undo_open = open;
+    }
+}
+
+void
+gv_undo_clear(void)
+{
+    GvDataMemento *memento;
+
+    if (!undo) gv_undo_init();    
+    while (undo->undo_stack_count)
+    {
+	memento = (GvDataMemento*)undo->undo_stack->data;
+	undo->undo_stack = g_list_pop(undo->undo_stack);
+	undo->undo_stack_count--;
+	
+	gv_data_del_memento(memento->data, memento);
+    }
+
+    undo->group_locked = FALSE;
+}
+
+void
+gv_undo_enable()
+{
+    if (!undo) gv_undo_init();
+    undo->undo_enabled = TRUE;
+}
+
+void
+gv_undo_disable()
+{
+    if (!undo) gv_undo_init();
+    undo->undo_enabled = FALSE;
+}
+
+gint
+gv_undo_can_undo(void)
+{
+    if (!undo) gv_undo_init();
+    return (undo->undo_enabled && undo->undo_stack_count > 0);
+}
+
+gint gv_undo_start_group(void)
+
+{
+    if (!undo) gv_undo_init();
+
+    if( undo->group_locked )
+    {
+        /* failure */
+        return 0;
+    }
+
+    undo->group_locked = TRUE;
+    return undo->next_group;
+}
+
+void 
+gv_undo_end_group(gint group)
+
+{
+    if (!undo) gv_undo_init();
+
+    if( !undo->group_locked || undo->next_group != group)
+    {
+        /* failure */
+        return;
+    }
+
+    undo->group_locked = FALSE;
+    undo->next_group++;
+}
+
+
+static void
+gv_undo_data_changing(GvData *data, gpointer change_info)
+{
+    GvDataMemento *memento;
+
+    if (!undo->undo_open) return;
+
+    memento = gv_data_get_memento(data, change_info);
+    if (memento)
+    {
+	gv_undo_push(memento);
+    }
+}
+
+static void
+gv_undo_data_destroy(GvData *data)
+{
+    /* FIXME: remove mementos corresponding to data from stack */
+    /* For now just clear the stack */
+    gv_undo_clear();
+}

Added: packages/openev/branches/upstream/current/gvundo.h
===================================================================
--- packages/openev/branches/upstream/current/gvundo.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvundo.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * $Id: gvundo.h,v 1.3 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Generic undo management.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvundo.h,v $
+ * Revision 1.3  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_UNDO_H__
+#define __GV_UNDO_H__
+
+#include "gvdata.h"
+
+void gv_undo_register_data(GvData *data);
+void gv_undo_open(void);
+void gv_undo_close(void);
+void gv_undo_push(GvDataMemento *memento);
+void gv_undo_pop(void);
+void gv_undo_clear(void);
+void gv_undo_enable();
+void gv_undo_disable();
+gint gv_undo_can_undo(void);
+gint gv_undo_start_group(void);
+void gv_undo_end_group(gint);
+
+#endif /*__GV_UNDO_H__*/

Added: packages/openev/branches/upstream/current/gvutils.c
===================================================================
--- packages/openev/branches/upstream/current/gvutils.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvutils.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,590 @@
+/******************************************************************************
+ * $Id: gvutils.c,v 1.22 2004/06/23 14:35:05 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Various utility functions.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvutils.c,v $
+ * Revision 1.22  2004/06/23 14:35:05  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.21  2004/04/21 15:13:58  andrey_kiselev
+ * Fix comparisons with NODATA value.
+ *
+ * Revision 1.20  2004/04/02 17:01:02  gmwalter
+ * Updated nodata support for complex and
+ * rgb data.
+ *
+ * Revision 1.19  2004/01/22 20:03:22  andrey_kiselev
+ * gv_format_point_query() returns "[NODATA]" label if value marked as NODATA.
+ *
+ * Revision 1.18  2001/08/08 17:43:22  warmerda
+ * avoid warning
+ *
+ * Revision 1.17  2001/04/22 17:32:25  pgs
+ * added get_short_path_name
+ *
+ * Revision 1.16  2000/10/10 17:38:41  srawlin
+ * changed lat/long decimal format to report position to 7 decimal digits
+ *
+ * Revision 1.15  2000/09/26 16:55:35  srawlin
+ * changed lat/long decimal format to report N/S and E/W instead of +/-
+ *
+ * Revision 1.14  2000/09/26 16:32:41  srawlin
+ * added degree_mode to set Lat/Long display to be either in the form dd:mm:ss (dms) or dd.dd (decimal)
+ *
+ * Revision 1.13  2000/08/25 20:08:44  warmerda
+ * added phase/magnitude reporting
+ *
+ * Revision 1.12  2000/08/24 19:49:27  warmerda
+ * don't operate o null strings
+ *
+ * Revision 1.11  2000/08/24 03:37:52  warmerda
+ * added PIXEL as a coordinate system
+ *
+ * Revision 1.10  2000/08/24 03:26:01  warmerda
+ * fixed +- sign for complex raster values
+ *
+ * Revision 1.9  2000/06/26 15:12:17  warmerda
+ * don't crash if gvraster is null
+ *
+ * Revision 1.8  2000/06/23 12:56:52  warmerda
+ * added multiple GvRasterSource support
+ *
+ * Revision 1.7  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <stdio.h>
+#include "gvutils.h"
+#include "gvrasterlayer.h"
+#include "ogr_srs_api.h"
+#include "cpl_conv.h"
+
+void gv_set_color_from_string( GvColor color, const char * string,
+                               float def_red, float def_green,
+                               float def_blue, float def_alpha )
+
+{
+    if( string != NULL )
+    {
+        def_alpha = 1.0;
+        sscanf( string, "%f %f %f %f",
+                &def_red, &def_green, &def_blue, &def_alpha );
+    }
+
+    if( def_red > 1.0 || def_green > 1.0 || def_blue > 1.0 || def_alpha > 1.0 )
+    {
+        def_red /= 255.0;
+        def_green /= 255.0;
+        def_blue /= 255.0;
+        def_alpha /= 255.0;
+    }
+    color[0] = def_red;
+    color[1] = def_green;
+    color[2] = def_blue;
+    color[3] = def_alpha;
+}
+
+void gv_complex_to_phase_mag( float real, float imaginary,
+                              float *phase, float *magnitude )
+
+{
+    *magnitude = sqrt(real*real+imaginary*imaginary);
+    *phase = atan2(imaginary, real);
+}
+
+const char *gv_format_point_query( GvViewArea *view,
+                                   GvProperties *properties,
+                                   double geo_x, double geo_y )
+
+{
+    static gchar buf[256];
+    double raster_x=0.0, raster_y=0.0, pix_real, pix_imaginary;
+    GvLayer *layer;
+    GvRaster *raster = NULL;
+    GvRasterLayer *raster_layer = NULL;
+    GList *layer_list, *node;
+    const char *coord_mode, *pixel_mode, *degree_mode;
+    const char *coord_sys = gv_view_area_get_projection(view);
+    char    *latlong_srs = NULL;
+    char east_west, north_south;
+
+    /* Check the properties to see what options are in effect */
+    coord_mode = gv_properties_get( properties, "_coordinate_mode" );
+    if( coord_mode == NULL )
+        coord_mode = "georef";
+
+    pixel_mode = gv_properties_get( properties, "_pixel_mode" );
+    if( pixel_mode == NULL )
+        pixel_mode = "on";
+
+    degree_mode = gv_properties_get( properties, "_degree_mode" );
+    if( degree_mode == NULL )
+        degree_mode = "dms";
+
+    /* This code should be factored out into gvviewarea at some point. */
+    layer_list = gv_view_area_list_layers( view );
+
+    for( node = layer_list; node != NULL; node = node->next )
+    {
+        layer = GV_LAYER(node->data);
+        if( gv_layer_is_visible(layer) && GV_IS_RASTER_LAYER( layer ) )
+        {
+            raster = GV_RASTER_LAYER(layer)->prototype_data;
+            raster_layer = GV_RASTER_LAYER(layer);
+
+            raster_x = geo_x;
+            raster_y = geo_y;
+
+            if( !gv_raster_layer_view_to_pixel( raster_layer,
+                                                &raster_x, &raster_y, NULL )
+                ||raster_x < 0 || raster_x >= raster->width
+                || raster_y < 0 || raster_y >= raster->height )
+            {
+                raster = NULL;
+                raster_layer = NULL;
+            }
+        }
+    }
+
+    /* Do we want to transform coordinate into a real `geo' space? */
+    if( raster != NULL
+        && (gv_view_area_get_projection(view) == NULL
+            || EQUAL(gv_view_area_get_projection(view),"PIXEL"))
+        && raster_layer->mesh_is_raw )
+    {
+        double geo_x_dbl, geo_y_dbl;
+
+        geo_x_dbl = raster_x;
+        geo_y_dbl = raster_y;
+        gv_raster_pixel_to_georef( raster,
+                                   &geo_x_dbl, &geo_y_dbl, NULL );
+        coord_sys = gv_data_get_projection( GV_DATA(raster) );
+        geo_x = geo_x_dbl;
+        geo_y = geo_y_dbl;
+    }
+
+    if( g_strcasecmp(coord_mode,"latlong") == 0
+        && (coord_sys != NULL && !EQUAL(coord_sys,"PIXEL")) )
+    {
+        latlong_srs = gv_make_latlong_srs( coord_sys );
+
+        if( latlong_srs != NULL )
+        {
+            double      x, y, z;
+
+            x = geo_x;
+            y = geo_y;
+            z = 0.0;
+
+            if( gv_reproject_points( coord_sys, latlong_srs,
+                                     1, &x, &y, &z ) )
+            {
+                geo_x = x;
+                geo_y = y;
+                coord_sys = latlong_srs;
+            }
+        }
+    }
+
+    buf[0] = '\0';
+    if( g_strcasecmp(coord_mode,"off") == 0 )
+    {
+        /* do nothing */
+    }
+    else if( g_strcasecmp(coord_mode,"raster") == 0 )
+    {
+        if( raster != NULL )
+            g_snprintf(buf, 64, "(%.2fP, %.2fL)", raster_x, raster_y );
+    }
+    else
+    {
+        if( coord_sys != NULL
+            && strstr(coord_sys,"PROJCS") == NULL
+            && strstr(coord_sys,"GEOGCS") != NULL )
+        {
+            /* Display in Degree Minute Second format */
+            if ( g_strcasecmp(degree_mode, "dms") == 0 )
+            {
+                strcat( buf, "(");
+                strcat( buf, GDALDecToDMS( geo_x, "Long", 2 ));
+                strcat( buf, "," );
+                strcat( buf, GDALDecToDMS( geo_y, "Lat", 2 ));
+                strcat( buf, ")" );
+            } else {
+                /* Display in decimal format */
+                if (geo_x < 0)
+                    east_west = 'W';
+                else
+                    east_west = 'E';
+
+                if (geo_y < 0)
+                    north_south = 'S';
+                else
+                    north_south = 'N';
+
+                g_snprintf(buf, 64, "(%.7f%c, %.7f%c)",
+                           fabs(geo_x), east_west, fabs(geo_y), north_south );
+            }
+        }
+        else if( coord_sys != NULL
+                 && g_strcasecmp(coord_sys,"PIXEL") == 0 )
+        {
+            g_snprintf(buf, 64, "(%.2fP, %.2fL)", geo_x, geo_y );
+        }
+        else
+        {
+            g_snprintf(buf, 64, "(%.2fE, %.2fN)", geo_x, geo_y );
+        }
+    }
+
+    if( latlong_srs != NULL )
+        g_free( latlong_srs );
+
+    /* Try to get a raster value */
+    if( raster_layer != NULL
+        && g_strcasecmp(pixel_mode, "on") == 0 )
+    {
+        const char  *nodata_mode;
+        GvRaster    *rsrc;
+
+        nodata_mode = gv_properties_get( properties, "_nodata_mode" );
+        if( nodata_mode == NULL )
+            nodata_mode = "on";
+
+        if( buf[0] != '\0' )
+            strcat( buf, ": " );
+
+        if( raster_layer->mode == GV_RLM_COMPLEX )
+        {
+            rsrc = gv_raster_layer_get_data(raster_layer,0);
+            if( rsrc != NULL
+                && gv_raster_get_sample( rsrc, raster_x, raster_y,
+                                         &pix_real, &pix_imaginary ) )
+            {
+                float   phase, magnitude;
+
+                if( pix_imaginary < 0.0 )
+                    g_snprintf(buf+strlen(buf), 64, "%g%gi",
+                               pix_real, pix_imaginary );
+                else
+                    g_snprintf(buf+strlen(buf), 64, "%g+%gi",
+                               pix_real, pix_imaginary );
+
+                gv_complex_to_phase_mag( pix_real, pix_imaginary,
+                                         &phase, &magnitude );
+                g_snprintf(buf+strlen(buf), 64, ", phase:%g, magnitude:%g",
+                           phase, magnitude );
+
+                if ( g_strcasecmp( nodata_mode, "on") == 0 )
+                {
+                    double  nodata_real, nodata_imaginary;
+
+                    if( gv_raster_layer_nodata_get( raster_layer, 0,
+                                                    &nodata_real,
+                                                    &nodata_imaginary )
+                        && ABS(pix_real - nodata_real) < 0.0000000001
+                        && ABS(pix_imaginary - nodata_imaginary) < 0.0000000001 )
+                    {
+                        g_snprintf(buf+strlen(buf), 64, " [NODATA]" );
+                    }
+                }
+            }
+        }
+        else if( raster_layer->mode == GV_RLM_RGBA )
+        {
+            int     isource;
+            double  rgba[4];
+            double  rgba_imag[4];
+
+            for( isource = 0; isource < 4; isource++ )
+            {
+
+                rsrc = gv_raster_layer_get_data(raster_layer,isource);
+                if( rsrc == NULL
+                    || !gv_raster_get_sample( rsrc, raster_x, raster_y,
+                                              rgba + isource, 
+                                              rgba_imag + isource) )
+                {
+                    rgba[isource] =
+                        gv_raster_layer_get_const_value(raster_layer,isource);
+                }
+            }
+
+            if( gv_raster_layer_get_data(raster_layer,3) == NULL )
+            {
+                if ((rgba_imag[0] != 0.0) || (rgba_imag[1] != 0.0) ||
+                    (rgba_imag[2] != 0.0))
+                {
+                    if (rgba_imag[0] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.5g%.5gi r ",
+                            rgba[0], rgba_imag[0] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.5g+%.5gi r ",
+                            rgba[0], rgba_imag[0] );
+
+                    if (rgba_imag[1] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.5g%.5gi g ",
+                            rgba[1], rgba_imag[1] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.5g+%.5gi g ",
+                            rgba[1], rgba_imag[1] );
+
+                    if (rgba_imag[2] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.5g%.5gi b ",
+                            rgba[2], rgba_imag[2] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.5g+%.5gi b ",
+                            rgba[2], rgba_imag[2] );
+                }
+                else
+                    g_snprintf(buf+strlen(buf), 64, "%gr %gg %gb ",
+                           rgba[0], rgba[1], rgba[2] );
+            }
+            else
+            {
+                if ((rgba_imag[0] != 0) || (rgba_imag[1] != 0) ||
+                    (rgba_imag[2] != 0) || (rgba_imag[3] != 0))
+                {
+                    if (rgba_imag[0] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.4g%.4gi r ",
+                            rgba[0], rgba_imag[0] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.4g+%.4gi r ",
+                            rgba[0], rgba_imag[0] );
+
+                    if (rgba_imag[1] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.4g%.4gi g ",
+                            rgba[1], rgba_imag[1] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.4g+%.4gi g ",
+                            rgba[1], rgba_imag[1] );
+
+                    if (rgba_imag[2] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.4g%.4gi b ",
+                            rgba[2], rgba_imag[2] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.4g+%.4gi b ",
+                            rgba[2], rgba_imag[2] );
+
+                    if (rgba_imag[3] < 0.0)
+                        g_snprintf(buf+strlen(buf), 64, "%.4g%.4gi a ",
+                            rgba[3], rgba_imag[3] );
+                    else
+                        g_snprintf(buf+strlen(buf), 64, "%.4g+%.4gi a ",
+                            rgba[3], rgba_imag[3] );
+                }
+                else
+                    g_snprintf(buf+strlen(buf), 64, "%gr %gg %gb %ga",
+                           rgba[0], rgba[1], rgba[2], rgba[3] );
+            }
+
+            if ( g_strcasecmp( nodata_mode, "on") == 0 )
+            {
+                double  nodata[3];
+
+                if( (gv_raster_layer_nodata_get(raster_layer, 0, nodata, NULL) 
+                     && ABS(rgba[0] - nodata[0]) < 0.0000000001) || 
+                    (gv_raster_layer_nodata_get(raster_layer, 1, nodata+1, NULL) 
+                     && ABS(rgba[1] - nodata[1]) < 0.0000000001) ||
+                    (gv_raster_layer_nodata_get(raster_layer, 2, nodata+2, NULL) 
+                     && ABS(rgba[2] - nodata[2]) < 0.0000000001) )
+                {
+                    g_snprintf(buf+strlen(buf), 64, " [NODATA]" );
+                }
+            }
+        }
+        else
+        {
+            rsrc = gv_raster_layer_get_data(raster_layer,0);
+            if( rsrc != NULL &&
+                gv_raster_get_sample( rsrc, raster_x, raster_y,
+                                      &pix_real, &pix_imaginary ) )
+            {
+                g_snprintf(buf+strlen(buf), 64, "%g", pix_real);
+            }
+
+            if ( g_strcasecmp( nodata_mode, "on") == 0 )
+            {
+                double  nodata;
+
+                if( gv_raster_layer_nodata_get( raster_layer, 0, &nodata, NULL )
+                    && ABS(pix_real - nodata) < 0.0000000001 )
+                {
+                    g_snprintf(buf+strlen(buf), 64, " [NODATA]" );
+                }
+            }
+        }
+    }
+
+    return buf;
+}
+
+char *gv_make_latlong_srs( const char * projected_srs )
+
+{
+    OGRSpatialReferenceH  hSRSProjected, hSRSLatLong;
+    char                  *osr_wkt, *glib_wkt;
+
+    if( projected_srs == NULL )
+        return NULL;
+
+    hSRSProjected = OSRNewSpatialReference( projected_srs );
+    if( hSRSProjected == NULL )
+        return FALSE;
+
+    if( OSRIsGeographic( hSRSProjected ) )
+    {
+        OSRDestroySpatialReference( hSRSProjected );
+        return g_strdup( projected_srs );
+    }
+
+    hSRSLatLong = OSRCloneGeogCS( hSRSProjected );
+    OSRDestroySpatialReference( hSRSProjected );
+    if( hSRSLatLong == NULL )
+        return NULL;
+
+    osr_wkt = NULL;
+    glib_wkt = NULL;
+    OSRExportToWkt( hSRSLatLong, &osr_wkt );
+    if( osr_wkt != NULL )
+    {
+        glib_wkt = g_strdup( osr_wkt );
+        CPLFree( osr_wkt );
+    }
+
+    if( hSRSLatLong != NULL )
+        OSRDestroySpatialReference( hSRSLatLong );
+
+    return glib_wkt;
+}
+
+int gv_reproject_points( const char *source_srs,
+                         const char *destination_srs,
+                         int count, double *x, double *y, double *z )
+
+{
+    int success = TRUE;
+    OGRSpatialReferenceH   hSRSNew = NULL, hSRSOld = NULL;
+    OGRCoordinateTransformationH hTransform = NULL;
+
+    if( source_srs == NULL || destination_srs == NULL )
+        return FALSE;
+
+    /*
+     * Try and establish if we can, or need to do reprojection.
+     */
+    hSRSNew = OSRNewSpatialReference( destination_srs );
+    if( hSRSNew == NULL )
+        return FALSE;
+
+    hSRSOld = OSRNewSpatialReference(source_srs);
+    if( hSRSOld == NULL )
+    {
+        OSRDestroySpatialReference( hSRSNew );
+        return FALSE;
+    }
+
+    if( OSRIsSame( hSRSOld, hSRSNew ) )
+    {
+        OSRDestroySpatialReference( hSRSOld );
+        OSRDestroySpatialReference( hSRSNew );
+
+        return TRUE;
+    }
+
+    /*
+     * Establish transformation.
+     */
+
+    hTransform = OCTNewCoordinateTransformation( hSRSOld, hSRSNew );
+    if( hTransform == NULL )
+    {
+        OSRDestroySpatialReference( hSRSOld );
+        OSRDestroySpatialReference( hSRSNew );
+
+        return FALSE;
+    }
+
+    /*
+     * Transform all the mesh points.
+     */
+
+    success = OCTTransform( hTransform, count, x, y, z );
+
+    OCTDestroyCoordinateTransformation( hTransform );
+    OSRDestroySpatialReference( hSRSOld );
+    OSRDestroySpatialReference( hSRSNew );
+
+    return success;
+}
+
+#ifndef WIN32
+int gv_launch_url( const char * url )
+
+{
+    return 0;
+}
+
+char * gv_short_path_name( const char * lpszLongPath)
+{
+	return (char *) lpszLongPath;
+}
+
+#else
+#include "windows.h"
+int gv_launch_url( const char * url )
+
+{
+    HINSTANCE   hInstance;
+
+    hInstance = ShellExecute( NULL, "open", url, "", "", SW_SHOWNORMAL );
+
+    return ((int) hInstance) > 32;
+}
+
+char * gv_short_path_name( const char * lpszLongPath)
+{
+	int ret;
+	char * lpszShortPath;
+
+	lpszShortPath = (char *) malloc(MAX_PATH * sizeof(char));
+	lpszShortPath[0] = 0;
+
+	ret = GetShortPathName(lpszLongPath, lpszShortPath, MAX_PATH);
+
+	#ifdef DEBUG
+		printf("gv_short_path_name: returned %d for (%s)\n", ret, lpszShortPath);
+	#endif
+
+	if (ret > 0)
+		return lpszShortPath;
+	else
+	{
+		free(lpszShortPath);
+		return "";
+	}
+}
+#endif

Added: packages/openev/branches/upstream/current/gvutils.h
===================================================================
--- packages/openev/branches/upstream/current/gvutils.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvutils.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,66 @@
+/******************************************************************************
+ * $Id: gvutils.h,v 1.5 2001/04/22 17:32:25 pgs Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Various utility functions.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvutils.h,v $
+ * Revision 1.5  2001/04/22 17:32:25  pgs
+ * added get_short_path_name
+ *
+ * Revision 1.4  2000/08/25 20:08:44  warmerda
+ * added phase/magnitude reporting
+ *
+ * Revision 1.3  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_UTILS_H__
+#define __GV_UTILS_H__
+
+#include "gvtypes.h"
+#include "gvproperties.h"
+#include "gvviewarea.h"
+
+void gv_set_color_from_string( GvColor, const char *,
+                               float, float, float, float );
+
+const char *gv_format_point_query( GvViewArea *view,
+                                   GvProperties *properties,
+                                   double geo_x, double geo_y );
+
+int gv_reproject_points( const char *source_srs,
+                         const char *destination_srs,
+                         int count, double *x, double *y, double *z );
+char *gv_make_latlong_srs( const char * );
+
+void gv_complex_to_phase_mag( float real, float imaginary,
+                              float *phase, float *magnitude );
+
+int gv_launch_url( const char * );
+
+char *gv_short_path_name( const char * );
+
+#endif /* __GV_UTILS_H__ */
+

Added: packages/openev/branches/upstream/current/gvviewarea.c
===================================================================
--- packages/openev/branches/upstream/current/gvviewarea.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvviewarea.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,3644 @@
+/******************************************************************************
+ * $Id: gvviewarea.c,v 1.125 2005/01/14 15:27:26 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GTK/OpenGL View Canvas
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvviewarea.c,v $
+ * Revision 1.125  2005/01/14 15:27:26  warmerda
+ * added flip flag access
+ *
+ * Revision 1.124  2004/08/20 18:01:14  warmerda
+ * Ensure that adding a first layer results in the flip_y flag being reset
+ * to 1.0 even if some previous raw image had resulted it in being set to
+ * -1.0.
+ *
+ * Revision 1.123  2004/04/28 01:27:08  sduclos
+ * GDK_GL_STENCIL_SIZE moved into S52 viewer glcontext
+ *
+ * Revision 1.122  2004/01/20 16:11:09  warmerda
+ * Default GDK_GL_STENCIL_SIZE to 1 for S52 viewer
+ *
+ * Revision 1.121  2003/08/27 19:58:43  warmerda
+ * added force_simple flag for gv_view_area_bmfont_draw
+ *
+ * Revision 1.120  2003/05/08 19:51:13  pgs
+ * modified adjustment logic forpage size
+ *
+ * Revision 1.119  2003/03/07 22:18:26  warmerda
+ * const correctness fix for get_layer_by_name
+ *
+ * Revision 1.118  2003/02/20 19:27:18  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.117  2002/12/12 03:42:14  warmerda
+ * verified Gillians change, and cleaned old cruft
+ *
+ * Revision 1.116  2002/12/12 01:04:26  gmwalter
+ * Fix to deal with upside-down symbol bug, symbol migration in non-georeferenced mode.
+ *
+ * Revision 1.115  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.114  2002/09/29 18:40:28  warmerda
+ * ensure new_x and new_y in gv_view_area_get_volume are float not int
+ * as per bug report from Sylvain Duclos.
+ *
+ * Revision 1.113  2002/09/16 17:20:02  warmerda
+ * ensure depth buffer cleared in 3d render_to_func
+ *
+ * Revision 1.112  2002/09/12 20:26:51  warmerda
+ * changed near clipping plane calc to improve depth resolution
+ *
+ * Revision 1.111  2002/09/11 18:58:33  warmerda
+ * dmsg bug 475: disable depth testing in 2D mode
+ *
+ * Revision 1.110  2002/09/10 13:26:43  warmerda
+ * added get_height_scale method
+ *
+ * Revision 1.109  2002/07/19 14:06:38  warmerda
+ * Fixed bug reported by Gillian with switching between raw and georeferenced
+ * mode for images with GCPs that effectively do x or y flips of the image
+ * (such as some esrin ceos scenes).   Changes all in gv_view_area_set_raw().
+ *
+ * Revision 1.108  2002/07/16 14:17:05  warmerda
+ * added support for getting background color
+ *
+ * Revision 1.107  2002/07/08 19:44:39  warmerda
+ * added properties on GvViewArea
+ *
+ * Revision 1.106  2002/05/02 19:16:26  gmwalter
+ * Updated key press function to allow smaller height and zoom increments in 3d
+ * (original step sizes now require that shift be pressed as well).
+ *
+ * Revision 1.105  2002/03/20 19:19:14  warmerda
+ * added exact_render flag
+ *
+ * Revision 1.104  2002/01/30 17:25:19  warmerda
+ * added set_state and get_primary_raster functions
+ *
+ * Revision 1.103  2001/12/13 03:29:17  warmerda
+ * avoid purging textures used in this render
+ *
+ * Revision 1.102  2001/12/12 15:32:39  warmerda
+ * ensure volume is computed for fit_all_layers
+ *
+ * Revision 1.101  2001/10/23 02:29:07  warmerda
+ * call gv_view_area_state_changed() in _add_layer()
+ *
+ * Revision 1.100  2001/10/19 13:26:47  warmerda
+ * fixed up checkf or mesa windows
+ *
+ * Revision 1.99  2001/10/12 17:44:18  warmerda
+ * avoid extra redraws when many raster layers displayed
+ *
+ * Revision 1.98  2001/10/12 02:11:45  warmerda
+ * Always render on non-win32, swapbuffer doesnt do what we want on X/OpenGL
+ *
+ * Revision 1.97  2001/10/12 01:58:19  warmerda
+ * avoid re-rendering if backing store OK
+ *
+ * Revision 1.96  2001/07/18 03:34:44  warmerda
+ * fixed flipping problem
+ *
+ * Revision 1.95  2001/07/09 20:21:28  warmerda
+ * fixed raw mesh info
+ *
+ * Revision 1.94  2001/07/03 14:26:05  warmerda
+ * added set/get raw ability
+ *
+ * Revision 1.93  2001/06/15 00:01:53  warmerda
+ * added GDK_GLX_RED/GREEN/BLUE_SIZE attribs for visual
+ *
+ * Revision 1.92  2001/05/07 19:14:02  warmerda
+ * removed a bunch of debugging cruft
+ *
+ * Revision 1.91  2001/05/07 19:08:03  warmerda
+ * draw text with origin off viewport properly
+ *
+ * Revision 1.90  2001/04/26 18:36:40  warmerda
+ * added glFinish() before swapbuffers
+ *
+ * Revision 1.89  2001/04/09 18:20:14  warmerda
+ * added ability to query list of available fonts
+ *
+ * Revision 1.88  2001/03/28 15:03:02  warmerda
+ * Added direct_render flag within gv_view_area_render_to_func().  This flag
+ * kicks in for 1:1 prints, and prevents any attempt to reapply the view
+ * transformation since the naive code loses rotations, and 3D perspectives.
+ * This relates to CIETMap bug 89, and OpenEV bug 212486.
+ *
+ * Revision 1.87  2001/03/27 18:38:05  warmerda
+ * fixed return value
+ *
+ * Revision 1.86  2001/03/26 19:18:35  warmerda
+ * restructure bmfont handling to preserve GdkFont handle
+ *
+ * Revision 1.85  2001/03/22 22:34:24  warmerda
+ * fixed bug in gv_view_load_bmfont() with reporting load errors
+ *
+ * Revision 1.84  2001/02/15 16:35:32  warmerda
+ * turned g_warning into CPLDebug call
+ *
+ * Revision 1.83  2001/02/03 22:21:08  warmerda
+ * added gv_view_area_get_mode() and python covers
+ *
+ * Revision 1.82  2001/01/30 19:33:58  warmerda
+ * removed printf
+ *
+ * Revision 1.81  2001/01/26 13:56:15  warmerda
+ * ensure fit_all_layers(), Home, and fit_extents all produce compatible results.
+ * Route all view setting through gv_viewarea_set_3d_view().
+ * Fix up default view.
+ * Remove view setting in get_volume() function.
+ *
+ * Revision 1.80  2001/01/19 15:19:49  warmerda
+ * improve near range calculation when over scene
+ *
+ * Revision 1.79  2000/10/06 16:48:56  warmerda
+ * added GvViewArea background color
+ *
+ * Revision 1.78  2000/09/29 16:09:17  srawlin
+ * added Goto function requring fuction to map lat/long to view coordinates
+ *
+ * Revision 1.77  2000/09/21 02:57:20  warmerda
+ * reorganized bitmap font support to allow any gdk supported font at runtime
+ *
+ * Revision 1.76  2000/09/13 15:58:55  srawlin
+ * added python bindings for gv_view_area_get_zoom
+ *
+ * Revision 1.75  2000/09/07 21:02:26  warmerda
+ * ensure projection string is cleared when last layer removed
+ *
+ * Revision 1.74  2000/08/25 19:58:00  warmerda
+ * Fixed problems with setting slider adjustments out of bounds. This sometimes
+ * caused a feedback loop as the slider itself tried to keep things legal.
+ *
+ * Revision 1.73  2000/08/24 17:05:01  srawlin
+ * fixed 3D eye_pos passed to vec_near_far_range() (near/far clipping plane
+ * calculation) to account for image flipping
+ *
+ * Revision 1.72  2000/08/24 15:44:30  srawlin
+ * fixed flip about y axis in 3D mode
+ *
+ * Revision 1.71  2000/08/24 02:22:46  warmerda
+ * fixed scrollbar step and page increments
+ *
+ * Revision 1.70  2000/08/23 15:45:45  warmerda
+ * updated adjustments after user scrolls
+ *
+ * Revision 1.69  2000/08/17 16:50:51  warmerda
+ * fixed leak of lock_adjustment flag
+ *
+ * Revision 1.68  2000/08/17 16:45:00  warmerda
+ * get_world_extents works without layers now
+ *
+ * Revision 1.67  2000/08/16 22:14:36  warmerda
+ * fixed scrolling for raw and projected views
+ *
+ * Revision 1.66  2000/08/16 14:07:47  warmerda
+ * added prototype scrollbar support
+ *
+ * Revision 1.65  2000/08/02 17:47:01  warmerda
+ * don't allow further processing of arrows or we will lose focus
+ *
+ * Revision 1.64  2000/07/25 17:51:48  warmerda
+ * added finalize, and debug statements
+ *
+ * Revision 1.63  2000/07/24 14:26:30  warmerda
+ * use up to char 126 in gdk_gl_use_gdk_font
+ *
+ * Revision 1.62  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.61  2000/07/17 19:10:00  warmerda
+ * added tentative support for scaling wait between redraws to actual redraw time
+ *
+ * Revision 1.60  2000/07/13 18:04:33  srawlin
+ * removed use of view.state.eye_az and .eye_el, contained same information as view.state.eye_dir
+ *
+ * Revision 1.59  2000/07/11 20:56:23  srawlin
+ * added methods to get and set viewing direction relative to z-plane in 3D
+ *
+ * Revision 1.58  2000/07/10 22:13:39  srawlin
+ * Added motion scaling based on distance to z-plane
+ *
+ * Revision 1.57  2000/07/10 20:45:00  srawlin
+ * Added 3D Translate controls
+ *
+ * Revision 1.56  2000/07/10 16:20:33  srawlin
+ * fixed bug when zooming in 3D and panning, also removed debug printfs
+ *
+ * Revision 1.55  2000/07/10 13:36:55  srawlin
+ * updated 3D controls to be more like 2D
+ *
+ * Revision 1.54  2000/07/07 17:53:32  warmerda
+ * ifdef out debug statement
+ *
+ * Revision 1.53  2000/07/05 11:26:26  srawlin
+ * 3D - changed initial view, some mouse movement directions, set angle boundaries
+ *
+ * Revision 1.52  2000/07/04 15:47:18  warmerda
+ * removed debug lines in 3d mode
+ *
+ * Revision 1.51  2000/07/03 20:58:31  warmerda
+ * eye_pos in georef coordinates now
+ *
+ * Revision 1.50  2000/06/29 16:14:58  warmerda
+ * added one more set_busy call
+ *
+ * Revision 1.49  2000/06/29 14:38:37  warmerda
+ * use GvManager for idle tasks
+ *
+ * Revision 1.48  2000/06/23 12:56:52  warmerda
+ * added multiple GvRasterSource support
+ *
+ * Revision 1.47  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvviewarea.h"
+#include "gvlayer.h"
+#include "gvrasterlayer.h"
+#include "gvundo.h"
+#include "gvmanager.h"
+#include "gvutils.h"
+#include <gtk/gtksignal.h>
+#include <gtkgl/gdkgl.h>
+#include <gdk/gdkkeysyms.h>
+#include "gextra.h"
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <math.h>
+
+#define DEG2RAD         0.01745329252
+#define LOG2(x)         (log(x) / 0.69314718056)
+#define DEAD_ZONE       8.0
+#define DRAG_THRESHOLD  10
+#define WHEEL_ZOOM_INC  0.1
+
+/* 3D motion Stuff */
+#define MOVE_SPEED      0.04
+#define AZ_SCALE        0.15
+#define EL_SCALE        0.15
+#define HEIGHT_SCALE    0.1
+#define CONTIN_3DMOVE_INC 20.0
+
+/* 2D Zooming */
+#define CONTIN_ZOOM_INC 1.5
+#define ZOOM_STEP       1
+
+/* useful 3D vector copy function */
+#define vec_copy(s,d) {d[0]=s[0];d[1]=s[1];d[2]=s[2];}
+#define vec_add(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define vec_scale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];}
+
+#define VIEW_EVENT_MASK (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | \
+             GDK_POINTER_MOTION_MASK | \
+             GDK_POINTER_MOTION_HINT_MASK | \
+             GDK_KEY_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | \
+             GDK_LEAVE_NOTIFY_MASK)
+
+enum
+{
+    GLDRAW,
+    GLCURSOR,
+    ACTIVE_CHANGED,
+    VIEW_STATE_CHANGED,
+    LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  ARG_HADJUSTMENT,
+  ARG_VADJUSTMENT,
+};
+
+static void gv_view_area_class_init(GvViewAreaClass *klass);
+static void gv_view_area_init(GvViewArea *view);
+
+static void gv_view_area_update_adjustments( GvViewArea *view );
+static gint gv_view_area_configure(GtkWidget *view, GdkEventConfigure *event);
+static gint gv_view_area_reset_projection(GvViewArea *view, gvgeocoord width, gvgeocoord height);
+static void gv_view_area_realize(GtkWidget *view);
+static void gv_view_area_unrealize(GtkWidget *view);
+static void gv_view_area_motion_handle_hint(GtkWidget *view, GdkEventMotion *event);
+static gint gv_view_area_motion_notify(GtkWidget *view, GdkEventMotion *event);
+static gint gv_view_area_button_press(GtkWidget *view, GdkEventButton *event);
+static gint gv_view_area_button_release(GtkWidget*view, GdkEventButton *event);
+static gint gv_view_area_key_press(GtkWidget *view, GdkEventKey *event);
+static void gv_view_area_change_notify(GvViewArea *view, gpointer info);
+static void gv_view_area_state_changed(GvViewArea *view);
+static void gv_view_area_fit_layer(GvViewArea *view, GvLayer *layer);
+static void gv_view_area_destroy(GtkObject *object);
+static void gv_view_area_fit_extents_3D(GvViewArea *view,
+                                     gvgeocoord llx, gvgeocoord lly, gvgeocoord minz,
+                                     gvgeocoord width, gvgeocoord height, gvgeocoord deltaz );
+static gint gv_view_area_zoompan_handler(gpointer data );
+static void gv_view_area_finalize( GtkObject *object );
+
+static guint view_area_signals[LAST_SIGNAL] = { 0 };
+
+static void  gv_view_area_set_arg        (GtkObject      *object,
+                                          GtkArg         *arg,
+                                          guint           arg_id);
+static void  gv_view_area_get_arg        (GtkObject      *object,
+                                          GtkArg         *arg,
+                                          guint           arg_id);
+
+static struct { char *gvname, *gdkname; } bmfontmap[] =
+{
+    { "Fixed", "-*-fixed-*-*-*-*-*-*-*-*-*-*-iso8859-*" },
+    { "Times, 20pt", "-*-times-*-*-*-*-20-*-*-*-*-*-iso8859-*" },
+    { "Times, 14pt", "-*-times-*-*-*-*-14-*-*-*-*-*-iso8859-*" },
+    { NULL, NULL }  /* Sentinel */
+};
+
+/* Incremented each render */
+static int render_counter = 0;
+
+void
+vec_point(vec3_t point, gvgeocoord az, gvgeocoord el)
+{
+    gvgeocoord c = cos(el * DEG2RAD);
+
+    point[0] = c * cos(az * DEG2RAD);
+    point[1] = c * sin(az * DEG2RAD);
+    point[2] = sin(el * DEG2RAD);
+}
+
+/* Inverse of vec_point, given direction vector find az and el, az in [-180, 180] */
+int
+inv_vec_point(vec3_t point, gvgeocoord *az, gvgeocoord *el)
+{
+    gvgeocoord c, az1, az2;
+
+    *el = asin(point[2]) / DEG2RAD;
+    c = cos( *el * DEG2RAD);
+    if ((c < 0.000001) &&  (c > -0.000001))
+        c = 0.00001;
+    az1 = (asin(point[1]/c)) / DEG2RAD;
+    az2 = (acos(point[0]/c)) / DEG2RAD;
+
+    if ((point[0] >= 0.0) && (point[1] >= 0.0))
+        *az = az1;
+    else if ((point[0] >= 0.0) && (point[1] <= 0.0))
+        *az = az1;
+    else if ((point[0] <= 0.0) && (point[1] >= 0.0))
+        *az = az2;
+    else if ((point[0] <= 0.0) && (point[1] <= 0.0))
+        *az = -az2;
+    else
+    {
+        printf("ERROR!  in gvviewarea.c, inv_vec_point.  Should never reach here.\n");
+        /* printf("point[0] %f, point[1] %f, c %f, az1 %f, az2 %f\n", point[0], point[1], c, az1, az2); */
+        return 0;
+    }
+
+    return 1;
+
+}
+
+
+static void
+vec_near_far_range( vec3_t point, double *volume, double *min, double *max )
+
+{
+    vec3_t   nearest, furthest;
+
+    /* Find nearest X in volume */
+    if( point[0] < volume[0] )
+        nearest[0] = volume[0];
+    else if( point[0] > volume[1] )
+        nearest[0] = volume[1];
+    else
+        nearest[0] = point[0];
+
+    /* Find nearest Y in volume */
+    if( point[1] < volume[2] )
+        nearest[1] = volume[2];
+    else if( point[1] > volume[3] )
+        nearest[1] = volume[3];
+    else
+        nearest[1] = point[1];
+
+    /* Find nearest Z in volume */
+    if( point[2] < volume[4] )
+        nearest[1] = volume[4];
+    else if( point[2] > volume[5] )
+        nearest[2] = volume[5];
+    else
+        nearest[2] = point[2];
+
+    /* Find furthest X in volume */
+    if( ABS(point[0] - volume[0]) > ABS(point[0] - volume[1]) )
+        furthest[0] = volume[0];
+    else
+        furthest[0] = volume[1];
+
+    /* Find furthest Y in volume */
+    if( ABS(point[1] - volume[2]) > ABS(point[1] - volume[3]) )
+        furthest[1] = volume[2];
+    else
+        furthest[1] = volume[3];
+
+    /* Find furthest Z in volume */
+    if( ABS(point[2] - volume[4]) > ABS(point[2] - volume[5]) )
+        furthest[2] = volume[4];
+    else
+        furthest[2] = volume[5];
+
+    /* find distances */
+
+    *min = sqrt( (nearest[0] - point[0]) * (nearest[0] - point[0])
+                 + (nearest[1] - point[1]) * (nearest[1] - point[1])
+                 + (nearest[2] - point[2]) * (nearest[2] - point[2]) );
+
+    *max = sqrt( (furthest[0] - point[0]) * (furthest[0] - point[0])
+                 + (furthest[1] - point[1]) * (furthest[1] - point[1])
+                 + (furthest[2] - point[2]) * (furthest[2] - point[2]) );
+
+}
+
+GtkType
+gv_view_area_get_type(void)
+{
+    static GtkType view_area_type = 0;
+
+    if (!view_area_type)
+    {
+    static const GtkTypeInfo view_area_info =
+    {
+        "GvViewArea",
+        sizeof(GvViewArea),
+        sizeof(GvViewAreaClass),
+        (GtkClassInitFunc) gv_view_area_class_init,
+        (GtkObjectInitFunc) gv_view_area_init,
+        /* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
+    };
+
+    view_area_type = gtk_type_unique( gtk_gl_area_get_type(),
+                      &view_area_info );
+    }
+    return view_area_type;
+}
+
+static void
+gv_view_area_class_init(GvViewAreaClass *klass)
+{
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    object_class = GTK_OBJECT_CLASS(klass);
+    widget_class = GTK_WIDGET_CLASS(klass);
+
+    view_area_signals[GLDRAW] =
+    gtk_signal_new ("gldraw",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvViewAreaClass, gldraw),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    view_area_signals[GLCURSOR] =
+    gtk_signal_new ("glcursor",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvViewAreaClass, glcursor),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    view_area_signals[ACTIVE_CHANGED] =
+    gtk_signal_new ("active-changed",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvViewAreaClass, active_changed),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    view_area_signals[VIEW_STATE_CHANGED] =
+    gtk_signal_new ("view-state-changed",
+            GTK_RUN_FIRST,
+            object_class->type,
+            GTK_SIGNAL_OFFSET (GvViewAreaClass,
+                       view_state_changed),
+            gtk_marshal_NONE__NONE,
+            GTK_TYPE_NONE, 0);
+
+    gtk_object_class_add_signals(object_class, view_area_signals, LAST_SIGNAL);
+
+    object_class->destroy = gv_view_area_destroy;
+    object_class->finalize = gv_view_area_finalize;
+    object_class->set_arg = gv_view_area_set_arg;
+    object_class->get_arg = gv_view_area_get_arg;
+
+    widget_class->realize = gv_view_area_realize;
+    widget_class->unrealize = gv_view_area_unrealize;
+    widget_class->configure_event = gv_view_area_configure;
+    widget_class->expose_event = gv_view_area_expose;
+    widget_class->button_press_event = gv_view_area_button_press;
+    widget_class->button_release_event = gv_view_area_button_release;
+    widget_class->motion_notify_event = gv_view_area_motion_notify;
+    widget_class->key_press_event = gv_view_area_key_press;
+
+    widget_class->set_scroll_adjustments_signal =
+        gtk_signal_new ("set_scroll_adjustments",
+                        GTK_RUN_LAST,
+                        object_class->type,
+                        GTK_SIGNAL_OFFSET (GvViewAreaClass,
+                                           set_scroll_adjustments),
+                        gtk_marshal_NONE__POINTER_POINTER,
+                        GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
+
+    klass->set_scroll_adjustments = gv_view_area_set_adjustments;
+    klass->gldraw = NULL;
+    klass->active_changed = NULL;
+    klass->view_state_changed = NULL;
+}
+
+static void
+gv_view_area_init(GvViewArea *view)
+{
+    view->state.tx = view->state.ty = view->state.rot = 0.0;
+    view->state.zoom = 0.0;
+    view->state.linear_zoom = 1.0;
+    view->state.flip_x = view->state.flip_y = 1.0;
+    view->state.shape_x = view->state.shape_y = 0.0;
+    view->volume_current = FALSE;
+    view->linear_measure = 1000.0;
+
+    /* 3D Specific stuff */
+    view->state.eye_pos[0] = 0.0;
+    view->state.eye_pos[1] = 0.0;
+    view->state.eye_pos[2] = 500.0;
+    vec_point(view->state.eye_dir, 90.0, -45.0);  /* rotation = 90,  tilt  45 deg down */
+    view->state.z_scale = 1.0;
+
+    view->exact_render = FALSE;
+
+    view->flag_3d = 0;    /* 0=2D, 1=3D, default 2D */
+
+    view->bmfonts = g_array_new(FALSE,TRUE,sizeof(GvBMFontInfo));
+
+    view->background[0] = view->background[1] = view->background[2] = 0.0;
+    view->background[3] = 1.0;
+
+    view->layers = NULL;
+    view->projection = NULL;
+
+    view->last_button = 0;
+    view->dragging_mode = FALSE;
+    view->display_dirty = TRUE;
+
+    /* ghost cursor stuff */
+    view->next_valid = FALSE;
+    view->last_valid = FALSE;
+    view->next_x = 0.0;
+    view->next_y = 0.0;
+    view->last_x = 0.0;
+    view->last_y = 0.0;
+    /* end of ghost cursor stuff */
+
+    view->last_draw_time = 0.0;
+    view->redraw_timer = NULL;
+
+    view->vadj = NULL;
+    view->hadj = NULL;
+    view->lock_adjustments = FALSE;
+
+    view->properties = NULL;
+
+    GTK_WIDGET_SET_FLAGS(GTK_WIDGET(view), GTK_CAN_FOCUS);
+
+    gtk_widget_set_events(GTK_WIDGET(view),
+              gtk_widget_get_events(GTK_WIDGET(view)) |
+              VIEW_EVENT_MASK);
+
+    /* Need to handle motion hints BEFORE other handlers */
+    gtk_signal_connect(GTK_OBJECT(view), "motion-notify-event",
+               GTK_SIGNAL_FUNC(gv_view_area_motion_handle_hint), NULL);
+}
+
+GtkWidget *
+gv_view_area_new(void)
+{
+    GdkGLContext *glcontext;
+    GvViewArea *view;
+    int attrlist[] = {
+        GDK_GL_RGBA, 
+        GDK_GL_DOUBLEBUFFER, 
+        GDK_GL_DEPTH_SIZE, 	4, 
+        GDK_GL_RED_SIZE, 	3, 
+        GDK_GL_GREEN_SIZE, 	3, 
+        GDK_GL_BLUE_SIZE, 	3, 
+        GDK_GL_NONE};
+
+#ifdef WIN32
+    /* figure out how to actually share context later */
+    glcontext = gdk_gl_context_attrlist_share_new(attrlist, NULL, TRUE);
+    if (glcontext == NULL)
+        return NULL;
+
+    view = gtk_type_new(gv_view_area_get_type());
+    GTK_GL_AREA(view)->glcontext = glcontext;
+
+    return GTK_WIDGET(view);
+#else
+    /* Largely copied from gtkglarea.c - should be in the constructor(?) */
+    GdkVisual *visual;
+
+    // SD check that geometric data type are in sync with OpenGL
+    g_assert(sizeof(gvgeocoord) == sizeof(GLgeocoord));
+
+    visual = gdk_gl_choose_visual(attrlist);
+    if (visual == NULL) return NULL;
+
+    glcontext = gdk_gl_context_share_new(visual, NULL , TRUE);
+    if (glcontext == NULL) return NULL;
+
+    /* use colormap and visual suitable for OpenGL rendering */
+    gtk_widget_push_colormap(gdk_colormap_new(visual, TRUE));
+    gtk_widget_push_visual(visual);
+
+    view = gtk_type_new(gv_view_area_get_type());
+    GTK_GL_AREA(view)->glcontext = glcontext;
+
+    /* pop back defaults */
+    gtk_widget_pop_visual();
+    gtk_widget_pop_colormap();
+
+    return GTK_WIDGET(view);
+#endif
+}
+
+gint
+gv_view_area_set_projection( GvViewArea *view, const char *projection )
+
+{
+    if( view->projection != NULL )
+        g_free( view->projection );
+
+    view->projection = g_strdup( projection );
+
+    return TRUE;
+}
+
+const char *
+gv_view_area_get_projection( GvViewArea *view )
+
+{
+    return view->projection;
+}
+
+
+/* Set mode to 2D or 3D */
+void
+gv_view_area_set_mode(GvViewArea *view, int flag_3d)
+{
+    view->flag_3d= flag_3d;    /* 0=2D, 1=3D,  */
+
+    /* Reset OpenGL Projection Method */
+    if( GTK_WIDGET_REALIZED(GTK_WIDGET(view)) )
+        gv_view_area_reset_projection(view,
+                                      view->state.shape_x,
+                                      view->state.shape_y);
+
+    gv_view_area_state_changed(view);
+}
+
+int gv_view_area_get_mode(GvViewArea *view)
+
+{
+    return view->flag_3d;
+}
+
+/* Change 3D Scaling factor - has no effect unless in 3D mode */
+void
+gv_view_area_height_scale(GvViewArea *view, gvgeocoord scale)
+{
+    view->state.z_scale = scale;
+    gv_view_area_state_changed(view);
+}
+
+gvgeocoord
+gv_view_area_get_height_scale( GvViewArea *view )
+
+{
+    return view->state.z_scale;
+}
+
+
+/* Change 3D view position and direction */
+void gv_view_area_set_3d_view( GvViewArea *view,
+                               vec3_t eye_pos, vec3_t eye_dir )
+
+{
+    int    changed = FALSE;
+
+    if( eye_pos != NULL
+        && (eye_pos[0] != view->state.eye_pos[0]
+            || eye_pos[1] != view->state.eye_pos[1]
+            || eye_pos[2] != view->state.eye_pos[2]) )
+    {
+        vec_copy( eye_pos, view->state.eye_pos );
+        changed = TRUE;
+    }
+
+    if( eye_dir != NULL
+        && (eye_dir[0] != view->state.eye_dir[0]
+            || eye_dir[1] != view->state.eye_dir[1]
+            || eye_dir[2] != view->state.eye_dir[2]) )
+    {
+        gvgeocoord mag = sqrt(eye_dir[0] * eye_dir[0]
+                         + eye_dir[1] * eye_dir[1]
+                         + eye_dir[2] * eye_dir[2] );
+
+        if( mag != 0.0 )
+        {
+            vec_scale( eye_dir, 1.0 / mag, view->state.eye_dir );
+            changed = TRUE;
+        }
+    }
+
+    if( changed )
+        gv_view_area_state_changed(view);
+}
+
+/* Change 3D view position and direction to given location on z-plane */
+void gv_view_area_set_3d_view_look_at( GvViewArea *view,
+                                       vec3_t eye_pos, gvgeocoord *eye_look_at )
+{
+    if( eye_look_at != NULL )
+    {
+        vec3_t eye_dir;
+        gvgeocoord mag;
+
+        eye_dir[0] = eye_look_at[0] - eye_pos[0];
+        eye_dir[1] = eye_look_at[1] - eye_pos[1];
+        eye_dir[2] = -eye_pos[2];
+
+        mag = sqrt(eye_dir[0] * eye_dir[0]
+                         + eye_dir[1] * eye_dir[1]
+                         + eye_dir[2] * eye_dir[2] );
+
+        if( mag != 0.0 )
+        {
+            vec_scale( eye_dir, 1.0 / mag, view->state.eye_dir );
+            gv_view_area_set_3d_view( view, eye_pos, eye_dir );
+        }
+    }
+    else
+    {
+        gv_view_area_set_3d_view( view, eye_pos, view->state.eye_dir );
+    }
+}
+
+
+/* Get point in z-plane that looking at, false if not meaningful */
+gint
+gv_view_area_get_look_at_pos( GvViewArea *view,
+                              gvgeocoord *x, gvgeocoord *y)
+{
+    /* Check if looking up above z-plane */
+    if (view->state.eye_dir[2] >= 0.0)
+    {
+        return FALSE;
+    } else {
+
+        *x = view->state.eye_pos[0] - view->state.eye_dir[0]
+            * (view->state.eye_pos[2]/ view->state.eye_dir[2]);
+
+        *y = view->state.eye_pos[1] - view->state.eye_dir[1]
+            * (view->state.eye_pos[2]/ view->state.eye_dir[2]);
+    }
+
+    return TRUE;
+}
+
+void
+gv_view_area_queue_draw(GvViewArea *view)
+{
+    gv_manager_set_busy( gv_get_manager(), TRUE );
+
+    view->display_dirty = TRUE;
+
+    gtk_widget_queue_draw(GTK_WIDGET(view));
+}
+void
+gv_view_area_queue_cursor_draw(GvViewArea *view, int next_valid, gvgeocoord x, gvgeocoord y)
+{
+    gv_manager_set_busy( gv_get_manager(), TRUE );
+    view->next_valid = next_valid;
+    view->next_x = x;
+    view->next_y = y;
+    gtk_widget_queue_draw(GTK_WIDGET(view));
+}
+
+void
+gv_view_area_zoom(GvViewArea *view, gvgeocoord zoom)
+{
+    view->state.zoom += zoom;
+    view->state.linear_zoom = pow(2.0, view->state.zoom);
+
+    gv_view_area_state_changed(view);
+}
+
+gvgeocoord
+gv_view_area_get_zoom(GvViewArea *view)
+{
+    return view->state.zoom;
+}
+
+void
+gv_view_area_rotate(GvViewArea *view, gvgeocoord angle)
+{
+    view->state.rot += angle;
+    gv_view_area_state_changed(view);
+}
+
+void
+gv_view_area_translate(GvViewArea *view, gvgeocoord dx, gvgeocoord dy)
+{
+    gvgeocoord cr, sr;
+
+    dx /= view->state.linear_zoom;
+    dy /= view->state.linear_zoom;
+    cr = cos(-view->state.rot * DEG2RAD);
+    sr = sin(-view->state.rot * DEG2RAD);
+    view->state.tx += (dx * cr - dy * sr) * view->state.flip_x;
+    view->state.ty += (dx * sr + dy * cr) * view->state.flip_y;
+
+    gv_view_area_state_changed(view);
+}
+
+void
+gv_view_area_set_translation(GvViewArea *view, gvgeocoord x, gvgeocoord y)
+{
+    view->state.tx = x;
+    view->state.ty = y;
+
+    gv_view_area_state_changed(view);
+}
+
+int
+gv_view_area_get_flip_x( GvViewArea *view )
+{
+    return view->state.flip_x;
+}
+
+int
+gv_view_area_get_flip_y( GvViewArea *view )
+{
+    return view->state.flip_y;
+}
+
+void gv_view_area_set_flip_xy( GvViewArea *view, int flip_x, int flip_y )
+{
+    if( flip_x == view->state.flip_x && flip_y == view->state.flip_y )
+        return;
+
+    view->state.flip_x = flip_x;
+    view->state.flip_y = flip_y;
+
+    gv_view_area_state_changed(view);
+}
+
+void
+gv_view_area_copy_state(GvViewArea *view, GvViewArea *copy)
+{
+    gv_view_area_set_state( view, &(copy->state) );
+}
+
+void
+gv_view_area_set_state(GvViewArea *view, GvViewAreaState *state)
+{
+    /* consider preserving mouse position, and re-deriving zoom or log_zoom */
+
+    view->state = *state;
+
+    gv_view_area_state_changed(view);
+}
+
+void
+gv_view_area_map_location(GvViewArea *view, gvgeocoord x, gvgeocoord y,
+             gvgeocoord *geo_x, gvgeocoord *geo_y)
+{
+    const char *coord_sys = gv_view_area_get_projection(view);
+    char    *latlong_srs = NULL;
+
+    *geo_x = x;
+    *geo_y = y;
+
+    /* Don't need to do anything in the case of a view with
+       raw PIXEL projection */
+    if (coord_sys != NULL && !EQUAL(coord_sys,"PIXEL"))
+    {
+        latlong_srs = gv_make_latlong_srs( coord_sys );
+
+        if( latlong_srs != NULL )
+        {
+            double      temp_x, temp_y, temp_z;
+
+            temp_x = x;
+            temp_y = y;
+            temp_z = 0.0;
+
+            if( gv_reproject_points( latlong_srs, coord_sys,
+                                     1, &temp_x, &temp_y, &temp_z ) )
+            {
+                *geo_x = temp_x;
+                *geo_y = temp_y;
+
+                /* printf(" (x,y)=(%f,%f), (geo_x, geo_y)=(%f,%f)\n", x,y,temp_x,temp_y); */
+            }
+        }
+    }
+
+    /* geo_x, geo_y contains the original x,y pair or the reprojected values
+       if view is lat/long */
+}
+
+void
+gv_view_area_map_pointer(GvViewArea *view, gvgeocoord px, gvgeocoord py,
+                                           gvgeocoord *x, gvgeocoord *y)
+{
+    gvgeocoord fx, fy, cr, sr;
+
+    cr = cos(-view->state.rot * DEG2RAD);
+    sr = sin(-view->state.rot * DEG2RAD);
+    fx = px - view->state.shape_x / 2.0;
+    fy = view->state.shape_y / 2.0 - py;
+    fx /= view->state.linear_zoom;
+    fy /= view->state.linear_zoom;
+    *x = (fx * cr - fy * sr) * view->state.flip_x - view->state.tx;
+    *y = (fx * sr + fy * cr) * view->state.flip_y - view->state.ty;
+}
+
+void
+
+gv_view_area_inverse_map_pointer(GvViewArea *view, gvgeocoord x,   gvgeocoord y,
+                                                   gvgeocoord *px, gvgeocoord *py)
+{
+    gvgeocoord fx, fy, cr, sr;
+
+    cr = cos(view->state.rot * DEG2RAD);
+    sr = sin(view->state.rot * DEG2RAD);
+
+    x += view->state.tx;
+    y += view->state.ty;
+
+    fx = (x * cr * view->state.flip_x - y * sr * view->state.flip_y)
+        * view->state.linear_zoom;
+    fy = (x * sr * view->state.flip_x + y * cr * view->state.flip_y)
+        * view->state.linear_zoom;
+
+    *px = fx + view->state.shape_x / 2.0;
+    *py = view->state.shape_y / 2.0 - fy;
+}
+
+void
+gv_view_area_correct_for_transform(GvViewArea *view, gvgeocoord x, gvgeocoord y,
+                   gvgeocoord *cx, gvgeocoord *cy)
+{
+    /* Correct a vector for rotation and zoom: used for sprites */
+    gvgeocoord cr, sr;
+
+    cr = cos(-view->state.rot * DEG2RAD);
+    sr = sin(-view->state.rot * DEG2RAD);
+
+    *cx = (x * cr - y * sr) * view->state.flip_x / view->state.linear_zoom;
+    *cy = (x * sr + y * cr) * view->state.flip_y / view->state.linear_zoom;
+}
+
+void
+gv_view_area_add_layer(GvViewArea *view, GtkObject *layer_obj)
+{
+    GvLayer *layer;
+
+    g_return_if_fail(GV_IS_LAYER(layer_obj));
+    layer = GV_LAYER(layer_obj);
+
+    if (GTK_WIDGET_REALIZED(GTK_WIDGET(view)))
+    {
+        /* Call the one time setup function for the layer */
+        /* If the view area is not yet realized, this is deferred to
+           gv_view_area_realize() */
+        if (gtk_gl_area_make_current(GTK_GL_AREA(view)))
+        {
+            gv_layer_setup(layer, view);
+        }
+
+        /* If this is the first layer, set initial view area
+           transformation based on layer extents.  Also ensure
+           we are reset back to unflipped even if we previously got
+          flipped for a raw image. */
+        if (view->layers == NULL)
+        {
+            view->state.flip_y = 1.0;
+            gv_view_area_fit_layer(view, layer);
+        }
+    }
+
+    /* Maintain reference to layer */
+    gtk_object_ref(GTK_OBJECT(layer));
+    gtk_object_sink(GTK_OBJECT(layer));
+
+    g_assert( layer->view == NULL );
+
+    view->volume_current = FALSE;
+    view->layers = g_list_append(view->layers, layer);
+
+    layer->view = view;
+
+    gtk_signal_connect_object(GTK_OBJECT(layer), "changed",
+                              GTK_SIGNAL_FUNC(gv_view_area_change_notify),
+                              GTK_OBJECT(view));
+    gtk_signal_connect_object(GTK_OBJECT(layer), "display-change",
+                              GTK_SIGNAL_FUNC(gv_view_area_change_notify),
+                              GTK_OBJECT(view));
+
+    gtk_signal_emit(GTK_OBJECT(view), view_area_signals[ACTIVE_CHANGED]);
+
+    gv_view_area_state_changed(view);
+}
+
+void
+gv_view_area_remove_layer(GvViewArea *view, GtkObject *layer_obj)
+{
+    GvLayer *layer;
+    GList *link;
+
+    g_return_if_fail(GV_IS_LAYER(layer_obj));
+    layer = GV_LAYER(layer_obj);
+
+    if (GTK_WIDGET_REALIZED(GTK_WIDGET(view)))
+    {
+    /* Allow layer to destroy gl handles if necessary */
+    if (gtk_gl_area_make_current(GTK_GL_AREA(view)))
+    {
+        gv_layer_teardown(layer, view);
+    }
+    }
+
+    if (layer_obj == view->active_layer)
+    {
+    gv_view_area_set_active_layer(view, NULL);
+    }
+
+    g_assert( layer->view == view );
+    layer->view = NULL;
+
+    link = g_list_find(view->layers, layer);
+    if (link == NULL)
+    {
+    CPLDebug( "OpenEV",
+                  "gv_view_area_remove_link(): layer %s is not in view",
+          gtk_type_name(GTK_OBJECT_TYPE(layer)));
+    return;
+    }
+
+    view->layers = g_list_remove_link(view->layers, link);
+    view->volume_current = FALSE;
+
+
+
+    /* clear projection if no layers are left. */
+    if( view->layers == NULL )
+    {
+        if( view->projection != NULL )
+        {
+            g_free( view->projection );
+            view->projection = NULL;
+        }
+    }
+
+    gtk_signal_disconnect_by_data(layer_obj, GTK_OBJECT(view));
+    gtk_object_unref(layer_obj);
+
+    gtk_signal_emit(GTK_OBJECT(view), view_area_signals[ACTIVE_CHANGED]);
+
+    gv_view_area_queue_draw(view);
+}
+
+GtkObject *
+gv_view_area_active_layer(GvViewArea *view)
+{
+    return view->active_layer;
+}
+
+void
+gv_view_area_set_active_layer(GvViewArea *view, GtkObject *layer)
+{
+    g_return_if_fail(layer == NULL || GV_IS_LAYER(layer));
+
+    /* Ignore if this is already the active layer */
+    if (layer == view->active_layer) return;
+
+    if (layer)
+    {
+    /* Make sure this layer is in the list */
+    if (g_list_find(view->layers, (gpointer)layer) == NULL)
+    {
+        CPLDebug( "OpenEV",
+                      "gv_view_area_set_active_layer(): layer %s not in view",
+              gtk_type_name(GTK_OBJECT_TYPE(layer)));
+        return;
+    }
+    }
+
+    view->active_layer = layer;
+
+    gtk_signal_emit(GTK_OBJECT(view), view_area_signals[ACTIVE_CHANGED]);
+}
+
+GtkObject *
+gv_view_area_get_layer_of_type(GvViewArea *view, GtkType layer_type,
+                               gint read_only_ok )
+{
+    GList *list;
+
+    /* First check the active layer */
+    if(view->active_layer &&
+       gtk_type_is_a(GTK_OBJECT_TYPE(view->active_layer), layer_type)
+       && (read_only_ok || !gv_data_is_read_only(GV_DATA(view->active_layer))))
+    {
+    return view->active_layer;
+    }
+
+    /* Next move through the list in order */
+    list = view->layers;
+    while (list)
+    {
+    GvLayer *layer = (GvLayer*)list->data;
+    if( gtk_type_is_a(GTK_OBJECT_TYPE(layer), layer_type)
+            && (read_only_ok || !gv_data_is_read_only(GV_DATA(layer))) )
+    {
+        return GTK_OBJECT(layer);
+    }
+    list = g_list_next(list);
+    }
+    return NULL;
+}
+
+GtkObject *
+gv_view_area_get_named_layer(GvViewArea *view, const char *name)
+{
+    GList *list;
+
+    list = view->layers;
+    while (list)
+    {
+    GvLayer *layer = (GvLayer*)list->data;
+    if (GV_DATA(layer)->name && strcmp(GV_DATA(layer)->name, name) == 0)
+    {
+        return GTK_OBJECT(layer);
+    }
+    list = g_list_next(list);
+    }
+    return NULL;
+}
+
+GList *
+gv_view_area_list_layers(GvViewArea *view)
+{
+    return g_list_copy(view->layers);
+}
+
+void
+gv_view_area_swap_layers(GvViewArea *view, gint index_a, gint index_b)
+{
+    gpointer *layer_a, *layer_b;
+
+    g_return_if_fail(index_a != index_b);
+
+    /* Put indicies in ascending order */
+    if (index_a > index_b)
+    {
+    gint tmp = index_b;
+    index_b = index_a;
+    index_a = tmp;
+    }
+
+    layer_a = g_list_nth_data(view->layers, index_a);
+    layer_b = g_list_nth_data(view->layers, index_b);
+    g_return_if_fail(layer_a && layer_b);
+
+    /* Perform swap */
+    view->layers = g_list_remove(view->layers, layer_a);
+    view->layers = g_list_remove(view->layers, layer_b);
+    view->layers = g_list_insert(view->layers, layer_b, index_a);
+    view->layers = g_list_insert(view->layers, layer_a, index_b);
+
+    gv_view_area_queue_draw(view);
+}
+
+GdkPixmap *
+gv_view_area_create_thumbnail(GvViewArea *view, GtkObject *layerobj,
+                  gint width, gint height)
+{
+#ifdef WIN32
+    return NULL;
+#else
+    GvLayer *layer;
+    GdkPixmap *pixmap;
+    GdkVisual *visual;
+    GdkGLContext *glcontext;
+    GdkGLPixmap *glpixmap;
+    GvRect extents;
+    gvgeocoord aspect;
+    gint visible;
+
+    if( !GTK_WIDGET_REALIZED(view) )
+        return NULL;
+
+    g_return_val_if_fail(GV_IS_LAYER(layerobj), NULL);
+
+    layer = GV_LAYER(layerobj);
+
+    visual = gtk_widget_get_visual(GTK_WIDGET(view));
+    pixmap = gdk_pixmap_new(NULL, width, height, visual->depth);
+    glcontext = GTK_GL_AREA(view)->glcontext;
+    glpixmap = gdk_gl_pixmap_new(visual, pixmap);
+
+    if (!gdk_gl_pixmap_make_current(glpixmap, glcontext))
+    {
+    CPLDebug( "OpenEV",
+                  "gv_view_area_create_thumbnail(): offscreen render failed.");
+    gdk_gl_pixmap_unref(glpixmap);
+    gdk_pixmap_unref(pixmap);
+    return NULL;
+    }
+
+    /* Add 5% to extents in each direction to get a nice border */
+    gv_layer_extents(layer, &extents);
+    extents.x -= extents.width * 0.05;
+    extents.y -= extents.height * 0.05;
+    extents.width *= 1.1;
+    extents.height *= 1.1;
+
+    /* Fix aspect ratio */
+    aspect = (gvgeocoord)width / (gvgeocoord)height;
+    if( (extents.width / extents.height) > aspect )
+    {
+    gvgeocoord newheight = extents.width / aspect;
+    extents.y -= (newheight - extents.height) / 2.0;
+    extents.height = newheight;
+    }
+    else
+    {
+    gvgeocoord newwidth = extents.height * aspect;
+    extents.x -= (newwidth - extents.width) / 2.0;
+    extents.width = newwidth;
+    }
+
+    /* glpixmap is in the front left buffer */
+    glDrawBuffer(GL_FRONT_LEFT);
+    glViewport(0, 0, width, height);
+
+    /* Set background to black */
+    glClearColor(view->background[0], view->background[1], view->background[2],
+                 view->background[3] );
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    /* Make sure there is something to draw */
+    if (extents.width > 0 || extents.height > 0)
+    {
+    /* Temporarily make visible, and turn on presentation mode */
+    visible = gv_layer_set_visible_temp(layer, TRUE);
+    gv_layer_set_presentation(layer, TRUE);
+
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+        if( view->state.flip_y < 0 )
+        {
+            gluOrtho2D(extents.x, extents.x + extents.width,
+                       extents.y + extents.height, extents.y );
+        }
+        else
+        {
+            gluOrtho2D(extents.x, extents.x + extents.width,
+                       extents.y, extents.y + extents.height);
+        }
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+
+    gv_layer_draw(layer, view);
+
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+
+    /* Turn off presentation mode and restore visibility */
+    gv_layer_set_visible_temp(layer, visible);
+    gv_layer_set_presentation(layer, FALSE);
+    }
+    glFinish();
+
+    /* Restore the view as the current context before destroying
+       the glpixmap */
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view)))
+    {
+    g_warning("gv_view_area_create_thumbnail(): view context restore failed.");
+    }
+    glViewport(0, 0, view->state.shape_x, view->state.shape_y);
+    glDrawBuffer(GL_BACK);
+
+    gdk_gl_pixmap_unref(glpixmap);
+    return pixmap;
+#endif
+}
+
+gint
+gv_view_area_render_to_func(GvViewArea *view,
+                            gint width, gint height,
+                            gint (*scanline_handler)( void *, void * ),
+                            void *cb_data )
+{
+    GvRect extents;
+    gvgeocoord aspect;
+    guint8 *scanline;
+    gvgeocoord xmin, xmax, ymin, ymax;
+    gint line, errcode = 0;
+    gint x_chunk, x_chunk_size, y_chunk, y_chunk_size;
+    GList *list;
+    guint8 *row_of_chunks;
+    int     direct_render, save_exact;
+
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view))) return -1;
+
+    direct_render = (width == view->state.shape_x)
+                 && (height == view->state.shape_y);
+
+    save_exact = view->exact_render;
+    view->exact_render = TRUE;
+
+    x_chunk_size = view->state.shape_x;
+    y_chunk_size = view->state.shape_y;
+
+    gv_view_area_get_extents(view, &xmin, &ymin, &xmax, &ymax );
+    extents.x = xmin;
+    extents.width = xmax - xmin;
+    extents.y = ymin;
+    extents.height = ymax - ymin;
+
+    /* Fix aspect ratio */
+    aspect = (gvgeocoord)width / (gvgeocoord)height;
+    if ( (extents.width / extents.height) > aspect )
+    {
+    gvgeocoord newheight = extents.width / aspect;
+    extents.y -= (newheight - extents.height) / 2.0;
+    extents.height = newheight;
+    }
+    else
+    {
+    gvgeocoord newwidth = extents.height * aspect;
+    extents.x -= (newwidth - extents.width) / 2.0;
+    extents.width = newwidth;
+    }
+
+    /* allocate a buffer large enough to hold one RGB row of chunks */
+    row_of_chunks = g_malloc(width * y_chunk_size * 3);
+    if( row_of_chunks == NULL )
+    {
+        g_warning( "out of memory in gv_view_area_render_to_func" );
+        view->exact_render = save_exact;
+        return -1;
+    }
+    memset( row_of_chunks, 0, width*y_chunk_size*3 );
+
+    /* glpixmap is in the front left buffer */
+    glDrawBuffer(GL_BACK);
+    glReadBuffer(GL_BACK);
+
+    /* Make sure there is something to draw */
+    if (extents.width == 0 || extents.height == 0)
+    {
+        view->exact_render = save_exact;
+        return -1;
+    }
+
+    for( y_chunk=0; y_chunk < height && errcode == 0; y_chunk += y_chunk_size)
+    {
+        GvRect chunk_extents;
+
+        chunk_extents.height =
+            extents.height * (y_chunk_size / (double) height);
+        if( view->state.flip_y < 0 )
+        {
+            chunk_extents.y = extents.y
+                + extents.height * (y_chunk / (double) height);
+        }
+        else
+        {
+            chunk_extents.y = extents.y + extents.height
+                - extents.height * ((y_chunk+y_chunk_size) / (double) height);
+        }
+
+        for( x_chunk = 0;
+             x_chunk < width && errcode == 0;
+             x_chunk += x_chunk_size )
+        {
+            chunk_extents.width =
+                extents.width * (x_chunk_size / (double) width);
+            chunk_extents.x = extents.x
+                + extents.width * (x_chunk / (double) width);
+
+            /* Set background to black */
+            glClearColor(view->background[0], view->background[1],
+                         view->background[2], view->background[3] );
+            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+            render_counter++;
+
+            if( !direct_render )
+            {
+                glMatrixMode(GL_PROJECTION);
+                glPushMatrix();
+                glLoadIdentity();
+                if( view->state.flip_y < 0 )
+                {
+                    gluOrtho2D(chunk_extents.x,
+                               chunk_extents.x + chunk_extents.width,
+                               chunk_extents.y + chunk_extents.height,
+                               chunk_extents.y );
+                }
+                else
+                {
+                    gluOrtho2D(chunk_extents.x,
+                               chunk_extents.x + chunk_extents.width,
+                               chunk_extents.y,
+                               chunk_extents.y + chunk_extents.height);
+                }
+
+                glMatrixMode(GL_MODELVIEW);
+                glLoadIdentity();
+            }
+
+            for( list = view->layers; list != NULL; list = g_list_next(list) )
+            {
+                GvLayer  *layer = GV_LAYER( list->data );
+
+                gv_layer_set_presentation(layer, TRUE);
+                gv_layer_draw(layer, view);
+                gv_layer_set_presentation(layer, FALSE);
+            }
+
+            if( !direct_render )
+            {
+                glMatrixMode(GL_PROJECTION);
+                glPopMatrix();
+                glMatrixMode(GL_MODELVIEW);
+            }
+
+            glFinish();
+
+            for( line = 0;
+                 line < y_chunk_size && errcode == 0
+                     && (line + y_chunk) < height;
+                 line++ )
+            {
+                scanline = row_of_chunks +
+                    x_chunk * 3
+                    + line * width * 3;
+
+                scanline[0] = 253;
+                scanline[1] = 101;
+                glReadPixels( 0, y_chunk_size - line - 1,
+                              MIN(x_chunk_size,width-x_chunk), 1,
+                              GL_RGB, GL_UNSIGNED_BYTE, scanline );
+
+                if( scanline[0] == 253 && scanline[1] == 101 )
+                {
+                    CPLDebug( "OpenEV",
+                              "glReadPixels() appears to have failed." );
+                    errcode = 1;
+                }
+            }
+        }
+
+        for( line = 0;
+             line < y_chunk_size && errcode == 0
+                 && (line + y_chunk) < height;
+             line++ )
+        {
+            scanline = row_of_chunks + line * width * 3;
+            errcode =  scanline_handler( cb_data, scanline );
+        }
+    }
+
+    g_free( row_of_chunks );
+
+    view->exact_render = save_exact;
+
+    return errcode;
+}
+
+GPtrArray *gv_view_area_get_fontnames( GvViewArea *view )
+
+{
+    GPtrArray   *ret_list;
+    int     font;
+
+    ret_list = g_ptr_array_new();
+
+    for( font = 0; bmfontmap[font].gvname != NULL; font++ )
+        g_ptr_array_add( ret_list, bmfontmap[font].gvname );
+
+    return ret_list;
+}
+
+gint
+gv_view_area_bmfont_load(GvViewArea *view, gchar *name)
+{
+    gint     font;
+    GvBMFontInfo new_finfo;
+
+    /* translate aliases */
+    for (font=0; bmfontmap[font].gvname != NULL; font++)
+    {
+    if (strcmp(name, bmfontmap[font].gvname) == 0)
+    {
+            name = bmfontmap[font].gdkname;
+        break;
+    }
+    }
+
+    /* Do we already have this font loaded? */
+    for (font=0; font < view->bmfonts->len; font++ )
+    {
+        GvBMFontInfo    *finfo;
+
+        finfo = &(g_array_index( view->bmfonts, GvBMFontInfo, font ));
+    if (strcmp(name, finfo->name) == 0)
+            return font;
+    }
+
+    /* Try to load the font */
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view)))
+    {
+        g_warning("gv_view_area_bmfont_load(): can't make view area current");
+        return -1;
+    }
+
+    new_finfo.listbase = glGenLists(96);
+    if (new_finfo.listbase == 0)
+    {
+        g_warning("gv_view_area_bmfont_load(): font allocation failed");
+        return -1;
+    }
+
+    new_finfo.gdkfont = gdk_font_load(name);
+    if (!new_finfo.gdkfont)
+    {
+        g_warning("gv_view_area_bmfont_load(): gdk font load failed: %s",
+                  name );
+        return -1;
+    }
+
+    gdk_gl_use_gdk_font(new_finfo.gdkfont, 0, 127, new_finfo.listbase);
+
+
+    new_finfo.name = g_strdup(name);
+    g_array_append_val( view->bmfonts, new_finfo);
+
+    return view->bmfonts->len-1;
+}
+
+GvBMFontInfo *
+gv_view_area_bmfont_get_info(GvViewArea *view, gint font)
+{
+    GvBMFontInfo    *finfo;
+
+    if (font < 0 || font >= view->bmfonts->len )
+        return NULL;
+
+    finfo = &(g_array_index( view->bmfonts, GvBMFontInfo, font ));
+    return finfo;
+}
+
+void
+gv_view_area_bmfont_draw(GvViewArea *view, gint font, gvgeocoord x, gvgeocoord y,
+                         gchar *text, int force_simple )
+{
+    GvBMFontInfo    *finfo;
+
+    if (font < 0 || font >= view->bmfonts->len )
+        return;
+
+    finfo = &(g_array_index( view->bmfonts, GvBMFontInfo, font ));
+
+    /*
+     * In 3D it would be too hard to determine whats in/out of view, so
+     * we just do the simple thing.  This will result in strings with an
+     * origin out of the view frustrum not being drawn, but that's life.
+     */
+    if( view->flag_3d || force_simple )
+    {
+        glRasterPos2f(x, y);
+    }
+    /*
+     * Ensure we initially position the raster position within the
+     * view frustrum, then offset to our desired location.
+     */
+    else
+    {
+        gvgeocoord  vx, vy, vx_screen, vy_screen, tx_screen, ty_screen;
+
+        vx_screen = view->state.shape_x/2;
+        vy_screen = view->state.shape_y/2;
+
+        gv_view_area_map_pointer( view, vx_screen, vy_screen,
+                                  &vx, &vy );
+        gv_view_area_inverse_map_pointer( view, x, y, &tx_screen, &ty_screen );
+
+        /* position in frustrum */
+        glRasterPos2f(vx, vy);
+
+        /* shift to desired location. */
+        glBitmap( 0, 0, 0, 0,
+                  (int) -(vx_screen - floor(tx_screen)),
+                  (int) (vy_screen - ceil(ty_screen)),
+                  NULL );
+    }
+
+    glPushAttrib(GL_LIST_BASE);
+    glListBase(finfo->listbase);
+    glCallLists(strlen(text), GL_UNSIGNED_BYTE, (GLubyte*)text);
+    glPopAttrib();
+}
+
+void gv_view_area_set_background_color( GvViewArea *view, GvColor new_color )
+
+{
+    if( new_color[0] != view->background[0]
+        || new_color[1] != view->background[1]
+        || new_color[2] != view->background[2]
+        || new_color[3] != view->background[3] )
+    {
+        gv_color_copy( view->background, new_color );
+        gv_view_area_state_changed(view);
+    }
+}
+
+void gv_view_area_get_background_color( GvViewArea *view, GvColor color )
+
+{
+    color[0] = view->background[0];
+    color[1] = view->background[1];
+    color[2] = view->background[2];
+    color[3] = view->background[3];
+}
+
+/*****************************************************************/
+
+static gint
+gv_view_area_reset_projection(GvViewArea *view, gvgeocoord width, gvgeocoord height)
+{
+    if( !gtk_gl_area_make_current(GTK_GL_AREA(view)) )
+        return 1;
+
+    glViewport(0, 0, width, height);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    if (! view->flag_3d)
+    {
+        /* Assumes clipping plane of [-1.0, 1.0]
+        gluOrtho2D(-width/2.0, width/2.0, -height/2.0, height/2.0); */
+        glOrtho(-width/2.0, width/2.0, -height/2.0, height/2.0, -1000.0f, 1000.0f);
+    } else {
+        gluPerspective(90.0f,(GLgeocoord)width/(GLgeocoord)height,10.0f,3000000.0f);
+    }
+    glMatrixMode(GL_MODELVIEW);
+
+    return 0;
+
+}
+static gint
+gv_view_area_configure(GtkWidget *widget, GdkEventConfigure *event)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+    int w, h;
+
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view))) return 0;
+
+    CPLDebug( "OpenEV", "VENDOR = %s", glGetString( GL_VENDOR ) );
+    CPLDebug( "OpenEV", "RENDERER = %s", glGetString( GL_RENDERER ) );
+    CPLDebug( "OpenEV", "VERSION = %s", glGetString( GL_VERSION ) );
+    CPLDebug( "OpenEV", "EXTENSIONS = %s", glGetString( GL_EXTENSIONS ) );
+
+    w = event->width;
+    h = event->height;
+
+    gv_view_area_reset_projection(view, (gvgeocoord)w, (gvgeocoord)h);
+
+    view->state.shape_x = (gvgeocoord)w;
+    view->state.shape_y = (gvgeocoord)h;
+
+    gv_view_area_state_changed( view );
+
+    return 0;
+}
+
+gint
+gv_view_area_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+    GList *list;
+    GvLayer *layer;
+    GTimer *timer;
+
+
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view))) return 0;
+
+    /*
+    ** If the display state hasn't changed in any way, then just refresh from
+    ** our display buffer.  Otherwise we will actually need to rerender with
+    ** OpenGL.  We avoid rerendering for interactivity on software rendered
+    ** systems.
+    */
+
+    if( (!view->display_dirty)
+        && ((strstr(glGetString(GL_RENDERER), "Mesa Windows") != NULL) ||
+        (strstr(glGetString(GL_RENDERER),"Mesa X11") != NULL) ||
+        (strstr(glGetString(GL_RENDERER), "Mesa GLX Indirect") != NULL)))
+    {
+
+        gtk_gl_area_swap_buffers(GTK_GL_AREA(view));
+    gtk_signal_emit(GTK_OBJECT(view), view_area_signals[GLCURSOR]);
+        return 0;
+    }
+
+    view->display_dirty = FALSE;
+
+    render_counter++;
+
+    gv_manager_set_busy( gv_get_manager(), TRUE );
+
+    timer = g_timer_new();
+    g_timer_start( timer );
+
+    glDisable(GL_DITHER);
+    glShadeModel(GL_FLAT);
+    glClearColor(view->background[0], view->background[1],
+                 view->background[2], view->background[3] );
+
+    /* Do we need these??? */
+    if (view->flag_3d)
+    {
+#ifdef notdef
+        glEnable(GL_CULL_FACE);
+#endif
+        glEnable(GL_NORMALIZE);
+        glEnable(GL_DEPTH_TEST);
+        glDepthFunc(GL_LEQUAL);
+    }
+    else
+    {
+        glDisable(GL_NORMALIZE);
+        glDisable(GL_DEPTH_TEST);
+    }
+
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glLoadIdentity();
+
+    if (view->flag_3d)
+    {
+        double     min_range, max_range;
+        vec3_t eye_pos, eye_dir, flip_eye_pos;
+        int   debug3d = 0;
+
+        if( gv_manager_get_preference(gv_get_manager(),"DEBUG3D") != NULL )
+            debug3d =
+                atoi(gv_manager_get_preference(gv_get_manager(),"DEBUG3D"));
+
+        eye_pos[0] = view->state.eye_pos[0];
+        eye_pos[1] = view->state.eye_pos[1];
+        eye_pos[2] = view->state.eye_pos[2];
+
+        eye_dir[0] = view->state.eye_dir[0];
+        eye_dir[1] = view->state.eye_dir[1];
+        eye_dir[2] = view->state.eye_dir[2];
+
+
+        /* Compute the near and far clipping planes - taking into account any image flips */
+        gv_view_area_get_volume( view, NULL );
+
+        flip_eye_pos[0] = view->state.flip_x * eye_pos[0];
+        flip_eye_pos[1] = view->state.flip_y * eye_pos[1];
+        flip_eye_pos[2] = eye_pos[2];
+
+        vec_near_far_range( flip_eye_pos, view->view_volume,
+                            &min_range, &max_range);
+
+        if( min_range < max_range / 500.0 || min_range >= max_range )
+            min_range = max_range / 500.0;
+
+        max_range = max_range * 1.2;
+        min_range = min_range * 0.5;
+
+        glMatrixMode(GL_PROJECTION);
+        glLoadIdentity();
+        gluPerspective(90.0f,
+                       (gvgeocoord)view->state.shape_x/(gvgeocoord)view->state.shape_y,
+                       min_range*0.5, max_range );
+        glMatrixMode(GL_MODELVIEW);
+
+        if( debug3d )
+        {
+            printf( "================ Redraw Start ===================\n" );
+            printf( "Clipping planes: near=%g, far=%g\n",
+                    min_range, max_range );
+        }
+
+        /* Set view orientation */
+        gluLookAt(eye_pos[0], eye_pos[1], eye_pos[2],
+              eye_pos[0] + eye_dir[0],
+                  eye_pos[1] + eye_dir[1],
+                  eye_pos[2] + eye_dir[2],
+                  0.0, 0.0, 1.0);
+
+        glScale(1.0 * view->state.flip_x, 1.0 * view->state.flip_y, view->state.z_scale);
+
+        if( debug3d )
+        {
+            printf( "eye=(%g,%g,%g)  eye_dir=(%g,%g,%g)\n",
+                    eye_pos[0], eye_pos[1], eye_pos[2],
+                    eye_dir[0], eye_dir[1], eye_dir[2] );
+        }
+
+    } else {
+        glRotate(view->state.rot, 0.0, 0.0, 1.0);
+
+        glScale(view->state.linear_zoom * view->state.flip_x,
+                 view->state.linear_zoom * view->state.flip_y, 1.0);
+
+        glTranslate(view->state.tx, view->state.ty, 0.0);
+    }
+
+    list = view->layers;
+    while (list)
+    {
+    layer = (GvLayer*)list->data;
+    gv_layer_draw(layer, view);
+    list = g_list_next(list);
+    }
+
+    /* Give other interested objects a chance to add to the drawing */
+    gtk_signal_emit(GTK_OBJECT(view), view_area_signals[GLDRAW]);
+
+    /* draw banded zoom area */
+    if (view->dragging_mode)
+    {
+        gvgeocoord x[4], y[4];
+
+        gv_view_area_map_pointer( view,
+                                  view->state.mpos_x, view->state.mpos_y,
+                                  x+0, y+0 );
+        gv_view_area_map_pointer( view,
+                                  view->last_mpos_x, view->state.mpos_y,
+                                  x+1, y+1 );
+        gv_view_area_map_pointer( view,
+                                  view->last_mpos_x, view->last_mpos_y,
+                                  x+2, y+2 );
+        gv_view_area_map_pointer( view,
+                                  view->state.mpos_x, view->last_mpos_y,
+                                  x+3, y+3 );
+
+    glColor3f(1.0, 0.5, 0.0);
+    glBegin(GL_LINE_LOOP);
+    glVertex2(x[0], y[0]);
+    glVertex2(x[1], y[1]);
+    glVertex2(x[2], y[2]);
+    glVertex2(x[3], y[3]);
+    glEnd();
+    }
+
+    gtk_gl_area_swap_buffers(GTK_GL_AREA(view));
+    /* Don't XOR last ghost cursor position, since view has been redrawn */
+    /* (only has effect in logical cursor mode) */
+    view->last_valid=FALSE;
+    gtk_signal_emit(GTK_OBJECT(view),view_area_signals[GLCURSOR]);
+
+    glFinish();
+
+    /*
+     * Capture time this draw took.
+     */
+    view->last_draw_time = g_timer_elapsed(timer,NULL);
+    g_timer_stop( timer );
+    g_timer_destroy( timer );
+
+    /*
+     * Setup timer so we know when we should do the next redraw, even if
+     * all the idle work isn't done yet.
+     */
+    if( view->redraw_timer != NULL )
+        g_timer_destroy( view->redraw_timer );
+
+    view->redraw_timer = g_timer_new();
+    g_timer_start( view->redraw_timer );
+
+    return 0;
+}
+
+static void
+gv_view_area_realize(GtkWidget *widget)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+    GtkGLAreaClass *parent_class;
+    GList *list;
+    GvLayer *layer;
+    const char *auto_fit_flag;
+
+    /* Call parent class function first */
+    parent_class = gtk_type_class(gtk_gl_area_get_type());
+    GTK_WIDGET_CLASS(parent_class)->realize(GTK_WIDGET(view));
+
+    g_return_if_fail(GTK_WIDGET_REALIZED(GTK_WIDGET(view)));
+
+    /* Make gl area current so layers can create gl handles */
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view))) return;
+
+    /* Call the one time setup function for all layers */
+    list = view->layers;
+    while (list)
+    {
+    layer = (GvLayer*)list->data;
+    gv_layer_setup(layer, view);
+    list = g_list_next(list);
+    }
+
+    /* Set up the inital view transformation using the first layer */
+    auto_fit_flag = gv_properties_get(&(view->properties),
+                                      "_supress_realize_auto_fit");
+    if (view->layers && (auto_fit_flag == NULL || EQUAL(auto_fit_flag,"off")))
+    {
+    layer = (GvLayer*)view->layers->data;
+    gv_view_area_fit_layer(view, layer);
+    }
+}
+
+static void
+gv_view_area_unrealize(GtkWidget *widget)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+    GtkGLAreaClass *parent_class;
+    GList *list;
+    gint font;
+
+    /* Make gl area current so layers can clean up gl handles */
+    if (!gtk_gl_area_make_current(GTK_GL_AREA(view)))
+    {
+    g_warning("gv_view_area_unrealize(): can't make view area current");
+    return;
+    }
+
+    /* Teardown all layers */
+    view->active_layer = NULL;
+    list = view->layers;
+    while (list)
+    {
+    GvLayer *layer = (GvLayer*)list->data;
+    gv_layer_teardown(layer, view);
+    list = g_list_next(list);
+    }
+
+    /* Destroy bmfont listbase array */
+    for( font=0; font < view->bmfonts->len; font++ )
+    {
+        GvBMFontInfo    *finfo;
+
+        finfo = &(g_array_index( view->bmfonts, GvBMFontInfo, font ));
+
+        if( finfo->gdkfont != NULL )
+            gdk_font_unref(finfo->gdkfont);
+
+        g_free( finfo->name );
+        glDeleteLists(finfo->listbase, 96);
+    }
+    g_array_free( view->bmfonts, TRUE );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gtk_gl_area_get_type());
+    GTK_WIDGET_CLASS(parent_class)->unrealize(GTK_WIDGET(view));
+}
+
+static void
+gv_view_area_fit_layer(GvViewArea *view, GvLayer *layer)
+{
+    GvRect rect;
+
+    /* Set the view transformation to show the full extents of the layer */
+
+    /* Make sure the view area is realized so the state.shape has been
+       set (a configure event has been recieved) */
+    if (!GTK_WIDGET_REALIZED(GTK_WIDGET(view))) return;
+
+    /*
+     * Set the view projection.
+     */
+    if( view->projection != NULL )
+    {
+        g_free( view->projection );
+        view->projection = NULL;
+    }
+
+    if( gv_data_get_projection(GV_DATA(layer)) != NULL )
+    {
+        view->projection = g_strdup(gv_data_get_projection(GV_DATA(layer)));
+    }
+
+    /*
+     * Get the layer extents.
+     */
+    gv_layer_extents(layer, &rect);
+
+    /*
+     * If this is an image, we want to try and orient the image with
+     * pixel (0,0) in the top left if possible.
+     */
+    if( GV_IS_RASTER_LAYER(layer) )
+    {
+        GvRaster     *raster;
+
+        raster = GV_RASTER_LAYER(layer)->prototype_data;
+
+        if( GV_RASTER_LAYER(layer)->mesh_is_raw
+            || (raster->gcp_count == 0
+                && raster->geotransform[2] == 0.0
+                && raster->geotransform[4] == 0.0
+                && raster->geotransform[5] > 0.0) )
+        {
+            rect.y = rect.y + rect.height;
+            rect.height *= -1;
+        }
+    }
+
+    gv_view_area_fit_extents(view, rect.x, rect.y, rect.width, rect.height );
+
+    /* Setup 3D specific extent information */
+    gv_view_area_fit_extents_3D(view,
+                                rect.x, rect.y, 0.0,
+                                rect.width, rect.height,
+                                (rect.width+rect.height)*0.5 );
+}
+
+void
+gv_view_area_fit_all_layers(GvViewArea *view)
+
+{
+    GvRect rect;
+    double volume[6];
+
+    /*
+     * Set 2D view.
+     */
+    gv_view_area_get_world_extents( view, &rect );
+    gv_view_area_fit_extents(view, rect.x, rect.y, rect.width, rect.height );
+
+    /*
+     * Also do a default 3D view.
+     */
+    gv_view_area_get_volume( view, volume );
+    gv_view_area_fit_extents_3D( view,
+                                 volume[0],
+                                 volume[2],
+                                 volume[4],
+                                 volume[1] - volume[0],
+                                 volume[3] - volume[2],
+                                 volume[5] - volume[4] );
+}
+
+void
+gv_view_area_get_world_extents(GvViewArea *view, GvRect *extents )
+
+{
+    GvRect rect;
+    int    flip_y = FALSE, first = TRUE, first_raster = TRUE;
+    GList *list;
+
+    rect.x = 0;
+    rect.y = 0;
+    rect.width = 0;
+    rect.height = 0;
+
+    /* Make sure the view area is realized so the state.shape has been
+       set (a configure event has been recieved) */
+    if (!GTK_WIDGET_REALIZED(GTK_WIDGET(view)))
+    {
+        *extents = rect;
+        return;
+    }
+
+    /*
+     * Get the layers extents.
+     */
+    list = view->layers;
+    while (list)
+    {
+    GvLayer *layer = (GvLayer*)list->data;
+        GvRect  layer_rect;
+
+        gv_layer_extents(layer, &layer_rect);
+
+        if( layer_rect.width == 0 && layer_rect.height == 0 )
+            /* do nothing */;
+
+        else if( first )
+        {
+            rect = layer_rect;
+            first = FALSE;
+
+            if( first_raster && GV_IS_RASTER_LAYER(layer) )
+            {
+                GvRasterLayer *rlayer = GV_RASTER_LAYER(layer);
+                GvRaster     *raster = rlayer->prototype_data;
+
+                if( rlayer->mesh_is_raw )
+                {
+                    flip_y = TRUE;
+                }
+                else if( raster->gcp_count == 0
+                         && raster->geotransform[2] == 0.0
+                         && raster->geotransform[4] == 0.0 )
+                {
+                    if( raster->geotransform[5] > 0.0 )
+                    {
+                        flip_y = TRUE;
+                    }
+                }
+
+                first_raster = FALSE;
+            }
+        }
+        else
+        {
+            gvgeocoord     new_x, new_y;
+
+            new_x = MIN(rect.x,layer_rect.x);
+            rect.width = MAX(rect.x+rect.width,
+                             layer_rect.x+layer_rect.width) - new_x;
+            new_y = MIN(rect.y,layer_rect.y);
+            rect.height = MAX(rect.y+rect.height,
+                              layer_rect.y+layer_rect.height) - new_y;
+            rect.x = new_x;
+            rect.y = new_y;
+        }
+
+    list = g_list_next(list);
+    }
+
+    /*
+     * If we found a "raw" raster layer, flip the coordinate system.
+     */
+    if( flip_y )
+    {
+        rect.y = rect.y + rect.height;
+        rect.height *= -1;
+    }
+
+    *extents = rect;
+}
+
+void
+gv_view_area_fit_extents(GvViewArea *view,
+                         gvgeocoord llx,   gvgeocoord lly,
+                         gvgeocoord width, gvgeocoord height )
+{
+    /* Make sure the view area is realized so the state.shape has been
+       set (a configure event has been recieved) */
+    if (!GTK_WIDGET_REALIZED(GTK_WIDGET(view))) return;
+
+    if (width == 0.0 || height == 0.0)
+    {
+    view->state.linear_zoom = 1.0;
+    }
+    else
+    {
+    gvgeocoord zoomx, zoomy;
+
+    zoomx = view->state.shape_x / width;
+    zoomy = view->state.shape_y / ABS(height);
+
+    view->state.linear_zoom = MIN(zoomx, zoomy);
+    }
+
+    view->state.rot = 0.0;
+    view->state.tx = -(width/2.0 + llx);
+    view->state.ty = -(height/2.0 + lly);
+    view->state.zoom = LOG2(view->state.linear_zoom);
+
+    if( height < 0.0 )
+    {
+        view->state.flip_y = -1.0;
+    }
+
+    gv_view_area_state_changed(view);
+}
+
+void
+gv_view_area_fit_extents_3D(GvViewArea *view,
+                            gvgeocoord llx, gvgeocoord lly, gvgeocoord minz,
+                            gvgeocoord width, gvgeocoord height, gvgeocoord delta_z )
+
+{
+    vec3_t  new_eye_pos, new_eye_dir;
+    double  eye_az, eye_el, linear_measure;
+
+    /* Make sure the view area is realized so the state.shape has been
+       set (a configure event has been recieved) */
+    if (!GTK_WIDGET_REALIZED(GTK_WIDGET(view))) return;
+
+    if (width == 0.0 || height == 0.0) return;
+
+    linear_measure = MAX(MAX(width,height),delta_z);
+
+    new_eye_pos[0] = llx + width * 0.5;
+    new_eye_pos[1] = lly - linear_measure * 0.5;
+    new_eye_pos[2] = minz + linear_measure * 0.5;
+
+    eye_az = 90.0;   /* rotation ? */
+    eye_el = -45.0;  /* tilt  0 --> extreem  -90 --> none*/
+    vec_point(new_eye_dir, eye_az, eye_el);
+
+    gv_view_area_set_3d_view( view, new_eye_pos, new_eye_dir );
+}
+
+void
+gv_view_area_get_extents(GvViewArea *view,
+                         gvgeocoord *xmin, gvgeocoord *ymin,
+                         gvgeocoord *xmax, gvgeocoord *ymax )
+
+{
+    gvgeocoord       x[4], y[4];
+
+    gv_view_area_map_pointer( view, 0, 0,
+                              x+0, y+0 );
+    gv_view_area_map_pointer( view, 0, view->state.shape_y,
+                              x+1, y+1 );
+    gv_view_area_map_pointer( view, view->state.shape_x, 0,
+                              x+2, y+2 );
+    gv_view_area_map_pointer( view, view->state.shape_x, view->state.shape_y,
+                              x+3, y+3 );
+
+    *xmin = MIN(MIN(x[0],x[1]),MIN(x[2],x[3]));
+    *ymin = MIN(MIN(y[0],y[1]),MIN(y[2],y[3]));
+    *xmax = MAX(MAX(x[0],x[1]),MAX(x[2],x[3]));
+    *ymax = MAX(MAX(y[0],y[1]),MAX(y[2],y[3]));
+}
+
+void
+gv_view_area_get_volume( GvViewArea *view, double *volume )
+
+{
+    GvRect rect;
+    int    first = TRUE;
+    GList *list;
+
+    if( view->volume_current )
+    {
+        if( volume != NULL )
+            memcpy( volume, view->view_volume, sizeof(double) * 6 );
+        return;
+    }
+
+    view->view_volume[0] = 0.0;
+    view->view_volume[1] = 1000.0;
+    view->view_volume[2] = 0.0;
+    view->view_volume[3] = 1000.0;
+    view->view_volume[4] = 0.0;
+    view->view_volume[5] = 1000.0;
+    view->linear_measure = 1000.0;
+
+    /* Make sure the view area is realized so the state.shape has been
+       set (a configure event has been recieved) */
+    if (!GTK_WIDGET_REALIZED(GTK_WIDGET(view))) return;
+
+    /*
+     * Get the layers extents.
+     */
+    list = view->layers;
+    while (list)
+    {
+    GvLayer *layer = (GvLayer*)list->data;
+        GvRect  layer_rect;
+
+        gv_layer_extents(layer, &layer_rect);
+
+        if( layer_rect.width == 0 && layer_rect.height == 0 )
+        {
+            /* do nothing */;
+        }
+
+        else if( first )
+        {
+            rect = layer_rect;
+            first = FALSE;
+        }
+        else
+        {
+            gvgeocoord    new_x, new_y;
+            new_x = MIN(rect.x,layer_rect.x);
+            rect.width = MAX(rect.x+rect.width,
+                             layer_rect.x+layer_rect.width) - new_x;
+            new_y = MIN(rect.y,layer_rect.y);
+            rect.height = MAX(rect.y+rect.height,
+                              layer_rect.y+layer_rect.height) - new_y;
+            rect.x = new_x;
+            rect.y = new_y;
+        }
+
+    list = g_list_next(list);
+    }
+
+    if( !first )
+    {
+        view->view_volume[0] = rect.x;
+        view->view_volume[1] = rect.x + rect.width;
+        view->view_volume[2] = rect.y;
+        view->view_volume[3] = rect.y + rect.height;
+        view->view_volume[4] = 0.0;
+        view->view_volume[5] = (rect.width + rect.height) * 0.5;
+        view->volume_current = TRUE;
+
+        view->linear_measure = MAX(rect.width,rect.height);
+    }
+
+    if( volume != NULL )
+        memcpy( volume, view->view_volume, sizeof(double) * 6 );
+
+#ifdef notdef
+    printf( "gv_view_area_get_volume(): %g, %g, %g, %g, %g, %g\n",
+            view->view_volume[0],
+            view->view_volume[1],
+            view->view_volume[2],
+            view->view_volume[3],
+            view->view_volume[4],
+            view->view_volume[5] );
+#endif
+}
+
+static void
+gv_view_area_motion_handle_hint(GtkWidget *view, GdkEventMotion *event)
+{
+    /* If this is a hint place the event mouse position */
+    /* with the real value (round trip query) */
+
+    if (event->is_hint)
+    {
+    int x, y;
+    gtk_widget_get_pointer(view, &x, &y);
+    event->x = (gdouble)x;
+    event->y = (gdouble)y;
+    }
+}
+
+void
+motion(GvViewArea *view, gvgeocoord move, gvgeocoord strafe, gvgeocoord vert)
+{
+    vec3_t delta;
+    vec3_t new_eye_pos, new_eye_dir;
+
+    /* scaling factor for all motion, always > 0 */
+    gvgeocoord scaling_factor = MOVE_SPEED * view->state.eye_pos[2] + (0.0001 * view->linear_measure);
+
+    vec_copy( view->state.eye_dir, new_eye_dir);
+    vec_copy( view->state.eye_pos, new_eye_pos);
+
+    if (move != 0.0)
+    {
+    vec_copy(new_eye_dir, delta);
+        /* Scale movement based on height above z plane, previously scaled
+           by view->linear_measure */
+    vec_scale(delta, move * scaling_factor, delta);
+    vec_add(new_eye_pos, delta, new_eye_pos);
+    }
+
+    if (strafe != 0.0)
+    {
+    gvgeocoord norm = sqrt(new_eye_dir[0]*new_eye_dir[0]
+                          + new_eye_dir[1]*new_eye_dir[1]);
+    if (norm > 0.0)
+    {
+        delta[0] = new_eye_dir[1] / norm;
+        delta[1] = -new_eye_dir[0] / norm;
+        delta[2] = 0.0;
+        vec_scale(delta, strafe * scaling_factor ,delta);
+        vec_add(new_eye_pos, delta, new_eye_pos);
+    }
+    }
+
+    if (vert != 0.0)
+    {
+        vec3_t perp;
+        gvgeocoord perp_norm;
+        gvgeocoord norm =  sqrt(new_eye_dir[0]*new_eye_dir[0]
+                          + new_eye_dir[1]*new_eye_dir[1]
+                          + new_eye_dir[2]*new_eye_dir[2]);
+
+        /* Get a vector perpendicular to eye_dir */
+        perp[0] = - new_eye_dir[0];
+        perp[1] = - new_eye_dir[1];
+        perp[2] =  (norm/ new_eye_dir[2]) - new_eye_dir[2];
+
+        perp_norm = 1 / sqrt(perp[0]*perp[0]
+                         + perp[1]*perp[1]
+                         + perp[2]*perp[2]);
+
+        vec_scale(perp, perp_norm * vert * scaling_factor, perp);
+        vec_add(new_eye_pos, perp, new_eye_pos);
+    }
+
+    gv_view_area_set_3d_view( view, new_eye_pos, new_eye_dir );
+}
+
+static gint
+gv_view_area_motion_notify(GtkWidget *widget, GdkEventMotion *event)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+
+    if (event->state & GDK_BUTTON1_MASK && view->dragging_mode)
+    {
+        gv_view_area_queue_draw( view );
+    }
+
+    else if( view->last_button != 0
+             && (ABS(view->last_mpos_x - event->x) > DRAG_THRESHOLD
+                 || ABS(view->last_mpos_y - event->y) > DRAG_THRESHOLD )
+             && (g_get_current_time_as_double() - view->last_zoom_time) > 0.0
+             &&  !view->flag_3d)
+    {
+        view->dragging_mode = TRUE;
+        view->last_button = 0;
+
+        gv_view_area_queue_draw(view);
+        return FALSE;
+    }
+
+    /* zoom */
+    else if (event->state & GDK_BUTTON2_MASK
+             && event->state & GDK_SHIFT_MASK )
+    {
+        gvgeocoord ax, ay, bx, by, dz;
+
+        /* Translate origin to the center of the view */
+        ax = view->state.mpos_x - view->state.shape_x / 2.0;
+        ay = view->state.shape_y / 2.0 - view->state.mpos_y;
+        bx = event->x - view->state.shape_x / 2.0;
+        by = view->state.shape_y / 2.0 - event->y;
+
+        /* Put an insensitive area about the origin */
+        if (!(fabs(bx) < DEAD_ZONE && fabs(by) < DEAD_ZONE) &&
+            !(fabs(ax) < DEAD_ZONE && fabs(ay) < DEAD_ZONE))
+        {
+            /* The "delta" zoom (multiplicative) is the projection of
+               b (new pos) on a (old pos) divided by the length of a.
+               That is: (a.b)/|a|^2 */
+            if (ax*ax+ay*ay != 0.0)
+            {
+                dz = (ax*bx+ay*by) / (ax*ax+ay*ay);
+
+                /* Sanity check on dz */
+                dz = MAX(dz, 1.0e-2);
+                dz = MIN(dz, 1.0e2);
+
+                /* gv_view_area_zoom() expects a log (base 2)
+                   zoom factor */
+                gv_view_area_zoom(view, LOG2(dz));
+            }
+        }
+    }
+    /* rotate */
+    else if (event->state & GDK_SHIFT_MASK
+             && event->state & GDK_BUTTON1_MASK )
+
+    {
+        gvgeocoord ax, ay, bx, by, dr;
+
+        /* Translate origin to the center of the view */
+        ax = view->state.mpos_x - view->state.shape_x / 2.0;
+        ay = view->state.shape_y / 2.0 - view->state.mpos_y;
+        bx = event->x - view->state.shape_x / 2.0;
+        by = view->state.shape_y / 2.0 - event->y;
+
+        /* Put an insensitive area about the origin */
+        if (!(fabs(bx) < DEAD_ZONE && fabs(by) < DEAD_ZONE))
+        {
+            /* The "delta" rotation is found by taking arc sin of the
+               the normalized length of the cross product of b (new pos)
+               and a (old pos).  That is: arcsin(|axb|/(|a||b|)) */
+            dr = asin((ax*by-ay*bx) /
+                      (sqrt(ax*ax+ay*ay) * sqrt(bx*bx+by*by)));
+
+            /* gv_view_area_rotate() expects an angle in degrees */
+            gv_view_area_rotate(view, dr / DEG2RAD);
+        }
+    }
+
+    /* translate */
+    else if (event->state & GDK_CONTROL_MASK
+             && event->state & GDK_BUTTON2_MASK )
+    {
+        gv_view_area_translate(view, event->x - view->state.mpos_x,
+                               view->state.mpos_y - event->y);
+    }
+
+    /* Auto-scroll as we drag outside of window */
+    if (event->state & GDK_BUTTON1_MASK)
+    {
+    if (event->x < 0.0 || event->x > view->state.shape_x ||
+        event->y < 0.0 || event->y > view->state.shape_y)
+    {
+        /* Scroll to mouse pos */
+        gvgeocoord dx = 0, dy = 0;
+        if (event->x < 0.0)
+        dx = -event->x;
+        if (event->x > view->state.shape_x)
+        dx = view->state.shape_x - event->x;
+        if (event->y < 0.0)
+        dy = event->y;
+        if (event->y > view->state.shape_y)
+        dy = event->y - view->state.shape_y;
+        gv_view_area_translate(view, dx, dy);
+
+        /* Need to put this event back on the queue
+           so it keeps scrolling -- see gtktext.c for example */
+    }
+    }
+
+    /* Change 3D view direction */
+    if (view->flag_3d
+        && !(event->state & GDK_CONTROL_MASK)
+        && ((event->state & GDK_BUTTON1_MASK)
+            || (event->state & GDK_BUTTON2_MASK)
+            || (event->state & GDK_BUTTON3_MASK)))
+    {
+        gvgeocoord eye_az, eye_el;
+        int return_value;
+
+        return_value = inv_vec_point(view->state.eye_dir, &eye_az, &eye_el);
+
+        eye_az += (event->x - view->state.mpos_x) * AZ_SCALE;
+        eye_el += (event->y - view->state.mpos_y) * EL_SCALE;
+
+        if (eye_el < -89.9) eye_el = -89.9;
+        if (eye_el > 45.1) eye_el = 45.1;
+
+        vec_point(view->state.eye_dir, eye_az, eye_el);
+
+        gv_view_area_state_changed(view);
+
+        /* For case when trying to zoom and pan - force zoom update */
+        gv_view_area_zoompan_handler((gpointer)view);
+    }
+
+    /* Change 3D position (translate on when CTRL) */
+    if (view->flag_3d
+        && (event->state & GDK_CONTROL_MASK)
+        && ((event->state & GDK_BUTTON1_MASK) ||
+            (event->state & GDK_BUTTON2_MASK) ||
+            (event->state & GDK_BUTTON3_MASK)))
+    {
+        motion(view, 0.0, -(event->x - view->state.mpos_x) * 0.10, -(event->y - view->state.mpos_y)* 0.10);
+    }
+    view->state.mpos_x = event->x;
+    view->state.mpos_y = event->y;
+
+    return 0;
+}
+
+
+static gint
+gv_view_area_zoompan_handler(gpointer data )
+
+{
+    GvViewArea *view = (GvViewArea *) data;
+    double      time_elapsed, cur_time;
+    gvgeocoord  zoom = 0.0;
+
+    if( view->last_button == 0 )
+        return( FALSE );
+
+    cur_time = g_get_current_time_as_double();
+    time_elapsed = cur_time - view->last_zoom_time;
+    view->last_zoom_time = MAX(cur_time,view->last_zoom_time);
+    if( time_elapsed < 0 )
+    {
+        time_elapsed = 0;
+    }
+    else if( time_elapsed > 1.0 )
+        time_elapsed = 1.0;
+    else if( !view->flag_3d
+             && (ABS(view->last_mpos_x - view->state.mpos_x) > DRAG_THRESHOLD
+                 || ABS(view->last_mpos_y - view->state.mpos_y) > DRAG_THRESHOLD ))
+    {
+        view->dragging_mode = TRUE;
+        view->last_button = 0;
+
+        gv_view_area_queue_draw(view);
+        return FALSE;
+    }
+
+    if( view->last_button == 1 )
+        zoom = CONTIN_ZOOM_INC * time_elapsed;
+    else if( view->last_button == 3 )
+        zoom = -(CONTIN_ZOOM_INC * time_elapsed);
+
+    if( zoom != 0.0 )
+    {
+        gv_view_area_translate(view,
+                               view->state.shape_x/2 - view->state.mpos_x,
+                               view->state.mpos_y - view->state.shape_y/2 );
+        gv_view_area_zoom(view, zoom );
+        gv_view_area_translate(view,
+                               view->state.mpos_x - view->state.shape_x/2,
+                               view->state.shape_y/2 - view->state.mpos_y );
+    }
+
+    return TRUE;
+}
+
+static gint
+gv_view_area_3d_motion_handler(gpointer data )
+
+{
+    GvViewArea *view = (GvViewArea *) data;
+    double      time_elapsed, cur_time;
+    gvgeocoord  move = 0.0;
+
+    if( view->last_button == 0 )
+        return( FALSE );
+
+    cur_time = g_get_current_time_as_double();
+    time_elapsed = cur_time - view->last_zoom_time;
+    view->last_zoom_time = MAX(cur_time,view->last_zoom_time);
+    if( time_elapsed < 0 )
+    {
+        time_elapsed = 0;
+    }
+    else if( time_elapsed > 1.0 )
+    {
+        time_elapsed = 1.0;
+    }
+
+    if( view->last_button == 1 )
+        move = CONTIN_3DMOVE_INC * time_elapsed;
+    else if( view->last_button == 3 )
+        move = -(CONTIN_3DMOVE_INC * time_elapsed);
+
+    /* Move Forward or backwards */
+    if( move != 0.0 )
+        motion(view, move, 0.0, 0.0);
+
+    return TRUE;
+}
+
+void
+gv_view_area_zoompan_event(GvViewArea *view, GdkEventButton *event)
+
+{
+    /* note: we want this function to work properly for "zoom mode"
+       with no control, and the modeless "cntl-button" method. */
+
+    if( event->type == GDK_2BUTTON_PRESS )
+    {
+        view->dragging_mode = FALSE;
+        gv_view_area_translate(view,
+                               view->state.shape_x/2 - event->x,
+                               event->y - view->state.shape_y/2 );
+
+        view->last_button = 0;
+
+        if( event->button == 1 )
+            gv_view_area_zoom( view, ZOOM_STEP );
+        else if( event->button == 3 )
+            gv_view_area_zoom( view, -ZOOM_STEP );
+
+    }
+    else if( event->type == GDK_BUTTON_PRESS
+             && (event->button == 1 || event->button == 3) )
+    {
+        view->last_button = event->button;
+        view->last_button_time = g_get_current_time_as_double();
+        view->last_zoom_time = view->last_button_time + 0.25;
+        view->last_mpos_x = view->state.mpos_x;
+        view->last_mpos_y = view->state.mpos_y;
+
+        gv_manager_queue_task( gv_get_manager(), "zoompan-handler", 2,
+                               gv_view_area_zoompan_handler, view );
+    }
+    else if( event->type == GDK_BUTTON_PRESS
+             || event->type == GDK_BUTTON_RELEASE )
+    {
+        view->last_button = 0;
+    }
+}
+
+void
+gv_view_area_3d_move_event(GvViewArea *view, GdkEventButton *event)
+
+{
+    /* note: we want this function to work properly for "zoom mode"
+       with no control, and the modeless "cntl-button" method. */
+
+    /* Double Click */
+    if( event->type == GDK_2BUTTON_PRESS )
+    {
+        view->last_button = 0;
+
+        if( event->button == 1 )
+            motion(view, 10.0, 0.0, 0.0);
+        else if( event->button == 3 )
+            motion(view, -10.0, 0.0, 0.0);
+
+    }
+    /* Click and hold */
+    else if( event->type == GDK_BUTTON_PRESS
+             && (event->button == 1 || event->button == 3) )
+    {
+        view->last_button = event->button;
+        view->last_button_time = g_get_current_time_as_double();
+        view->last_zoom_time = view->last_button_time + 0.25;
+        view->last_mpos_x = view->state.mpos_x;
+        view->last_mpos_y = view->state.mpos_y;
+
+        gv_manager_queue_task( gv_get_manager(), "3d-motion-handler", 2,
+                               gv_view_area_3d_motion_handler, view );
+    }
+    /* Click and release */
+    else if( event->type == GDK_BUTTON_PRESS
+             || event->type == GDK_BUTTON_RELEASE )
+    {
+        view->last_button = 0;
+    }
+}
+
+static gint
+gv_view_area_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+
+    if (event->button == 4)
+    {
+        gv_view_area_zoom(view, -WHEEL_ZOOM_INC);
+    }
+    else if (event->button == 5)
+    {
+        gv_view_area_zoom(view, WHEEL_ZOOM_INC);
+    }
+
+    /* new zoom logic stuff */
+    if( (event->button == 1 || event->button == 3)
+        && (event->state & GDK_CONTROL_MASK) )
+    {
+        if ( view->flag_3d )
+        {
+            gv_view_area_3d_move_event( view, event);
+        } else {
+            gv_view_area_zoompan_event( view, event );
+        }
+    }
+    else if (view->flag_3d && (event->button == 1 || event->button == 2 || event->button == 3))
+    {
+            gv_view_area_3d_move_event( view, event);
+    }
+    else if( event->state & GDK_CONTROL_MASK )
+    {
+        if ( view->flag_3d )
+        {
+            /* Nothing? */
+        } else {
+            gv_view_area_zoompan_event( view, event );
+        }
+    }
+
+
+    view->state.mpos_x = event->x;
+    view->state.mpos_y = event->y;
+
+    return 0;
+}
+
+static gint
+gv_view_area_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+
+    view->state.mpos_x = event->x;
+    view->state.mpos_y = event->y;
+
+    if( view->dragging_mode )
+    {
+        gvgeocoord x[4], y[4], min_x, max_x, min_y, max_y;
+
+        gv_view_area_map_pointer( view,
+                                  view->state.mpos_x, view->state.mpos_y,
+                                  x+0, y+0 );
+        gv_view_area_map_pointer( view,
+                                  view->last_mpos_x, view->state.mpos_y,
+                                  x+1, y+1 );
+        gv_view_area_map_pointer( view,
+                                  view->last_mpos_x, view->last_mpos_y,
+                                  x+2, y+2 );
+        gv_view_area_map_pointer( view,
+                                  view->state.mpos_x, view->last_mpos_y,
+                                  x+3, y+3 );
+
+        min_x = MIN(MIN(x[0],x[1]),MIN(x[2],x[3]));
+        min_y = MIN(MIN(y[0],y[1]),MIN(y[2],y[3]));
+        max_x = MAX(MAX(x[0],x[1]),MAX(x[2],x[3]));
+        max_y = MAX(MAX(y[0],y[1]),MAX(y[2],y[3]));
+
+        gv_view_area_fit_extents( view, min_x, min_y,
+                                  max_x - min_x, max_y - min_y );
+
+        view->dragging_mode = FALSE;
+    }
+
+    /* new zoom logic stuff */
+    if( (event->button == 1 || event->button == 3) )
+    {
+        if ( view->flag_3d )
+        {
+            gv_view_area_3d_move_event(view, event);
+        } else {
+            gv_view_area_zoompan_event( view, event );
+        }
+    }
+
+    return 0;
+}
+
+static gint
+gv_view_area_key_press(GtkWidget *widget, GdkEventKey *event)
+{
+    GvViewArea *view = GV_VIEW_AREA(widget);
+
+    gvgeocoord move = 0.0;
+    gvgeocoord strafe = 0.0;
+    gvgeocoord vert = 0.0;
+
+    int     big_step_x, big_step_y;
+
+    big_step_x = (int) (view->state.shape_x / 1.3);
+    big_step_y = (int) (view->state.shape_x / 1.3);
+
+    switch (event->keyval)
+    {
+      case GDK_z:
+        if (event->state & GDK_CONTROL_MASK)
+        {
+            gv_undo_pop();
+        }
+        break;
+
+      case GDK_Escape:
+        if( view->dragging_mode )
+        {
+            view->dragging_mode = FALSE;
+            gv_view_area_queue_draw( view );
+        }
+        break;
+
+      case '-':
+          if (view->flag_3d)
+          {
+              view->state.z_scale -= HEIGHT_SCALE*0.1;
+              gv_view_area_state_changed(view);
+          }
+      break;
+
+      case '_':
+          if (view->flag_3d)
+          {
+              view->state.z_scale -= HEIGHT_SCALE;
+              gv_view_area_state_changed(view);
+          }
+      break;
+
+      case '=':
+          if (view->flag_3d)
+          {
+              view->state.z_scale += HEIGHT_SCALE*0.1;
+              gv_view_area_state_changed(view);
+          }
+      break;
+
+      case '+':
+          if (view->flag_3d)
+          {
+              view->state.z_scale += HEIGHT_SCALE;
+              gv_view_area_state_changed(view);
+          }
+      break;
+
+      case GDK_F1:
+        if( event->state & GDK_CONTROL_MASK )
+        {
+            view->state.flip_x *= -1;
+            gv_view_area_state_changed(view);
+        }
+        break;
+
+      case GDK_F2:
+          /* Flip along y */
+        if( event->state & GDK_CONTROL_MASK )
+        {
+            view->state.flip_y *= -1;
+            gv_view_area_state_changed(view);
+
+        } else {
+
+            /* Switch between 2D and 3D Modes on-the-fly */
+            if ( view->flag_3d )
+            {
+                gv_view_area_set_mode(view, 0);
+                gv_view_area_state_changed(view);
+            } else {
+                gv_view_area_set_mode(view, 1);
+                gv_view_area_state_changed(view);
+            }
+        }
+        break;
+
+      case GDK_Page_Up:
+        if (view->flag_3d)
+        {
+            move = 0.1;
+            if (event->state & GDK_SHIFT_MASK)
+                move = 1.0; /* Use bigger inc. if shifted */
+
+            motion(view, move, strafe, vert);
+        } else {
+            gv_view_area_zoom( view, ZOOM_STEP );
+        }
+        break;
+
+      case GDK_Page_Down:
+        if (view->flag_3d)
+        {
+            move = -0.1;
+            if (event->state & GDK_SHIFT_MASK)
+                move = -1.0; /* Use bigger inc. if shifted */
+
+            motion(view, move, strafe, vert);
+        } else {
+            gv_view_area_zoom( view, -ZOOM_STEP );
+        }
+        break;
+
+      case GDK_Right:
+          if (view->flag_3d)
+          {
+              strafe = 1.0;
+              motion(view, move, strafe, vert);
+          } else {
+
+              if (event->state & GDK_CONTROL_MASK)
+                  gv_view_area_translate( view, -big_step_x, 0 );
+              else if (event->state & GDK_SHIFT_MASK)
+                  gv_view_area_translate( view, -big_step_x/2, 0 );
+              else
+                  gv_view_area_translate( view, -10, 0 );
+          }
+          return TRUE; /* Don't let focus change */
+          break;
+
+      case GDK_Left:
+          if (view->flag_3d)
+          {
+              strafe = -1.0;
+              motion(view, move, strafe, vert);
+          } else {
+              if (event->state & GDK_CONTROL_MASK)
+                  gv_view_area_translate( view, big_step_x, 0 );
+              else if (event->state & GDK_SHIFT_MASK)
+                  gv_view_area_translate( view, big_step_x/2, 0 );
+              else
+                  gv_view_area_translate( view, 10, 0 );
+          }
+          return TRUE; /* Don't let focus change */
+          break;
+
+      case GDK_Up:
+          if (view->flag_3d)
+          {
+              vert = 1.0;
+              motion(view, move, strafe, vert);
+          } else {
+              if (event->state & GDK_CONTROL_MASK)
+                  gv_view_area_translate( view, 0, -big_step_y );
+              else if (event->state & GDK_SHIFT_MASK)
+                  gv_view_area_translate( view, 0, -big_step_y/2 );
+              else
+                  gv_view_area_translate( view, 0, -10 );
+          }
+          return TRUE; /* Don't let focus change */
+          break;
+
+      case GDK_Down:
+          if (view->flag_3d)
+          {
+              vert = -1.0;
+              motion(view, move, strafe, vert);
+          } else {
+              if (event->state & GDK_CONTROL_MASK)
+                  gv_view_area_translate( view, 0, big_step_y );
+              else if (event->state & GDK_SHIFT_MASK)
+                  gv_view_area_translate( view, 0, big_step_y/2 );
+              else
+                  gv_view_area_translate( view, 0, 10 );
+          }
+          return TRUE; /* Don't let focus change */
+          break;
+
+      case GDK_Home:
+        gv_view_area_fit_all_layers( view );
+        break;
+    }
+    /* FIXME: add flipping controls, etc. */
+
+    return 0;
+}
+
+static void
+gv_view_area_change_notify(GvViewArea *view, gpointer info)
+{
+    gv_view_area_queue_draw(view);
+}
+
+static void
+gv_view_area_state_changed(GvViewArea *view)
+{
+    gtk_signal_emit(GTK_OBJECT(view), view_area_signals[VIEW_STATE_CHANGED]);
+    gv_view_area_queue_draw(view);
+    gv_view_area_update_adjustments( view );
+}
+
+static void
+gv_view_area_destroy(GtkObject *object)
+{
+    GtkGLAreaClass *parent_class;
+    GvViewArea *view;
+
+    view = GV_VIEW_AREA(object);
+
+    CPLDebug( "OpenEV", "gv_view_area_destroy" );
+
+    /* Remove all layers */
+    view->active_layer = NULL;
+    while (view->layers != NULL)
+    {
+    GvLayer *layer = (GvLayer*)view->layers->data;
+    gv_view_area_remove_layer(view, GTK_OBJECT(layer));
+    }
+
+    gv_view_area_set_adjustments( view, NULL, NULL );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gtk_gl_area_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(object);
+}
+
+static void
+gv_view_area_finalize(GtkObject *object)
+{
+    GvLayerClass *parent_class;
+
+    CPLDebug( "OpenEV", "gv_view_area_finalize" );
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gtk_gl_area_get_type());
+    GTK_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+gv_view_area_set_arg (GtkObject        *object,
+                      GtkArg           *arg,
+                      guint             arg_id)
+{
+    GvViewArea *view;
+
+    view = GV_VIEW_AREA (object);
+
+    switch (arg_id)
+    {
+      case ARG_HADJUSTMENT:
+        gv_view_area_set_adjustments (view,
+                                      GTK_VALUE_POINTER (*arg),
+                                      view->vadj);
+        break;
+      case ARG_VADJUSTMENT:
+        gv_view_area_set_adjustments (view,
+                                      view->hadj,
+                                      GTK_VALUE_POINTER (*arg));
+        break;
+      default:
+        break;
+    }
+}
+
+static void
+gv_view_area_get_arg (GtkObject        *object,
+                      GtkArg           *arg,
+                      guint             arg_id)
+{
+    GvViewArea *view;
+
+    view = GV_VIEW_AREA (object);
+
+    switch (arg_id)
+    {
+      case ARG_HADJUSTMENT:
+        GTK_VALUE_POINTER (*arg) = view->hadj;
+        break;
+      case ARG_VADJUSTMENT:
+        GTK_VALUE_POINTER (*arg) = view->vadj;
+        break;
+      default:
+        arg->type = GTK_TYPE_INVALID;
+        break;
+    }
+}
+
+static void
+gv_view_area_update_adjustments( GvViewArea *view )
+
+{
+    GvRect   world_extents;
+    GvRect   view_extents;
+
+    if( !GTK_WIDGET_REALIZED(GTK_WIDGET(view)) )
+        return;
+
+    if( view->hadj == NULL && view->vadj == NULL )
+        return;
+
+    if( view->lock_adjustments )
+        return;
+
+    gv_view_area_get_world_extents( view, &world_extents );
+    if( world_extents.width == 0 || world_extents.height == 0 )
+        return;
+
+    if( world_extents.height < 0.0 )
+    {
+        world_extents.y += world_extents.height;
+        world_extents.height *= -1;
+    }
+
+    gv_view_area_get_extents( view, &view_extents.x, &view_extents.y,
+                              &view_extents.width, &view_extents.height);
+    view_extents.width = ABS(view_extents.x-view_extents.width);
+    view_extents.height = ABS(view_extents.y-view_extents.height);
+
+    view->lock_adjustments = TRUE;
+
+    if( view->hadj != NULL )
+    {
+        if( view->state.flip_x < 0 )
+        {
+            view->hadj->upper = -world_extents.x;
+            view->hadj->lower = -(world_extents.x + world_extents.width);
+            view->hadj->value = -(view_extents.x+view_extents.width);
+            view->hadj->page_size = ABS(view_extents.width);
+        }
+        else
+        {
+            view->hadj->lower = world_extents.x;
+            view->hadj->upper = world_extents.x + world_extents.width;
+            view->hadj->value = view_extents.x;
+            view->hadj->page_size = view_extents.width;
+        }
+
+        if( view->hadj->page_size > view->hadj->upper - view->hadj->lower )
+            view->hadj->page_size = view->hadj->upper - view->hadj->lower;
+        if( view->hadj->value < view->hadj->lower )
+            view->hadj->value = view->hadj->lower;
+        if( view->hadj->value+view->hadj->page_size > view->hadj->upper )
+            view->hadj->value = view->hadj->upper - view->hadj->page_size;
+
+        view->hadj->page_increment = view->hadj->page_size;
+        view->hadj->step_increment = view->hadj->page_increment / 4;
+
+        gtk_adjustment_changed( view->hadj );
+    }
+
+    if( view->vadj != NULL )
+    {
+        if( view->state.flip_y > 0 )
+        {
+            view->vadj->upper = -world_extents.y;
+            view->vadj->lower = -(world_extents.y + world_extents.height);
+            view->vadj->value = -(view_extents.y+view_extents.height);
+            view->vadj->page_size = ABS(view_extents.height);
+        }
+        else
+        {
+            view->vadj->lower = world_extents.y;
+            view->vadj->upper = world_extents.y + world_extents.height;
+            view->vadj->value = view_extents.y;
+            view->vadj->page_size = view_extents.height;
+        }
+
+        if( view->vadj->page_size > view->vadj->upper - view->vadj->lower )
+            view->vadj->page_size = view->vadj->upper - view->vadj->lower;
+        if( view->vadj->value < view->vadj->lower )
+            view->vadj->value = view->vadj->lower;
+        if( view->vadj->value+view->vadj->page_size > view->vadj->upper )
+            view->vadj->value = view->vadj->upper - view->vadj->page_size;
+
+        view->vadj->page_increment = view->vadj->page_size;
+        view->vadj->step_increment = view->vadj->page_increment / 4;
+
+        gtk_adjustment_changed( view->vadj );
+    }
+
+    view->lock_adjustments = FALSE;
+}
+
+static void
+gv_view_area_adjustment (GtkAdjustment *adjustment,
+                         GvViewArea    *view)
+{
+    gvgeocoord     xmin, ymin, width, height;
+    static int local_lock = FALSE;
+
+    g_return_if_fail (adjustment != NULL);
+    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+    g_return_if_fail (view != NULL);
+    g_return_if_fail (GV_IS_VIEW_AREA (view));
+
+    if( !GTK_WIDGET_REALIZED(GTK_WIDGET(view)) )
+        return;
+
+    if( view->lock_adjustments || local_lock )
+        return;
+
+    if( view->vadj == NULL || view->hadj == NULL )
+        return;
+
+    xmin = view->hadj->value * view->state.flip_x;
+    width = view->hadj->page_size * view->state.flip_x;
+
+    if( view->state.flip_y > 0 )
+    {
+        ymin = (-1 * view->vadj->value) - view->vadj->page_size;
+        height = view->vadj->page_size;
+    }
+    else
+    {
+        ymin = view->vadj->value;
+        height = view->vadj->page_size;
+    }
+
+    view->lock_adjustments = TRUE;
+    gv_view_area_fit_extents( view, xmin, ymin, width, height );
+    view->lock_adjustments = FALSE;
+
+    local_lock = TRUE;
+    gv_view_area_update_adjustments( view );
+    local_lock = FALSE;
+}
+
+static void
+gv_view_area_disconnect (GtkAdjustment *adjustment,
+                         GvViewArea    *view)
+{
+    g_return_if_fail (adjustment != NULL);
+    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+    g_return_if_fail (view != NULL);
+    g_return_if_fail (GV_IS_VIEW_AREA (view));
+
+    if (adjustment == view->hadj)
+        gv_view_area_set_adjustments (view, NULL, view->vadj);
+    if (adjustment == view->vadj)
+        gv_view_area_set_adjustments (view, view->hadj, NULL);
+}
+
+void
+gv_view_area_set_adjustments (GvViewArea    *view,
+                              GtkAdjustment *hadj,
+                              GtkAdjustment *vadj)
+{
+    g_return_if_fail (view != NULL);
+    g_return_if_fail (GV_VIEW_AREA (view));
+
+    if (hadj)
+        g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
+
+    if (vadj)
+        g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
+
+    if (view->hadj && (view->hadj != hadj))
+    {
+        gtk_signal_disconnect_by_data (GTK_OBJECT (view->hadj), view);
+        gtk_object_unref (GTK_OBJECT (view->hadj));
+    }
+
+    if (view->vadj && (view->vadj != vadj))
+    {
+        gtk_signal_disconnect_by_data (GTK_OBJECT (view->vadj), view);
+        gtk_object_unref (GTK_OBJECT (view->vadj));
+    }
+
+    if( hadj == NULL )
+        view->hadj = NULL;
+    else if (view->hadj != hadj)
+    {
+        view->hadj = hadj;
+        gtk_object_ref (GTK_OBJECT (view->hadj));
+        gtk_object_sink (GTK_OBJECT (view->hadj));
+
+        gtk_signal_connect (GTK_OBJECT (view->hadj), "changed",
+                            (GtkSignalFunc) gv_view_area_adjustment,
+                            view);
+        gtk_signal_connect (GTK_OBJECT (view->hadj), "value_changed",
+                            (GtkSignalFunc) gv_view_area_adjustment,
+                            view);
+        gtk_signal_connect (GTK_OBJECT (view->hadj), "disconnect",
+                            (GtkSignalFunc) gv_view_area_disconnect,
+                            view);
+
+        gv_view_area_adjustment (hadj, view);
+    }
+
+    if( vadj == NULL )
+        view->vadj = NULL;
+    else if (view->vadj != vadj)
+    {
+        view->vadj = vadj;
+        gtk_object_ref (GTK_OBJECT (view->vadj));
+        gtk_object_sink (GTK_OBJECT (view->vadj));
+
+        gtk_signal_connect (GTK_OBJECT (view->vadj), "changed",
+                            (GtkSignalFunc) gv_view_area_adjustment,
+                            view);
+        gtk_signal_connect (GTK_OBJECT (view->vadj), "value_changed",
+                            (GtkSignalFunc) gv_view_area_adjustment,
+                            view);
+        gtk_signal_connect (GTK_OBJECT (view->vadj), "disconnect",
+                            (GtkSignalFunc) gv_view_area_disconnect,
+                            view);
+        gv_view_area_adjustment (vadj, view);
+    }
+
+    gv_view_area_update_adjustments( view );
+}
+
+/************************************************************************/
+/*                        gv_view_area_set_raw()                        */
+/*                                                                      */
+/*      Try to reset whether this layer is in georeferenced or raw      */
+/*      mode relative to the indicated raster layer.                    */
+/************************************************************************/
+
+int gv_view_area_set_raw( GvViewArea *view, GtkObject *ref_layer,
+                          int raw_enable )
+
+{
+    GvRasterLayer *rlayer = NULL;
+    gvgeocoord    xmin, ymin, xmax, ymax;
+    double    pl_xmin, pl_ymin, pl_xmax, pl_ymax;
+
+    if( ref_layer != NULL && GV_IS_RASTER_LAYER(ref_layer) )
+        rlayer = GV_RASTER_LAYER(ref_layer);
+    else
+        rlayer = GV_RASTER_LAYER(gv_view_area_get_primary_raster(view));
+
+    if( !raw_enable == !gv_view_area_get_raw(view, ref_layer) )
+        return TRUE;
+
+    /* For now we need a raster layer. */
+    if( rlayer == NULL )
+        return FALSE;
+
+    /* Get the current view extents, and transform to raster PL coordinates.*/
+    gv_view_area_get_extents( view, &xmin, &ymin, &xmax, &ymax );
+
+    pl_xmin = xmin;
+    pl_ymin = ymin;
+    pl_xmax = xmax;
+    pl_ymax = ymax;
+
+    if( !gv_raster_layer_view_to_pixel( rlayer, &pl_xmin, &pl_ymin, NULL )
+        || !gv_raster_layer_view_to_pixel( rlayer, &pl_xmax, &pl_ymax, NULL ) )
+        return FALSE;
+
+    /* Now force the raster layer to change it's mesh coordinate system. */
+    if( !gv_raster_layer_set_raw( rlayer, raw_enable ) )
+        return FALSE;
+
+    /* Clear or set projection on view. */
+    if( raw_enable || gv_data_get_projection(GV_DATA(rlayer)) == NULL )
+        gv_view_area_set_projection( view, "PIXEL" );
+    else
+        gv_view_area_set_projection( view,
+                                     gv_data_get_projection(GV_DATA(rlayer)) );
+
+    /* Reset the view */
+    if( !gv_raster_layer_pixel_to_view( rlayer, &pl_xmin, &pl_ymin, NULL )
+        || !gv_raster_layer_pixel_to_view( rlayer, &pl_xmax, &pl_ymax, NULL ) )
+        return FALSE;
+
+    /*
+     * A bunch of hacky stuff to ensure the correct orientation of the
+     * new extents even if this requires some flipping.  We must always
+     * keep an upper-right origin in pixel/line space (raw) and a lower left
+     * origin in georeferenced space.
+     */
+    if( !raw_enable && pl_ymax < pl_ymin )
+    {
+        double  temp;
+
+        temp = pl_ymax;
+        pl_ymax = pl_ymin;
+        pl_ymin = temp;
+    }
+    else if( raw_enable && pl_ymax > pl_ymin )
+    {
+        double  temp;
+
+        temp = pl_ymax;
+        pl_ymax = pl_ymin;
+        pl_ymin = temp;
+    }
+    if( pl_xmax < pl_xmin )
+    {
+        double  temp;
+
+        temp = pl_xmax;
+        pl_xmax = pl_xmin;
+        pl_xmin = temp;
+
+    }
+
+    if( !raw_enable )
+        view->state.flip_y = 1.0;
+
+    gv_view_area_fit_extents( view,
+                              pl_xmin, pl_ymin,
+                              pl_xmax - pl_xmin, pl_ymax - pl_ymin );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                        gv_view_area_get_raw()                        */
+/*                                                                      */
+/*      Determine whether this view is in "raw" mode relative to the    */
+/*      given layer.  TRUE is returned for raw mode.                    */
+/************************************************************************/
+
+int gv_view_area_get_raw( GvViewArea *view, GtkObject *ref_layer )
+
+{
+    GvRasterLayer *rlayer = NULL;
+
+    if( ref_layer != NULL && GV_IS_RASTER_LAYER(ref_layer) )
+        rlayer = GV_RASTER_LAYER(ref_layer);
+    else
+        rlayer = GV_RASTER_LAYER(gv_view_area_get_primary_raster(view));
+
+    if( rlayer )
+        return rlayer->mesh_is_raw;
+    else
+        return FALSE;
+}
+
+/************************************************************************/
+/*                    gv_view_area_redraw_timeout()                     */
+/*                                                                      */
+/*      Returns TRUE if "quite a while" has elapsed since the last      */
+/*      view redraw.  This is used by the various layers to decide      */
+/*      if they should stop doing idle work.                            */
+/************************************************************************/
+
+int gv_view_area_redraw_timeout( GvViewArea *view )
+
+{
+    float   max_work_time;
+
+    if( view->redraw_timer == NULL )
+        return TRUE;
+
+    max_work_time = MAX(0.25,MIN(2.0,view->last_draw_time*3));
+
+    if( g_timer_elapsed(view->redraw_timer,NULL) > max_work_time )
+    {
+        g_timer_destroy( view->redraw_timer );
+        view->redraw_timer = NULL;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+/************************************************************************/
+/*                   gv_view_area_pending_idle_work()                   */
+/*                                                                      */
+/*      Returns TRUE if any of the layers displayed on this view        */
+/*      report they still have idle work pending, else it returns       */
+/*      FALSE.                                                          */
+/************************************************************************/
+
+int gv_view_area_pending_idle_work( GvViewArea *view )
+
+{
+    GList *list;
+
+
+    list = view->layers;
+    while (list)
+    {
+    GvLayer *layer = (GvLayer*)list->data;
+        if( layer->pending_idle )
+            return TRUE;
+    list = g_list_next(list);
+    }
+
+    return FALSE;
+}
+
+/************************************************************************/
+/*                       gv_get_render_counter()                        */
+/************************************************************************/
+
+int gv_get_render_counter()
+
+{
+    return render_counter;
+}
+
+/************************************************************************/
+/*                  gv_view_area_get_primary_raster()                   */
+/************************************************************************/
+
+GtkObject *gv_view_area_get_primary_raster( GvViewArea *view )
+
+{
+    GList *layer_list, *node;
+
+    layer_list = gv_view_area_list_layers( view );
+
+    for( node = layer_list; node != NULL; node = node->next )
+    {
+        GvLayer *layer;
+
+        layer = GV_LAYER(node->data);
+        if( gv_layer_is_visible(layer) && GV_IS_RASTER_LAYER( layer ) )
+            return GTK_OBJECT(layer);
+    }
+
+    return NULL;
+}
+
+const char *
+gv_view_area_get_property(GvViewArea *data, const char *name)
+{
+    return gv_properties_get( &(data->properties), name );
+}
+
+void
+gv_view_area_set_property(GvViewArea *data, const char *name, const char *value)
+{
+    gv_properties_set( &(data->properties), name, value );
+}
+
+GvProperties *
+gv_view_area_get_properties(GvViewArea *data)
+{
+    return &(data->properties);
+}
+

Added: packages/openev/branches/upstream/current/gvviewarea.h
===================================================================
--- packages/openev/branches/upstream/current/gvviewarea.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvviewarea.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,354 @@
+/******************************************************************************
+ * $Id: gvviewarea.h,v 1.60 2005/01/14 15:27:28 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  GTK/OpenGL View Canvas
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvviewarea.h,v $
+ * Revision 1.60  2005/01/14 15:27:28  warmerda
+ * added flip flag access
+ *
+ * Revision 1.59  2003/08/27 19:58:43  warmerda
+ * added force_simple flag for gv_view_area_bmfont_draw
+ *
+ * Revision 1.58  2003/03/07 22:18:25  warmerda
+ * const correctness fix for get_layer_by_name
+ *
+ * Revision 1.57  2003/02/20 19:27:18  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.56  2002/11/05 04:14:54  warmerda
+ * fixed prototype
+ *
+ * Revision 1.55  2002/11/05 04:14:11  warmerda
+ * added prototype
+ *
+ * Revision 1.54  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.53  2002/09/10 13:26:43  warmerda
+ * added get_height_scale method
+ *
+ * Revision 1.52  2002/07/16 14:17:06  warmerda
+ * added support for getting background color
+ *
+ * Revision 1.51  2002/07/08 19:44:39  warmerda
+ * added properties on GvViewArea
+ *
+ * Revision 1.50  2002/03/20 19:19:14  warmerda
+ * added exact_render flag
+ *
+ * Revision 1.49  2002/01/30 17:25:19  warmerda
+ * added set_state and get_primary_raster functions
+ *
+ * Revision 1.48  2001/12/13 03:29:17  warmerda
+ * avoid purging textures used in this render
+ *
+ * Revision 1.47  2001/10/12 17:44:18  warmerda
+ * avoid extra redraws when many raster layers displayed
+ *
+ * Revision 1.46  2001/10/12 01:58:19  warmerda
+ * avoid re-rendering if backing store OK
+ *
+ * Revision 1.45  2001/07/03 14:26:05  warmerda
+ * added set/get raw ability
+ *
+ * Revision 1.44  2001/04/09 18:20:14  warmerda
+ * added ability to query list of available fonts
+ *
+ * Revision 1.43  2001/03/26 19:18:35  warmerda
+ * restructure bmfont handling to preserve GdkFont handle
+ *
+ * Revision 1.42  2001/02/03 22:21:08  warmerda
+ * added gv_view_area_get_mode() and python covers
+ *
+ * Revision 1.41  2000/10/06 16:48:56  warmerda
+ * added GvViewArea background color
+ *
+ * Revision 1.40  2000/09/29 16:09:17  srawlin
+ * added Goto function requring fuction to map lat/long to view coordinates
+ *
+ * Revision 1.39  2000/09/27 19:16:33  warmerda
+ * *** empty log message ***
+ *
+ * Revision 1.38  2000/09/21 02:57:20  warmerda
+ * reorganized bitmap font support to allow any gdk supported font at runtime
+ *
+ * Revision 1.37  2000/09/13 15:58:55  srawlin
+ * added python bindings for gv_view_area_get_zoom
+ *
+ * Revision 1.36  2000/08/16 14:07:47  warmerda
+ * added prototype scrollbar support
+ *
+ * Revision 1.35  2000/08/07 17:18:13  warmerda
+ * added windows printing support
+ *
+ * Revision 1.34  2000/08/03 18:20:41  warmerda
+ * implemented print scaling and paper sizes properly
+ *
+ * Revision 1.33  2000/07/21 01:31:11  warmerda
+ * added read_only flag for GvData, and utilize for vector layers
+ *
+ * Revision 1.32  2000/07/20 03:21:26  warmerda
+ * added is_rgb for print_to_file()
+ *
+ * Revision 1.31  2000/07/17 19:10:00  warmerda
+ * added tentative support for scaling wait between redraws to actual redraw time
+ *
+ * Revision 1.30  2000/07/13 18:05:22  srawlin
+ * removed use of view.state.eye_az and .eye_el, contained same information 
+ * as view.state.eye_dir
+ *
+ * Revision 1.29  2000/07/11 20:56:23  srawlin
+ * added methods to get and set viewing direction relative to z-plane in 3D
+ *
+ * Revision 1.28  2000/07/10 16:20:50  srawlin
+ * removed unused function def
+ *
+ * Revision 1.27  2000/07/10 13:36:58  srawlin
+ * updated 3D controls to be more like 2D
+ *
+ * Revision 1.26  2000/07/03 20:58:31  warmerda
+ * eye_pos in georef coordinates now
+ *
+ * Revision 1.25  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_VIEW_AREA_H__
+#define __GV_VIEW_AREA_H__
+
+#include <gtkgl/gtkglarea.h>
+#include "gvtypes.h"
+#include "gvproperties.h"
+
+#define GV_TYPE_VIEW_AREA            (gv_view_area_get_type ())
+#define GV_VIEW_AREA(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_VIEW_AREA, GvViewArea))
+#define GV_VIEW_AREA_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_VIEW_AREA, GvViewAreaClass))
+#define GV_IS_VIEW_AREA(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_VIEW_AREA))
+#define GV_IS_VIEW_AREA_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_VIEW_AREA))
+
+typedef struct _GvViewArea       GvViewArea;
+typedef struct _GvViewAreaClass  GvViewAreaClass;
+typedef struct _GvViewAreaState  GvViewAreaState;
+
+typedef gvgeocoord vec3_t[3];  /* xyz */
+
+struct _GvViewAreaState
+{
+    gvgeocoord tx, ty;                        /* translation of center (georef) */
+    gvgeocoord rot;                           /* xy-plane rotation in degrees */
+    gvgeocoord zoom;                          /* log 2 based zoom, 0 is 1:1 */
+    gvgeocoord linear_zoom;                   /* xy plane zoom */
+    gvgeocoord flip_x,  flip_y;
+    gvgeocoord shape_x, shape_y;              /* width/height of window in pixels */
+    gvgeocoord mpos_x,  mpos_y;               /* mouse position (pixel/line)*/
+
+    vec3_t eye_pos, eye_dir;             /* 3D only, eye location and view 
+                                            direction */
+    gvgeocoord z_scale;
+};
+
+typedef struct 
+{
+    char	*name;
+    gint	listbase;
+
+    GdkFont	*gdkfont;
+} GvBMFontInfo;
+
+struct _GvViewArea
+{
+    GtkGLArea glarea;
+
+    GvViewAreaState state;
+
+    GList *layers;
+    GtkObject *active_layer;
+
+    GArray   *bmfonts;
+
+    char     *projection;                /* projection of view is in, eg UTM */
+
+    GvColor   background;                /* background color used in clears */
+
+    int       exact_render;              /* normally set for prints */
+
+    int flag_3d;                         /* 2D or 3D mode flag */
+
+    double    linear_measure;            /* Diaganonal of view extents for
+                                            scaling linear step sizes */
+    int       volume_current;
+    double    view_volume[6];            /* xmin/xmax/ymin/ymax/zmin/zmax */
+    
+    gint      last_button;               /* zero mean no current button */
+    double    last_button_time;
+    double    last_zoom_time;
+
+    gvgeocoord   last_mpos_x, last_mpos_y;
+    gint      dragging_mode;
+
+    GtkAdjustment *hadj;
+    GtkAdjustment *vadj;
+    int        lock_adjustments;
+
+    int        display_dirty;            /*does the display need rerendering?*/
+
+    /* ghost cursor stuff */
+    gvgeocoord   next_x, next_y;
+    gvgeocoord   last_x, last_y;
+    int        next_valid;    /* flag to indicate if ghost cursor should draw next_x, next_y */
+    int        last_valid;    /* flag to indicate if last cursor should be erased (logical cursor) */
+    /* end of ghost cursor stuff */
+
+    float     last_draw_time;
+    GTimer   *redraw_timer;
+
+    GvProperties  properties;
+};
+
+struct _GvViewAreaClass
+{
+    GtkGLAreaClass parent_class;
+
+    void (* gldraw) (GvViewArea *view);
+    void (* glcursor) (GvViewArea *view);
+    void (* active_changed) (GvViewArea *view);
+    void (* view_state_changed) (GvViewArea *view);
+    void  (*set_scroll_adjustments)   (GvViewArea    *view,
+                                       GtkAdjustment  *hadjustment,
+                                       GtkAdjustment  *vadjustment);
+};
+
+GtkType    gv_view_area_get_type   (void);
+GtkWidget* gv_view_area_new        (void);
+
+void gv_view_area_set_mode(GvViewArea *view, int flag_3d);
+int  gv_view_area_get_mode(GvViewArea *view);
+void gv_view_area_height_scale(GvViewArea *view, gvgeocoord scale);
+gvgeocoord gv_view_area_get_height_scale(GvViewArea *view);
+void gv_view_area_set_3d_view( GvViewArea *view, vec3_t eye_pos, 
+                               vec3_t eye_dir );
+void gv_view_area_set_3d_view_look_at( GvViewArea *view, vec3_t eye_pos, 
+                               gvgeocoord *eye_look_at );
+gint gv_view_area_get_look_at_pos( GvViewArea *view,
+                              gvgeocoord *x, gvgeocoord *y);
+
+gint gv_view_area_set_raw( GvViewArea *view, GtkObject *ref_layer, 
+                           int raw_enable );
+gint gv_view_area_get_raw( GvViewArea *view, GtkObject *ref_layer );
+
+void gv_view_area_queue_draw(GvViewArea *view);
+void gv_view_area_zoom(GvViewArea *view, gvgeocoord zoom);
+gvgeocoord gv_view_area_get_zoom(GvViewArea *view);
+void gv_view_area_rotate(GvViewArea *view, gvgeocoord angle);
+void gv_view_area_translate(GvViewArea *view, gvgeocoord dx, gvgeocoord dy);
+void gv_view_area_set_translation(GvViewArea *view, gvgeocoord x, gvgeocoord y);
+int gv_view_area_get_flip_x(GvViewArea *view);
+int gv_view_area_get_flip_y(GvViewArea *view);
+void gv_view_area_set_flip_xy(GvViewArea *view, int flip_x, int flip_y );
+void gv_view_area_fit_all_layers(GvViewArea *view);
+void gv_view_area_fit_extents(GvViewArea *view,
+                          gvgeocoord llx, gvgeocoord lly, gvgeocoord width, gvgeocoord height );
+gint gv_view_area_expose(GtkWidget *view, GdkEventExpose *event);
+void gv_view_area_get_extents(GvViewArea *view,
+                              gvgeocoord *xmin, gvgeocoord *ymin,
+                              gvgeocoord *xmax, gvgeocoord *ymax );
+void gv_view_area_get_world_extents(GvViewArea *view, GvRect *extents );
+void gv_view_area_get_volume( GvViewArea *view, double *volume );
+
+void gv_view_area_map_location(GvViewArea *view, gvgeocoord x, gvgeocoord y,
+                               gvgeocoord *geo_x, gvgeocoord *geo_y);
+void gv_view_area_copy_state(GvViewArea *view, GvViewArea *copy);
+void gv_view_area_set_state(GvViewArea *view, GvViewAreaState *state);
+void gv_view_area_map_pointer(GvViewArea *view, gvgeocoord px, gvgeocoord py,
+			      gvgeocoord *x, gvgeocoord *y);
+void gv_view_area_inverse_map_pointer(GvViewArea *view, gvgeocoord x, gvgeocoord y,
+				      gvgeocoord *px, gvgeocoord *py);
+void gv_view_area_correct_for_transform(GvViewArea *view, gvgeocoord x, gvgeocoord y,
+					gvgeocoord *cx, gvgeocoord *cy);
+
+void gv_view_area_add_layer(GvViewArea *view, GtkObject *layer);
+void gv_view_area_remove_layer(GvViewArea *view, GtkObject *layer);
+GtkObject* gv_view_area_active_layer(GvViewArea *view);
+void gv_view_area_set_active_layer(GvViewArea *view, GtkObject *layer);
+GtkObject* gv_view_area_get_layer_of_type(GvViewArea *view, GtkType layer_type,
+                                          gint read_only_ok );
+GtkObject* gv_view_area_get_named_layer(GvViewArea *view, const char *name);
+GList* gv_view_area_list_layers(GvViewArea *view);
+GtkObject *gv_view_area_get_primary_raster( GvViewArea *view );
+void gv_view_area_swap_layers(GvViewArea *view, gint layer_a, gint layer_b);
+GdkPixmap* gv_view_area_create_thumbnail(GvViewArea *view, GtkObject *layer, gint width, gint height);
+
+GPtrArray *gv_view_area_get_fontnames(GvViewArea *view);
+gint gv_view_area_bmfont_load(GvViewArea *view, gchar *name);
+GvBMFontInfo *gv_view_area_bmfont_get_info(GvViewArea *view, gint font);
+void gv_view_area_bmfont_draw(GvViewArea *view, gint font, gvgeocoord x, gvgeocoord y, gchar *text, int force_simple );
+void gv_view_area_set_background_color(GvViewArea *view, GvColor color);
+void gv_view_area_get_background_color(GvViewArea *view, GvColor color);
+
+void
+gv_view_area_set_adjustments (GvViewArea    *view, GtkAdjustment *hadj,
+                              GtkAdjustment *vadj);
+
+gint gv_view_area_set_projection(GvViewArea *view, const char *projection);
+const char *gv_view_area_get_projection(GvViewArea *view);
+gint gv_view_area_print_to_file(GvViewArea *, int, int, const char *,
+                                const char *, int );
+gint gv_view_area_print_postscript_to_file(
+    GvViewArea *, int, int, float, float, float, float, int, const char * );
+gint gv_view_area_render_postscript(GvViewArea *, int, int, 
+                                    float, float, float, float, int,
+                                    gint (*)(void *, const char *),
+                                    void * );
+gint gv_view_area_render_to_func(GvViewArea *, int, int,
+                                 gint (*)(void*, void*), void * );
+void gv_view_area_page_setup();
+gint gv_view_area_print_to_windriver(GvViewArea *, int, int,
+                                     float, float, float, float,
+                                     int );
+void gv_view_area_zoompan_event(GvViewArea *, GdkEventButton *);
+#define gv_view_area_get_width(view) (view)->state.shape_x
+#define gv_view_area_get_height(view) (view)->state.shape_y
+
+int gv_view_area_redraw_timeout(GvViewArea *view);
+int gv_view_area_pending_idle_work(GvViewArea *view);
+
+int gv_get_render_counter();
+
+void   gv_view_area_set_property(GvViewArea *data, const char *name, 
+                                 const char *value);
+const char *gv_view_area_get_property(GvViewArea *data, const char *name);
+GvProperties *gv_view_area_get_properties(GvViewArea *data);
+void gv_view_area_queue_cursor_draw(GvViewArea *view, int next_valid, gvgeocoord x, gvgeocoord y);
+
+#endif /* __GV_VIEW_AREA_H__ */
+
+
+

Added: packages/openev/branches/upstream/current/gvviewlink.c
===================================================================
--- packages/openev/branches/upstream/current/gvviewlink.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvviewlink.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,643 @@
+/******************************************************************************
+ * $Id: gvviewlink.c,v 1.12 2005/01/18 19:55:42 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Manage linked GvViewAreas.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvviewlink.c,v $
+ * Revision 1.12  2005/01/18 19:55:42  gmwalter
+ * Avoid georef->pixel->georef transformations
+ * when they are not necessary: second order
+ * warps don't invert exactly, and error
+ * accumulates.
+ *
+ * Revision 1.11  2003/03/31 22:27:56  gmwalter
+ * Alter copy state zoom level calculation so it doesn't change under rotation.
+ *
+ * Revision 1.10  2003/03/04 15:30:27  gmwalter
+ * Avoid artifacts when cursor mode switched.
+ *
+ * Revision 1.9  2003/02/21 22:35:30  gmwalter
+ * Removed unused variables.
+ *
+ * Revision 1.8  2003/02/20 19:27:19  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.7  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.6  2002/03/25 19:53:41  warmerda
+ * fix for NULL projection
+ *
+ * Revision 1.5  2002/01/30 17:26:11  warmerda
+ * make link state copying smarter
+ *
+ * Revision 1.4  2000/08/10 16:52:40  warmerda
+ * watch for destroy, not delete-event
+ *
+ * Revision 1.3  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvviewlink.h"
+#include <gtk/gtksignal.h>
+#include "gvrasterlayer.h"
+#include "gvutils.h"
+
+static void gv_view_link_class_init(GvViewLinkClass *klass);
+static void gv_view_link_init(GvViewLink *link);
+static void gv_view_link_view_state_changed(GvViewLink *link, GvViewArea *view);
+static void gv_view_link_destroy(GtkObject *object);
+
+/* Ghost cursor stuff */
+static void gv_view_link_cursor_draw(GvViewLink *link, GvViewArea *view);
+
+static void gv_view_link_cursor_motion_notify(GvViewLink *link,GdkEventMotion *event, GvViewArea *view);
+static void gv_view_link_cursor_leave_notify(GvViewLink *link, GdkEventCrossing *event, GvViewArea *view);
+static void gv_view_link_cursor_enter_notify(GvViewLink *link, GdkEventCrossing *event, GvViewArea *view);
+static void gv_view_link_cursor_set_x_y(GvViewLink *link, double x, double y, GvViewArea *view);
+static void gv_view_link_cursor_get_geo_x_y(double *event_x, double *event_y, GvViewArea *view);
+
+GtkType
+gv_view_link_get_type(void)
+{
+    static GtkType view_link_type = 0;
+
+    if (!view_link_type)
+    {
+	static const GtkTypeInfo view_link_info =
+	{
+	    "GvViewLink",
+	    sizeof(GvViewLink),
+	    sizeof(GvViewLinkClass),
+	    (GtkClassInitFunc) gv_view_link_class_init,
+	    (GtkObjectInitFunc) gv_view_link_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	view_link_type = gtk_type_unique(gtk_object_get_type(),
+					 &view_link_info);
+    }
+    return view_link_type;
+}
+
+static void
+gv_view_link_class_init(GvViewLinkClass *klass)
+{
+    GtkObjectClass *object_class;
+
+    object_class = (GtkObjectClass*) klass;
+
+    object_class->destroy = gv_view_link_destroy;
+}
+
+static void
+gv_view_link_init(GvViewLink *link)
+{
+    link->views = NULL;
+    link->enabled = FALSE;
+    link->blocked = FALSE;
+
+    /* Ghost cursor stuff */
+    link->cursor_mode = GV_LINK_CURSOR_OFF;
+    link->src_view=NULL;
+}
+
+GtkObject *
+gv_view_link_new(void)
+{
+    return GTK_OBJECT(gtk_type_new(GV_TYPE_VIEW_LINK));
+}
+
+void
+gv_view_link_register_view(GvViewLink *link, GvViewArea *view)
+{
+    /* Add a reference to this view */
+    gtk_object_ref(GTK_OBJECT(view));
+    
+    /* Add view to registered views list */
+    link->views = g_list_prepend(link->views, view);
+
+    /* Connect to the view state changed event */
+    gtk_signal_connect_object(GTK_OBJECT(view), "view-state-changed",
+			      GTK_SIGNAL_FUNC(gv_view_link_view_state_changed),
+			      GTK_OBJECT(link));
+
+    /* Connect to view area delete event */
+    gtk_signal_connect_object(GTK_OBJECT(view), "destroy",
+			      GTK_SIGNAL_FUNC(gv_view_link_remove_view),
+			      GTK_OBJECT(link));
+
+    /* Next two connections are only needed for ghost cursor drawing */
+    /* Connect to view area glcursor signal */
+    gtk_signal_connect_object(GTK_OBJECT(view), "glcursor",
+			      GTK_SIGNAL_FUNC(gv_view_link_cursor_draw),
+			      GTK_OBJECT(link));
+
+    /* Connect to motion event */
+    gtk_signal_connect_object(GTK_OBJECT(view), "motion-notify-event",
+			      GTK_SIGNAL_FUNC(gv_view_link_cursor_motion_notify),
+			      GTK_OBJECT(link));
+
+    /* Connect to view area leave signal  */
+    gtk_signal_connect_object(GTK_OBJECT(view), "leave-notify-event",
+			      GTK_SIGNAL_FUNC(gv_view_link_cursor_leave_notify),
+			      GTK_OBJECT(link));
+
+    /* Connect to view area enter signal  */
+    gtk_signal_connect_object(GTK_OBJECT(view), "enter-notify-event",
+			      GTK_SIGNAL_FUNC(gv_view_link_cursor_enter_notify),
+			      GTK_OBJECT(link));
+
+}
+
+void
+gv_view_link_remove_view(GvViewLink *link, GvViewArea *view)
+{
+    GList *list;
+
+    list = g_list_find(link->views, view);
+    if (list == NULL)
+    {
+	g_warning("gv_view_link_remove_view(): view not linked.");
+	return;
+    }
+
+    link->views = g_list_remove_link(link->views, list);
+    
+    gtk_signal_disconnect_by_data(GTK_OBJECT(view), GTK_OBJECT(link));
+    gtk_object_unref(GTK_OBJECT(view));
+}
+
+void
+gv_view_link_enable(GvViewLink *link)
+{
+    link->enabled = TRUE;
+}
+
+void
+gv_view_link_disable(GvViewLink *link)
+{
+    GList *list;
+
+    link->enabled = FALSE;
+    list = link->views;
+    while (list)
+    {
+	GvViewArea *view = (GvViewArea*)list->data;
+	gv_view_area_queue_cursor_draw(view,FALSE,0.0,0.0);
+	list = g_list_next(list);
+    }
+}
+
+/**************************************************************/
+
+static int
+gv_view_link_copy_state(GvViewLink *link, 
+                        GvViewArea *src_view, 
+                        GvViewArea *dst_view)
+
+{
+    GvViewAreaState  state;
+    GvRasterLayer   *src_raster, *dst_raster;
+    double	x, y, xo, yo;
+    gvgeocoord       f_xo, f_yo;
+
+    state = src_view->state;
+    x = -state.tx;
+    y = -state.ty;
+    gv_view_area_map_pointer( src_view, 0.0, 0.0, &f_xo, &f_yo );
+    xo = f_xo;
+    yo = f_yo;
+    if (gv_view_area_get_primary_raster(src_view) != NULL)
+    {
+        src_raster = 
+                GV_RASTER_LAYER(gv_view_area_get_primary_raster(src_view));
+        if( gv_view_area_get_raw(src_view,NULL) )
+        {
+            /* If no cursor-link transformation has been set, these default to
+             * the usual gv_raster_pixel_to_georef.
+             */
+            gv_raster_pixel_to_georefCL( src_raster->prototype_data, &x, &y, NULL );
+            gv_raster_pixel_to_georefCL( src_raster->prototype_data, &xo, &yo, NULL);
+        }
+        else if ( src_raster->prototype_data->poly_orderCL > -1 )
+        {
+            /* Only need the georef->pixel->georefCL transformation if specific
+             * cursor-link transformations have been set.
+             */
+            gv_raster_georef_to_pixel( src_raster->prototype_data, &x, &y, NULL );
+            gv_raster_georef_to_pixel( src_raster->prototype_data, &xo, &yo, NULL);
+            gv_raster_pixel_to_georefCL( src_raster->prototype_data, &x, &y, NULL );
+            gv_raster_pixel_to_georefCL( src_raster->prototype_data, &xo, &yo, NULL);
+        }
+
+    }
+    
+    if( gv_view_area_get_projection(src_view) != NULL
+        && gv_view_area_get_projection(dst_view) != NULL
+        && !EQUAL(gv_view_area_get_projection(src_view),
+                  gv_view_area_get_projection(dst_view)) 
+        && !EQUAL(gv_view_area_get_projection(src_view),"PIXEL")
+        && !EQUAL(gv_view_area_get_projection(dst_view),"PIXEL") )
+    {
+        if( !gv_reproject_points( gv_view_area_get_projection(src_view),
+                                  gv_view_area_get_projection(dst_view),
+                                  1, &x, &y, NULL ) )
+        {
+            CPLDebug( "GvViewLink", "gv_reproject_points(%s,%s) failed.", 
+                      gv_view_area_get_projection(src_view), 
+                      gv_view_area_get_projection(dst_view) );
+            return FALSE;
+        }
+        gv_reproject_points( gv_view_area_get_projection(src_view),
+                             gv_view_area_get_projection(dst_view),
+                             1, &xo, &yo, NULL );
+    }
+
+    if (gv_view_area_get_primary_raster(dst_view) != NULL)
+    {
+        dst_raster = 
+                GV_RASTER_LAYER(gv_view_area_get_primary_raster(dst_view));
+        if( gv_view_area_get_raw(dst_view,NULL) )
+        {
+            gv_raster_georef_to_pixelCL( dst_raster->prototype_data, &x, &y, NULL );
+            gv_raster_georef_to_pixelCL( dst_raster->prototype_data, &xo, &yo, NULL);
+        }
+        else if ( dst_raster->prototype_data->poly_orderCL > -1 )
+        {
+            gv_raster_georef_to_pixelCL( dst_raster->prototype_data, &x, &y, NULL );
+            gv_raster_georef_to_pixelCL( dst_raster->prototype_data, &xo, &yo, NULL);
+            gv_raster_pixel_to_georef( dst_raster->prototype_data, &x, &y, NULL );
+            gv_raster_pixel_to_georef( dst_raster->prototype_data, &xo, &yo, NULL);
+
+        }
+
+    }
+
+    state.tx = -x;
+    state.ty = -y;
+
+/* -------------------------------------------------------------------- */
+/*      Try to set the zoom level so that the same region from the      */
+/*      source view will be seen in the destination view, even if it    */
+/*      is a different size.  Try to account for rotation.              */
+/* -------------------------------------------------------------------- */
+
+    state.linear_zoom = 0.5*sqrt((dst_view->state.shape_y*dst_view->state.shape_y +
+                                  dst_view->state.shape_x*dst_view->state.shape_x)/
+                                 ((yo-y)*(yo-y) + (xo-x)*(xo-x)));  
+ 
+    state.zoom = log(state.linear_zoom) / log(2.0);
+
+/* -------------------------------------------------------------------- */
+/*      The shape is view specific, and should not be updated.  The     */
+/*      flip value is less obvious, but if one view is                  */
+/*      georeferenced, and the other is "raw" we don't want the flip    */
+/*      values propagating and screwing stuff up.  This may require     */
+/*      future review.                                                  */
+/* -------------------------------------------------------------------- */
+    state.shape_x = dst_view->state.shape_x;
+    state.shape_y = dst_view->state.shape_y;
+
+    state.flip_x = dst_view->state.flip_x;
+    state.flip_y = dst_view->state.flip_y;
+
+    /* we could try to derive a rough rotation from the change in the
+       direction of the (x,y) to (xd,yd) vector */
+    gv_view_area_set_state( dst_view, &state );
+    return TRUE;
+}
+
+/**************************************************************/
+
+static void
+gv_view_link_view_state_changed(GvViewLink *link, GvViewArea *view)
+{
+    GList *list;
+    int x, y;
+    double geo_xd,geo_yd;
+    
+    if (! link->enabled) return;
+    if (link->blocked) return;
+
+    /* Copy the view state to the other views in the list */
+    list = link->views;
+    link->blocked = TRUE;
+
+    /* ghost cursor needs to be reset because motion notify not always done! */
+    gtk_widget_get_pointer(GTK_WIDGET(view), &x, &y);
+    geo_xd = (double)x;
+    geo_yd = (double)y;
+    gv_view_link_cursor_get_geo_x_y(&geo_xd, &geo_yd, view);
+
+    while (list)
+    {
+	GvViewArea *other_view = (GvViewArea*)list->data;
+	if (other_view != view)
+	{
+	    gv_view_link_copy_state(link, view, other_view);
+	    other_view->next_valid = 1;
+	    other_view->next_x = geo_xd;
+	    other_view->next_y = geo_yd;
+	}	
+	list = g_list_next(list);
+    }
+    
+    link->blocked = FALSE;
+}
+
+/* Ghost cursor-related functions */
+static void
+gv_view_link_cursor_motion_notify(GvViewLink *link,GdkEventMotion *event, GvViewArea *view)
+{    
+
+    if (link->blocked) return; /* don't think this is necessary, cause of interrupt processing */
+    if (link->cursor_mode == GV_LINK_CURSOR_OFF) return;
+    gv_view_link_cursor_set_x_y(link,event->x,event->y,view);
+}
+
+static void gv_view_link_cursor_enter_notify(GvViewLink *link,GdkEventCrossing *event,GvViewArea *view)
+{
+
+    if (link->blocked) return; /* don't think this is necessary, cause of interrupt processing */
+    if (link->cursor_mode == GV_LINK_CURSOR_OFF) return;
+    gv_view_link_cursor_set_x_y(link,event->x,event->y,view);
+}
+
+static void gv_view_link_cursor_leave_notify(GvViewLink *link,GdkEventCrossing *event,GvViewArea *view)
+{
+    GList *list;
+
+    if (link->blocked) return; /* don't think this is necessary, cause of interrupt processing */
+    if (link->cursor_mode == GV_LINK_CURSOR_OFF) return;
+
+    list = link->views;
+    while (list)
+    {
+	GvViewArea *other_view = (GvViewArea*)list->data;
+	/* ALL views should be redrawn on leave event, to avoid artifacts in logical cursor case */
+	gv_view_area_queue_cursor_draw(other_view,FALSE,0.0,0.0);
+	list = g_list_next(list);
+    }  
+}
+
+static void
+gv_view_link_cursor_get_geo_x_y(double *event_x, double *event_y, GvViewArea *view)
+{
+    GvRasterLayer *src_raster;
+    gvgeocoord geo_x, geo_y;
+    double geo_xd,geo_yd;
+
+    gv_view_area_map_pointer(view, *event_x, *event_y,
+			     &geo_x, &geo_y);
+
+    /* hack to get around the fact that some things expect double, others gvgeocoord */
+    geo_xd=(double) geo_x;
+    geo_yd=(double) geo_y;
+    if (gv_view_area_get_primary_raster(view) != NULL)
+    {
+        src_raster = GV_RASTER_LAYER(gv_view_area_get_primary_raster(view));
+        if( gv_view_area_get_raw(view,NULL) )
+        {
+            gv_raster_pixel_to_georefCL( src_raster->prototype_data, &geo_xd, &geo_yd, NULL );
+        }
+        else if ( src_raster->prototype_data->poly_orderCL > -1 )
+        {
+            /* convert from display georeferenced coordinates to pixel coordinates to */
+            /* link-cursor georeferenced coordinates                                  */
+
+            gv_raster_georef_to_pixel( src_raster->prototype_data, &geo_xd, &geo_yd, NULL );
+            gv_raster_pixel_to_georefCL( src_raster->prototype_data, &geo_xd, &geo_yd, NULL );
+        }
+    }
+    *event_x = geo_xd;
+    *event_y = geo_yd;
+}
+
+static void
+gv_view_link_cursor_set_x_y(GvViewLink *link, double event_x, double event_y, GvViewArea *view)
+{
+    GList *list;
+    double geo_xd,geo_yd;
+    
+    
+    geo_xd = event_x;
+    geo_yd = event_y;
+    gv_view_link_cursor_get_geo_x_y(&geo_xd, &geo_yd, view);
+    link->src_view = view;
+
+    list = link->views;
+    while (list)
+    {
+	GvViewArea *other_view = (GvViewArea*)list->data;
+	if(other_view != view) {
+	  gv_view_area_queue_cursor_draw(other_view,TRUE,geo_xd,geo_yd);
+        }
+	list = g_list_next(list);
+    }
+
+
+}
+
+
+static void
+gv_view_link_cursor_draw(GvViewLink *link, GvViewArea *view)
+{
+    gvgeocoord dx, dy, x, y;
+    GvRasterLayer   *dst_raster;
+    double xdouble, ydouble;
+
+    /* Uncomment next line to get rid of cursor in non-linked case */
+    /*if (! link->enabled) return;*/
+    if (link->cursor_mode == GV_LINK_CURSOR_OFF) return;
+    
+    /* Don't draw cursor if application has just started up (src_view */
+    /* is NULL) */
+    if (link->src_view == NULL) return;
+
+    dx = 6.0;  /* Later should make this flexible */
+    dy = 0.0;
+
+    gv_view_area_correct_for_transform(view, dx, dy, &dx, &dy);    
+
+    if(view->next_valid) {
+      xdouble = (double) view->next_x;
+      ydouble = (double) view->next_y;
+
+      if( gv_view_area_get_projection(link->src_view) != NULL
+	  && gv_view_area_get_projection(view) != NULL
+	  && !EQUAL(gv_view_area_get_projection(link->src_view),
+		    gv_view_area_get_projection(view)) 
+	  && !EQUAL(gv_view_area_get_projection(link->src_view),"PIXEL")
+	  && !EQUAL(gv_view_area_get_projection(view),"PIXEL") )
+	{
+	  if( !gv_reproject_points( gv_view_area_get_projection(link->src_view),
+				    gv_view_area_get_projection(view),
+				    1, &xdouble, &ydouble, NULL ) )
+	    {
+	      CPLDebug( "GvViewLink", "gv_reproject_points(%s,%s) failed.", 
+			gv_view_area_get_projection(link->src_view), 
+			gv_view_area_get_projection(view) );
+	    }
+	}
+      if (gv_view_area_get_primary_raster(view) != NULL)
+	{
+  	  dst_raster = 
+                GV_RASTER_LAYER(gv_view_area_get_primary_raster(view));
+
+	  if( gv_view_area_get_raw(view,NULL) )
+	    {
+	      gv_raster_georef_to_pixelCL( dst_raster->prototype_data, &xdouble, &ydouble, NULL );
+	    }
+          else if ( dst_raster->prototype_data->poly_orderCL > -1 )
+          {
+	      gv_raster_georef_to_pixelCL( dst_raster->prototype_data, &xdouble, &ydouble, NULL );
+	      gv_raster_pixel_to_georef( dst_raster->prototype_data, &xdouble, &ydouble, NULL );
+          }
+	} 
+      x = (gvgeocoord) xdouble;
+      y = (gvgeocoord) ydouble;
+    } else {
+      x = 0.0;
+      y = 0.0;
+    }
+
+    if (link->cursor_mode == GV_LINK_CURSOR_ON_LOGICAL)
+    {
+        glEnable(GL_COLOR_LOGIC_OP);
+        glLogicOp(GL_XOR);
+    }
+    else
+    {
+        glDrawBuffer(GL_FRONT_LEFT);
+    }
+
+    /*glRenderMode(GL_RENDER);*/
+    if (link->cursor_mode == GV_LINK_CURSOR_ON_LOGICAL)
+        glColor3f(1.0,1.0,1.0);
+    else
+        glColor3f(0.0,1.0,0.0);
+
+    /* Draw crosshairs */
+    glBegin(GL_LINES);
+
+    if ((link->cursor_mode == GV_LINK_CURSOR_ON_LOGICAL) && (view->last_valid))
+    {
+        glVertex2f(view->last_x-dx, view->last_y-dy);
+        glVertex2f(view->last_x+dx, view->last_y+dy);
+        glVertex2f(view->last_x+dy, view->last_y-dx);
+        glVertex2f(view->last_x-dy, view->last_y+dx);
+    }
+
+    if (view->next_valid)
+    {
+        glVertex2f(x-dx, y-dy);
+        glVertex2f(x+dx, y+dy);
+        glVertex2f(x+dy, y-dx);
+        glVertex2f(x-dy, y+dx);
+    }
+    glEnd();
+
+    if (link->cursor_mode == GV_LINK_CURSOR_ON_LOGICAL)
+    {
+        glDisable(GL_COLOR_LOGIC_OP);
+        gtk_gl_area_swap_buffers(GTK_GL_AREA(view)); 
+        if (view->next_valid)
+        {   
+            view->last_x = x;
+            view->last_y = y;
+            view->last_valid = TRUE;
+        }
+        else 
+            view->last_valid = FALSE;
+    }
+    else
+    {
+        glFinish();
+        glDrawBuffer(GL_BACK);
+        /*gtk_gl_area_swap_buffers(GTK_GL_AREA(view));*/    
+    }
+    
+
+}
+
+void
+gv_view_link_set_cursor_mode(GvViewLink *link, int cursor_mode)
+{
+    GList *list;
+
+    if (link->cursor_mode != GV_LINK_CURSOR_OFF)
+    {
+        /* If cursor wasn't off, make sure no artifacts are left */
+        list = link->views;
+        while (list)
+        {
+	    GvViewArea *other_view = (GvViewArea*)list->data;
+	    /* ALL views should be redrawn on leave event, to avoid artifacts in logical cursor case */
+	    gv_view_area_queue_cursor_draw(other_view,FALSE,0.0,0.0);
+	    list = g_list_next(list);
+        }  
+    }
+
+    if (cursor_mode == GV_LINK_CURSOR_OFF)
+        link->cursor_mode = GV_LINK_CURSOR_OFF;
+    else if (cursor_mode == GV_LINK_CURSOR_ON_DEFAULT)
+        link->cursor_mode = GV_LINK_CURSOR_ON_DEFAULT;
+    else if (cursor_mode == GV_LINK_CURSOR_ON_LOGICAL)
+        link->cursor_mode = GV_LINK_CURSOR_ON_LOGICAL;
+    else
+        g_warning("set_gv_view_link_cursor_mode(): invalid cursor mode");
+           
+}      
+
+/* End of ghost cursor-related functions */
+
+static void
+gv_view_link_destroy(GtkObject *object)
+{
+    GtkObjectClass *parent_class;
+    GvViewLink *link;
+
+    link = GV_VIEW_LINK(object);
+
+    /* Remove all views */
+    while (link->views)
+    {
+	GvViewArea *view = (GvViewArea*)link->views->data;
+	gv_view_link_remove_view(link, view);
+    }
+
+    /* Call parent class function */
+    parent_class = gtk_type_class(gtk_gl_area_get_type());
+    GTK_OBJECT_CLASS(parent_class)->destroy(object);
+}
+

Added: packages/openev/branches/upstream/current/gvviewlink.h
===================================================================
--- packages/openev/branches/upstream/current/gvviewlink.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvviewlink.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,96 @@
+/******************************************************************************
+ * $Id: gvviewlink.h,v 1.5 2003/02/21 22:35:31 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Manage linked GvViewAreas.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvviewlink.h,v $
+ * Revision 1.5  2003/02/21 22:35:31  gmwalter
+ * Removed unused variables.
+ *
+ * Revision 1.4  2003/02/20 19:27:19  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.3  2002/01/30 17:26:11  warmerda
+ * make link state copying smarter
+ *
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_VIEW_LINK_H__
+#define __GV_VIEW_LINK_H__
+
+#include <gtk/gtkobject.h>
+#include "gvviewarea.h"
+
+#define GV_TYPE_VIEW_LINK            (gv_view_link_get_type ())
+#define GV_VIEW_LINK(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_VIEW_LINK, GvViewLink))
+#define GV_VIEW_LINK_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_VIEW_LINK, GvViewLinkClass))
+#define GV_IS_VIEW_LINK(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_VIEW_LINK))
+#define GV_IS_VIEW_LINK_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_VIEW_LINK))
+
+typedef struct _GvViewLink       GvViewLink;
+typedef struct _GvViewLinkClass  GvViewLinkClass;
+
+typedef enum {
+    GV_LINK_CURSOR_OFF = 0,
+    GV_LINK_CURSOR_ON_DEFAULT = 1,
+    GV_LINK_CURSOR_ON_LOGICAL = 2
+} GvLinkCursorMode;
+
+
+struct _GvViewLink
+{
+    GtkObject object;
+
+    GList *views;
+    gint enabled : 1;
+    gint blocked : 1;
+
+    /* Ghost cursor stuff */
+    GvLinkCursorMode cursor_mode;
+    GvViewArea *src_view;
+};
+
+struct _GvViewLinkClass
+{
+    GtkObjectClass parent_class;
+};
+
+GtkType    gv_view_link_get_type(void);
+GtkObject* gv_view_link_new(void);
+void gv_view_link_register_view(GvViewLink *link, GvViewArea *view);
+void gv_view_link_remove_view(GvViewLink *link, GvViewArea *view);
+void gv_view_link_enable(GvViewLink *link);
+void gv_view_link_disable(GvViewLink *link);
+/* Ghost cursor function */
+void gv_view_link_set_cursor_mode(GvViewLink *link, int cursor_mode);
+
+#endif /*__GV_VIEW_LINK_H__ */

Added: packages/openev/branches/upstream/current/gvwinprint.c
===================================================================
--- packages/openev/branches/upstream/current/gvwinprint.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvwinprint.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,276 @@
+/******************************************************************************
+ * $Id: gvwinprint.c,v 1.4 2001/05/01 19:21:51 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  MS Windows Print Support
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvwinprint.c,v $
+ * Revision 1.4  2001/05/01 19:21:51  warmerda
+ * include cpl_error.h
+ *
+ * Revision 1.3  2001/05/01 19:20:19  warmerda
+ * reimplement using StretchDIBit(), avoid upsidedown DIB
+ *
+ * Revision 1.2  2001/04/27 20:50:49  warmerda
+ * interim update with everything in one big buffer
+ *
+ * Revision 1.1  2000/08/07 17:17:12  warmerda
+ * New
+ *
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include "gvviewarea.h"
+#include "cpl_error.h"
+
+static  PRINTDLG prDlg;
+static  PAGESETUPDLG psDlg;
+static  HGLOBAL  hDevMode = NULL;
+
+void gv_view_area_page_setup()
+
+{
+    printf( "gv_view_area_page_setup\n" );
+    
+    psDlg.Flags = 0;
+    psDlg.hwndOwner = NULL;
+    psDlg.hDevNames = NULL;
+    psDlg.lStructSize = sizeof (PAGESETUPDLG);
+    psDlg.hDevMode = hDevMode;
+    
+    if (!PageSetupDlg (&psDlg))
+    {
+        if (CommDlgExtendedError ())
+            g_message ("PageSetupDlg failed: %d",
+                       CommDlgExtendedError ());
+        return;
+    }
+    psDlg.Flags |= PSD_MARGINS;
+
+    if( psDlg.hDevMode != NULL )
+        hDevMode = psDlg.hDevMode;
+}
+
+typedef struct {
+    int            width;
+    int            height;
+    int            nextScanline;
+    unsigned char *bgrRow;
+    HDC            hdcMem;
+    float          devOffsetX;
+    float          devOffsetY;
+    float          devScaleX;
+    float          devScaleY;
+} WinDriverOptions;
+
+static gint windriver_handler( void * cb_data, void * scanline_in )
+
+{
+    unsigned char *scanline = (unsigned char *) scanline_in;
+    WinDriverOptions *options = (WinDriverOptions *) cb_data;
+    int            i, scan_off, line_len;
+
+    line_len = 4 * ((options->width * 3 + 3) / 4);
+    scan_off = line_len * (options->height - options->nextScanline - 1);
+    /* We need to convert into BGR format */
+    for( i = 0; i < options->width; i++ )
+    {
+        options->bgrRow[i*3+2+scan_off] = scanline[i*3+0];
+        options->bgrRow[i*3+1+scan_off] = scanline[i*3+1];
+        options->bgrRow[i*3+0+scan_off] = scanline[i*3+2];
+    }
+    
+    options->nextScanline++;
+    
+    return 0;
+}
+
+gint 
+gv_view_area_print_to_windriver(GvViewArea *view, int width, int height,
+                                float ulx, float uly, float lrx, float lry,
+                                int is_rgb )
+
+{
+    DOCINFO	docInfo;
+    double	devResX, devResY;
+    double	imgResX, imgResY;
+    HBITMAP	hBitmap;
+    HANDLE	oldBm;
+    BITMAPV4HEADER bmHeader;
+    HGLOBAL	hDevNames;
+    WinDriverOptions options;
+    gint        errcode;
+  
+    prDlg.hwndOwner = NULL;
+    prDlg.hDevNames = NULL;
+    prDlg.Flags |=
+        PD_RETURNDC | PD_DISABLEPRINTTOFILE | PD_HIDEPRINTTOFILE
+        | PD_NOSELECTION;
+    prDlg.nMinPage = prDlg.nMaxPage = 0;
+    prDlg.nCopies = 1;
+    prDlg.lStructSize = sizeof (PRINTDLG);
+    prDlg.hDevMode = hDevMode;
+
+    if (!PrintDlg (&prDlg))
+    {
+        if (CommDlgExtendedError ())
+            g_message ("PrintDlg failed: %d",
+                       CommDlgExtendedError ());
+        return 1;
+    }
+
+    if( hDevMode == NULL )
+        hDevMode = prDlg.hDevMode;
+    
+    hDevNames = prDlg.hDevNames;
+
+    if( prDlg.hDevMode != NULL )
+        hDevMode = prDlg.hDevMode;
+
+    if (!(GetDeviceCaps(prDlg.hDC, RASTERCAPS) & RC_BITBLT)) 
+    {
+        g_message ("Printer doesn't support bitmaps");
+        return 1;
+    }
+
+    /* Start print job. */
+    docInfo.cbSize = sizeof (DOCINFO);
+    docInfo.lpszDocName = "OpenEV Print";
+    docInfo.lpszOutput = NULL;
+    docInfo.lpszDatatype = NULL;
+    docInfo.fwType = 0;
+
+    if (StartDoc (prDlg.hDC, &docInfo) == SP_ERROR)
+        return 1;
+
+    /* Prepare printer to accept a page. */
+    if (StartPage (prDlg.hDC) <= 0)
+    {
+        g_message ("StartPage failed");
+        AbortDoc (prDlg.hDC);
+        return 1;
+    }
+    
+    options.hdcMem = CreateCompatibleDC (prDlg.hDC);
+
+    memset( &bmHeader, 0, sizeof(bmHeader) );
+    bmHeader.bV4Size = sizeof (BITMAPV4HEADER);
+    bmHeader.bV4Width = width;
+    bmHeader.bV4Height = height;
+    bmHeader.bV4Planes = 1;
+    bmHeader.bV4BitCount = 24;
+    bmHeader.bV4V4Compression = BI_RGB;
+    bmHeader.bV4SizeImage = 0;
+    bmHeader.bV4XPelsPerMeter = 0;
+    bmHeader.bV4YPelsPerMeter = 0;
+    bmHeader.bV4ClrUsed = 0;
+    bmHeader.bV4ClrImportant = 0;
+    bmHeader.bV4CSType = 0;
+
+    hBitmap = CreateDIBSection (options.hdcMem,
+                                (BITMAPINFO *) &bmHeader,
+                                DIB_RGB_COLORS,
+                                (PVOID *) &(options.bgrRow),
+                                NULL,
+                                0);
+    if (hBitmap == NULL)
+    {
+        g_message ("CreateDIBSection failed");
+        AbortDoc (prDlg.hDC);
+        return 1;
+    }
+
+    options.nextScanline = 0;
+    options.width = width;
+    options.height = height;
+    
+    devResX = GetDeviceCaps(prDlg.hDC, LOGPIXELSX); /* in dpi */
+    devResY = GetDeviceCaps(prDlg.hDC, LOGPIXELSY); /* in dpi */
+    
+    CPLDebug( "gvwinprint", "devResX = %f, devResY = %f\n",
+            devResX, devResY );
+
+    CPLDebug( "gvwinprint", "Size=%dmm x %dmm\n",
+            GetDeviceCaps( prDlg.hDC, HORZSIZE ),
+            GetDeviceCaps( prDlg.hDC, VERTSIZE ) );
+    
+    CPLDebug( "gvwinprint", "Resolution=%dp x %dp\n",
+            GetDeviceCaps( prDlg.hDC, HORZRES ),
+            GetDeviceCaps( prDlg.hDC, VERTRES ) );
+
+    options.devOffsetX = 0;
+    options.devOffsetY = 0;
+
+    if( GetDeviceCaps( prDlg.hDC, HORZRES ) / (float) width
+        < GetDeviceCaps( prDlg.hDC, VERTRES ) / (float) height )
+    {
+        options.devScaleX =
+            GetDeviceCaps( prDlg.hDC, HORZRES ) / (float) width;
+        options.devScaleY =
+            GetDeviceCaps( prDlg.hDC, HORZRES ) / (float) width;
+    }
+    else
+    {
+        options.devScaleX =
+            GetDeviceCaps( prDlg.hDC, VERTRES ) / (float) height;
+        options.devScaleY =
+            GetDeviceCaps( prDlg.hDC, VERTRES ) / (float) height;
+    }
+    
+    oldBm = SelectObject (options.hdcMem, hBitmap);
+	  
+    errcode = gv_view_area_render_to_func( view, width, height, 
+                                           windriver_handler, &options );
+    
+    if( StretchDIBits( prDlg.hDC,
+                       (int) options.devOffsetX,
+                       (int) options.devOffsetY,
+                       (int) (width * options.devScaleX),
+                       (int) (height * options.devScaleY),
+                       0, 0, width, height,
+                       (PVOID *) options.bgrRow,
+                       (BITMAPINFO *) &bmHeader,
+                       DIB_RGB_COLORS,
+                       SRCCOPY ) == 0 )
+    {
+        CPLDebug( "gvwinprint", "StretchDIBits() error.\n" );
+        errcode = 1;
+    }
+    
+    SelectObject (options.hdcMem, oldBm);
+    DeleteObject (hBitmap);
+    
+    if (EndPage (prDlg.hDC) <= 0)
+    {
+        g_message ("EndPage failed");
+        EndDoc (prDlg.hDC);
+        return 1;
+    }
+
+    EndDoc (prDlg.hDC);
+
+    DeleteDC( prDlg.hDC );
+    
+    return errcode;
+}

Added: packages/openev/branches/upstream/current/gvzoompantool.c
===================================================================
--- packages/openev/branches/upstream/current/gvzoompantool.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvzoompantool.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,144 @@
+/******************************************************************************
+ * $Id: gvzoompantool.c,v 1.7 2000/07/10 13:37:05 srawlin Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Zoom/Pan editing mode (most zoompan code in gvviewarea.c)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvzoompantool.c,v $
+ * Revision 1.7  2000/07/10 13:37:05  srawlin
+ * updated 3D controls to be more like 2D
+ *
+ * Revision 1.6  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gvzoompantool.h"
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include <GL/gl.h>
+
+static void gv_zoompan_tool_class_init(GvZoomPanToolClass *klass);
+static void gv_zoompan_tool_init(GvZoomPanTool *tool);
+static void gv_zoompan_tool_button_press(GvTool *tool, GdkEventButton *event);
+static void gv_zoompan_tool_button_release(GvTool *tool, GdkEventButton *event);
+static void gv_zoompan_tool_motion_notify(GvTool *tool, GdkEventMotion *event);
+static void gv_zoompan_tool_deactivate(GvTool *tool, GvViewArea *view);
+
+GtkType
+gv_zoompan_tool_get_type(void)
+{
+    static GtkType zoompan_tool_type = 0;
+
+    if (!zoompan_tool_type)
+    {
+	static const GtkTypeInfo zoompan_tool_info =
+	{
+	    "GvZoomPanTool",
+	    sizeof(GvZoomPanTool),
+	    sizeof(GvZoomPanToolClass),
+	    (GtkClassInitFunc) gv_zoompan_tool_class_init,
+	    (GtkObjectInitFunc) gv_zoompan_tool_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	zoompan_tool_type = gtk_type_unique(gv_tool_get_type(),
+					      &zoompan_tool_info);
+    }
+    return zoompan_tool_type;
+}
+
+static void
+gv_zoompan_tool_class_init(GvZoomPanToolClass *klass)
+{
+    GvToolClass *tool_class;
+
+    tool_class = (GvToolClass*)klass;
+    tool_class->deactivate = gv_zoompan_tool_deactivate;
+    tool_class->button_press = gv_zoompan_tool_button_press;
+    tool_class->button_release = gv_zoompan_tool_button_release;
+    tool_class->motion_notify = gv_zoompan_tool_motion_notify;
+}
+
+static void
+gv_zoompan_tool_init(GvZoomPanTool *tool)
+{
+}
+
+GvTool *
+gv_zoompan_tool_new(void)
+{
+    return GV_TOOL(gtk_type_new(GV_TYPE_ZOOMPAN_TOOL));
+}
+
+static void
+gv_zoompan_tool_button_press(GvTool *tool, GdkEventButton *event)
+{
+    if( (event->state & GDK_CONTROL_MASK) 
+        || (event->state & GDK_SHIFT_MASK) 
+        || tool->view->flag_3d)
+        return;
+
+    gv_view_area_zoompan_event(tool->view, event);
+}
+
+static void
+gv_zoompan_tool_button_release(GvTool *tool, GdkEventButton *event)
+{
+    gv_view_area_zoompan_event( tool->view, event );
+}
+
+static void
+gv_zoompan_tool_motion_notify(GvTool *tool, GdkEventMotion *event)
+{
+    GvViewArea *view = tool->view;
+
+    if( (event->state & GDK_BUTTON2_MASK)
+        && !(event->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) )
+
+    {
+        gv_view_area_translate(view, event->x - view->state.mpos_x,
+                               view->state.mpos_y - event->y);
+    }
+
+    /*
+    if (event->state & GDK_BUTTON3_MASK)
+    {
+        gv_view_area_3d_direction_event(tool->view, event);
+    }
+    */
+
+}
+
+static void
+gv_zoompan_tool_deactivate(GvTool *tool, GvViewArea *view)
+{
+    /* Call the parent class func */
+    GV_TOOL_DEACTIVATE(tool, view);
+
+    if( tool->view )
+        tool->view->last_button = 0;
+}
+

Added: packages/openev/branches/upstream/current/gvzoompantool.h
===================================================================
--- packages/openev/branches/upstream/current/gvzoompantool.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/gvzoompantool.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,60 @@
+/******************************************************************************
+ * $Id: gvzoompantool.h,v 1.2 2000/06/20 13:27:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Zoom/Pan editing mode (most zoompan code in gvviewarea.c)
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvzoompantool.h,v $
+ * Revision 1.2  2000/06/20 13:27:08  warmerda
+ * added standard headers
+ *
+ */
+
+#ifndef __GV_ZOOMPAN_TOOL_H__
+#define __GV_ZOOMPAN_TOOL_H__
+
+#include "gvtool.h"
+
+#define GV_TYPE_ZOOMPAN_TOOL            (gv_zoompan_tool_get_type ())
+#define GV_ZOOMPAN_TOOL(obj)            (GTK_CHECK_CAST ((obj), GV_TYPE_ZOOMPAN_TOOL, GvZoompanTool))
+#define GV_ZOOMPAN_TOOL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GV_TYPE_ZOOMPAN_TOOL, GvZoompanToolClass))
+#define GV_IS_ZOOMPAN_TOOL(obj)         (GTK_CHECK_TYPE ((obj), GV_TYPE_ZOOMPAN_TOOL))
+#define GV_IS_ZOOMPAN_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GV_TYPE_ZOOMPAN_TOOL))
+
+typedef struct _GvZoomPanTool       GvZoomPanTool;
+typedef struct _GvZoomPanToolClass  GvZoomPanToolClass;
+
+struct _GvZoomPanTool
+{
+    GvTool tool;
+};
+
+struct _GvZoomPanToolClass
+{
+    GvToolClass parent_class;
+};
+
+GtkType gv_zoompan_tool_get_type(void);
+GvTool* gv_zoompan_tool_new(void);
+
+#endif /* __GV_ZOOMPAN_TOOL_H__ */

Added: packages/openev/branches/upstream/current/html/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/html/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,69 @@
+/Tool_Export.html/1.2/Fri Apr 11 18:03:30 2003//
+/busy.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/classify.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/customization.html/1.4/Tue May 18 13:29:48 2004//
+/edittools.gif/1.3/Wed Jun 25 17:47:46 2003/-kb/
+/edittools.html/1.3/Wed Jun 25 17:47:42 2003//
+/equalize.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/exporttool_advgui.gif/1.2/Fri Apr 11 18:03:30 2003/-kb/
+/exporttool_basicgui.gif/1.2/Fri Sep  3 17:43:18 2004/-kb/
+/files.html/1.1/Tue Aug 15 20:08:37 2000//
+/gvogrdlg.gif/1.1/Tue Jan  7 03:36:53 2003/-kb/
+/gvpquerypropdlg.html/1.1/Tue Jul 25 15:39:52 2000//
+/gvpquerypropdlg_drawstyle.gif/1.1/Tue Jul 25 15:39:52 2000/-kb/
+/gvpquerypropdlg_general.gif/1.1/Tue Jul 25 15:39:52 2000/-kb/
+/gvprint.gif/1.1/Mon Aug  7 21:26:35 2000/-kb/
+/gvprint.html/1.1/Mon Aug  7 21:26:47 2000//
+/gvrasterpropdlg.html/1.2/Tue Dec 11 20:14:34 2001//
+/gvrasterpropdlg_drawstyle.gif/1.2/Tue Dec 11 20:14:34 2001/-kb/
+/gvrasterpropdlg_general.gif/1.2/Tue Dec 11 20:14:34 2001/-kb/
+/gvrasterpropdlg_imageinfo.gif/1.1/Tue Dec 11 20:14:34 2001/-kb/
+/gvrasterpropdlg_lut.jpg/1.2/Tue Dec 11 20:14:34 2001/-kb/
+/gvrasterpropdlg_source.gif/1.2/Tue Dec 11 20:14:34 2001/-kb/
+/gvvectorpropdlg.html/1.1/Tue Jul 25 15:52:51 2000//
+/gvvectorpropdlg_drawstyle.gif/1.1/Tue Jul 25 15:52:51 2000/-kb/
+/gvvectorpropdlg_general.gif/1.1/Tue Jul 25 15:52:51 2000/-kb/
+/help.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/idle.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/layerdlg.gif/1.1/Tue Jul 11 16:11:34 2000/-kb/
+/layerdlg.html/1.1/Tue Jul 11 16:11:34 2000//
+/layerdlg_delete.gif/1.1/Tue Jul 11 16:11:34 2000/-kb/
+/layerdlg_lower.gif/1.1/Tue Jul 11 16:11:34 2000/-kb/
+/layerdlg_new.gif/1.1/Tue Jul 11 16:11:34 2000/-kb/
+/layerdlg_raise.gif/1.1/Tue Jul 11 16:11:34 2000/-kb/
+/legend.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/linear.gif/1.1/Wed Dec 12 15:16:20 2001/-kb/
+/log.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/logo.jpg/1.1/Thu Aug 17 14:09:24 2000/-kb/
+/mainwindow.gif/1.2/Wed Dec 12 15:11:54 2001/-kb/
+/mainwindow.html/1.5/Wed Dec 12 15:11:54 2001//
+/nonelut.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/onetoone.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/open3d.gif/1.3/Fri Sep  3 17:57:52 2004/-kb/
+/open3d.html/1.2/Fri May  3 21:01:39 2002//
+/openevmain.html/1.6/Thu May 13 20:29:16 2004//
+/openfile.gif/1.1/Thu Aug 10 19:45:14 2000/-kb/
+/performance.html/1.4/Wed Sep 27 19:33:41 2000//
+/pref_cache.gif/1.1/Mon Aug 14 20:21:20 2000/-kb/
+/pref_help.gif/1.1/Mon Aug 14 20:21:20 2000/-kb/
+/pref_raster.gif/1.3/Tue Dec 11 20:14:34 2001/-kb/
+/pref_tracking.gif/1.2/Fri Sep 29 19:17:40 2000/-kb/
+/preferences.html/1.4/Tue Dec 11 20:14:34 2001//
+/print.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/pyshell.html/1.4/Mon Jul 28 19:42:33 2003//
+/pyshell_default.gif/1.1/Mon Jul 28 19:42:33 2003/-kb/
+/refresh.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/rotatetool.gif/1.1/Wed Jun 25 17:47:35 2003/-kb/
+/seeall.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/tool_hist.gif/1.2/Fri Sep  3 18:00:31 2004/-kb/
+/tool_roigeneral.gif/1.1/Fri May  3 21:01:39 2002/-kb/
+/tools_openev.gif/1.2/Fri Sep  3 18:02:27 2004/-kb/
+/veclayerselect.html/1.1/Tue Jan  7 03:36:46 2003//
+/viewarea_keys.html/1.11/Fri Mar 12 21:01:00 2004//
+/windowed.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/worldg.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/worldrgb.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/zoom_control.gif/1.1/Wed Dec 12 15:14:52 2001/-kb/
+/zoomin.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+/zoomout.gif/1.1/Thu Aug 10 19:45:15 2000/-kb/
+D

Added: packages/openev/branches/upstream/current/html/CVS/Entries.Log
===================================================================
--- packages/openev/branches/upstream/current/html/CVS/Entries.Log	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/CVS/Entries.Log	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+A D/developer_info////

Added: packages/openev/branches/upstream/current/html/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/html/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/html

Added: packages/openev/branches/upstream/current/html/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/html/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/html/Tool_Export.html
===================================================================
--- packages/openev/branches/upstream/current/html/Tool_Export.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/Tool_Export.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,102 @@
+<html>
+<head>
+<title>Export Tool</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Export Tool</h1>
+
+The export tool can be used to convert raster files from one format to another.
+It has a basic mode for simple conversions, and an advanced mode that 
+can interact with OpenEV to window and scale the data according to the current
+view settings (complex data and colour tables are not currently
+supported for the latter option).  The exported file will be full 
+resolution unless resolution reduction is requested.<p>
+
+<table border=0><tr>
+<td><img src="exporttool_basicgui.gif"></td>
+
+<td>
+<ul>
+<li> 
+In the basic form of the gui, the user simply selects the input and output
+file, the output file format, and the resolution of the exported file relative
+to the input file (Full, 1/2, 1/4, or 1/8). <p> 
+
+<li>
+Data Files: The files may be specified by typing in the complete path (directories 
+must exist already) or selected through the file dialogs launched when the Set Input/
+Set Output buttons are clicked.  When the export tool is launched, it will try to find
+the filename of the currently active layer and fill it into the input file field.
+<p>
+
+<li>
+Output Format: output file format (Geotiff, MFF2, etc.).
+<p>
+
+<li>
+To enable the advanced form of the gui, click the "Advanced Options" toggle.
+<p>
+
+<li>
+Click "Export" to perform the conversion, "Close" to close the tool.
+<p>
+
+</ul>
+
+</td>
+
+</table>
+
+<ul>
+
+
+
+<table border=0><tr>
+<td><img src="exporttool_advgui.gif"></td>
+
+<td>
+<ul>
+<li> 
+In the advanced form of the gui, the user has the additional options of windowing and scaling the data to the
+current view settings.<p> 
+
+<li>
+When "Window Input File" is toggled on, the Edit toolbar's "Draw ROI" mode is activated. Left-clicking and 
+dragging out a region on the view will automatically update the input window information in the gui.  The input
+window can also be entered manually.  Clicking the "Draw ROI mode" button will re-activate the ROI selection mode 
+(this is provided for convenience; launching the edit toolbar and pressing Draw ROI there will do the same thing).
+<p>
+
+<li>
+When "Scale to View Settings" is toggled on, the tool will search for the raster min/max settings of the current
+view, and will use these to scale the input file values from 0 to 255 for export.
+<p>
+
+<li>
+When "Active Layer->Input Filename" is pressed, the tool will update the Input File field with the filename of the
+currently active layer.
+<p>
+
+<li>
+The Geocoding Preference option can be used to specify the types of geocoding information to allow for storage in 
+the output file.  Selecting "Geotransform" will ensure that only geotransform-style geocoding information (if present) 
+is transferred; "GCP" will ensure that only ground control point information is stored. This can be used to force the
+georeferencing information in MFF or MFF2 files to be stored as either ground control points or geotransforms when
+translating to GeoTiff, in cases where the corner and center coordinates can be expressed as a geotransform without
+loss of information (simple rotation/translation/scaling, but no warping).   
+<p>
+
+</ul>
+
+</td>
+
+</table>
+
+<ul>
+
+<p>
+
+
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/busy.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/busy.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/classify.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/classify.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/customization.html
===================================================================
--- packages/openev/branches/upstream/current/html/customization.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/customization.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,581 @@
+<html>
+<head>
+<title>OpenEV Customization</title>
+</head>
+<body bgcolor="#ffffff">
+
+<h1><a name="contents">OpenEV Customization</a></h1>
+<ul>
+
+<li><a href="#section1">Introduction</a>
+<li><a href="#section1a">Tools (Plug-ins)</a>
+<li><a href="#section1b">Tutorial: Adding a new Plug-in</a>
+<li><a href="#section1c">XML File Configuration</a>
+
+</ul>
+
+<h1><a name="section1">Introduction</a></h1>
+
+Certain applications may require capabilities beyond those of
+the standard OpenEV viewer, or may wish to hide some of the existing
+functionality where it is not needed.  While standard python inheritance
+is sometimes the best route, OpenEV does provides some mechanisms
+for customizing its appearance and functionality without rewriting the main
+application or view window code. <p>
+
+XML configuration files can be used to select which of the available menu
+and icon entries will be displayed in OpenEV view windows launched
+by the main application and in the OpenEV python shell, and whether
+or not the history area and progress bar will be shown in the OpenEV
+python shell. The names of the menu entries and icon files used can also
+be altered through the configuration files.  The XML configuration
+files cannot be used to create new callbacks, they can only dictate
+how those callbacks that are present are referenced.<p> 
+
+The functionality of OpenEV can be extended using the Tool_GViewApp class.  
+In the most general case, this class contains only a reference to the
+current OpenEV (GViewApp) instance and a set of entries to add
+to the menu and icon bars of the view windows and python shell.  
+Each menu or pymenu entry is defined by a default string
+describing the menu location (eg. 'Tools/ROI Analysis Tool'), an 
+integer position relative to the existing items within the same menu 
+(eg. 2 would place 'ROI Analysis Tool' after the second entry
+within the 'Tools' menu), and a callback.  The reference to the main 
+application allows the tool to interact with the current views, 
+layers, and edit toolbar.  Each icon entry is defined by
+an iconfile, hint text, default position, callback, help topic (not currently used),
+and text.  All except the callbacks can be customized using XML, though
+the mechanism is slightly different than for the standard window items
+because the callback must be located through the information in the
+XML file since the tool's callback code isn't part of the gvviewwindow class.<p>
+
+<h2><a name="section1a">Tools (Plug-ins)</a></h2>
+
+OpenEV allows you to add hooks to your own callback functions through the
+main application using the Tool_GViewApp class stored in gviewapp.py.
+The base class is very simple- it simply stores a reference to the
+main application (the main application can also be found by importing
+gview, where it is stored as gview.app, but a reference is stored in
+each tool for convenience), and objects that store the
+view and pyshell menu and icon entries added by the tool.
+ 
+<pre>
+class Tool_GViewApp:
+    # Abstract base class to derive tools from
+    def __init__(self,app=None):
+        self.app = app
+        self.menu_entries = Tool_GViewAppMenuEntries()
+        self.icon_entries = Tool_GViewAppIconEntries()
+        self.pymenu_entries = Tool_GViewAppMenuEntries()
+        self.pyicon_entries = Tool_GViewAppIconEntries()
+</pre>
+
+Menu entry information is stored in a dictionary, with the
+entry path as the key and the position and callback as
+a tuple for the value.
+
+<pre>
+class Tool_GViewAppMenuEntries:
+    # Class to store entries to be added to openev's menu
+    def __init__(self):
+        self.entries = {}
+    
+    def set_entry(self,item,position=0,callback=None,accelerator=None):
+        # item = a string describing menu location
+        # position = default location in the menu (integer): Ignored if an
+        #            xml menu entry is specified for the tool.  Note:
+        #            when used, the position refers to position in the
+        #            lowest level menu.  Eg. if a menu entry is
+        #            'File/menu1/entryN', position refer's to entryN's
+        #            position within menu1, not menu1's position in
+        #            File.  For more flexibility, use the xml form of
+        #            configuration.
+        # callback = callback
+        # accelerator = shortcut key
+
+        if (type(item) == type('')):
+            if (type(position) == type(0)):
+                self.entries[item] = (position,callback, accelerator)
+            else:
+                raise AttributeError,"position should be an integer"
+        else:
+            raise AttributeError,"Menu entry item must be a string"
+
+</pre>
+
+Icon entry information is stored as a list, with each
+item in the list containing icon file, hint, position,
+callback, and help topic information.  The icon files
+must be specified by a full path unless they are
+located in OpenEV's tools or pics directory, in which
+case the basename alone can be specified.
+
+<pre>
+class Tool_GViewAppIconEntries:
+    # Class to store entries to be added to openev's menu
+    def __init__(self):
+        self.entries = []
+    
+    def set_entry(self,iconfile,hint_text,position=0,callback=None,help_topic=None,label=None,icontype='xpm'):
+        # iconfile=icon filename (xpm case), or some other string not yet defined
+        #          (pixmap/widget case- not yet supported- may never be)
+        # hint_text=tooltip text to use
+        # position = default location in the icon bar (integer)
+        # callback = callback
+        # help topic = html help file (not yet used by anything)
+        # label = some gtk think- not sure what this does
+        # icontype = 'xpm' (later may allow 'pixmap' or 'widget', but not yet)
+
+        if (type(iconfile) == type('')):
+            import os
+            if os.path.isfile(iconfile):
+                fullfilename=iconfile
+            elif os.path.isfile(os.path.join(gview.home_dir,'tools',iconfile)):
+                fullfilename=os.path.join(gview.home_dir,'tools',iconfile)
+            elif os.path.isfile(os.path.join(gview.home_dir,'pics',iconfile)):
+                fullfilename=os.path.join(gview.home_dir,'pics',iconfile)                
+            else:
+                txt = "Cannot find file "+iconfile+'.  Either the full\n'
+                txt = txt+"path must be specified, or "+iconfile+ " must be\n"
+                txt = txt+"placed in the tools or pics directory."
+                raise AttributeError,txt
+
+            # On nt, path separators need to be trapped and doubled to avoid
+            # being interpreted as an escape before special characters.
+            if os.name == 'nt':
+                import string
+                fullfilename=string.replace(fullfilename,"\\","\\\\")
+                
+            if (type(position) == type(0)):
+                self.entries.append((fullfilename,label,hint_text,position,callback,help_topic,icontype))
+            else:
+                raise AttributeError,"position should be an integer"
+        else:
+            txt = "Cannot find file "+iconfile+'.  Either the full\n'
+            txt = txt+"path must be specified, or "+iconfile+ " must be\n"
+            txt = txt+"placed in the tools or pics directory."
+            raise AttributeError,txt
+
+</pre>
+
+
+<h3>An Example: The GeneralROITool</h3> 
+In practice, the user defines a class which inherits from the
+Tool_GViewApp class (or at least has the same basic components
+defined), and adds menu entries and the callbacks associated with 
+these entries.
+The GeneralROITool class in toolexample.py shows how this works.
+This is a demonstration tool that adds a 'Tools/ROI Analysis Tool' entry
+to the menu.  When called, it activates the Edit toolbar's 'Draw ROI'
+method, and displays a dialog (see below).  When the user selects
+a region, it updates the dialog to display the currently selected
+region.  Pressing 'Analyze' will get it to print these values in the
+terminal window. <p>
+
+<img src="tool_roigeneral.gif"><p>
+<img src="tools_openev.gif"><p>
+
+In its initialization function, the GeneralROITool:
+<ul>
+<li>  Calls gviewapp.Tool_GViewApp's __init__ function.  
+This creates the reference to the main application
+(self.app) and an empty set of menu entries (self.menu_entries).
+<li>  Creates a StoredROIPOI class instance to store the last selected 
+region of interest (used to avoid tool failure if the 'Draw ROI' method
+has been deactivated in the Edit toolbar).
+<li>  Calls its init_dialog function to create the dialog displayed when
+the 'Tools/ROI Analysis Tool' menu entry is selected.  This dialog is
+hidden until the menu entry is called.
+<li>  Calls its init_menu function to set up the menu entry.  The
+associated callback shows the dialog created in the init_dialog step.
+<li>  Calls init_connections to set up signal connections between the tool and
+its dialog (such as the 'analyze-pressed' signal emitted by the dialog), and 
+between the tool and the main application (such as the 'roi-changed' signal
+emitted by the roi tool in the Edit toolbar). 
+</ul> 
+
+<h3>A More Advanced Example: The Histogram Tool</h3>
+The histogram tool inherits the dialog and connections of the GeneralROITool,
+but instead of just printing out the bounds of the currently selected region,
+it extracts the values contained in that region and forms a histogram
+from them.  The tool's dialog has been extended to display this.
+This tool is available from SourceForge CVS (openev/tools/gvrastertools.py),
+but is not yet included in pre-built openev distributions, as it is still
+under development and not yet stable for all platforms and data types.
+<p>
+
+<img src="tool_hist.gif"><p>
+
+<h3>Registering A Tool</h3>
+Two mechanisms are provided for registering custom tools:<p>
+<ul>
+<li><b>Auto-Registration</b>  OpenEV will automatically search through
+all the py-files in its 'tools' directory, and will load any tools
+listed in the global 'TOOL_LIST' variable for each module. 
+<li><b>Command Line Argument</b>  OpenEV allows you to specify a text
+file that lists modules to import and the tools to load from them at
+the command line.  For instance, starting up openev with the command
+"openev -t (full or relative path)/tool_example.txt", where 
+tool_example.txt contains:<p>
+<pre>
+MODULE_NAME = toolexample
+TOOL_NAME=GeneralROITool
+TOOL_NAME=GeneralPOITool
+</pre>
+will result in OpenEV loading the GeneralROITool and GeneralPOITool
+tools from toolexample.py.  Note that the PYTHONPATH variable must
+include toolexample.py's directory for this to work. 
+</ul>
+
+<h2><a name="section1b">Tutorial: Adding a new Plug-in</a></h2>
+
+To be able to write a comprehensive OpenEV GUI plug-in you should be familiar with
+GTK+ programming using Python (PyGTK).<p>
+
+OpenEV modules should be placed in the <i>tools/</i>
+subdirectory to be loaded automatically.<p>
+
+We will write a simple GUI module to automate the Fast Fourier Transform task. As a
+first attempt we make a menu item to perform an FFT.<p>
+
+<pre>
+# OpenEV module fft.py
+
+from gtk import *
+
+import gview
+import GtkExtra
+import gviewapp
+import gdalnumeric
+import FFT
+
+class FFTTool(gviewapp.Tool_GViewApp):
+
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Image processing/FFT",2,self.calculate_fft)
+
+    def calculate_fft(self,*args):
+        layer = gview.app.sel_manager.get_active_layer()
+        ds = layer.get_parent().get_dataset()
+        data = gdalnumeric.DatasetReadAsArray(ds)
+        data_tr = FFT.fft2d(data)
+        array_name = gdalnumeric.GetArrayFilename(data_tr)
+        gview.app.file_open_by_name(array_name)
+
+TOOL_LIST = ['FFTTool']
+</pre>
+
+This module adds a new top level menu "Image processing" with single item
+"FFT". Save this text into file, place it into <i>tools/</i> and restart
+OpenEV. Load an image, and click on the new menu item. The transformed image
+appears as a new layer (you may need click on the "Fit All Layers" button to see
+it).<p>
+
+Note, that you can easily extend the functionality of OpenEV with this
+approach. You can write scripts for your most often performed tasks (like
+calibration or NDVI calculation, described in the python shell section) and place them
+on the menu or taskbar.<p>
+
+Ok, our simplest script works, but now we will go further and make a dialog
+box to allow the user to select what type of transformation (forward or inverse) we
+should perform and where to place the results (in new layer of the same view or
+create a new view and place results there).<p>
+
+<pre>
+# OpenEV module fft2.py
+
+from gtk import *
+
+import gview
+import GtkExtra
+import gviewapp
+import gdalnumeric
+import FFT
+
+class FFT2Tool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        self.win = FFTDialog()
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Image processing/FFT2",2,self.launch_dialog)
+
+class FFTDialog(GtkWindow):
+
+    def __init__(self,app=None):
+        GtkWindow.__init__(self)
+        self.set_title('Fast Fourier Transform')
+        self.create_gui()
+        self.show()
+
+    def show(self):
+        GtkWindow.show_all(self)
+
+    def close(self, *args):
+        self.destroy()
+
+    def create_gui(self):
+        self.box1 = GtkVBox(spacing = 10)
+        self.box1.set_border_width(10)
+        self.add(self.box1)
+        self.box1.show()
+
+        self.switch_forward = GtkRadioButton(None, "Forward")
+        self.box1.pack_start(self.switch_forward)
+        self.switch_forward.show()
+        self.switch_inverse = GtkRadioButton(self.switch_forward, "Inverse")
+        self.box1.pack_start(self.switch_inverse)
+        self.switch_inverse.show()
+
+        self.separator = GtkHSeparator()
+        self.box1.pack_start(self.separator, expand=FALSE)
+
+        self.switch_new_view = GtkCheckButton("Create new view")
+        self.box1.pack_start(self.switch_new_view)
+        self.switch_new_view.show()
+
+        self.separator = GtkHSeparator()
+        self.box1.pack_start(self.separator, expand=FALSE)
+
+        self.box2 = GtkHBox(spacing=10)
+        self.box1.pack_start(self.box2, expand=FALSE)
+        self.box2.show()
+
+        self.execute_btn = GtkButton("Ok")
+        self.execute_btn.connect("clicked", self.execute_cb)
+        self.box2.pack_start(self.execute_btn)
+        self.execute_btn.set_flags(CAN_DEFAULT)
+        self.execute_btn.grab_default()
+        
+        self.close_btn = GtkButton("Cancel")
+        self.close_btn.connect("clicked", self.close)
+        self.box2.pack_start(self.close_btn)
+
+    def execute_cb( self, *args ):
+        layer = gview.app.sel_manager.get_active_layer()
+        ds = layer.get_parent().get_dataset()
+        data = gdalnumeric.DatasetReadAsArray(ds)
+
+        if self.switch_forward.get_active():
+            data_tr = FFT.fft2d(data)
+        else:
+            data_tr = FFT.inverse_fft2d(data)
+        array_name = gdalnumeric.GetArrayFilename(data_tr)
+
+        if self.switch_new_view.get_active():
+            gview.app.new_view()
+            gview.app.file_open_by_name(array_name)
+        else:
+            gview.app.file_open_by_name(array_name)
+
+TOOL_LIST = ['FFT2Tool']
+</pre>
+
+As you can see, the most laborious part of the work is creating GUI
+controls.<p> 
+
+<h2><a name="section1c">XML File Configuration</a></h2>
+XML configuration files are located in OpenEV's "xmlconfig" directory.
+Parts of a view menu XML file are shown below.  The GViewAppMenu enclosing
+entry indicates that this is a configuration file for the menus in the main
+view window; this entry would be GViewAppIconBar for the main view window 
+icon bar, or GViewAppPyshell for the python shell. <p>
+
+The &#60;entry&#62; items below are used to indicate the locations of standard
+view window entries- ie. those that have callbacks defined in gvviewwindow.py.
+The &#60;path&#62; part of the entry indicates how the entry will show up in the
+menu; &#60;callback&#62; indicates which gvviewwindow callback to use.  The
+'File/rfl1' entry demonstrates how a simple argument (int or string) can
+be passed to the callback. <p>
+
+The &#60;tools&#62;, &#60;simpletoolentry&#62;, and &#60;complextoolentry&#62; items are used
+to define which tools' menu and icon entries will show up, and to
+customize their appearance.  If no tool entries will be specified, all
+currently loaded tools will be shown.  
+If a tool is indicated in the file but is not loaded, an error will occur.
+The &#60;tools&#62; entry, if present, must contain one of the strings All,
+None, or Some.  If &#60;tools&#62; is All, all currently loaded tools will have
+entries in the view menu.  If &#60;tools&#62; is None, no tool entries will be
+loaded in the view menu, and an error will occur if &#60;simpletoolentry&#62;
+or &#60;complextoolentry&#62; items occur later in the XML file.  If &#60;tools&#62; is
+Some, only entries specified through &#60;simpletoolentry&#62; or &#60;complextoolentry&#62;
+items will show up. <p>
+
+The &#60;simpletoolentry&#62; item is used to put in tool entries using their
+Menu paths.  The position of the menu entry will be determined by
+the entry's position in the XML file.  For instance, GDALTool (the
+Export tool- Tool_Export.py) defines a menu entry "File/Export".  In
+the case below, its entry would show up between "Import" and "Print" 
+in the "File" menu. <p>
+
+The &#60;complextoolentry&#62; item is used to redefine the path names, and to
+select among entries to show in cases where a tool defines more than
+one menu entry.  Each &#60;complextoolentry&#62; must define the tool,
+the old menu path (tool default) corresponding to the callback to be
+used, and a new menu path.  The old menu path is used as a key to
+the tool's menu_entries dictionary to locate the desired callback.  
+For instance, the &#60;complextoolentry&#62; item
+below indicates that the callback from RenderTestTool that corresponds
+to its "Render Test" entry under the "Tools" menu should appear in this view 
+as a "Render Test" entry under the "File" menu, between the "Print"
+and separator items.  If RenderTestTool specified other menu entries,
+they would have to appear as separate &#60;complextoolentry&#62; items in
+order to appear in the view menu. <p>
+
+<pre>
+&#60;GViewAppMenu&#62;
+ &#60;Menu&#62;
+  &#60;tools&#62;All&#60;/tools&#62;
+  &#60;entry&#62;
+    &#60;path&#62;'File/Import'&#60;/path&#62;
+    &#60;callback&#62;self.file_import_cb&#60;/callback&#62;
+  &#60;/entry&#62;
+  &#60;simpletoolentry&#62;
+     &#60;name&#62;GDALTool&#60;/name&#62;
+  &#60;/simpletoolentry&#62;
+  &#60;entry&#62;
+...
+    &#60;path&#62;'File/Print'&#60;/path&#62;
+    &#60;callback&#62;self.print_cb&#60;/callback&#62;
+  &#60;/entry&#62;
+  &#60;complextoolentry&#62;
+     &#60;name&#62;RenderTestTool&#60;/name&#62;
+     &#60;oldpath&#62;'Tools/Render Test'&#60;/oldpath&#62;
+     &#60;newpath&#62;'File/Render Test'&#60;/newpath&#62;
+  &#60;/complextoolentry&#62;
+  &#60;entry&#62;
+    &#60;path type="separator"&#62;'File/'&#60;/path&#62;
+  &#60;/entry&#62;
+  &#60;entry&#62;
+    &#60;path&#62;'File/rfl1'&#60;/path&#62;
+    &#60;callback&#62;self.rfl_cb&#60;/callback&#62;
+    &#60;arguments&#62;
+      &#60;arg&#62;1&#60;/arg&#62;
+    &#60;/arguments&#62;
+  &#60;/entry&#62;
+...
+ &#60;/Menu&#62;
+&#60;/GViewAppMenu&#62;
+</pre>
+
+In the icon bar XML file, the entry parameters defined are the file to use
+for the icon (&#60;xpm&#62;), the tooltip text (&#60;hint&#62;), and the
+callback.  Position in the icon bar is determined by an entry's position
+in the file.<p>  
+
+As in the menu bar XML file, the tool entries are specified using the &#60;tool&#62;,
+&#60;simpletoolentry&#62;, and &#60;complextoolentry&#62; items.  Tool icon entries are stored 
+internally as a list rather than a dictionary in case an application wishes to 
+use the same icon file twice with different tooltip text to differentiate between 
+callbacks, so an index is used to specify which of a tool's icon entries to use.
+The &#60;complextoolentry&#62; item can be used to override the icon file, hint, help (not
+currently used by OpenEV for icons) and position of an item.<p>
+
+An example of an XML icon file for view windows is given below (... indicates 
+where sections have been removed for brevity):
+
+<pre>
+&#60;GViewAppIconBar&#62;
+ &#60;Iconbar&#62;
+   &#60;icon&#62;
+     &#60;xpm&#62;'openfile.xpm'&#60;/xpm&#62;
+     &#60;hint&#62;'Open and Display Raster/Vector File'&#60;/hint&#62;
+     &#60;callback&#62;self.file_open_cb&#60;/callback&#62;
+   &#60;/icon&#62;
+   &#60;icon&#62;
+     &#60;xpm&#62;'print.xpm'&#60;/xpm&#62;
+     &#60;hint&#62;'Print Current View'&#60;/hint&#62;
+     &#60;callback&#62;self.print_cb&#60;/callback&#62;
+     &#60;help&#62;'gvprint.html'&#60;/help&#62;
+   &#60;/icon&#62;
+   ...
+   &#60;complextoolentry&#62;
+       &#60;name&#62;ShapesGridTool&#60;/name&#62;
+       &#60;hint&#62;Newhint&#60;/hint&#62;
+       &#60;index&#62;0&#60;/index&#62;
+   &#60;/complextoolentry&#62;
+   ...
+ &#60;/Iconbar&#62;
+&#60;/GViewAppIconBar&#62;
+</pre>
+
+The python shell menu and icons entries are combined together
+in a single file that also indicates other options in the pyshell layout, such
+as whether a history area and progress bar are present. The menu and icon
+specifications are analogous to the view menu and icon specifications.  The message
+area and prompt will always be present.  An example of a python shell XML configuration
+file is given below: <p>
+
+<pre>
+&#60;GViewAppPyshell&#62;
+ &#60;Menu&#62;
+  &#60;tools&#62;All&#60;/tools&#62;
+  &#60;entry&#62;
+    &#60;path&#62;'File/Preferences'&#60;/path&#62;
+    &#60;callback&#62;self.preferences_cb&#60;/callback&#62;
+  &#60;/entry&#62;
+  &#60;entry&#62;
+    &#60;path&#62;'File/Quit'&#60;/path&#62;
+    &#60;callback&#62;self.close_cb&#60;/callback&#62;
+    &#60;accelerator&#62;control+D&#60;/accelerator&#62;
+  &#60;/entry&#62;
+  &#60;entry&#62;
+    &#60;path&#62;'Help/Help'&#60;/path&#62;
+    &#60;callback&#62;self.launch_help_cb&#60;/callback&#62;
+  &#60;/entry&#62;
+ &#60;/Menu&#62;
+ &#60;Iconbar&#62;
+   &#60;tools&#62;Some&#60;/tools&#62;
+   &#60;complextoolentry&#62;
+       &#60;name&#62;MyTool&#60;/name&#62;
+       &#60;hint&#62;MyToolHint&#60;/hint&#62;
+       &#60;index&#62;0&#60;/index&#62;
+   &#60;/complextoolentry&#62;
+ &#60;/Iconbar&#62;
+ &#60;History&#62;
+ &#60;/History&#62;
+ &#60;Progress&#62;
+ &#60;/Progress&#62;
+&#60;/GViewAppPyshell&#62;
+</pre>
+In the case above, an empty entry (eg. &#60;History&#62; &#60;/History&#62;) 
+indicates that the entry should be present with the default settings.  
+If an entry is not present (eg. the whole &#60;Menu&#62;...&#60;/Menu&#62; 
+entry was removed), that component will not be present.  If the component is 
+present, only the subentries
+specified will be used.  The file above specifies a python shell with
+a menu with all tool menu entries, plus entries for the help callback
+(Help-&gt Help), a quit entry (File-&gt Quit), and an entry for specifying 
+paths to search for python modules (File-&gt Preferences).
+The iconbar would contain only a single
+entry- the first icon entry specified in tool MyTool, with its hint text
+replaced by MyToolHint.  The history bar and progress bar would be
+present with their default settings (currently there are no options
+to alter in these- they are either present or not present).
+The progress bar is not used automatically even if it is present, though 
+functions that are going to take a long time can update it by 
+importing gview and placing the following line inside the function
+wherever they want the progress bar to update:
+
+<pre>
+gview.app.shell.show_progress(i,msg)
+</pre>
+<p>
+where <i>i</i> is a number between 0 and 100 (the amount of progress
+to show in the bar), and msg is a string to display in the message
+section.
+<p>
+ 
+<a href="#contents">Top</a><p>
+<a href="openevmain.html>OpenEV Help</a><p>
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_big_picture.png
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/COURSE1_big_picture.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_gdal_datamodel.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/COURSE1_gdal_datamodel.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/COURSE1_gdal_datamodel.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,259 @@
+<html>
+<head>
+<title>GDAL Data Model</title>
+</head>
+
+<body BGCOLOR="#FFFFFF">
+
+<body>
+<pre>
+
+			GDAL Data Model
+		        ===============
+
+This document attempts to describe the GDAL data model.  That is the
+types of information that a GDAL data store can contain, and their 
+semantics.
+
+Dataset
+=======
+
+A dataset (represented by the GDALDataset class) is an assembly of 
+related raster bands and some information common to them all.  In particular
+the dataset has a concept of the raster size (in pixels and lines) that
+applies to all the bands.  The dataset is also responsible for the 
+georeferencing transform and coordinate system definition of all bands.  The
+dataset itself can also have associated metadata, a list of name/value
+pairs in string form. 
+
+Note that the GDAL dataset, and raster band data model is loosely 
+based on the OpenGIS Grid Coverages specification. 
+
+Coordinate System
+-----------------
+
+Dataset coordinate systems are represented as OpenGIS Well Known Text
+strings.  This can contain:
+
+ o An overall coordinate system name. 
+ o A geographic coordinate system name. 
+ o A datum identifier. 
+ o An ellipsoid name, semi-major axis, and inverse flattening. 
+ o A prime meridian name and offset from Greenwich. 
+ o A projection method type (ie. Transverse Mercator). 
+ o A list of projection parameters (ie. central_meridian). 
+ o A units name, and conversion factor to meters or radians. 
+ o Names and ordering for the axes. 
+ o Codes for most of the above in terms of predefined coordinate systems
+   from authorities such as EPSG. 
+
+For more information on OpenGIS WKT coordinate system definitions, and 
+mechanisms to manipulate them, refer to the osr_tutorial document and/or the 
+OGRSpatialReference class documentation.
+
+The coordinate system returned by GDALDataset::GetProjectionRef() 
+describes the georeferenced coordinates implied by the affine georeferencing
+transform returned by GDALDataset::GetGeoTransform().  The coordinate
+system returned by GDALDataset::GetGCPProjection() describes the 
+georeferenced coordinates of the GCPs returned by GDALDataset::GetGCPs().
+
+Note that a returned coordinate system strings of "" indicates nothing
+is known about the georeferencing coordinate system.  
+
+Affine GeoTransform
+-------------------
+
+GDAL datasets have two ways of describing the relationship between
+raster positions (in pixel/line coordinates) and georeferenced coordinates.
+The first, and most commonly used is the affine transform (the other is
+GCPs).  
+
+The affine transform consists of six coefficients returned by 
+GDALDataset::GetGeoTransform() which map pixel/line coordinates into 
+georeferenced space using the following relationship:
+
+    Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2)
+    Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5)
+
+In case of north up images, the GT(2) and GT(4) coefficients are zero, and
+the GT(1) is pixel width, and GT(5) is pixel height.  The (GT(0),GT(3))
+position is the top left corner of the top left pixel of the raster.
+
+
+GCPs
+----
+
+A dataset can have a set of control points relating one or more positions
+on the raster to georeferenced coordinates.  All GCPs share a georeferencing
+coordinate system (returned by GDALDataset::GetGCPProjection()).  Each GCP
+(represented as the GDAL_GCP class) contains the following:
+
+typedef struct
+{
+    char	*pszId; 
+    char	*pszInfo;
+    double 	dfGCPPixel;
+    double	dfGCPLine;
+    double	dfGCPX;
+    double	dfGCPY;
+    double	dfGCPZ;
+} GDAL_GCP;
+
+The pszId string is intended to be a unique (and often, but not always
+numerical) identifier for the GCP within the set of GCPs on this dataset.
+The pszInfo is usually an empty string, but can contain any user defined
+text associated with the GCP.  Potentially this can also contain machine 
+parsable information on GCP status though that isn't done at this time.
+
+The (Pixel,Line) position is the GCP location on the raster.  The (X,Y,Z)
+position is the associated georeferenced location with the Z often being
+zero. 
+
+The GDAL data model does not imply a transformation mechanism that must
+be generated from the GCPs ... this is left to the application.  However
+1st to 5th order polynomials are common. 
+
+Normally a dataset will contain either an affine geotransform, GCPs or
+neither.  It is uncommon to have both, and it is undefined which is 
+authoritative.
+
+
+Metadata
+--------
+
+GDAL metadata is auxilary format and application specific textual data
+kept as a list of name/value pairs.  The names are required to be well 
+behaved tokens (no spaces, or odd characters).  The values can be of
+any length, and contain anything except an embedded null (ASCII zero).  
+
+The metadata handling system is well tuned to handling very large bodies
+of metadata.  Handling of more than 100K of metadata for a dataset is likely
+to lead to performance degredation. 
+
+Over time there will be some well known names defined with established
+semantics; however, that has not occured at this time. 
+
+Some formats will support generic (user defined) metadata, while other
+format drivers will map specific format fields to metadata names.  For
+instance the TIFF driver returns a few information tags as metadata
+including the date/time field which is returned as:
+
+TIFFTAG_DATETIME=1999:05:11 11:29:56
+
+Raster Band
+===========
+
+A raster band is represented in GDAL with the GDALRasterBand class.  It 
+represents a single raster band/channel/layer.  It does not necessarily
+represent a whole image.  For instance, a 24bit RGB image would normally
+be represented as a dataset with three bands, one for red, one for green
+and one for blue. 
+
+A raster band has the following properties:
+
+ o A width and height in pixels and lines.  This is the same as that 
+   defined for the dataset, if this is a full resolution band. 
+
+ o A datatype (GDALDataType).  One of Byte, UInt16, Int16, UInt32, Int32, 
+   Float32, Float64, and the complex types CInt16, CInt32, CFloat32, and 
+   CFloat64.
+
+ o A block size.  This is a preferred (efficient) access chunk size.  For
+   tiled images this will be one tile.  For scanline oriented images this will 
+   normally be one scanline. 
+
+ o A list of name/value pair metadata in the same format as the dataset,
+   but of information that is potentially specific to this dataset.
+
+ o An optional description string. 
+
+ o An optional list of category names (effectively class names in a 
+   thematic image).  
+
+ o An optional minimum and maximum value. 
+
+ o An optional offset and scale for transforming raster values into 
+   meaningful values (ie translate height to meters) 
+
+ o An optional raster unit name.  For instance, this might indicate linear 
+   units for elevation data.
+
+ o A color interpretation for the band.  This is one of:
+   - GCI_Undefined: the default, nothing is known.
+   - GCI_GrayIndex: this is an independent grayscale image
+   - GCI_PaletteIndex: this raster acts as an index into a color table
+   - GCI_RedBand: this raster is the red portion of an RGB or RGBA image
+   - GCI_GreenBand: this raster is the green portion of an RGB or RGBA image
+   - GCI_BlueBand: this raster is the blue portion of an RGB or RGBA image
+   - GCI_AlphaBand: this raster is the alpha portion of an RGBA image
+   - GCI_HueBand: this raster is the hue of an HLS image
+   - GCI_SaturationBand: this raster is the saturation of an HLS image
+   - GCI_LightnessBand: this raster is the hue of an HLS image
+   - GCI_CyanBand: this band is the cyan portion of a CMY or CMYK image
+   - GCI_MagentaBand: this band is the magenta portion of a CMY or CMYK image
+   - GCI_YellowBand: this band is the yellow portion of a CMY or CMYK image
+   - GCI_BlackBand: this band is the black portion of a CMYK image. 
+
+ o A color table, described in more detail later. 
+
+ o Knowledge of reduced resolution overviews (pyramids) if available. 
+
+
+Color Table
+-----------
+
+A color table conists of zero or more color entries described in C by the
+following structure:
+
+typedef struct
+{
+    /- gray, red, cyan or hue -/
+    short      c1;      
+
+    /- green, magenta, or lightness -/    
+    short      c2;      
+
+    /- blue, yellow, or saturation -/
+    short      c3;      
+
+    /- alpha or blackband -/
+    short      c4;      
+} GDALColorEntry;
+
+The color table also has a palette interpretation value (GDALPaletteInterp)
+which is one of the following values, and indicates how the c1/c2/c3/c4 values
+of a color entry should be interpreted. 
+
+ o GPI_Gray: Use c1 as grayscale value. 
+ o GPI_RGB: Use c1 as red, c2 as green, c3 as blue and c4 as alpha.
+ o GPI_CMYK: Use c1 as cyan, c2 as magenta, c3 as yellow and c4 as black. 
+ o GPI_HLS: Use c1 as hue, c2 as lightness, and c3 as saturation. 
+
+To associate a color with a raster pixel, the pixel value is used as a
+subscript into the color table.  That means that the colors are always
+applied starting at zero and accending.  There is no provision for indicating
+a prescaling mechanism before looking up in the color table. 
+
+
+Overviews
+---------
+
+A band may have zero or more overviews.  Each overview is represented as
+a "free standing" GDALRasterBand.  The size (in pixels and lines) of the 
+overview will be different than the underlying raster, but the geographic
+region covered by overviews is the same as the full resolution band.  
+
+The overviews are used to display reduced resolution overviews more quickly
+than could be done by reading all the full resolution data and downsampling. 
+
+Bands also have a HasArbitraryOverviews property which is TRUE if the
+raster can be read at any resolution efficiently but with no distinct
+overview levels.  This applies to some FFT encoded images, or images pulled
+through gateways (like OGDI) where downsampling can be done efficiently
+at the remote point. 
+
+</pre>
+<a href="COURSE1_oev_talk.html">Frank's Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a>
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_lib_inherit.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/COURSE1_lib_inherit.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_numpy.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/COURSE1_numpy.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/COURSE1_numpy.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,55 @@
+<html>
+<head>
+<title>Numerical Python</title>
+</head>
+
+<body BGCOLOR="#FFFFFF">
+
+<body>
+<pre>
+
+	Numerical Python
+	================
+
+Numerical Python adds a fast, compact, multidimensional array language 
+facility to Python.  
+
+ o Complete information is available at http://www.pfdubois.com/numpy/
+
+ o Data types for arrays include byte, integer, float, double and complex.
+
+ o Arrays support a variety of python style slicing operations to access
+   subregions of the array. 
+
+ o Numpy includes interfaces to the LAPACK linear algebra library and the
+   FFTPACK Fourier transforms library. 
+
+ o Convenient methods have been adding for translating GDAL images into 
+   NumPy arrays, and for displaying NumPy arrays in OpenEV.  However, the
+   NumPy representation loses georeferencing, metadata, and colortables. 
+
+This example loads a band from a GDAL supported file (dist_file) into a
+NumPy array (distmap), converts it to floating point, does some manipulations,
+displays the result and then saves to another file. 
+
+   from Numeric import *
+   from gdalnumeric import *
+
+   distmap = LoadFile( dist_file )
+   # hack: make sure no values are zero
+   distmap = distmap.astype(Float32) + 0.001
+   weightmap = sample[3] / power (distmap , exponent)
+   weightmap = weightmap.astype(Float32)
+   display( weightmap )
+   SaveArray( weightmap, weight_file, 'PAux' )
+
+(demo similar steps in OpenEV using the Interactive Shell)   
+
+It would be good to have a more in depth review of NumPy another time.
+</pre>
+<a href="COURSE1_gdal_datamodel.html">Next</a><br>
+<a href="COURSE1_oev_talk.html">Frank's Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a>
+</body>
+</html>
+

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_oev_talk.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/COURSE1_oev_talk.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/COURSE1_oev_talk.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,34 @@
+<html>
+<head>
+<title>Frank Warmerdam's OpenEV Course</title>
+</head>
+
+<body BGCOLOR="#FFFFFF">
+
+<body>
+
+<h1><a name="Introduction">Introduction</a></h1>
+
+This document is based on notes from a course that Frank 
+Warmerdam gave to new developers at Atlantis a few years
+ago.  It provides an introduction to the major components of the 
+development environment (Python, OpenGL, GTK, GDAL, PROJ.4, 
+NumPy), the OpenEV system architecture,
+initial design objectives, and technical information about 
+the data model, layer drawing, editing
+tools, user interface, and command-line interface.
+
+<p>
+<h1><a name="Contents">Contents</a></h1>
+<ul>
+<li> <a href="COURSE1_openev.html">OpenEV Architecture</a>
+<li> <a href="COURSE1_python.html">Python</a>
+<li> <a href="COURSE1_numpy.html">Numerical Python</a>
+<li> <a href="COURSE1_gdal_datamodel.html">GDAL Data Model</a>
+</ul>
+
+<a href="COURSE1_openev.html">Next</a><br>
+<a href="../openevmain.html">OpenEV Help</a>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_openev.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/COURSE1_openev.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/COURSE1_openev.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,293 @@
+<html>
+<head>
+<title>OpenEV Architecture</title>
+</head>
+
+<body BGCOLOR="#FFFFFF">
+
+<body>
+<pre>
+		OpenEV Architecture
+		===================
+
+</pre>
+
+See the <a href="http://openev.sourceforge.net/lib_design_doc/openev.html">library design document</a> 
+for the detailed library design.
+
+<pre>
+The following figure shows an inheritance diagram of the main OpenEV
+classes, described below.
+</pre>
+<img src="COURSE1_lib_inherit.gif">
+<pre>
+
+GvViewArea
+----------
+
+ o Derived from GtkGLArea (a GTK widget for drawing with OpenGL).
+
+ o Maintains a of GvLayers (in drawing order), and a concept of which is 
+   the "active" layer for the purposes of editing, and some other operations.
+
+ o Maintains a current view state (camera position and orientation in 3D, 
+   zoom factor and center point in 2D), 
+
+ o Provides some automatic event handling for zooming, and panning.  
+
+ o Generates the "active-changed", "view-state-changed", and "gldraw" events.
+ 
+
+GvData
+------
+
+ o Derived from GtkData. 
+
+ o Primarily it provides services to implement undo semantics for many
+   different types of objects.  See Undo discussion below.
+
+ o Generates the "changing", "changed" and "meta-changed" events. 
+
+ o Implements core services for managing undo "mementos".  
+
+ o Also contains a properties list, a projection, a name, and a concept of
+   a parent.  
+
+GvShapes
+--------
+
+ o Derived from GvData. 
+
+ o Acts as a container for the vector objects (GvShape structures). 
+
+ o Implements all sorts of create, update (ie. translate) and delete 
+   operations. 
+
+ o Implements meaningful undo for all updates, create and delete operations. 
+
+ o Note that the GvShapes container has no concept of selection ... that is
+   a GvShapesLayer concept. 
+
+GvShape
+-------
+
+ o Unlike essentially everything else described here, the GvShape is just
+   a simple structure, it isn't derived from GtkObject.  This is intended to
+   keep it lightweight. 
+
+ o It represents a single vector feature.  
+
+ o The supported geometry types are point, line and area.  Areas may have
+   multiple rings allowing them to have holes.  
+
+ o Geometries are all 3D, though Z is usually 0.
+
+ o Each GvShape has a properties list (list of name/value pairs). 
+
+ o GvShape's are reference counted. 
+
+ o GvShape's do retain some rendering related information (tesselation of
+   areas). 
+
+GvRaster
+--------
+
+ o Derived from GvData.
+
+ o Represents one band of raster data (real, or complex) - it corresponds
+   directly to a GDALRasterBand.
+
+ o It maintains a tile oriented cache of raster data (in addition to the 
+   cache maintained by GDAL!).  The cache of raster data is still at the
+   original data depth ... that is it hasn't been scaled yet. 
+
+ o The GvRaster cache is also maintained in different levels of detail
+   (currently up to 8 levels of detail in powers of 2). 
+
+ o The GvRaster implements a number of averaging methods when computing
+   low res overviews from high res data, but they basically devolve to 
+   averaging or sampling. 
+
+
+GvLayer
+-------
+
+ o Derived from GvData. 
+
+ o Represents a layer of stuff that can be placed in a view (GvViewArea)
+
+ o Provides the "setup", "teardown", "draw", "get-extents" and "display-change"
+   methods. 
+
+GvShapeLayer
+------------
+
+ o Derived from GvLayer.
+
+ o Abstract class for all the "shape editing" concepts.  These are implemented
+   as events, the most significant of which from an outside point of view are
+   "selection-changed", and "subselection-changed". 
+
+ o Eventually it is likely that GvShapeLayer will be merged into GvShapesLayer
+   when we are sure we don't want other types of layers with vector editing
+   semantics, and when the old GvPointLayer, GvLineLayer and GvAreaLayer are
+   gone. 
+
+GvShapesLayer
+-------------
+
+ o Derived from GvShapeLayer
+
+ o Handles all the drawing and selection for vectors, including points, lines,
+   areas, text, symbols and so forth.  
+
+ o Interacts with an underlying GvShapes which contains the shape (feature)
+   data.
+
+ o Rendering of features is based on a partial implementation of the OGR 
+   Feature Style Specification. 
+
+
+GvRasterLayer
+-------------
+
+ o Derived from GvLayer
+
+ o Is used to render raster layers.
+
+ o Supported modes are RGBA, Greyscale (+alpha), Pseudo-coloured, and 
+   2D (Complex) Pseudocolored.  
+
+ o The GvRasterLayer has a pseudocolor table (used only in some modes). 
+
+ o Depending on the mode 1-4 GvRasters may be used for source data.  The
+   GvRasters are called "sources" in the GvRasterLayer API, and represented
+   as the GvRasterSource structure.
+
+ o Each source may be set to a constant value (instead of reading from a 
+   GvRaster).  Normally the "alpha" (transparency) source is set to the
+   constant 255 indicating there is no transparency. 
+
+ o Each source has independent scaling information (min/max), an optional 
+   "nodata value", and a greyscale (enhancement) lut.  
+
+ o Note that source raster data is read from a GvRaster, put through the
+   indicated scaling to 256 levels, optionally passed through the LUT and then
+   utliized as part of forming a GvRasterLayer image which may include it's own
+   pseudocolor table. 
+
+ o The GvRasterLayer maintains a cache of OpenGL textures for fully rendered
+   data.  This "texture" cache normally resides in the memory of the OpenGL
+   compliant video card, allowing very fast refresh of raster data.  The
+   textures are the size of the tiles from the GvRaster ... currently hardcoded
+   to be 256x256 (254x254 active area due to required texture overlap). 
+
+ o The GvRasterLayer maintains a mesh for each texture.  This mesh describes
+   how the texture should be warped into "view" coordinates.  The view 
+   coordinates are normally georeferenced space (ie. UTM meters, lat/long
+   decimal degrees, or raw scene pixel/line).   The mesh includes a Z 
+   coordinate which is normally zero for 2d scenes.  
+
+ o Normally the mesh just consists of values at the four corners of the texture
+   but when doing 3D modelling the mesh is generally produced at a higher
+   resolution (perhaps 8x8).  The resolution of the mesh will dictate how 
+   detailed a terrain can be represented; however, the more refined the mesh 
+   the slower the rendering by OpenGL.  It is prohibitively expensive in 
+   OpenGL terms to have a mesh the same size as the raster for even a medium
+   sized raster. 
+ 
+ o The GvRasterLayer tries hard to avoid "blocking" the program for very
+   long while loading data.  This is mainly accomplished by only loading
+   one or a few tiles before re-rendering and returning control to the main
+   event loop. 
+
+
+Undo
+----
+
+ o Discuss mementos.
+
+
+Edit Tools
+----------
+
+ o Discuss
+
+
+Higher Level (Python) OpenEV Architecture
+=========================================
+
+The above really describes the core C services of OpenEV.  It doesn't
+really address how much of the high level application is composed.  A
+few services, and classes worthy of note include:
+
+GvManager
+---------
+
+ o Implemented in C, python interfaces in gview.py.
+
+ o Maintains application wide preferences (actually loaded by python 
+   code in gview.py). 
+
+ o Maintains a list of open GDALDatasets, and GvRasters which have been
+   created for the GDALRasterBands.  Essentially a central "file" cache.
+
+ o Provides prioritized "idle task" services, for stuff like deferred
+   raster loading.
+
+ o Maintains the "busy" flag. 
+
+GvViewWindow
+------------
+
+ o Derived from GtkWindow.  Implemented in Python.
+
+ o Provides the standard OpenEV dialog, including a menu (optional),
+   iconbar (optional), tracker (optional) and scrollbars (optional). 
+
+ o Provides implementation of many of the menu options. 
+
+GViewApp
+--------
+
+ o Derived from Signaler ... no GUI component. 
+
+ o One instance of the class must be instantiated
+   for an application and provides some services shared between all views.
+
+ o Maintains recently used file list. 
+
+ o Manages layer dialog, and toolbar dialog. 
+ 
+ o Creates new views (as generic GvViewWindows). 
+
+ o Open a file on the current view. 
+
+ o Launch preferences panel, toolbar, python shell, etc.  Basically lots
+   of dialogs of which there should be once instance for the application. 
+
+
+</pre>
+<img src="COURSE1_test.png">
+<pre>
+
+Toolbar
+-------
+
+Standard (OpenEV) editing toolbar. 
+
+
+
+The following figure shows how OpenEV, GDAL, GTK, Python, etc. fit together.
+</pre>
+<img src="COURSE1_big_picture.png" height=1000 width=1000>
+
+<a href="COURSE1_python.html">Next</a><br>
+<a href="COURSE1_oev_talk.html">Frank's Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a>
+</body>
+</html>
+
+
+
+   

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_python.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/COURSE1_python.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/COURSE1_python.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,130 @@
+<html>
+<head>
+<title>Python</title>
+</head>
+
+<body BGCOLOR="#FFFFFF">
+
+<body>
+<h1><a name="section1">Python</a></h1>
+<pre>
+
+Python is an open source interpreted, object-oriented, high-level programming 
+language with dynamic semantics. Its high-level built in data structures, 
+combined with dynamic typing and dynamic binding, make it very attractive for 
+Rapid Application Development, as well as for use as a scripting or glue 
+language to connect existing components together. 
+
+ o Relatively simple syntax, but a full fledged language.  
+
+ o Extensive standard libraries include string manipulation, subprocesses, 
+   file access, math, sockets, time. 
+
+ o Fairly easy to make C code callable from Python, normally by writing
+   a layer of "glue" code.  In some cases the glue code can be automatically
+   generated using mechanisms like SWIG (used for GDAL).
+
+ o Python has built in data types for things like lists, tuples, and 
+   dictionaries.
+
+ o Python is the scripting language for OpenEV, and Peppers and a good
+   chunk of each is written directly in Python.  Atlantis has also implemented
+   Python access to GDAL and the OGR projection services. 
+
+ o Python has many rich extension packages, including NumPy (Numeric Python)
+   an interpreted array math environment vaguely like Matlab or IDL.
+
+ o Python is not very declarative, thus relatively few errors can be found
+   at compile time.  It is much more easy to write code that compiles, but 
+   fails at runtime than in more declarative languages like Java.  Python
+   is a weakly typed language. 
+
+ o Debugging environments do exist for Python; however, I haven't had much
+   success with them.  On the plus side, the fact that python "crashes" are
+   normally just generates run-time exceptions which report a traceback can
+   make fixing errors fairly easy.  I personally, have often sunk back to the
+   level of debugging by adding print statements in Python.
+
+   I think it would be useful for us to review python IDE environments like
+   IDLE to see if we should be using them. (talk to Paul about his experiences)
+ 
+ o Python is compiled on the fly, so generally instead of the 
+   edit-compile-link-run cycle, you just have the edit-run cycle.  However,
+   generally speaking you do have to restart the application which is often
+   the "heavy" part of the whole debugging cycle. 
+
+ o It is possible to use a conventional debugger (ie. gdb) to debug problems
+   in C modules called from Python.  However, you can't get any meaningful
+   traceback information from the Python side of stuff. 
+
+</pre>
+
+<h1><a name="section2">Python Bindings</a></h1>
+<pre>
+
+Example of implementing a Gtk style binding for a C function to be
+accessable from Python.  Gtk style bindings are used for the bulk of the
+OpenEV C entry points (and Peppers?).  
+
+The declarations file (gv.defs) contains information on each class, and 
+method along with the methods return type, and types passed to it.  
+
+(define-object GvRasterLayer (GvLayer))
+
+(define-func gv_raster_layer_zoom_set
+  int
+  ((GvRasterLayer layer)
+   (int max_mode)
+   (int min_mode)))
+
+...
+
+Once processed C wrapper code is created that presents a function style
+interface to the method.  To make it operate like a method on a class, the
+following Python wrapper code is added to the class (in gview.py in this case).
+Note the style of inline documentation which can be extracted and placed on
+the web. 
+
+  class GvRasterLayer(GvLayer):
+    ...
+    def zoom_set(self,mag_mode,min_mode):
+        """Set interpolation method
+
+        I believe mag_mode sets the interpolation mode when zooming in past
+        1:1 on a texture, and min_mode is the interpolation mode used for
+        downsampling from the texture, but I am not sure.  Both default to
+        bilinear, and are normally changed together.
+        
+        mag_mode -- One of gview.RL_FILTER_BILINEAR or gview.RL_FILTER_NEAREST.
+        min_mode -- One of gview.RL_FILTER_BILINEAR or gview.RL_FILTER_NEAREST.
+        """
+        return _gv.gv_raster_layer_zoom_set( self._o, mag_mode, min_mode )
+
+
+A significant portion of C functions/methods cannot be automatically wrapped
+like the above, so instead custom C code is written to wrap them.  This is
+normally required in cases where pointers to basic data types are passed or
+where it is desired to translate things into more natural Python data types
+(like tuples or dictinoaries).  The following is an example of a simple 
+C binding to expose a function to Python.  Note that a Python "method" style
+wrapper is still generally required, like the other method. 
+
+static PyObject *
+_wrap_gv_layer_extents(PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_layer_extents", &PyGtk_Type, &layer))
+        return NULL;
+    gv_layer_extents(GV_LAYER(PyGtk_Get(layer)), &rect);
+    return Py_BuildValue("(ffff)", rect.x, rect.y, rect.width, rect.height);
+}
+</pre>
+<a href="COURSE1_numpy.html">Next</a><br>
+<a href="COURSE1_oev_talk.html">Frank's Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a>
+</body>
+</html>
+
+

Added: packages/openev/branches/upstream/current/html/developer_info/COURSE1_test.png
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/COURSE1_test.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,61 @@
+/COURSE1_big_picture.png/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/COURSE1_gdal_datamodel.html/1.2/Thu May 13 20:29:16 2004//
+/COURSE1_lib_inherit.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/COURSE1_numpy.html/1.2/Thu May 13 20:29:16 2004//
+/COURSE1_oev_talk.html/1.2/Thu May 13 20:29:16 2004//
+/COURSE1_openev.html/1.2/Thu May 13 20:29:16 2004//
+/COURSE1_python.html/1.2/Thu May 13 20:29:16 2004//
+/COURSE1_test.png/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE.html/1.2/Thu May 13 20:29:16 2004//
+/DEVCOURSE_building_maintenance.html/1.4/Thu Apr  7 21:11:23 2005//
+/DEVCOURSE_classify_example.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_colour_raster.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_colour_raster.tif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_components.html/1.2/Thu May 13 20:29:16 2004//
+/DEVCOURSE_gdal.html/1.2/Thu May 13 20:29:16 2004//
+/DEVCOURSE_greyscale_raster.tif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_label_ss1.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_label_ss2.gif/1.2/Thu Sep 23 15:34:03 2004/-kb/
+/DEVCOURSE_label_ss3.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_label_ss4.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_label_ss5.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_mini_raster.tif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_open3d_ss1.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_open3d_ss2.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_open3d_ss3.gif/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_openev_files.html/1.3/Thu Apr  7 21:11:23 2005//
+/DEVCOURSE_point_classes.dbf/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_point_classes.shp/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_point_classes.shx/1.1/Thu Sep 11 23:02:29 2003/-kb/
+/DEVCOURSE_print_ss1.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_pyshell_example.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_pyshell_ss1.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_pyshell_ss2.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_pyshell_ss3.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_pyshell_ss4.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_pyshell_ss5.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_python_bindings.html/1.2/Thu May 13 20:29:16 2004//
+/DEVCOURSE_rasterfile_ss1.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_rasterfile_ss2.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_rasterfile_ss3.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_rasterfile_ss4.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_scripts.html/1.2/Thu May 13 20:29:16 2004//
+/DEVCOURSE_shape_example.dbf/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_shape_example.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_shape_example.shp/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_shape_example.shx/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_shapefile_ss1.gif/1.1/Thu Sep 11 23:02:30 2003/-kb/
+/DEVCOURSE_subpixel_interpolation.gif/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/DEVCOURSE_tools_commands.html/1.2/Thu May 13 20:29:16 2004//
+/DEVCOURSE_tutorial1.html/1.3/Thu Sep 23 14:26:09 2004//
+/DEVCOURSE_tutorial2.html/1.4/Thu Sep 23 15:34:03 2004//
+/DEVCOURSE_tutorial3.html/1.3/Thu Sep 23 14:26:09 2004//
+/DEVCOURSE_vector_classes.dbf/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/DEVCOURSE_vector_classes.shp/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/DEVCOURSE_vector_classes.shx/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/DEVCOURSE_vector_classify.gif/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/DEVCOURSE_vector_layerstyles.gif/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/pyshell_example.gif/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/pyshell_full.gif/1.1/Thu Sep 11 23:02:31 2003/-kb/
+/pyshell_help.gif/1.1/Thu Sep 11 23:02:31 2003/-kb/
+D

Added: packages/openev/branches/upstream/current/html/developer_info/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/html/developer_info

Added: packages/openev/branches/upstream/current/html/developer_info/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,38 @@
+<html>
+<head>
+<title>OpenEV Developer Course</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+
+<body>
+<h1>Introduction</h1>
+
+This document was written for an internal
+OpenEV developer course at Atlantis. It covers
+OpenEV components, major files, some aspects of
+multi-platform building/troubleshooting, extending
+OpenEV, and scripting.  
+
+<h1><a name="Contents">Contents</a></h1>
+<ul>
+<li>Overview: The 
+<a href="../openevmain.html">OpenEV help files</a> provide
+a description of OpenEV's dialogs and capabilities.  The 
+<a href="DEVCOURSE_tutorial1.html">Tutorials</a> supply a demonstration
+of some of these.
+<li><a href="DEVCOURSE_components.html">Components: GTK, OpenGL, Proj, etc.</a>
+<li><a href="DEVCOURSE_gdal.html">Geospatial Data Abstraction Library (GDAL)</a>
+<li><a href="DEVCOURSE_python_bindings.html">Python Bindings</a>
+<li><a href="DEVCOURSE_openev_files.html">Important OpenEV Files</a>
+<li><a href="DEVCOURSE_tutorial1.html">Tutorial: General Overview</a>
+<li><a href="DEVCOURSE_tutorial2.html">Tutorial: Working with Vectors</a>
+<li><a href="DEVCOURSE_tutorial3.html">Tutorial: Working with Rasters</a>
+<li><a href="DEVCOURSE_tools_commands.html">Extending OpenEV- Tools and Commands</a>
+<li><a href="DEVCOURSE_scripts.html">Scripting with OpenEV/GDAL</a>
+<li><a href="DEVCOURSE_building_maintenance.html">Building and Maintenance</a>
+</ul>
+<p>
+<a href="../openevmain.html">OpenEV Help</a><br>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_building_maintenance.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_building_maintenance.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_building_maintenance.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,172 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title>Building and Maintenance</title>
+</head>
+<body style="background-color: rgb(255, 255, 255);">
+<h1>Building and Maintenance</h1>
+<h2>Building OpenEV from scratch</h2>
+<h3>Linux/Unix</h3>
+<ol>
+  <li>First, download the source for Mesa, gtkglarea, python, pygtk,
+gtk/gdk/glib, gtk+extra, python-gtkextra, gnuplot, and proj (for
+gnuplot and proj, just the binary executables and
+library are fine). Note that OpenEV uses gtk 1.2.x and pygtk 0.6.x, not
+gtk 2.x and pygtk 1.99.x. </li>
+  <li>Optionally, create a new directory to install the libraries in.
+You could
+install directly to your system directories, but different versions of
+gtk/Mesa in particular might not mesh with other applications, so a new
+directory is safest. </li>
+  <li> Create a "build_setup.csh" setup file to set your paths
+to point to the new installation directory first. This is
+important for ensuring that the correct libraries are found for those
+packages that depend on others (eg. pygtk). The following example
+assumes that the newly-built libraries have been installed in
+/data/local_installations.:
+    <pre># setup for openev/gdal, other builds<br><br>setenv PATH "/data/local_installations/bin:/usr/bin:/bin:/usr/X11R6/bin:/usr/local/bin:/opt/bin:/usr/X11R6/bin:/usr/X11R6/bin"<br>setenv PYTHONHOME "/data/local_installations"<br>setenv LD_LIBRARY_PATH "/data/local_installations/lib"<br><br># environment variables needed by gdal/proj for some coordinate transformations<br># at run time (not technically needed for build).  NOTES: Some additional gdal .csv<br># data files can be found in the source for libgeotiff.  Also, if<br># you are going to be doing nad27-nad83 conversion, see proj/nad/README for information<br># on downloading additional (platform-dependent) data files for share/proj.<br><br>setenv GEOTIFF_CSV "/data/local_installations/share/gdal"<br>setenv GDAL_DATA "/data/local_installations/share/gdal"<br>setenv PROJ_LIB "/data/local_installations/share/proj"<br></pre>
+  </li>
+On solaris and irix, the standard directories will be different,
+and
+you may have to add other paths to your LD_LIBRARY_PATH (after
+the new directory path). Source this setup script before doing any
+building. <li> On irix, you may also have to set the environment
+variable CPP
+to be
+"gcc -E" if the default preprocessor is a Kernaghan &amp; Ritchie one
+(this
+might be the case if configure complains about the presence of
+elif's in headers). </li>
+  <li> On solaris, if the build stops with messages about "Relocation
+errors",
+you might have to add "-fPIC" to the C and C++ compile options in the
+Makefile. </li>
+  <li> On irix or solaris, if you get messages about "Undefined
+symbols",
+make sure that all the necessary headers are in the include path in
+the Makefile, and that the most up-to-date header is the one being
+used by the Makefile (directories with the latest builds should be
+first
+in the include list). </li>
+  <li> You may also have to play with the CC, CXX/CPP, and LD_SHARED
+variables in the Makefile if OpenEV is to work with applications
+built with a specific version of C/C++ compilers. </li>
+  <li> Configure and build and install glib:
+    <pre>./configure  --prefix=/data/local_installations<br>make<br>make install<br></pre>
+  </li>
+  <li> Configure and build and install gtk:
+    <pre>./configure  --prefix=/data/local_installations --with-glib-prefix=/data/local_installations<br>make<br>make install<br></pre>
+  </li>
+  <li> Configure and build and install Mesa
+    <pre> ./configure  --prefix=/data/local_installations<br>make<br>make install</pre>
+  </li>
+  <li>If 10 doesn't work, try:
+    <pre>cp Makefile.X11 Makefile<br>make linux-x86 (or whatever is appropriate- see the list at the top of the Makefile)<br>- copy all the .so's to your install library directory<br>- copy the include/GL to your install include directory<br></pre>
+  </li>
+  <li> Configure and build and install gtkglarea
+    <pre> ./configure  --prefix=/data/local_installations --with-gtk-prefix=/data/local_installations --with-GL-prefix=/data/local_installations --with-lib-GL<br>make<br>make install<br></pre>
+  </li>
+  <li> Configure and build Python:
+    <pre> ./configure  --prefix=/data/local_installations<br>make<br>make install<br></pre>
+  </li>
+  <li> Install the python extensions:
+    <pre>python setup.py install<br></pre>
+  </li>
+  <li> Download and install Numeric python.
+    <pre>python setup.py install<br></pre>
+  </li>
+  <li> Configure and make and install pygtk:
+    <pre> ./configure  --prefix=/data/local_installations --with-gtk-prefix=/data/local_installations --with-gl-prefix=/data/local_installations --with-gtkgl-prefix=/data/local_installations --disable-imlibtest --disable-gdk-pixbuftest --disable-thread<br>make<br>make install<br></pre>
+  </li>
+  <li> Configure and make and install gtk+extra:
+    <pre> ./configure  --prefix=/data/local_installations --with-gtk-prefix=/data/local_installations<br>make <br>make install<br></pre>
+  </li>
+  <li> Install python-gtkextra:
+    <pre>python setup.py install<br>cd /data/local_installations/lib/python?.?/site-packages<br>mv gtkextra pygtkextra<br></pre>
+The last move avoids namespace conflicts with pygtk's GtkExtra on
+Windows. </li>
+  <li> Configure and make and install proj:
+    <pre>./configure  --prefix=/data/local_installations<br>make<br>make install<br></pre>
+  </li>
+  <li> Check out and build GDAL:
+    <pre>setenv CVSROOT :pserver:cvsanon at cvs.maptools.org:/cvs/maptools/cvsroot<br>cvs login<br>password: *enter*<br>cvs checkout gdal <br>cd gdal<br>./configure --prefix=/data/local_installations --with-libz=internal --with-png=internal --with-libtiff=internal --with-geotiff=internal<br>make<br>make install<br></pre>
+The options included here force gdal to compile with internal code
+rather than using
+other libraries for png/libz/libtiff/geotiff (in case end users do not
+have these libraries installed). </li>
+  <li> Check out and build OpenEV:
+    <pre>setenv CVSROOT <tt>:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev</tt><enter><br>cvs checkout openev<br></enter>password: *enter*<br><enter>./configure  --with-gtk-prefix=/data/local_installations --with-gl-prefix=/data/local_installations --with-gtkgl-prefix=/data/local_installations --prefix=/data/local_installations --with-ogr<br>make<br></enter></pre>
+  </li>
+You may have to adjust the makefiles (Makefile and
+pymod/Makefile) to
+find the necessary headers on solaris/irix (configure doesn't always
+work properly). In particular, "-I/data/local_installations/include"
+may need to be added to Makefile and pymod/Makefile depending on where
+gtk/glib installed the headers. <li> Creating a distribution: There is
+a "mkdist" script in OpenEV's
+"delivery" directory that will
+copy the necessary files into two directories under "delivery"-
+"common", and "(Platform)", where
+(Platform) is either Linux, IRIX64, or SunOS. You will have to edit
+mkdist to find the libraries
+properly on your machine before running it, and may have to change the
+version numbers. Prior to running mkdist, you should make sure that
+none of your local files are in the openev directories (especially
+pymod), as they will be copied to the distribution. It is best to build
+a release from clean
+openev/gdal builds. After this, you will need to set the permissions on
+the files properly, eg.:
+    <pre>#!/bin/sh<br><br># Sets file permissions in deliverable.<br><br>openev_DIR=`pwd`<br>platform_NAME=`uname -s`<br><br>case "${platform_NAME}" in<br>    "Linux")  plat_DIR="${openev_DIR}/Linux";;<br>    "SunOS")  plat_DIR="${openev_DIR}/SunOS";;<br>    "IRIX64") plat_DIR="${openev_DIR}/IRIX64";;<br>    *) echo "Unknown Platform! Exiting..."; exit 1;;<br>esac<br><br>common_DIR="${openev_DIR}/common"<br><br>cd "${plat_DIR}"<br>find . -type f -exec chmod a+r {} \;<br>find . -type d -exec chmod 755 {} \;<br>find . -name "*.so*" -exec chmod 755 {} \;<br>find . -name "openev.py" -exec chmod 755 {} \;<br>find . -name "openev" -exec chmod 755 {} \;<br>find . -name "python" -exec chmod 755 {} \;<br>find . -name "gdaladdo" -exec chmod 755 {} \;<br>find . -name "gdalinfo" -exec chmod 755 {} \;<br>find . -name "gdal_translate" -exec chmod 755 {} \;<br>find . -name "gvtest" -exec chmod 755 {} \;<br><br>cd "${common_DIR}"<br>find . -type f -exec chmod a+r {} \;<br>find . -type d -exec chmod 755 {} \;<br></pre>
+After running mkdist, you must also be sure to create links in the lib
+directory to match those in the installation you copied from, and to
+change the ownership and group of both common and (Platform)
+recursively to "root". The common and (Platform) directories can then
+be tarred up for distribution. </li>
+</ol>
+<h3>Windows</h3>
+<ol>
+  <li> The following steps should be done in a DOS command shell. This
+description is for compiling with
+Microsoft Visual C++ version 6.0: </li>
+  <li> Download python source and follow the build instructions for
+windows. </li>
+  <li> Download and Mesa and follow the build instructions for windows
+(the OpenGL headers in MSVC++ may be too old- need at least OpenGL 1.1
+for raster symbols to work in openev). </li>
+  <li> Check out pkgsrc from sourceforge:
+    <pre>setenv CVSROOT <tt>:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev</tt><br>cvs checkout pkgsrc<br></pre>
+This contains versions of glib, gtk, etc. that have been slightly
+modified to compile
+and work properly on windows. </li>
+  <li> Edit pkgsrc's nmake.opt to point to the correct directories on
+your machine. </li>
+  <li> Find Microsoft Visual C++ (MSVC++)'s VCVARS32.BAT file and run
+it (this sets up the environment variables that MSVC++ needs). </li>
+  <li> nmake /f makefile.vc </li>
+  <li> Check out gdal, update the directory locations in nmake.opt for
+your machine. </li>
+  <li> nmake /f makefile.vc (at the top level of gdal) </li>
+  <li> Check out openev parallel to gdal and pkgsrc </li>
+  <li> nmake /f makefile.vc </li>
+</ol>
+Note that sometimes windows makefiles get messed up (something to do
+with linefeeds). If you get a cryptic error, open the file in MSVC++,
+accept its changes if it beeps at you, delete
+extra lines that shouldn't be there, and save. The windows makefiles
+are also not always updated properly. You may have to add/remove things
+based on the linux/unix versions if the problem isn't the linefeeds.
+<h2>Maintenance Issues</h2>
+<ul>
+  <li> Updating- Often people work with different versions of proj,
+GDAL, and even python/gtk. If you see an error message that
+mentions something beginning with "pj", eg. "pj_get_defs", you
+may need to update your proj library. </li>
+  <li> New modules- Before using new python modules (or other
+open source software) found on the web, check to ensure that their
+licenses are LGPL-compliant (Library or Lesser GNU Public License). </li>
+</ul>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+<br>
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_classify_example.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_classify_example.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_colour_raster.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_colour_raster.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_colour_raster.tif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_colour_raster.tif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_components.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_components.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_components.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,143 @@
+<html>
+<head>
+<title>OpenEV Components</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+
+<body>
+<h1> OpenEV Components</h1>
+<h2>GTK</h2>
+A complete description of GTK can be found at the 
+<a href="http://www.gtk.org/">GTK Website</a>.  A few points are
+noted here:
+<p>
+
+<ul>
+<li> GTK+ stands for "The GIMP Toolkit", and was created as part of the GIMP
+  ("GNU Image Manipulation Program") implementation. It is used for
+  creating graphical user interfaces.
+
+<li> It is written in c, and in version 1.2.x (the version that OpenEV
+     uses), it is composed of three main libraries: 
+  <ol>
+  <li> glib- provides definitions and routines for lists, trees, memory 
+     allocation,basic data types (points, lines), etc.
+  <li> gdk ("The GIMP Drawing Kit")- provides functions to trap keyboard and
+     mouse events, and also to create a drawable window and draw basic 
+     objects such as lines, points, rectangles etc.  The code in gdk is
+     highly platform-dependent: event trapping, window drawing are
+     very different between X-windows systems such as Linux, and Windows.
+     gdk wraps these functions into a common API so that the gtk code can
+     be less platform dependent.  Events include button press/click/releases,
+     key pressing, cursor motion, etc.
+  <li> gtk - provides more advanced widgets such as buttons, toolbars, 
+     menus, tree views, file selection dialogs.  For instance, the gtk 
+     button (gtkbutton.c) contains functions that know how to draw a button 
+     in a window, and functions to execute when gdk events are trapped 
+     (eg. what to do when the button is pressed, how to show it is 
+     raised/lowered, etc).  These are based heavily on gdk functions.
+   </ol>
+<li> All of OpenEV's windows (the main viewing window, dialogs, etc.) build
+upon the GtkWindow class.
+<li> There is a helpful online <a href="http://www.moeraki.com/pygtktutorial/pygtktutorial/index.html">tutorial</a> describing GTK's python bindings 
+with examples.
+</ul>
+
+<h2>OpenGL</h2>
+
+<ul>
+<li> OpenGL defines a hardware-independent software interface to graphics 
+  hardware.  It specifies how various drawing-related functions should
+  be defined (names, parameters, etc.).  The advantage of this is that
+  code using this API can work with any hardware that supplies an OpenGL 
+  driver.
+
+<li> Mesa implements this API in software- if <path to OpenEV>/lib/Mesa 
+  is added to your LD_LIBRARY_PATH variable on Linux/Unix, Mesa (libGL.so,
+  libGLU.so) will be used.  Hardware-accelerated platforms provide the 
+  interface without the need for Mesa, and are faster (though sometimes
+  flakey).  Mesa relies on having
+  the "glx" extension on X-Windows systems and the "wgl" routines on 
+  Windows.  These extend the window systems enough so that the basic OpenGL
+  commands can be defined in an efficient manner.  For instance, on X 
+  servers, the glx extension allows OpenGL to bypass the X server's 
+  involvement and render graphics directly in places where it is necessary for 
+  performance.  For more on Mesa, see <a href="http://www.mesa3d.org/">this site</a>.
+
+<li> The actual OpenGL commands in OpenEV are all at the c-level.
+</ul>
+
+An example of OpenGL commands follows:
+<pre>
+    if (view->dragging_mode)
+    {
+        gvgeocoord x[4], y[4];
+
+        gv_view_area_map_pointer( view, 
+                                  view->state.mpos_x, view->state.mpos_y,
+                                  x+0, y+0 );
+        gv_view_area_map_pointer( view, 
+                                  view->last_mpos_x, view->state.mpos_y,
+                                  x+1, y+1 );
+        gv_view_area_map_pointer( view, 
+                                  view->last_mpos_x, view->last_mpos_y,
+                                  x+2, y+2 );
+        gv_view_area_map_pointer( view, 
+                                  view->state.mpos_x, view->last_mpos_y,
+                                  x+3, y+3 );
+
+	glColor3f(1.0, 0.5, 0.0);
+	glBegin(GL_LINE_LOOP);
+	glVertex2(x[0], y[0]);
+	glVertex2(x[1], y[1]);
+	glVertex2(x[2], y[2]);
+	glVertex2(x[3], y[3]);
+	glEnd();
+    }
+</pre>
+
+This is the piece of code that draws the little orange rectangle when
+you left-click and drag to zoom in on a region.  It takes the corner
+coordinates of the area you dragged out, maps them into georeferenced
+coordinates (the gv_view_area_map_pointer calls), sets the drawing
+colour to orange (the glColor3f command), specifies that it is beginning
+to draw a closed loop (the glBegin call), draws the vertices, then
+ends the loop with glEnd.  Mesa or your hardware driver provide the
+definitions of the OpenGL commands (glColor3f, glBegin, etc.). For
+more, see the gv_view_area_expose function in gvviewarea.c, or the draw
+functions in the layer c-code (gvrasterlayer.c, gvshapeslayer.c).
+
+<h2>gtkglarea</h2>
+
+GTK defines gtk_drawing_area as a base class for widgets that need a box
+to draw into.  gtkglarea extends this class to allow it to accept OpenGL
+commands for drawing.  OpenEV's gvviewarea adds the concept of layers
+and tools, and adds the OpenGL commands necessary for georeferenced
+display, 3D, etc.  For more information, see 
+<a href="http://sal.kachinatech.com/F/3/GTKGLAREA.html">this site</a>.
+
+<h2>proj</h2>
+
+Proj is used by GDAL to do projection transformations, for instance 
+between lat/longs and utm. OpenEV will function without proj, but in certain
+cases the georeferenced display will not be correct.  For instance, when a 
+georeferenced MFF2 file is read in, 
+corner coordinates are specified in lat/longs even if the image is 
+projected in utm coordinates.  In the utm case, GDAL must convert the corner
+coordinates from lat/longs to utm prior to display so that the image does
+not appear incorrectly warped.  Proj is used in this conversion.  For more 
+information see the <a href="http://www.remotesensing.org/proj/">Proj website</a>. 
+
+<h2>Gnuplot</h2>
+
+Provides plotting functionality.  This is used in OpenEV through the 
+gvplot.py wrapper.  For more information, see the
+<a href="http://www.comnets.rwth-aachen.de/doc/gnu/gnuplot37/gnuplot.html">Gnuplot website</a>.
+
+<p>
+<a href="DEVCOURSE_gdal.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_gdal.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_gdal.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_gdal.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,97 @@
+<html>
+<head>
+<title>GDAL (Geospatial Data Abstraction Library)</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+<h1>GDAL Summary (from an OpenEV perspective)</h1>
+<ul>
+<li> A library used to read/write datasets
+
+<li> Datasets fall in two main categories from OpenEV's perspective: 
+  raster, and vector.  Raster files
+  are image data, a complete 2 or more dimensional array of values displayed
+  in OpenEV as a grayscale grid (one or two bands) or in colour if there 
+  are more than 2 bands (one colour grid for each of three bands, with
+  the option to use another band to specify the degree of transparency).  
+  Vector files consist
+  of a set of points, lines, or polygons (groups of points).
+  Here is an example of a raster image displayed in an OpenEV window: 
+  <img src="DEVCOURSE_colour_raster.gif">
+  <p>
+  Here is an example of a vector file, displayed in an OpenEV window:
+  <img src="DEVCOURSE_shape_example.gif">
+
+<li> GDAL started out as a raster library, then pulled in OGR to become a 
+  vector library as well. OpenEV does not use GDAL/OGR to read shapefiles- it
+  has its own internal code to read in this information (shpopen.c), but it
+  does use OGR to open other types of vector files (eg. UK.NTF, Arc/Info 
+  Binary Coverage, Mapinfo File).  OGR is an optional configuration option
+  in both gdal and OpenEV- on by default in gdal, off in OpenEV.
+  There is separate code in OpenEV that is only compiled when ogr is
+  turned on (gvogr.c)- this converts the ogr objects to a GvShapes 
+  object (GvShapes is the class that OpenEV uses to represent collections 
+  of vector objects for display).  In gdal,
+  OGR is also used in doing coordinate transformations.
+
+<li> Aside: OGR used to stand for "OpenGIS Simple Features Reference
+  Implementation", however it no longer is compliant with the Simple
+  Feature specification, so although the term OGR is still used, 
+  its meaning is irrelevant.
+
+<li> GDAL reads in a file and gathers the basic information needed for display.
+  For a raster file, the most basic information is the image size, the offset
+  where the actual data starts within the file or fileset, the datatype, and
+  how the data is stored (eg. byte order, band interleaving etc.).  Additional
+  information may also be pulled out and stored (eg. metadata, georeferencing
+  information), depending on the format. The information is stored as a 
+  gdal Dataset (see Dataset in gdal.py for code).  For a vector file, the
+  number and type of shapes stored must be known, along with how to extract
+  the vectors and attribute information.  This information may be stored in
+  an OpenEV GvShapes object directly (if read from a shapefile), or stored 
+  in an ogr Dataset and converted to a GvShapes object before display.
+
+<li> GDAL can take in extra information from other libraries if configured to
+  do so. For instance, if it is configured with --libtiff=internal, it will use
+  internal code for tiffs that just contains enough information to read
+  basic fields.  However, it can also be configured to link to an external
+  tiff library that may offer more specific information on certain fields,
+  provided the library follows the expected API. Some filetypes can only be 
+  read by linking to external libraries (eg. jpeg 2000); these are not
+  configured in by default.
+  The gdal web page indicates the maximum number of formats supported, not
+  the default.  Some versions of OpenEV have a formats tool (Help->Formats
+  menu entry) that searches GDAL's driver list at runtime and displays
+  a widget summarizing this information.
+
+
+<li> GDAL utilities (apply to rasters only): 
+<ol>
+<li>gdal_translate- Translate files into other formats, window files,
+    rescale files, extract bands. 
+<li>gdaladdo- Add tiled overviews to a raster. 
+<li>gdalinfo- Give information about a raster file (format, size, metadata). 
+<li>gdal_warp- Reprojection and warping utility, mosaicing. 
+<li>gdaltindex- Build a shapefile with a record for each input raster
+file, an attribute containing the filename, and a polygon geometry outlining
+the raster.
+<li> rgb2pct.py- compute a pseudocolor table for an rgb image.
+<li>gdal_merge.py- Mosaic a set of images.
+<li>gdal-config- Information about the gdal installation.
+</ol> 
+For more, see the <a href="http://www.remotesensing.org/gdal/gdal_utilities.html">GDAL Utilities</a> website.
+<li>There are also OGR utilities for vectors:
+<ol>
+<li> ogrinfo- Give information about a vector file.
+<li> ogr2ogr- Convert between ogr-supported formats.
+<li> ogrtindex- Create a tileindex (file containing the identities and
+spatial extent of a list of files).
+</ol>
+For more, see the <a href="http://gdal.velocet.ca/projects/opengis/ogrhtml/ogr_utilities.html">OGR Utilities</a> website.
+</ul>
+<a href="DEVCOURSE_python_bindings.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_greyscale_raster.tif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_greyscale_raster.tif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss1.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss1.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss2.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss2.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss3.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss3.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss4.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss4.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss5.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_label_ss5.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_mini_raster.tif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_mini_raster.tif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss1.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss1.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss2.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss2.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss3.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_open3d_ss3.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_openev_files.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_openev_files.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_openev_files.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title>Main OpenEV Files</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Main OpenEV Files</h1>
+The following is not intended to be a high-level description of
+OpenEV's classes, as a complete description can be found in the
+<a href="http://openev.sourceforge.net/lib_design_doc/openev.html">Library
+Design Document</a>.
+This is meant to describe the purpose and a bit
+about six key openev files. These should provide a suitable starting
+point for learning and experimentation.
+<h2>openev</h2>
+This is a shell script that can be used to launch openev. All it does
+is set some environment variables, then run
+openev.py. It varies depending on the type of installation.
+<p>Here are some examples:
+</p>
+<p>Type 1:
+</p>
+<pre>#!/bin/sh<br>OPENEV_HOME=`dirname $0`/..<br><br># Setup Environment Variables for OpenEV<br><br>PYTHONHOME=${OPENEV_HOME}<br>PYTHONPATH=${OPENEV_HOME}/pymod:${PYTHONPATH}<br>GEOTIFF_CSV=${OPENEV_HOME}/share/gdal<br>GDAL_DATA=${OPENEV_HOME}/share/gdal<br>PROJ_LIB=${OPENEV_HOME}/share/proj<br>LD_LIBRARY_PATH=${OPENEV_HOME}/lib:${LD_LIBRARY_PATH}<br>PATH=${OPENEV_HOME}/bin:${PATH}<br><br># check for accelerated hardware flag<br>if [ "${1}" = "-h" ]; then<br>    shift<br>    echo "Setup for user installed hardware acceleration"<br>else<br>    echo "Default software rendering mode (use -h if accelerated video card installed)."<br>    LD_LIBRARY_PATH=${OPENEV_HOME}/lib/Mesa:${LD_LIBRARY_PATH}<br>fi<br><br>export PYTHONHOME PYTHONPATH LD_LIBRARY_PATH PATH OPENEV_HOME GEOTIFF_CSV<br><br><br># Run OpenEV<br>${OPENEV_HOME}/pymod/openev.py "$@"<br></pre>
+This one will run without requiring a setup script to be sourced,
+because it sets all the environment variables it needs. This is
+the type of script included in Frank Warmerdam's ftp site
+distributions.
+<p>Type 2:
+</p>
+<pre>#!/bin/sh<br>${PYTHONHOME}/bin/python ${OPENEVHOME}/pymod/openev.py $*<br></pre>
+This type assumes that a setup script (eg. .cshrc) has set all the
+necessary
+variables, and simply launches OpenEV.
+<h2>openev.py</h2>
+This is a python script that creates an OpenEV application instance and
+launches a view.
+<p></p>
+<pre>import gviewapp<br>import gview<br>import gtk<br>import sys<br>import os<br>import getopt<br><br>if __name__ == '__main__':<br><br> <br>    # get command line options and args<br>    # openev -m menufile -i iconfile -t toolfile image1 image2 ......<br>    (options, ifiles) = getopt.getopt(sys.argv[1:], 'm:i:t:p:')<br> <br>    if os.path.isdir(os.path.join(gview.home_dir, 'config')):<br>        mfile = os.path.join(gview.home_dir, 'config', 'DefaultMenuFile.xml')<br>        ifile = os.path.join(gview.home_dir, 'config', 'DefaultIconFile.xml')<br>        pfile = os.path.join(gview.home_dir, 'config', 'DefaultPyshellFile.xml')<br>    else:<br>        mfile=None<br>        ifile=None<br>        pfile=None<br>         <br>    tfile = None<br> <br>    for opt in options[0:]:<br>        if opt[0] == '-m':<br>            mfile=opt[1]<br>        elif opt[0] == '-i':<br>            ifile=opt[1]<br>        elif opt[0] == '-p':<br>            pfile=opt[1]<br>        elif opt[0] == '-t':<br>            tfile=opt[1]<br> <br>    app = gviewapp.GViewApp(toolfile=tfile,menufile=mfile,iconfile=ifile,pyshellfile=pfile)<br>    gview.app = app      # handle to the application for other modules to use<br>    app.subscribe('quit',gtk.mainquit)   # connect to gtk's quit mechanism<br>    app.show_layerdlg()                  # show layer dialog<br>    app.new_view(None)                   # create initial view window<br>    app.do_auto_imports()  # imports modules specified in preferences<br><br>    for item in ifiles:<br>        app.file_open_by_name(item)     # open command line files<br><br>    gtk.mainloop()         # start the main event loop<br><br></pre>
+<h2>gview.py</h2>
+This provides access to the c-level openev code. Layers, view areas,
+rasters,
+etc. are defined here, however no GUI's are defined gview.py- this is
+left
+to other modules.
+As an example, here is the snippet of gview.py code that creates the
+point of
+interest tool:
+<pre>class GvPoiTool(GvTool):<br>    """Point of Interest Selection Tool<br><br>    Signals:<br><br>    poi-changed -- generated when the POI has been changed.<br>    """<br>    <br>    get_type = _gv.gv_poi_tool_get_type<br>    def __init__(self, _obj=None):<br>        if _obj: self._o = _obj; return<br>        self._o = _gv.gv_poi_tool_new()<br><br>    def get_point(self):<br>        """ Returns the current POI """<br>        return _gv.gv_poi_tool_get_point(self._o)<br><br>    def set_point(self, point):<br>        """ Sets the current POI.<br><br>        point -- a tuple (column, row)<br>        """<br>        return _gv.gv_poi_tool_new_point(self._o, point)<br></pre>
+It inherits from the GvTool class:
+<pre>class GvTool(_gtk.GtkObject):<br>    get_type = _gv.gv_tool_get_type<br>    def __init__(self, _obj=None):<br>        if _obj: self._o = _obj; return<br>        <br>    def activate(self, view):<br>        _gv.gv_tool_activate(self._o, view._o)<br>        <br>    def deactivate(self, view):<br>        _gv.gv_tool_deactivate(self._o, view._o)<br>        <br>    def get_view(self):<br>        v_o = _gv.gv_tool_get_view(self._o)<br>        if v_o is None:<br>            return None<br>        else:<br>            return GvViewArea(_obj=v_o)<br>        <br>    def set_boundary(self, boundary):<br>        """Set constraint rectangle.<br><br>        boundary -- boundary is a tuple in the form (column,row,width,height)<br>        """<br>        return _gv.gv_tool_set_boundary(self._o, boundary)<br></pre>
+You can connect to the POI tool's poi-changed signal using the connect
+function:
+<pre>class my_object:<br>    def __init__(self):<br>        # The poi tool is stored in the main application's toolbar:<br>        # Get a handle to the main application through gview (gview.app<br>        # is set to the main application in openev.py).  This makes<br>        # use of the fact that every module is only loaded once in<br>        # an application, unless the "reload" command is called<br>        # on the module.  All subsequent imports point to the same<br>        # space in memory.  This means that setting gview.app in <br>        # openev.py results in app being accessible wherever gview is<br>        # loaded.<br>        # The application is sometimes passed as an argument<br>        # to make it less confusing in OpenEV's tools, but the<br>        # end effect is the same.<br><br>        import gview<br>        gview.app.toolbar.poi_tool.connect('poi-changed',self.my_update_cb)<br><br>    def my_update_cb(self,*args)<br>        poi_info = self.app.toolbar.get_poi()<br>        txt=str(poi_info[0])+'     '+str(poi_info[1])<br></pre>
+This connects the poi tool's "poi-changed" signal to your object's
+"my_update_cb" callback. Note that the poi tool's "connect" function
+is inherited from gtk.GtkObject through the GvTool base class.
+<p></p>
+<h2>gviewapp.py and gvviewwindow.py</h2>
+These modules provide gui's to go with the gview.py objects, and
+super-structures to control their interaction.
+<p>The top-level application
+is the GViewApp class in gviewapp.py. It does not have its own gui, but
+initializes the layer dialog, view manager, selection manager, toolbar,
+tools, and preferences. The first view window is launched through a
+call
+to the GViewApp's "new_view" function, which creates a new GvViewWindow
+and adds tool menu entries to it.
+gviewapp.py also contains the code for the edit toolbar and preference
+dialog GUIs.
+</p>
+<p>gvviewwindow.py contains the code to create an OpenEV view. This
+consists
+of a GtkWindow with an embedded menubar, icon bar, and GvViewArea (the
+black area- see gview.py for the wrappers, gvviewarea.c for the
+nitty-gritty).
+</p>
+<p>Other python files that provide GUIs include layerdlg.py (the layer
+dialog),
+oeattedit.py (the vector attributes dialog launched from the edit
+menu),
+gvvectorpropdlg.py (the vector properties dialog launched by right
+clicking on a vector file in the layer dialog), and gvrasterpropdlg.py
+(the raster properties dialog). </p>
+<p></p>
+<h2>gvsignaler.py</h2>
+One other class that you should be aware of as an OpenEV developer is
+the
+Signaler class (code below):
+<pre>"""<br>MODULE<br>   gvsignaler<br>   <br>DESCRIPTION<br>   Provides an event subscription/notification mechanism a bit like<br>   Gtk signal handling.  Classes which derive from Signaler can<br>   publish a list of named signals.  These signals can then be<br>   attached to arbitrary callback methods/functions using subscribe().<br>   More than one callback function per signal can be attached.<br>   The Signaler executes the callback functions with the notify()<br>   method.<br><br>   Arguments to the callback functions are (in order):<br>     1. The Signaler instance.<br>     2. Any signal specific arguments provided to notify().<br>     3. Subscriber baggage arguments provided to subscribe().<br>   The baggage arguments act like the 'data' argument of Gtk signals.<br>"""<br><br>class UnpublishedSignalError(Exception): pass<br>class SignalExistsError(Exception): pass<br><br>class Signaler:<br>    "Base class for objects with published signals"<br>    signal = {}  # Prevents AttributeErrors<br><br>    def publish(self, *sigs):<br>        "Publish one or more named signals"<br>        if not self.__dict__.has_key('signal'):<br>            self.signal = {}<br>        for s in sigs:<br>            if self.signal.has_key(s):<br>                raise SignalExistsError<br>            self.signal[s] = [0, []]  # Blocked flag, handlers list<br><br>    def subscribe(self, name, meth, *args):<br>        "Attach a callback function/method to a signal"<br>        try:<br>            self.signal[name][1].append((meth, args))<br>        except KeyError:<br>            raise UnpublishedSignalError<br>            <br>    def unsubscribe(self, name, meth):<br>        "Remove a callback function/method for a named signal"<br>        try:<br>            l = len(self.signal[name][1])<br>            for si in range(l):<br>                if self.signal[name][1][si][0] == meth:<br>                    del self.signal[name][1][si]<br>                    break<br>        except KeyError:<br>            raise UnpublishedSignalError<br><br>            <br>    def notify(self, name, *args):<br>        "Execute callbacks attached to the named signal"<br>        try:<br>            sig = self.signal[name]<br>        except KeyError:<br>            raise UnpublishedSignalError<br>        # Check for blocked signal<br>        <br>        if sig[0] == 0:<br>            for s in sig[1]:<br>                apply(s[0], (self,) + args + s[1])<br><br>    def block(self, name):<br>        "Prevent a signal from being emitted"<br>        try:<br>            self.signal[name][0] = 1<br>        except KeyError:<br>            raise UnpublishedSignalError<br><br>    def unblock(self, name):<br>        "Allows a blocked signal to be emitted"<br>        try:<br>            self.signal[name][0] = 0<br>        except KeyError:<br>            raise UnpublishedSignalError<br><br></pre>
+An object of this class stores a dictionary of signals that it will
+recognize.
+A new signal is added to the dictionary using the "publish" function,
+and
+the signal is emitted using the "notify" function. Another object can
+connect to this signal using the "subscribe" function, which appends
+the
+desired callback (passed through the subscribe arguments) to the list
+of callbacks for that signal. "notify" cycles through all attached
+callbacks
+for a given signal, applying them. The block/unblock functions are used
+to temporarily prevent the callbacks from being executed. This is
+similar
+to the gtk signaling mechanism, with the difference that the gtk
+signals
+are defined at the c-level, and are emitted and connected to through
+python wrappers. The gtk mechanism uses the terms "connect" and "emit"
+rather than "subscribe" and "notify". Some objects inherit both
+mechanisms,
+for example the Layer Dialog class:
+<pre>class LayerDlg(GtkWindow,gvsignaler.Signaler):<br>    def __init__(self):<br>        GtkWindow.__init__(self)<br>        self.set_title('Layers')<br>        self.set_usize(250, 500)<br>        self.set_border_width(3)<br>        self.set_policy(TRUE,TRUE,FALSE)<br>        self.connect('delete-event',self.close)<br>        shell = GtkVBox(spacing=3)<br>        self.add(shell)<br>       <br>        # Bunch of other code...<br><br>        # Layer list<br>        layerbox = GtkScrolledWindow()<br>        shell.pack_start(layerbox)<br>        if self.thumbnail:<br>            layerlist = GtkCList(cols=3)<br>        else:<br>            layerlist = GtkCList(cols=2)<br>            <br>        layerbox.add_with_viewport(layerlist)<br>        layerlist.set_shadow_type(SHADOW_NONE)<br>        layerlist.set_selection_mode(SELECTION_SINGLE)<br>        layerlist.set_row_height(THUMB_H + 4)<br>        layerlist.set_column_width(0, EYE_W)<br>        if self.thumbnail:<br>            layerlist.set_column_width(1, THUMB_W + 4)<br>        layerlist.connect('select-row', self.layer_selected)<br>        layerlist.connect('button-press-event', self.list_clicked)<br><br><br>        # Bunch of other code...<br><br>        # Publish signals<br>        self.publish('active-view-changed')<br>        <br></pre>
+In this case, 'delete-event' is a gtk signal (the little x on the
+layer dialog box being clicked), as are 'select-row' and
+'button-press-event', but 'active-view-changed' is
+a Signaler signal.
+<p>As a historical note, the GViewApp class used to be in openev.py,
+then
+was separated out to its own gviewapp.py file. </p>
+<p><a href="DEVCOURSE_tutorial1.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+</p>
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.dbf
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.dbf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.shp
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.shp
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.shx
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_point_classes.shx
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_print_ss1.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_print_ss1.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_example.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_example.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss1.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss1.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss2.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss2.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss3.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss3.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss4.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss4.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss5.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_pyshell_ss5.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_python_bindings.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_python_bindings.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_python_bindings.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,42 @@
+<html>
+<head>
+<title>Python Bindings</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+Python is a scripting language, a bit like matlab.  You can find detailed information
+at the main <a href="http://www.python.org">Python Website</a>, or a summary in the
+<a href="COURSE1_python.html">python section</a> of Frank Warmerdam's course notes.
+
+<p>
+<h1>Python Bindings</h1>
+
+Python bindings wrap c-code into modules that can be imported by python:
+<ul>
+<li> pygtk (_gtkmodule.so/gtk.py under OpenEV/lib/python2.1/site-packages on 
+  Linux/Unix) provides python bindings to gtk functions.  _gtkmodule.so contains
+  the binary functions; gtk.py wraps them in a friendlier interface. For more, see the
+  <a href="http://www.daa.com.au/~james/pygtk/">PyGTK Website</a>.
+
+<li> OpenEV's python bindings are provided by _gvmodule.so and _gtkmissing.so, 
+  and wrapped in gview.py, gvviewwindow.py (among others).  Some of OpenEV's bindings
+  are generated using the the python script mkgv.py and a text file
+  specifying a little information about each function (gv.defs).  This is based
+  on pygtk's method of generating bindings; the mkgv.py script
+  calls pygtk's "generate.py" module with a few arguments.  The output
+  of mkgv.py is two files, gvmodule_defs.c and gvmodule_impl.c, that contain the
+  code for the bindings.  Some of the functions are too
+  complicated to have automatically generated bindings; these are in gvmodule.c.
+  These three files are compiled and their object files included in _gvmodule.so.
+</ul>
+
+<p>
+A more detailed description of python bindings and an example are given in the 
+<a href="COURSE1_python.html#section2">Python Binding</a> section of Frank's course notes.
+<p>
+<a href="DEVCOURSE_openev_files.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss1.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss1.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss2.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss2.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss3.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss3.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss4.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_rasterfile_ss4.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_scripts.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_scripts.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_scripts.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,56 @@
+<html>
+<head>
+<title>Scripting with OpenEV/GDAL</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+<h1>Scripting with OpenEV/GDAL</h1>
+
+OpenEV and GDAL's python functions can also be used in standalone scripts.
+For example, the following script takes in a shapefile and picks out
+the shapes that have [field] = [value], where [field] is one of the
+shape's attributes, and [value] is one of the values that attribute can
+take on.  It saves these to a file [inputfile]_[field]_[value].shp, where
+[inputfile] is the input shapefile name minus its extension.
+<pre>
+import gview
+import sys
+
+if __name__ == '__main__':
+
+    if len(sys.argv) < 4:
+        print "Query_polygons.py: "
+        print "    Get relevant polygons from a vector layer."
+        print ""
+        print "Usage: Query_polygons.py shapefile field value"
+    else:
+        fshp = sys.argv[1]
+        field = sys.argv[2]
+        value = sys.argv[3]
+        fshp2 = fshp[:-4] + '_' + field + '_' + value + '.shp'
+        
+        # Turn the shapefile into a GvShapes object
+        sdata=gview.GvShapes(shapefilename=fshp)
+
+        # Create a new shapes object to store the 
+        # selected shapes
+        new_shapes=gview.GvShapes()
+    
+        for shp_indx in range(len(sdata)):
+            temp_shp = sdata[shp_indx]
+            if temp_shp is None:
+                continue
+            
+            if temp_shp.get_property(field) == value:
+                new_shapes.append(temp_shp.copy())
+
+        new_shapes.save_to(fshp2)  # save the selected shapes to a file
+
+</pre>
+<p>
+<a href="DEVCOURSE_building_maintenance.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.dbf
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.dbf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.shp
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.shp
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.shx
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shape_example.shx
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shapefile_ss1.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_shapefile_ss1.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_subpixel_interpolation.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_subpixel_interpolation.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tools_commands.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tools_commands.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tools_commands.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,290 @@
+<html>
+<head>
+<title>Extending OpenEV</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+<h1>Extending OpenEV- Tools and Commands</h1>
+
+<h2>Python Shell Commands</h2>
+The first method of extending OpenEV's functionality is to add
+new functions or commands to the python shell.  Examples of this
+would be the "display" function and "newview" command.  
+Functions are regular python functions imported as soon as the
+python shell is launched via a "from gvshell import *" command.
+You can import your own functions in OpenEV's python shell if
+you have placed your module in OpenEV's python path.
+Commands, such as "newview", follow a more complicated structure
+and differ from functions in the following way:
+<ol>
+<li> They are intercepted rather than being passed to the
+python shell's interpreter.
+<li> They use the syntax "commandname arg1 arg2..." rather than
+"commandname(arg1,arg2,...)"
+<li> They have associated usage strings that are displayed when
+you enter "commands" at the prompt.
+<li> They have argument checking.
+</ol>
+Here is an example of the python shell and the results of the
+display function and newview commands:
+<img src="pyshell_example.gif">
+<p>
+The relevant files here are:
+<ul>
+<li> gviewapp.py- contains the callback (pyshell) that launches the python 
+shell.
+<pre>
+    def pyshell(self, *args):
+        import pyshell
+        pyshell.launch(pyshellfile=self.pyshellfile)
+</pre>
+<li> pyshell.py- defines the interactive python shell, MyInteractiveConsole.
+This shell is based on python's code.InteractiveConsole, but overrides some
+of the commands.  For instance, instead of passing a command line directly to 
+the python interpreter, the "push" function in MyInteractiveConsole does
+the following (excerpts of the code have cut out and replaced with 
+summaries):
+<pre>
+    def push(self, line):
+        ( update command shell history list if present )
+
+        ( if current entry is a macro command, do some macro-specific stuff )
+
+        ( if journaling is on, do record the command to the journal file )
+        
+        if len(self.cmdlist) > 0:
+            # determine if the line starts with a command
+            cmd_name, remainder = parse_interpreter_line(line)
+
+            if self.cmdlist.has_key( cmd_name ):
+                # if code gets here, user has entered a registered
+                # command (eg. newview).  This part parses the
+                # command, converts it to text that can be run
+                # as a python function, then pushes it through
+                # the interpreter. 
+            
+                ( convert command line to python function text )
+
+                return code.InteractiveConsole.push(self,txt)
+
+
+        # If line does not start with a command, push it
+        # through the interpreter as-is.
+        return code.InteractiveConsole.push(self,line)
+           
+</pre>
+<li> gvshell.py- contains additional regular python functions that get 
+imported into the python shell (display, get_roi, roi).
+<li> gvcommand.py- this contains the code that parses the core commands
+and defines the base class for core commands.
+<li> gvcorecmds.py- this contains the core commands implemented to date.
+</ul>
+
+
+<h2>Tools</h2>
+The idea behind the tool mechanism and the methods of registering a tool
+(automatic and non-automatic) are described in the 
+<a href="../customization.html">customization</a> section of 
+OpenEV's help.
+<p>
+The base tool class is defined as follows in gviewapp.py:
+<pre>
+
+class Tool_GViewApp:
+    # Abstract base class to derive tools from
+    def __init__(self,app=None):
+        self.app = app
+        self.menu_entries = Tool_GViewAppMenuEntries()
+        self.icon_entries = Tool_GViewAppIconEntries()
+        self.pymenu_entries = Tool_GViewAppMenuEntries()
+        self.pyicon_entries = Tool_GViewAppIconEntries()
+
+class Tool_GViewAppMenuEntries:
+    # Class to store entries to be added to openev's menu
+    def __init__(self):
+        self.entries = {}
+    
+    def set_entry(self,item,position=0,callback=None,accelerator=None):
+        # item = a string describing menu location
+        # position = default location in the menu (integer): Ignored if an
+        #            xml menu entry is specified for the tool.  Note:
+        #            when used, the position refers to position in the
+        #            lowest level menu.  Eg. if a menu entry is
+        #            'File/menu1/entryN', position refer's to entryN's
+        #            position within menu1, not menu1's position in
+        #            File.  For more flexibility, use the xml form of
+        #            configuration.
+        # callback = callback
+        # accelerator = shortcut key
+
+        if (type(item) == type('')):
+            if (type(position) == type(0)):
+                self.entries[item] = (position,callback, accelerator)
+            else:
+                raise AttributeError,"position should be an integer"
+        else:
+            raise AttributeError,"Menu entry item must be a string"
+
+(similar class for Icon entries)
+
+</pre>
+This defines the way the tools add menu or icon entries to OpenEV's toolbar, 
+and stores a handle to the application so that the tool can interact
+with it.  Below is a simple tool that adds a new menu
+entry Tools/Example, and when called prints out a list of the tools loaded
+in the application.
+<pre>
+#!/usr/bin/env python
+import gviewapp
+
+class Tool_example(gviewapp.Tool_GViewApp):
+
+    def __init__(self, app=None,startpath=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+
+        self.init_menu()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Example...",1,self.example_tool_cb)
+
+    def example_tool_cb(self,*args):
+        print self.app.Tool_List
+
+
+TOOL_LIST=['Tool_example']
+</pre>
+Placing this tool in OpenEV's "tools" directory will register it 
+automatically. The relevant sections of gviewapp.py are:
+<p>
+Initialization:  <p>
+The tools are loaded up and stored in
+self.Tool_List.  Tools passed as a command line argument are
+loaded using self.load_tools_file; auto-registering tools
+(those in OpenEV's "tools" directory) are loaded using 
+self.scan_tools_directories.  These create an instance of the
+tool, tool_inst, and append a tuple consisting of
+(tool_name,tool_inst) to the Tool_List.
+<pre>
+class GViewApp(Signaler):
+    def __init__(self,toolfile=None,menufile=None,iconfile=None,pyshellfile=None):
+        self.view_manager = ViewManager()
+        self.sel_manager = gvselbrowser.GvSelectionManager( self.view_manager )
+        self.pref_dialog = None
+        self.filename = None
+
+        # Toolbar
+        self.toolbar = Toolbar()
+        self.view_manager.set_toolbar( self.toolbar )
+
+        # Other dialogs, etc.
+        self.layerdlg = layerdlg.Launch()
+        self.view_manager.set_layerdlg(self.layerdlg)
+
+        self.publish('quit')
+        self.publish('rfl-change')
+
+        # Verify that float() works properly.
+        try:
+            x = float('0.9')
+        except:
+            gvutils.warning( 'It appears that float() doesn\'t work properly on your system.\nThis is likely due to use of a numeric locale with an alternate decimal\nrepresentation.  Please try setting the LC_NUMERIC environment variable\nto C and restarting OpenEV.' )
+
+        # Default configuration files for view and python shell
+        self.menufile=menufile
+        self.iconfile=iconfile
+        self.pyshellfile=pyshellfile
+        
+        # External tools to import and add to view menu
+        self.Tool_List = []
+        if toolfile is not None:
+            self.load_tools_file( toolfile )
+
+        self.scan_tools_directories()
+
+        # Tool index: a dictionary with the tool name as a
+        # key and the tool's position in the list as the value
+        self.tool_index = {}
+        for idx in range(len(self.Tool_List)):
+            self.tool_index[self.Tool_List[idx][0]]=idx
+
+        self.shell = None
+</pre>
+New views: <p>
+When a new view is created, the main application cycles through
+its tool list, and appends their menu and icon entries to the menu/icon bars.
+<pre>
+    def new_view(self, title=None, menufile=None,iconfile=None, *args):
+        # If menu/icon files aren't specified, use application-wide
+        # defaults
+        if ((menufile is None) and (self.menufile is not None)):
+            menufile=self.menufile
+        if ((iconfile is None) and (self.iconfile is not None)):
+            iconfile=self.iconfile
+
+        view_window = gvviewwindow.GvViewWindow(app=self, title=title, menufile=menufile, 
+                      iconfile=iconfile)
+        view_name=view_window.title
+        view_menu = view_window.menuf    
+              
+        if ((len(self.Tool_List) > 0) and (menufile is None)):
+            # If no menu configuration file is specified, put
+            # tools in the default positions specified by
+            # the tool menu entry's position parameter.
+            for cur_tool_list in self.Tool_List:
+                cur_tool = cur_tool_list[1]
+                if hasattr(cur_tool.menu_entries.entries,'keys'):
+                    for item in cur_tool.menu_entries.entries.keys():
+                        view_menu.insert_entry(
+                            cur_tool.menu_entries.entries[item][0],
+                            item,
+                            cur_tool.menu_entries.entries[item][2],
+                            cur_tool.menu_entries.entries[item][1],
+                            (view_name))
+
+        # Icons- Note: currently it is assumed that the tool icons are
+        #        xpms.  Support for pixmaps and widgets will be added
+        #        later if necessary (icon type would have to be detected
+        #        from last entry of the relevant tool icon entry, and
+        #        a function would have to be created to deal with them.
+        #        They are slightly more complicated than the xpm case
+        #        and wouldn't use insert_tool_icon.  Would also need
+        #        code in the complextoolentry case to avoid an icon
+        #        of one type (eg. xpm) being replaced by another type
+        #        (eg. widget, pixmap).
+        #
+        #        ALSO: GtkToolbar does not allow callback information
+        #              to be specified, so the viewname cannot be
+        #              passed as an argument to tool icon callbacks
+        #              the way it is for menu callbacks. However,
+        #              if the view is needed, it can be obtained
+        #              using:
+        #              view=args[0].get_toplevel()
+        #              The view title is under:
+        #              view['title'].  Note that this is not quite
+        #              the same as the view's self.title, but is based
+        #              on it (usually 'OpenEV: '+self.title)
+            
+        if ((len(self.Tool_List) > 0) and (iconfile is None)):
+            for cur_tool_list in self.Tool_List:
+                cur_tool=cur_tool_list[1]
+                for item in cur_tool.icon_entries.entries:
+                    view_window.insert_tool_icon(
+                        item[0], # filename
+                        item[1], # label
+                        item[2], # hint text
+                        item[4], # callback
+                        item[5], # help topic
+                        item[3],  # position
+                            )
+        view_window.show()
+        return view_window
+
+
+</pre>
+
+<a href="DEVCOURSE_scripts.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Help</a><br>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial1.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial1.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial1.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,287 @@
+<html>
+<head>
+<title>Tutorials 1</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+<h1>Tutorial 1: General Overview</h1>
+<ul>
+<li>Launch openev, and load up DEVCOURSE_greyscale_raster.tif (it should be
+in openev's html/developer_info directory).
+<li>Launch the <a href="../layerdlg.html">layers</a> dialog (Edit->Layers...). 
+<p>
+<img src="../layerdlg.gif">
+<p>
+<li>Place the mouse over the layer dialog, click to select it, then hit the "F1" key.  This should bring up
+OpenEV's layer dialog help.  This is one of OpenEV's standard help mechanisms- if you
+place the mouse over a dialog or view window and hit the "F1" key, it will bring up
+the help for that dialog, if help is available for it.
+<li>Right-click on the greyscale raster layer name.  This will bring up a 
+<a href="../gvrasterpropdlg.html">raster properties</a> dialog.
+<p>
+<img src="../gvrasterpropdlg_general.gif">
+<p>
+<li>Under the "Raster Source tab, change the view scaling by sliding the 
+Scale Min and Scale Max slider bars.  You can also type the minimum 
+and maximum values in.  In a greyscale image, values falling below
+Scale Min are set to black, and those above Scale Max are white.
+<li>You can also change the view scaling by clicking on the
+enhancement icons (<img src="../linear.gif"><img src="../log.gif">
+<img src="../equalize.gif"><img src="../nonelut.gif">
+<img src="../windowed.gif">) on the <a href="../mainwindow.html">main window</a>
+icon bar.
+<li>Select 10:1 from the zoom control menu <img src="../zoom_control.gif">, then
+under the "Draw Style" tab, toggle the "Subpixel Interpolation"
+menu between "Off (Nearest)" and "Linear".
+<p>
+<img src="DEVCOURSE_subpixel_interpolation.gif">
+<p>
+<li>Click the <img src="../seeall.gif"> icon or press the "Home" key to rescale the raster to its
+default display size.
+<li>Click <img src="../classify.gif"> to bring up the classification dialog.
+<li>Toggle through the colour ramps menu to the "Green-Yellow-Red" Ramp, then click "Apply".
+<li>Click the <img src="../legend.gif"> icon to create a legend for it.
+<img src="DEVCOURSE_classify_example.gif">
+<li>The colour ramps are simple text files located in the "ramps" directory of
+OpenEV.  The green-yellow-red one below is defined by the file "green_yellow_red_ramp.txt",
+and contains the following:
+<pre>
+Green-Yellow-Red
+0
+0.0 (0.0, 1.0, 0.0, 1.0)
+0.5 (1.0, 1.0, 0.0, 1.0)
+1.0 (1.0, 0.0, 0.0, 1.0)
+</pre>
+The first line specifies the name, the second line indicates the type of colour ramp
+(0 for continuous, 1 for discrete values), and the subsequent lines indicate the
+colours to vary between, specified as a red-green-blue-alpha grouping, where alpha
+is the opacity level.  You can reset the location of your ramps directory by editing the
+".openev" file in your home directory to contain the line (this one sets it to /data/ramps):
+<pre>
+ramp_directory=/data/ramps
+</pre>
+<li>Now place the mouse over the window and press control and the left mouse button, and 
+drag out a region on the image.  This should draw an orange rectangle highlighting the
+area you have selected, and when the mouse button is released, OpenEV will zoom in to
+this area. Pressing control and holding the left mouse button without dragging will
+result in a continuous zoom in; the right mouse button is used to zoom out.  There 
+many other <a href="../viewarea_keys.html">key sequences</a> that
+can be used to zoom in different ways.
+<li>Next launch the Interactive Python Shell ("Edit->Python Shell").
+<p>
+<img src="../pyshell_default.gif">
+<p>
+<li>At the command prompt, enter "get im1", then "show im1" (without the quotes).  A 
+new window should pop up:
+<p>
+<img src="DEVCOURSE_pyshell_ss1.gif">
+<p>
+This demonstrates the use of two OpenEV Python shell core commands, get 
+(extract the underlying data from the currently active view/layer into a 
+shell variable- in this case the shell variable will be called "im1"), 
+and show (display an array or GvShapes variable in
+an OpenEV view).  If "get /s im1" is used instead of "get im1", OpenEV will
+try to grab a screenshot rather than extracting the underlying data (note:
+with some video card drivers this doesn't work very well):
+<p>
+<img src="DEVCOURSE_pyshell_ss2.gif">
+<p>
+If you are not using the /s option and want to extract only part of the 
+data, you can specify a "Region of Interest" (ROI) on the OpenEV view using
+the ROI Tool:
+<ol>
+<li> Activate the ROI tool by selecting the "Edit->Edit Toolbar" menu entry
+     and pressing the "Draw ROI" button.
+<li> Left click and drag out the ROI on your OpenEV view (it should appear
+as an orange rectangle).
+</ol>
+<p>
+If an ROI is drawn, "get im2" should grab the data that corresponds to 
+the region you selected rather than the whole image.  ROI's are ignored
+if the screenshot (/s) option is used.
+<li>Regular Python functions can also be run from the command
+shell (the difference between OpenEV commands and regular Python functions is
+explained in the <a href="../pyshell.html">Python shell help</a>).
+On startup, OpenEV's Python shell imports a variety of functions from 
+<a href="http://www.pfdubois.com/numpy">Numeric Python</a> for creating and 
+manipulating arrays of data.  Enter "shape(im1)" to see the size of
+im1 (it should be 512x512 since the whole image was extracted), 
+then set the first 50 rows and lines to zero: "im1[:50,:50]=0".
+Enter "newview" to create a new OpenEV view, then "display(im1)" 
+to see the the altered im1 in that view (display is a function that
+behaves similarly to the show command- if you want to display
+the results of an expression rather than just a variable, you 
+should use display rather than show, eg. "display(im+75))":
+<p>
+<img src="DEVCOURSE_pyshell_ss4.gif">
+<p>
+Python arrays are indexed from 0:N-1, where N is the length of the
+array in the dimension being indexed.  Slices take the form
+start:stop:step, where start is the start index, stop is the index
+of the element after the last element to be included, and
+step is the step size.  If the start, stop, or step indices are left out,
+they are set to 0,N, and 1 respectively for a dimension of length N.
+Continuing to operate on im1, try setting "im1[::2,:]=0".  This will
+set every second line to zero:
+<p>
+<img src="DEVCOURSE_pyshell_ss5.gif">
+<p>
+Numeric Python offers much more array manipulation capability, including
+matrix addition/subtraction, multiplication/division 
+(both element-wise and regular multiplication), and logical operations.
+It also overloads common operators such as "+" so that
+elementwise addition can be accomplished using statements like
+"im3=im1+im2", where im1 and im2 are same-size arrays.  It also has
+a number of other modules that can be imported to do 
+more sophisticated operations (eg. FFT).
+
+Note that if you are not using floating point arrays, some
+operations may result in overflow errors.  These errors can be avoided
+either by using floating point arrays to start with, or
+by casting the arrays in the statement, for
+example: "im3=im1.astype('d')+im2".
+The type codes in Numeric Python include:
+<ul>
+<li> Character ('c')
+<li> Integer ('1','s','i','l')
+<li> Unsigned Integer ('b','w','u')
+<li> Float ('f','d')
+<li> Complex ('F','D')
+</ul> 
+<p>
+The division within each type corresponds to different numbers of bits
+used to store the type (eg. 8-bit versus 16-bit integer).  You can
+create arrays of any of these types in Numeric Python using the 
+"array", "ones", and "zeros" functions.
+ 
+<li>  Try entering "b=arange(10)", then
+"plot(b,xaxis='my x',yaxis='my y',title='my plot')" in the Python shell.  The
+Numerical Python "arange" function creates an array of values (in this case, 
+the array [0,1,2,3,4,5,6,7,8,9]).  The plot function call above
+plots the array b, and labels it with 'my x' as the x axis, 'my y' as
+the y axis, and 'my plot' as the title.  OpenEV's plot function is based on
+<a href="http://www.comnets.rwth-aachen.de/doc/gnu/gnuplot37/gnuplot.html">Gnuplot</a>. 
+<p>
+<img src="DEVCOURSE_pyshell_ss3.gif">
+<p>
+Enter "commands" and "functions" at the command line to see lists of the
+available OpenEV commands and currently loaded Python functions.  "help /g" or "help -g"
+will display these in graphical format.  
+<p>
+<img src="pyshell_help.gif">
+<p>
+<li>Launch a new view ("File->New View"), and select "File->Open 3D".  
+Choose "DEVCOURSE_greyscale_raster.tif" as
+both the drape and the DEM (Digital Elevation Map).  Set the mesh level of detail
+(LOD) to 5.  This means that OpenEV will use the values in DEVCOURSE_greyscale_raster.tif to 
+provide both the intensity (drape) and height (DEM) values in display.  The mesh
+LOD specifies the resolution of the grid used to sample the DEM for height values
+(higher LOD means better resolution).
+<p>
+<img src="DEVCOURSE_open3d_ss1.gif">
+<p>
+<li>The view will initially appear grey because the view is quite zoomed in and
+there is not a lot of variation in DEVCOURSE_greyscale_raster.tif.  Press Control and hold
+down the right mouse button to zoom out until you can see the raster more clearly.
+<p>
+<img src="DEVCOURSE_open3d_ss2.gif">
+<p>
+This example demonstrates the limitations of using a regular grid of values 
+to sample a DEM- it doesn't work well on an image that is mostly uniform, but
+has a few radical changes.  It looks okay for many natural scenes, but will have
+to be updated if urban fly-throughs are to look good in OpenEV.  The next image
+shows the results if the mesh LOD is upped to 8:
+<p>
+<img src="DEVCOURSE_open3d_ss3.gif">
+<p>
+This is somewhat better, but much slower to render.  Some applications use a
+triangulated irregular network (TIN) representation of the DEM to get around this 
+problem- this allows higher resolution where it is needed; lower where
+the scene is uniform.  This sort of thing will be a consideration if further 
+development is done on OpenEV's 3D capabilities.
+<li>Press "+" and "-" on the keyboard to see the height scaling change.  Holding
+the "Shift" button while pressing these will give steps that are 10 times larger.
+<li>Press "F2" to toggle between 2-D and 3-D viewing modes.
+<li>Launch the <a href="../gvprint.html">Print</a> dialog using File->Print or 
+the <img src="../print.gif">
+icon.
+<p>
+<img src="DEVCOURSE_print_ss1.gif">
+<p>
+This dialog will allow you to print what you see in OpenEV's view area directly to 
+a printer or to file.
+<li>In a new view, load up the file "DEVCOURSE_vector_classes.shp" from openev's html/developer_info 
+directory.
+<li>Launch the vector layer properties dialog by right-clicking on the 
+DEVCOURSE_vector_classes.shp layer in the layer dialog.
+<li>Alter the edge and fill colours of the polygons using the "Areas" section under
+the "Draw Styles" tab.
+<p>
+<img src="DEVCOURSE_vector_layerstyles.gif"> 
+<p>
+<li>Enable the "Select" tool on the edit toolbar ("Edit/Edit Toolbar...") by clicking on it.  
+Launch the vector attributes dialog (Edit->Vector Layer Attributes), and select
+one of the polygons by left-clicking on it.  Each polygon has a an attribute called
+"class" that has a value of "office", "gym", or "playground".
+<li>Click <img src="../classify.gif"> to bring up the classification dialog
+for vector layers, and apply one of the ramps.  This should colour the polygons according
+to their class.  If a vector file has several attribute fields, the menu bar in the
+top right corner of the dialog will allow you to choose which one the classification
+should use to distinguish between polygons.
+<img src="DEVCOURSE_vector_classify.gif">
+<p>
+Vector files containing point and line objects can also be manipulated through
+their vector property dialogs and the classification tool.
+<li>OpenEV also has a number of other standard items, briefly described here:
+<ol>
+<li>File->Import: This asks for a (raster) filename, then converts the raster to GeoTiff format
+and creates tiled overviews for it.  The new raster is saved to a file with extension ".tif".
+<li>File->Save Project (only in some OpenEV distributions): This saves the current 
+openev state (number of views, what is loaded in
+them) to an xml file.  For instance, if you start OpenEV, load up "DEVCOURSE_greyscale_raster.tif", and
+then save the project to projectfile_example.opf (.opf = Openev Project File), it will
+contain:
+<pre>
+&lt;GViewApp&gt;
+  &lt;GvViewWindow module="gvviewwindow" width="620" height="680" x="46" y="94"&gt;
+    &lt;title&gt View 1&lt;/title&gt;
+    &lt;GvViewArea Mode="0" Raw="0"&gt;
+      &lt;Translation x="-256.0" y="-256.0"/&gt;
+      &lt;Zoom&gt 0.211888298392&lt;/Zoom&gt;
+      &lt;Background red="0.0" green="0.0" blue="0.0" alpha="1.0"/&gt;
+      &lt;Layers&gt;
+        &lt;GvRasterLayer mode="1" mesh_lod="7.162109375" visible="1" read_only="0" name="/data/openev/html/DEVCOURSE_greyscale_raster.tif"&gt;
+          &lt;Prototype band="1"&gt;/data/openev/html/DEVCOURSE_greyscale_raster.tif&lt;/Prototype&gt;
+          &lt;Source index="0" min="0.0" max="255.0" nodata="-100000000.0" band="1"&gt;/data/openev/html/DEVCOURSE_greyscale_raster.tif&lt;/Source&gt;
+        &lt;/GvRasterLayer&gt;
+      &lt;/Layers&gt;
+    &lt;/GvViewArea&gt;
+  &lt;/GvViewWindow&gt;
+&lt;/GViewApp&gt;
+</pre>
+This file indicates that there is one view, with title "View 1", of width 620 and height 680 pixels.
+The view area is translated -256 pixels in both x and y directions and has a zoom level 0.211888298392.
+The background is black, and the view area contains a single raster layer, DEVCOURSE_greyscale_raster.tif.
+When "projectfile_example.opf" is loaded up in openev, the views present when the project file
+was saved (in this case, "View 1" with DEVCOURSE_greyscale_raster.tif in it) will be recreated, and all other 
+views closed.  "Save Project" uses the "serialize" functions found in gviewapp.py, gvviewwindow.py, 
+and others to create a list of the items to include in the project file, and uses gdal.SerializeXMLTree
+to convert this list to XML text for writing to a file.  GvViewWindowFromXML in gvviewwindow.py recreates
+a view based on the values saved in the project file.
+<li>Edit->Undo: Undoes the last editing operation performed on a vector layer.
+<li>Edit->Go To: Translate the view to center on the location entered in the Go To dialog.
+<li>Edit->3D Position: Translate the 3D position to the location and orientation entered in the
+3D Position dialog.
+<li>Edit->Preferences:  Allows the user to set various preferences.  See the
+<a href="../preferences.html">Preferences</a> section of OpenEV's help for more.
+</ol>
+</ul>
+
+
+<a href="DEVCOURSE_tutorial2.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Main</a><br>
+
+</body>
+</html>
+

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial2.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial2.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial2.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,217 @@
+<html>
+<head>
+<title>Tutorial 2</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+
+<h1>Developer Tutorial 1: Working with Vectors</h1>
+
+The first part of this tutorial demonstrates the mechanism that OpenEV uses for
+displaying points and polygons.  Start by loading up the shapefile examined
+in the <a href="DEVCOURSE_tutorial1.html">General Overview</a> (if you are continuing
+directly from the first tutorial and still have the classified shapes view, use it
+and skip the first four steps):
+<ul>
+<li>In a new view, load up the file "DEVCOURSE_vector_classes.shp" from openev's html 
+directory.
+<li>Launch the vector layer properties dialog by right-clicking on the 
+DEVCOURSE_vector_classes.shp layer in the layer dialog.
+<li>Alter the edge and fill colours of the polygons using the "Areas" section under
+the "Draw Styles" tab.
+<p>
+<img src="DEVCOURSE_vector_layerstyles.gif"> 
+<p>
+<li>Launch the vector attributes dialog (Edit->Vector Layer Attributes), and select
+one of the polygons by left-clicking on it.  Each polygon has a an attribute called
+"class" that has a value of "office", "gym", or "playground".
+<li>Click <img src="../classify.gif"> to bring up the classification dialog
+for vector layers, and apply one of the ramps.  This should colour the polygons according
+to their class.  If a vector file has several attribute fields, the menu bar in the
+top right corner of the dialog will allow you to choose which one the classification
+should use to distinguish between polygons.
+<p>
+<img src="DEVCOURSE_vector_classify.gif">
+<p>
+</ul>
+<p>
+After the classification ramp has been applied, re-select a shape to
+refresh the vector properties dialog.  A new field, "_gv_ogrfs" appears as
+a property in each shape.  This is the field that openev uses to specify how that
+shape should be displayed.  The "BRUSH" section indicates fill colour (for the
+"office" class, this is yellow, or "ffff00ff" when expressed in red-green-blue-alpha 
+form).  The "PEN" section indicates edge colour. The vector attributes dialog 
+specifies properties of a layer; these are over-ridden by the "_gv_ogrfs" field.
+For point shapes, this field can also be used to specify the symbol to use to 
+represent the point, and to indicate any labelling.  For instance, setting
+_gv_ogrfs for a point shape to the string:
+<pre>
+LABEL(dy:-10.0,dx:10.0,t:{Alias},f:"-adobe-helvetica-medium-r-normal-*-*-240-*-*-p-*-iso10646-1",c:#FEFF06);SYMBOL(c:#FF00FFFF,id:ogr-sym-5)
+</pre>
+creates a magenta (c:#FF00FFFF) filled square (id:ogr-sym-5) symbol with a yellow (c:#FEFF06) 
+helvetica font (f:"-adobe-helvetica-medium-r-normal-*-*-240-*-*-p-*-iso10646-1") label based on
+the contents of the point shape's Alias field (t:{Alias}).  The label is offset 10 pixels in the x
+and y directions from the symbol (dy:-10.0,dx:10.0). The symbol may be one of the vector symbols
+(ogr-sym-*) or a raster symbol (in this case, id should be a path to the raster file to use).  The
+label text can be derived from a field (as above), or specified directly (eg. t:"Hello" instead of
+t:{Alias} would write "Hello" as a label).
+
+<ul>
+<li> Launch a new view, create a new shapes layer by pressing <img src="../layerdlg_new.gif"> on
+the Layers dialog, and select "Draw Labels" on the toolbar ("Edit->Edit Toolbar").
+<li> Place the mouse over the OpenEV view area, click to activate it, and press "Enter" on the keyboard.
+<li> From the "Label Edit Tool" dialog that appears, select Font: helvetica (adobe) on Linux or Arial on Windows,
+Font Style: medium (normal on Windows), Size: 17 pixels (16 on Windows).
+
+<li> In the "Label Edit Tool" Text entry box, enter "Hello", then click on the view
+area again.  The "Hello" label should appear.
+<p>
+<img src="DEVCOURSE_label_ss1.gif"> 
+<p>
+<li> Click on the label to select it.  In the "Shape Attributes" dialog, add ";SYMBOL(id:ogr-sym-5)" to the end of the _gv_ogrfs
+property.  A dialog will appear asking whether you would like to add a new string-type property "_gv_ogrfs".  Click "Yes" to add 
+"_gv_ogrfs" to the list of properties that would be stored to a new file if the vector layer was saved through the
+"File/Save Vector Layer" menu entry.  Click on the view again.  You should see a filled square appear
+next to the "Hello" label.
+<p>
+<img src="DEVCOURSE_label_ss2.gif">
+<p>
+If you press "Select" on the Edit toolbar and then left-click on the label, a box should appear around 
+the label.
+<li> Change the symbol id from "ogr-sym-5" to "ogr-sym-7" (a filled triangle should replace the square),
+then to the full path to DEVCOURSE_mini_raster.tif (eg. SYMBOL(id:/data/openev/html/DEVCOURSE_mini_raster.tif).  Move the SYMBOL(...) section before the LABEL(...) section so that the label appears on top.
+<p>
+<img src="DEVCOURSE_label_ss4.gif">
+<p>
+One quirk of this mechanism is that if there isn't room for the raster to appear as an icon (if it is
+too large for the current zoom level and part of it would be outside the zoom area), it doesn't appear at all.
+If the raster is not visible, drag the label to the center of the view.
+There is no scaling mechanism for raster icons yet.
+<li> In addition to the manual methods used above, labels based on shape properties can be added to 
+points layers through the vector attributes dialog.  This uses a similar mechanism to the one described
+above to alter the display, but provides a simpler interface.  In this case, the "_gv_ogrfs_point" property
+is set on the layer as a whole rather than the "_gv_ogrfs" property being set on individual shapes.
+Load up the file "point_classes.shp" from
+the "developer_course" directory on your cd.  This contains several shapes with a single attribute,
+"class", that is either "office", "gym", or "playground".  Right-click on the point_classes.shp layer
+on the layer dialog to bring up the vector layer properties, and click on the "Draw Styles" tab.
+Under the "Labels" section, change Label Field from "disabled" to "class".  The points should now 
+be displayed as labels.  The symbol used can also be changed, using the "Points" section of the dialog.
+<p>
+<img src="DEVCOURSE_label_ss5.gif">
+<p>
+</ul>
+The next part of this tutorial manipulates vector (GvShapes) objects in the
+OpenEV Python shell to demonstrate some of their features.  Create a shapefile:
+<ol>
+<li>Launch a new view
+<li>Launch toolbar (Edit->toolbar)
+<li>Select "Draw Rectangle" on the toolbar
+<li>On layer dialog, click the <img src="../layerdlg_new.gif"> icon
+<li>On the view, left-click and drag out two rectangles
+<li>Launch the vector attributes dialog (Edit->Vector Layer Attributes)
+<li>Select "Select" on the toolbar
+<li>Left click on a shape to select it
+<li>Add a property to the shape by typing "property1: 'hello'
+in the shape attributes box.  Click "Yes" when it asks you
+if you want to create the new property- this will allow
+it to be saved later.
+<li>In the Shape Attributes dialog, select "integer" from the field
+properties menu and add a new property "property2: 3"
+<li>Add these properties to the second rectangle, using different values.
+</ol>
+<img src="DEVCOURSE_shapefile_ss1.gif">
+<p>
+<ul>
+<li>Save the file as a shapefile (shape_example.shp) using 
+File->Save Vector Layer
+<li>Launch the Python shell (Edit->Python shell)
+</ul>
+In the Python shell, enter (note that path names are case-sensitive, even on Windows):
+<ul>
+<li>import gview 
+<li>sshapes=gview.GvShapes(shapefilename='/full path/shape_example.shp')
+</ul>
+This reads in the shapefile and creates a GvShapes object (sshapes) from its contents.
+Now enter:
+<ul>
+<li>sshapes.get_schema()
+</ul>
+This gets a list of the properties stored in the shapefile.
+Now enter:
+<ul>
+<li>sshapes[0].get_properties()
+</ul>
+This accesses the first GvShape in sshapes, and will show the values of 
+the properties you entered for it.  
+Now enter:
+<ul>
+<li>sshapes[0].get_nodes()
+</ul>
+This returns the number of nodes in the shape- 5 for a rectangle, because
+the last and first nodes are the same.  You can use:
+<ul>
+<li>sshapes[0].get_node(0)
+</ul>
+To get the location of the first node (and similar for the others).  There
+is also a set_node function.  More complicated shapes may have
+multiple "rings" (the main encompassing ring plus areas
+cut out of it).  In these cases, you must enter the ring
+number along with the node number in the get_node and set_node functions,
+to specify which ring's nodes you are accessing (the default is ring 0,
+which is fine for simple, 1-ring shapes)
+<p>
+You can type dir(gview.GvShapes) and
+dir(gview.GvShape) to see other functions associated with shape collections
+and individual shapes.  
+<p>
+OpenEV allows you to draw 3 types of shapes: points, lines, and polygons, and
+it will allow you to draw any of them in a given layer.  However, it is 
+important to note that the specification only allows one type of
+shape to be saved to a given shapefile, so saving the layer as a vector file
+will only save the shapes of one type.  
+<p>
+Next, we will turn the GvShapes object into a GvShapesLayer, ie. a collection
+of shapes that knows how to draw itself, as opposed to just a collection
+of shapes:
+<ul>
+<li>sshapes_layer=gview.GvShapesLayer(shapes=sshapes)
+</ul>
+Create a new view to work in (File->New View), and make sure it is 
+the active one by clicking on it, and by making sure it appears as the 
+current view in the layer dialog box.
+<p>
+Get a handle to the GvViewArea in this view using the fact that openev
+set gview.app to the main application during initialization:
+<ul>
+<li>cview=gview.app.sel_manager.get_active_view()
+</ul>
+This grabs the active view through the selection manager, which is an object
+that keeps track of all layers, views, and selections in the application.
+Now, add the layer to the view:
+<ul>
+<li>cview.add_layer(sshapes_layer)
+</ul>
+Now create a new shape:
+<ul>
+<li>new_shape=gview.GvShape(type=gview.GVSHAPE_AREA)
+<li>new_shape.set_node(1.0,1.0,0.0,0)
+<li>new_shape.set_node(1.0,10.0,0.0,1)
+<li>new_shape.set_node(10.0,10.0,0.0,2)
+<li>new_shape.set_node(10.0,1.0,0.0,3)
+<li>new_shape.set_node(1.0,1.0,0.0,4)
+</ul>
+Add it to the collection:
+<ul>
+</li>sshapes.append(new_shape)
+</ul>
+The new shape should appear in the view.
+<p>
+
+<a href="DEVCOURSE_tutorial3.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Main</a><br>
+
+</body>
+</html>
+

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial3.html
===================================================================
--- packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial3.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_tutorial3.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,142 @@
+<html>
+<head>
+<title>Tutorial 3</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<body>
+
+<h1>Developer Tutorial 2: Working with rasters</h1>
+
+<ul>
+<li>Start openev and launch the Python shell (Edit->Python shell)
+</ul>
+Load the colour raster example as a Numeric Python array using:
+<ul>
+<li>imgarr=LoadFile('DEVCOURSE_colour_raster.tif')
+</ul>
+If openev was not started from the directory containing DEVCOURSE_colour_raster.tif,
+you will have to enter the full path in the filename.  The array can be 
+displayed in the current view using:
+<ul>
+<li>display(imgarr)
+</ul>
+<img src="DEVCOURSE_rasterfile_ss1.gif">
+<p>
+This method can be used to display any greyscale (NxM) or 
+red-green-blue (3xNxM) Numeric Python array.  Now, change
+imgarr a bit:
+<ul>
+<li>imgarr[:,:100,:100]=0
+</ul>
+and press the refresh button <img src="../refresh.gif"> on 
+the iconbar.  You should see the top left corner turn black.
+<p>
+<img src="DEVCOURSE_rasterfile_ss2.gif">
+<p>
+Now launch the toolbar (Edit->Tool bar) and select "Draw ROI".
+Left click and drag out a region on the image
+<p>
+<img src="DEVCOURSE_rasterfile_ss3.gif">
+<p>
+Now get the region of interest from the image using the
+get_roi() command:
+<ul>
+<li>img2=get_roi(imgarr)
+</ul>
+Display it in a new view:
+<ul>
+<li>newview
+<li>display(img2)
+</ul>
+Save it as a tiff file that can be loaded up by openev or other
+applications:
+<ul>
+<li>SaveArray(img2,'mini_raster.tif','GTiff')
+</ul>
+
+<h2>Working with rasters in code</h2>
+
+Now we will try something similar that goes more under the hood
+of OpenEV/gdal:
+<ul>
+<li>imgds=gdal.Open('DEVCOURSE_colour_raster.tif')
+</ul>
+Typing dir(imgds) and dir(gdal.Dataset) shows the properties of the
+image and the functions available to the class.  The gdal.Dataset
+class stores all the information that gdal was able to pull from the
+image file, including ground control points (GCPs) and metadata, and
+provides functions to access the data in each raster band.
+<p>
+Now, turn it into a GvRaster:
+<ul>
+<li>imgraster=gview.GvRaster(dataset=imgds)
+</ul>
+A GvRaster can also be created by passing the filename directly:
+<ul>
+<li>imgraster=gview.GvRaster(filename='DEVCOURSE_colour_raster.tif')
+</ul>
+The GvRaster class allows you to manipulate the data stored in
+the gdal Dataset (or file).  For instance, the functions 
+pixel_to_georef/georef_to_pixel allow you to convert from
+pixel/line coordinates to georeferenced coordinates.  It also
+stores (at the c-level) scaling minimum and maximum values
+to use in display, and provides functions to retrieve
+and change them.  The GvRaster
+also has 'data-changed' and 'data-changing' signal that were
+meant to be used in the context of editing and the undo mechanism, but these
+have never really been used because currently OpenEV's editing capabilities
+(through the toolbar) are limited to vectors.
+<p>
+Next, create a raster layer from the raster, and display it in a new
+view:
+<ul>
+<li>imgrlayer=gview.GvRasterLayer(raster=imgraster)
+<li>newview
+<li>cview=gview.app.sel_manager.get_active_view()
+<li>cview.add_layer(imgrlayer)
+</ul>
+This will show one of the bands of the image in greyscale in the view.
+Now we will add the others:
+<ul>
+<li>imgraster2=gview.GvRaster(dataset=imgds,real=2)
+<li>imgraster3=gview.GvRaster(dataset=imgds,real=3)
+</ul>
+The real=2 and real=3 arguments indicate that the raster should
+be formed from bands 2 and 3 of imgds (default is band 1).  It should
+be noted here that gvviewwindow.py in OpenEV uses a different type of 
+call to create these rasters:
+<ul>
+<li>green_raster = gview.manager.get_dataset_raster(dataset,2)
+</ul>
+This call goes through the gview manager to ensure that the data for
+a given dataset is only stored in one place in memory, even if the
+image is loaded as a layer in several views.  This saves space if,
+for instance, a user is looking at the same image in two views at 
+different zoom levels.
+<p>
+Attach the new rasters to the layer:
+<ul>
+<li>imgrlayer.set_source(1,imgraster2)
+<li>imgrlayer.set_source(2,imgraster3)
+</ul>
+You should see the image take on colours.  You can also add on an alpha
+band (in this case, we set it to the blue band:
+<ul>
+<li>imgrlayer.blend_mode_set(gview.RL_BLEND_FILTER)
+<li>imgrlayer.set_source(3,imgraster3)
+</ul>
+Now, the colours that have blue components show up with an intensity
+proportional to the blue component: (red,green,blue)*blue/255.  The
+blend_mode_set call tells OpenGL to use the alpha component; without it,
+the usual red-green-blue image will be shown.
+<ul>
+<li>imgrlayer.blend_mode_set(gview.RL_BLEND_OFF)
+</ul>
+<p>
+<a href="DEVCOURSE_tools_commands.html">Next</a><br>
+<a href="DEVCOURSE.html">Developer Course Outline</a><br>
+<a href="../openevmain.html">OpenEV Main</a><br>
+
+</body>
+</html>
+

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.dbf
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.dbf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.shp
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.shp
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.shx
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classes.shx
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classify.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_classify.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_layerstyles.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/DEVCOURSE_vector_layerstyles.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/pyshell_example.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/pyshell_example.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/pyshell_full.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/pyshell_full.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/developer_info/pyshell_help.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/developer_info/pyshell_help.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/edittools.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/edittools.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/edittools.html
===================================================================
--- packages/openev/branches/upstream/current/html/edittools.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/edittools.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,256 @@
+<html>
+<head>
+<title>Edit Tools</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Edit Tools</h1>
+
+The follow tools allow manipulation of vectors, as well as some special
+view management operations.  Only one mode may be active at a time (with the
+exception of Link Views), and most operate on a single vector layer (with the
+exception of Zoom, Draw ROI, and Link Views) which must be the active layer
+in the <a href="layerdlg.html">Layer Management Dialog</a>.  The various
+vector editing tools will attempt to make a vector layer active if there is
+no active layer, or if the active layer is not a vector layer.  It is a 
+common frustration to be unable to select or modify vectors because they are
+not in the active layer.<p>
+
+Most of the editing modes have particular button combinations to do actions,
+but in all editing modes the standard <a href="viewarea_keys.html">View Area
+Button/Key Sequences</a> remain valid.  Note that while not explicitly disabled
+in 3D mode the various editing interactions will generally not work properly
+in 3D mode.<p>
+
+Also, note that essentially all editing operations can be undone by hitting
+Control-Z or selecting Edit->Undo in the menu.<p>
+
+<img src="edittools.gif">
+
+<p>
+
+<h2>Select</h2>
+
+Select mode allows selection of vector features, and limited manipulation
+of those features.  Selected features are highlighted with small diamonds
+around the nodes.  Selection may be accomplished in several ways:
+
+<ul>
+<li> <b>LeftButton-Click</b>: Select the feature at the click location.  Note
+that points and lines have only a modest tolerance for selection.  Clicking 
+inside a filled area will select the area.  If more than one features overlaps
+the click location, only one will be selected (based on internal stacking 
+order).  
+
+<li> <b>LeftButton-Drag</b>: Drags out a box and on release selects all 
+features intersecting the box.  Note that the box drag has to start at a 
+location where there is no feature, otherwise it will be interpreted as a
+single selection, and the selected feature will be dragged instead of a box
+being drawn. <p>
+
+<li> <b>Shift-LeftButton-Click</b>: Operates like LeftButton-Click, but adds
+or removes the selected feature from the current selection instead of clearing
+the existing selection.  In most editing systems this would be 
+Control-LeftButton-Click, but that is already bound to continuous zoom.<p>
+
+</ul>
+
+While in selection mode it is also possible to drag the selected features
+around by left clicking on a selected feature, and dragging.  If multiple
+features are already selected, they are all dragged as a group. <p>
+
+The <b>Delete</b> or <b>Backspace</b> key may be used to delete the 
+currently selected feature(s).<p>
+
+
+<h2>Zoom</h2>
+
+Zoom mode is not for editing of vector features, but instead adds some
+simplified view operations.  The following are always available via the
+generic <b>Control</b> chorded alternatives described in 
+<a href="viewarea_keys.html">View Area Button/Key Sequences</a>
+but in zoom mode these simplier alternatives are made available.<p>
+
+<ul>
+
+<li> <b>LeftButton-DoubleClick</b>: Zoom in by one increment (normally
+a factor of two), and recenter on target location.<p>
+
+<li> <b>LeftButton-Drag</b>: Drag out a zoom rectangle, and zoom on release.<p>
+
+<li> <b>LeftButton-PressAndHold</b>: Continuous zoom in.<p>
+
+<li> <b>RightButton-DoubleClick</b>: Zoom out by one increment 
+(normally a factor of two), and recenter on target location.<p>
+
+<li> <b>RightButton-PressAndHold</b>: Continuous zoom out.<p>
+
+<li> <b>MiddleButton-Drag</b>: Drag the image to pan.<p>
+
+</ul>
+
+<h2>Point Edit</h2>
+
+Point edit mode is for creation of point features.<p>
+
+Each <b>LeftButton-Clock</b> in the view will result in a new point feature
+being created, and after creation it will be the selected feature.  The
+<b>Delete</b> key can be used to delete the currently selected point if there
+is one.  It is not possible to move, or otherwise manipulate points in this
+mode.  Switch to <b>Select</b> mode to do that.<p>
+
+<h2>Point Query</h2>
+
+Point Query mode operates the same as Point Edit mode, but it operates on a
+point query layer, instead of a normal vector layer. <p>
+
+On entering Point Query mode, a new point query layer will be created if one
+is not already available on the current view.  Otherwise the point query layer
+will be made the active layer if it is not already active. <p>
+
+Point query layers can show location and raster pixel value information for
+the selected points.  The <a href="gvpquerypropdlg.html">Point Query
+Properties</a> panel has options for controlling what is shown for each
+query point.<p>
+
+<h2>Draw Line</h2>
+
+Line edit mode is for digitizing new line features.  Note that Select
+mode is used to modify existing lines, and Node edit mode is used to modify
+individual nodes within an existing line. <p>
+
+The <b>LeftButton-Click</b> action is used to initiate digitizing of a 
+new polyline.  Subsequent left clicks will add new nodes to the polyline. 
+The <b>RightButton-Click</b> or <b>Escape</b> key can be used to terminate
+line mode.  While digitizing a line, the <b>Delete</b> key can be used to
+delete the last node added.<p>
+
+Note that while digitizing a line undo support is temporarily disabled.<p>
+
+<h2>Rotate/Resize</h2>
+
+Rotate/resize mode allows lines, areas and point symbols to be rotated and 
+resized.  At this time textual labels and plain points (not using symbols) 
+cannot be rotated or resized.<p>
+
+The <b>LeftButton-Click</b> action can be used to select a shape to operate
+on.  Once selected the shape will be drawn with the normal selection styling
+but a resize/rotate control will also be drawn in red, centered on the shape 
+to be modified.  It will look something like this:<p>
+
+<img src="rotatetool.gif"><p>
+
+The <b>LeftButton-Drag</b> action on the red two-directional arrowhead
+will rotate the shape in question.  A <b>LeftButton-Drag</b> action on the
+red one directional arrow head will allow scaling the shape larger or smaller. 
+A <b>RightButton-Drag</b> action on either arrowhead will result in a rotation
+and scaling effect.<p>
+
+Rotation and scaling are around a center point for the shape (the crossing
+point of the two arrows).  Rotation and scaling applied to a point symbol 
+result in the angle and scaling parameters being modified.  Rotation and
+scaling applied to a line or area shape result in the actual vertex
+locations being modified.<p>
+
+Note that selecting the boxes around vertex of lines or areas has no effect
+in rotation and scaling mode.  The <b>Escape</b> key may be used to terminate
+a rotation or scaling drag.<p>
+
+<h2>Draw Rectangle</h2>
+
+Rectangle drawing mode is for digitizing and modifying rectangular area 
+features.  While the rectangles created are just stored internally as normal 
+area polygons, like
+those produced by Draw Area, rectangle mode offers special editing capabilities
+that maintains the rectangular constraint.<p>
+
+In rectangle mode <b>LeftButton-Drag</b> can be used to draw a new rectangle
+feature.  If you click on the corner of an existing rectangle, that corner
+can be dragged, resizing the existing rectangle.  If you click on the edge
+of an existing rectangle, that edge can be dragged resizing the existing
+rectangle.<p>
+
+The <b>Escape</b> key can be used to abort creation of a new rectangle, but 
+while dragging an edge or corner of an existing rectangle it will just stop
+the dragged, equivelent to releasing the button. <p>
+
+<h2>Draw Area</h2>
+
+Area drawing mode is for digitizing area (polygon) features, which can contain
+holes.  Moving existing areas is done with Select mode, and adding, removing
+or moving nodes in an existing polygon is done with Node Edit mode.<p>
+
+Digitizing a new polygon is accomplished by <b>LeftButton-Click</b>ing at
+each vertex location.  When complete a <b>RightButton-Clock</b> terminates
+the process, and the polygon is closed.<p>
+
+Adding an internal ring (a hole) to an existing polygon is done by digitizing
+a new polygon entirely within the old polygon.  The nesting is recognised, and
+the new polygon is treated as an internal ring to the main polygon.  Note that
+this implies it is impossible to digitize a separate polygon that is inside
+another polygon ... to accomplish this, digitize it elsewhere, and then drag
+it into the area.  If you digitize an a new polygon that starts inside
+another polygon, but goes outside the parent, it will be discarded on 
+termination as a corrupt hole.<p>
+
+<h2>Edit Node</h2>
+
+Edit node mode is used to add, move, and remove individual nodes making up
+point, line and area features.  To create new features use the point, 
+line and area drawing modes.<p>
+
+In order to manipulate the nodes of a feature it is necessary to feature
+select the feature.  Node editing mode only supports selection of a single
+feature at a time, by <b>LeftButton-Click</b>ing on it.  Area selects are
+not supported.  Selecting a feature will highlight all it's nodes at which
+point one of them can be selected, by left clicking on it.<p>
+
+A new node may be introduced into the selected feature by 
+<b>LeftButton-Press</b>ing on an edge, but not too close to a node.  The 
+new node may then be positioned by dragging it before releasing.<p>
+
+An existing node is selected by clicking on it (or very close to it), at
+which point the node will be highlighted and filled in.  The node may then
+be dragged to a new location.  A node may be deleted by hitting the 
+<b>Delete</b> key.<p>
+
+<h2>Draw Labels</h2>
+
+Label mode is used to draw labels on a shapes layer.
+
+<h2>Draw ROI</h2>
+
+The ROI tool allows a rectangular region of interest (ROI) to be drawn.  The
+value of a raster rectangle selected with the ROI tool can also be used
+with the get_roi() function to create a subregion array for use with
+Numeric Python. <p>
+
+The ROI tool does not operate on a feature layer.  It draws a free standing
+rectangle by <b>LeftButton-Drag</b>ing it out.  Edges, and corners can also
+be grabbed and dragged.<p>
+
+<h2>Choose POI</h2>
+
+The POI tool allows a point of interest (POI) to be drawn.  The location
+of the selected pixel can be found using the get_poi() function.  The
+POI tool does not operate on a feature layer.  It can be used to draw a 
+free standing point by <b>LeftButton-Click</b>ing.
+
+<h2>Link Views</h2>
+
+Selecting Link Views will cause the view of each all windows to be
+linked.  Pressing the button again will unlink the views.  Any change of
+view in a linked window will result in all other windows also being updated
+similarly.<p>
+
+View characteristics includes the view position, zoom level, and rotation in 
+2D.  In 3D this includes the view position, view direction, and vertical 
+exaggeration.  View characters does not include details about layers 
+displayed, layer colors or anything of that sort.<p>
+
+Link windows will only operate correctly if all views are in the same 
+coordinate system.  For instance, if one view is georeferenced in UTM, and
+another is in lat/long degrees, the linked views will not operate in a useful
+manner.<p>
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/equalize.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/equalize.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/exporttool_advgui.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/exporttool_advgui.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/exporttool_basicgui.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/exporttool_basicgui.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/files.html
===================================================================
--- packages/openev/branches/upstream/current/html/files.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/files.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,135 @@
+<html>
+<head>
+<title>File Access</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>File Access</h1>
+
+OpenEV supports a number of raster and vector file formats for display.  The
+File-&gt;Open action will check to see if the selected file is a shapefile 
+(ends in .shp) and if so attempt to load it as a new vector layer.  If not, it
+will attempt to open it as a GDAL supported file format (listed below), and
+display it as a raster layer.  If this fails, the optional OGR library is
+tried to see if it is another supported vector format. <p>
+
+Note that the File-&gt;Import action only operates on GDAL supported raster
+files.  Vector files cannot be "imported". <p>
+
+<h2>Supported Raster Formats</h2>
+
+OpenEV uses 
+<a href="http://www.remotesensing.org">GDAL</a> (Geospatial Data Abstraction 
+Library) for accessing raster files, so any file formats supported by GDAL 
+should be supported by OpenEV.  The original, and authoritative list of 
+<a href="http://www.remotesensing.org/gdal/formats_list.html">GDAL
+Supported Formats</a> should be checked for a current format list, and
+details of each format. <p>
+
+<table border>
+
+<tr>
+<th>Long Format Name</th>
+<th>Code</th>
+<th>Georeferencing</th>
+</tr>
+
+<tr><td> Arc/Info Binary Grid
+</td><td> AIG
+</td><td> Yes
+</td></tr>
+
+<tr><td> CEOS (Spot for instance)
+</td><td> CEOS
+</td><td> No
+</td></tr>
+
+<tr><td> First Generation USGS DOQ
+</td><td> DOQ1
+</td><td> Yes
+</td></tr>
+
+<tr><td> New Labelled USGS DOQ
+</td><td> DOQ2
+</td><td> Yes
+</td></tr>
+
+<tr><td> Military Elevation Data
+</td><td> DTED
+</td><td> Yes
+</td></tr>
+
+<tr><td> Eosat Fast Format
+</td><td> EFF
+</td><td> No
+</td></tr>
+
+<tr><td> ESRI .hdr Labelled
+</td><td> EHdr
+</td><td> Yes
+</td></tr>
+
+<tr><td> Arc/Info Binary Grid
+</td><td> GIO
+</td><td> Yes
+</td></tr>
+
+<tr><td> TIFF / GeoTIFF
+</td><td> GTiff
+</td><td> Yes
+</td></tr>
+
+<tr><td> Erdas Imagine .hfa
+</td><td> HFA
+</td><td> No
+</td></tr>
+
+<tr><td> Atlantis HKV Blob
+</td><td> HKV
+</td><td> Yes
+</td></tr>
+
+<tr><td> JPEG JFIF
+</td><td> JPEG
+</td><td> Yes
+</td></tr>
+
+<tr><td> Atlantis MFF
+</td><td> MFF
+</td><td> Yes
+</td></tr>
+
+<tr><td> PCI .aux Labelled
+</td><td> PAux
+</td><td> No
+</td></tr>
+
+<tr><td> Portable Network Graphics
+</td><td> PNG
+</td><td> No
+</td></tr>
+
+<tr><td> USGS SDTS DEM
+</td><td> SDTS
+</td><td> Yes
+</td></tr>
+
+<tr><td> SAR CEOS
+</td><td> SAR_CEOS
+</td><td> Yes
+</td></tr>
+
+</table>
+
+<h2>Supported Vector Formats</h2>
+
+OpenEV includes builtin support for reading and writing ESRI Shapefiles. 
+This is a widely used file format for simple vector features with attributes,
+including support for points, polylines and polygonal areas (potentially
+with holes).<p>
+
+Additional formats can be read via an optional linkage to the OGR 
+vector library, providing support for formats such as Mapinfo, SDTS, S-57, 
+Tiger, and NTF.  <p>
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/gvogrdlg.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvogrdlg.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvpquerypropdlg.html
===================================================================
--- packages/openev/branches/upstream/current/html/gvpquerypropdlg.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/gvpquerypropdlg.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+<html>
+<head>
+<title>Point Query Properties</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Point Query Properties</h1>
+
+The Point Query Properties dialog is used to display and edit properties of a 
+point query layer.  It can be launched by right clicking on the desired
+point query layer in the Layer Manipulation Dialog.<p>
+
+<h2>General</h2>
+
+<img src="gvpquerypropdlg_general.gif"><p>
+
+<ul>
+<li> <b>Layer</b>: view, and modify the layer name.  This name is also
+displayed in the Layer Manipulation dialog.<p>
+
+<li> <b>Visibility</b>: Make this layer visible, or invisible.  The same
+thing can be accomplished with the eye icon on the Layer Manipulation
+dialog.<p>
+
+<li> <b>Editable</b>: Toggle whether this layer can be edited or not using
+the various vector editing tools.<p>
+
+</ul>
+
+<h2>Drawing Style</h2>
+
+<img src="gvpquerypropdlg_drawstyle.gif"><p>
+
+<ul>
+<li> <b>Color</b>: Set the color used to draw the points, and text for
+the point query layer. <p>
+
+<li> <b>Point Size</b>:  Set the size of the point cross hair (in screen
+pixels). <p>
+
+<li> <b>Coordinate</b>: Select coordinate display option for point query
+points.<p>
+
+<ol>
+<li> <b>Off</b>: Disable display of point coordinates.
+<li> <b>Raster Pixel/Line</b>: Display pixel/line (row/column) locations
+on the top raster layer under this location.
+<li> <b>Georeferenced</b>: Show georeferenced coordinates in the georeferencing
+system of the view.
+<li> <b>Geodetic (lat/long)</b>: Show coordinates reproject into latitude and
+longitude.<p>
+</ol>
+<p>
+
+<li> <b>Pixel Value</b>: Select whether raster pixel values from the top most
+raster layer under the point should be displayed (<b>On</b>) or not 
+(<b>Off</b>).<p>
+
+</ul>
+
+
+</body>
+</html>
+
+
+
+
+

Added: packages/openev/branches/upstream/current/html/gvpquerypropdlg_drawstyle.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvpquerypropdlg_drawstyle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvpquerypropdlg_general.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvpquerypropdlg_general.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvprint.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvprint.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvprint.html
===================================================================
--- packages/openev/branches/upstream/current/html/gvprint.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/gvprint.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,57 @@
+<html>
+<head>
+<title>Printing Dialog</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Printing Dialog</h1>
+
+The OpenEV Print Dialog is used to capture the contents of a view for
+printing, or other purposes. <p>
+
+<img src="gvprint.gif"></td><p>
+
+<ul>
+
+<li> <b>Driver</b>: One of:
+<ol>
+<li> PostScript: This is the only driver that supports the Paper and Scale
+controls in order to control placement on a page.  The Output Type: Greyscale
+option should produce Level 1 PostScript that will work on any PostScript 
+print.<p>
+<li> TIFF: Produces an RGB or Greyscale TIFF file.  The resulting file is
+not georeferenced.<p>
+<li> PNG: Produces an RGB or Greyscale PNG file suitable for web publishing.<p>
+<li> Windows Print Driver: Prints via an installed MS Windows print driver.
+In this case the Print button leads to a dialog for selection of a specific
+driver, and control of other printer options.  The image will always be
+printed as large as possible given the selection of paper type. <p>
+</ol>
+
+<li> <b>Device</b>: Select whether the resulting output is spooled directly
+to the printer or saved to a file.  This option is always File for PNG and
+TIFF, and always Spool To Printer for Windows Print Driver.  PostScript may
+be spooled, or directed to a file. <p>
+
+<li> <b>File/Command</b>: If the Device is File this is the name of a file
+to save the print to.  If it is Spool to Printer this will be a command to
+be used to spool to the printer.  The PostScript text will be directed to 
+the standard input of the indicated command.<p>
+
+<li> <b>Output Type</b>: Either Greyscale or Color to control the type of
+output produced.<p>
+
+<li> <b>Paper</b>: (PostScript only) Select the paper size.  This is used
+to establish size and positioning of the view.<p>
+
+<li> <b>Scale</b>: (PostScript only) Modifies the scale at which the print
+is produced.  A value of 0.5 would cause the view to be 50% the normal size, 
+but still centered on the paper. <p>
+
+<li> <b>Resolution</b>: The resolution at which the current view will be
+rendered for output.  A value of 2.0 would cause the view to be internally
+rendered at double the resolution of the screen display for output. <p>
+
+<ul>
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/gvrasterpropdlg.html
===================================================================
--- packages/openev/branches/upstream/current/html/gvrasterpropdlg.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/gvrasterpropdlg.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,121 @@
+<html>
+<head>
+<title>Raster Properties</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Raster Properties</h1>
+
+The Raster Properties dialog is used to display properties of a raster
+display layer.  It can be launched by right clicking on the desired
+raster layer in the Layer Manipulation Dialog.<p>
+
+<h2>General</h2>
+
+<img src="gvrasterpropdlg_general.gif"><p>
+
+<ul>
+<li> <b>Layer</b>: view, and modify the layer name.  This name is also
+displayed in the Layer Manipulation dialog.<p>
+
+<li> <b>Visibility</b>: Make this layer visible, or invisible.  The same
+thing can be accomplished with the eye icon on the Layer Manipulation
+dialog.<p>
+
+<li> <b>Editable</b>: Currently this control has no effect.<p>
+
+</ul>
+
+<h2>Drawing Style</h2>
+
+<img src="gvrasterpropdlg_drawstyle.gif"><p>
+
+<ul>
+<li> <b>Modulation Color</b>: Set the color to modulate the layer with. 
+Selecting red, green or blue will cause the layer to only affect red, 
+green or blue, with other RGB components <i>showing through</i>.  An RGB
+composite can be created from three layers by setting each to be modulated
+by different component colors.<p>
+
+Setting the opacity of the modulation 
+color (via the custom... entry) effectivly sets the opacity (alpha) for
+the whole layer.  Alpha blending can also be achieved using the Alpha band
+of RGB layers on the Raster Sources tab.<p>
+
+<li> <b>Subpixel Interpolation</b>: Determines whether screen values should
+be deduced from image values by linear interpolation, or decimation (pick 
+one of possible pixels).<p>
+
+</ul>
+
+<h2>Raster Sources</h2>
+
+<img src="gvrasterpropdlg_source.gif"><p>
+
+The Raster Sources tab allows control of the input raster bands used
+to generate the display layer.  Each input component can be independently
+scaled, band mappings can be changed, and components can be set to use
+fixed scales instead of source bands. <p>
+
+<b>Band</b>: Select the band to use for this component from the
+file for this layer.  At this time it is not possible to alter the source
+file after creation of the raster layer.  If the value <b>constant</b> is
+selected instead of a band number, the scaling min/max controls will be
+replaced with an entry control which can be used to enter a single fixed
+value to use for that component for the entire layer.<p>
+
+Non-eight bit data (16bit integer, and floating point) is scaled to eight
+bit (range 0-255) before compositing, or putting through the layer lookup
+table.  The scale minimum is mapped to 0, and the scale maximum to 255.
+The default scaling values are selected based on a 2% tail trim of a
+histogram of 10000 sample pixels from the image.  Values outside the
+initial min/max range may be selected by typing them into text entry 
+control.<p>
+
+<b>Scale Min</b>: Set the minimum value for scaling to eight bit.<p>
+
+<b>Scale Max</b>: Set the maximum value for scaling to eight bit.<p>
+
+<h2>LUT</h2>
+
+<img src="gvrasterpropdlg_lut.jpg"><p>
+
+The LUT tab only appears for complex raster layers, and allows five options
+for how the 2D (real and imaginary) components of the complex data will be
+mapped into a color for display.  The options are:
+
+<ol>
+<li> Magnitude and Phase: Produce an image with HSV hue indicated by the
+phase, and HSV value indicated by the magnitude of the complex values.<p>
+
+<li> Phase: Produce an image with HSV hue indicating the phase of the
+complex values.<p>
+
+<li> Magnitude: Produce a red scale image showing the magnitude of the
+real and imaginary values.<p>
+
+<li> Real: Produce a greyscale image showing only the real component of
+the complex data. <p>
+
+<li> Imaginary: Produce a greyscale image showing only the imaginary
+component of the complex data.
+
+</ol>
+
+The displayed color swatch is the 2D LUT, with the vertical axis indicating
+increasing real values downwards, and the horizontal axis indiating increasing
+imaginary values to the right. <p>
+
+<h2>Image Info</h2>
+
+<img src="gvrasterpropdlg_imageinfo.gif"><p>
+
+The Image Info tab displays additional information about the image, including 
+any metadata stored within the file. <p>
+
+</body>
+</html>
+
+
+
+
+

Added: packages/openev/branches/upstream/current/html/gvrasterpropdlg_drawstyle.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvrasterpropdlg_drawstyle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvrasterpropdlg_general.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvrasterpropdlg_general.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvrasterpropdlg_imageinfo.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvrasterpropdlg_imageinfo.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvrasterpropdlg_lut.jpg
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvrasterpropdlg_lut.jpg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvrasterpropdlg_source.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvrasterpropdlg_source.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvvectorpropdlg.html
===================================================================
--- packages/openev/branches/upstream/current/html/gvvectorpropdlg.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/gvvectorpropdlg.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,50 @@
+<html>
+<head>
+<title>Vector Properties</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Vector Properties</h1>
+
+The Vector Properties dialog is used to display and edit properties of a 
+vector layer.  It can be launched by right clicking on the desired
+vector layer in the Layer Manipulation Dialog.<p>
+
+<h2>General</h2>
+
+<img src="gvvectorpropdlg_general.gif"><p>
+
+<ul>
+<li> <b>Layer</b>: view, and modify the layer name.  This name is also
+displayed in the Layer Manipulation dialog.<p>
+
+<li> <b>Visibility</b>: Make this layer visible, or invisible.  The same
+thing can be accomplished with the eye icon on the Layer Manipulation
+dialog.<p>
+
+<li> <b>Editable</b>: Toggle whether this layer can be edited or not using
+the various vector editing tools.<p>
+
+</ul>
+
+<h2>Drawing Style</h2>
+
+<img src="gvvectorpropdlg_drawstyle.gif"><p>
+
+<ul>
+<li> <b>Points</b>: Set the color, and pixel cross hair size for point 
+shapes in this layer (if any).  The point size is measured in screen pixels.<p>
+
+<li> <b>Lines</b>: Set the line color for line shapes.<p>
+
+<li> <b>Areas</b>: Set the edge, and fill colors for areas.<p>
+
+</ul>
+
+
+</body>
+</html>
+
+
+
+
+

Added: packages/openev/branches/upstream/current/html/gvvectorpropdlg_drawstyle.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvvectorpropdlg_drawstyle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/gvvectorpropdlg_general.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/gvvectorpropdlg_general.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/help.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/help.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/idle.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/idle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/layerdlg.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/layerdlg.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/layerdlg.html
===================================================================
--- packages/openev/branches/upstream/current/html/layerdlg.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/layerdlg.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,67 @@
+<html>
+<head>
+<title>Layers Management Dialog</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Layers Management Dialog</h1>
+
+The OpenEV Layers Management Dialog is used to manage the order of display
+layers within a view, to change between views, and to launch properties panels
+for layers.<p>
+
+<table border=0><tr>
+<td><img src="layerdlg.gif"></td>
+
+<td>
+<ul>
+<li> 
+The option menu at the top of the dialog may be used to select a view 
+to operate on.  Selecting an alternate view will cause that view to be
+popped up, as well as displaying the views layers in the Layers Management
+Dialog.<p>
+
+<li> Each layer is listed with it's name (typically the file it comes from), 
+and an eye icon (if visible).  The order of the layers indicates their
+precidence when drawn, with the top most layer in the list drawn over
+all other layers.<p>
+
+<li> Left clicking on a layer will cause it to become the selected 
+(highlighted) layer.  Many operations such as enhancements, and vector
+editing operate on the selected layer of the selected view. <p>
+
+<li> Clicking on the eye will cause the layer to toggle between
+being visible, and not being visible.  <p>
+
+<li> Right clicking on the layer will cause a layer properties dialog to
+be displayed.<p>
+
+</ul>
+
+</td>
+
+</table>
+
+<ul>
+
+<li> The <img src="layerdlg_raise.gif"> icon will cause currently selected
+layer to move up in the layer dialog, causing it to be drawn over the layer
+that was previously above it. <p>
+
+<li> The <img src="layerdlg_lower.gif"> icon will cause currently selected
+layer to move down in the layer dialog, causing it to be drawn under the layer
+that was previously below it. <p>
+
+<li> The <img src="layerdlg_new.gif"> icon can be used to create a new
+vector layer for digitizing into.<p>
+
+<li> The <img src="layerdlg_delete.gif"> icon can be used to delete the 
+currently selected layer.<p>
+
+</ul>
+
+<p>
+
+
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/layerdlg_delete.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/layerdlg_delete.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/layerdlg_lower.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/layerdlg_lower.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/layerdlg_new.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/layerdlg_new.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/layerdlg_raise.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/layerdlg_raise.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/legend.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/legend.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/linear.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/linear.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/log.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/log.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/logo.jpg
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/logo.jpg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/mainwindow.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/mainwindow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/mainwindow.html
===================================================================
--- packages/openev/branches/upstream/current/html/mainwindow.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/mainwindow.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,286 @@
+<html>
+<head>
+<title>Main OpenEV Window</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Main OpenEV Window</h1>
+
+The main OpenEV display window contains the geospatial data viewing
+area, as well as menus and an iconbar.  The button and key sequences for
+manipulating view state are decribed in the separate
+<a href="viewarea_keys.html">View Area Button/Key Sequences</a> page.<p>
+
+The area at the bottom of the view window is the <b>track tool</b> and
+display position and raster value under the cursor.  Details preferences
+for controlling the tracking tool are available in the application
+preferences panel (Edit->Preferences).<P>
+
+<img src="mainwindow.gif"><p>
+
+<h2>Icon Bar</h2>
+
+<dl compact>
+
+<dt><img src="openfile.gif">
+<dd><b>Open File</b>: 
+Open a raster or vector file, and add to this view.  Same as File->Open.<p>
+
+<dt><img src="print.gif">
+<dd><b>Print</b>:
+Launch the <a href="gvprint.html">Printing Dialog</a> to print the current
+view.<p>
+
+<dt><img src="nonelut.gif">
+<dd><b>Remove Enhancement</b>:
+Remove any greyscale enhancement lut (such as applied by the equalize
+icon) from the active raster layer.  Does nothing if no raster layer is
+selected in the layers dialog.
+Note that a linear scaling stretch will still be in effect for
+non-eight bit data.<p>
+
+<dt><img src="linear.gif">
+<dd><b>Linear Enhancement</b>:
+Perform a linear stretch enhancement.<p>
+
+<dt><img src="equalize.gif">
+<dd><b>Histogram Equalization</b>:
+Apply a histogram equalization enhancement LUT to the active raster.  This
+is a non-linear constrast stretch intended to flatten out the post-enhancement
+histogram.  See the GvRasterLayer::equalize() method in pymod/gview.py for
+details on the algorithm used.  Does nothing if a raster layer is not
+selected in the layers dialog. <p>
+
+<dt><img src="log.gif">
+<dd><b>Logarithmic Enhancement</b>:
+Perform a logarithmic stretch enhancement.  The enhancment lut is recomputed
+to map the minimum to zero, the maximum to 255 and intermediate values
+based on logarithmic scaling.  See GvRasterLayer::log() for algorithmic
+details.<p>
+
+<dt><img src="windowed.gif">
+<dd><b>Windowed Raster Re-enhancement</b>:
+Recompute the last enhancement (linear, equalize, logarithmic) based on
+pixels currently visible in the view.  The scaling min/max and enhancement
+are recomputed.  In 3D mode substantial numbers of pixels not actually
+visible in the view may be included in the calculation. 
+<p>
+
+<dt><img src="classify.gif">
+<dd><b>Classify Raster</b>:
+Launches a dialog for applying a pseudo-color classification to a single
+raster band.  This is a GIS style classification, not a multispectral 
+classification.  The dialog can only be launched if a raster layer is
+selected.  The dialog is not currently further documented. 
+<p>
+
+<dt><img src="legend.gif">
+<dd><b>Show Legend</b>:
+Launches a dialog displaying the current classification legend generated
+by the Classify Raster dialog.  If there is no classification in force on
+the current raster, or if no raster layer is selected nothing will
+happen. 
+<p>
+
+<dt><img src="seeall.gif">
+<dd><b>Fit all Layers</b>:
+Resets the view position, and zoom level such that all the data in all
+view layers is shown.  As currently implemented the any rotation is lost.
+<p>
+
+<dt><img src="zoom_control.gif">
+<dd><b>Zoom Control</b>:
+Displays the zoom level of the currently selected raster layer.  A value of
+1:2 would indicate decimation by 2, while 2:1 would indicate duplication
+of raw pixels by a factor of two.  The user can selected a desired zoom level
+from popup menu, or directly type in zoom ratios.   The control has no
+effect when a non-raster layer is selected.
+<p>
+
+<dt><img src="zoomin.gif">
+<dd><b>Zoom In</b>:
+Zoom in (making features bigger) by a factor of 2.
+
+<dt><img src="zoomout.gif">
+<dd><b>Zoom Out</b>:
+Zoom out (making features larger) by a factor of 2.
+
+<dt><img src="refresh.gif">
+<dd><b>Refresh Raster from Disk</b>:
+Causes the current raster layer to be reloaded from disk, or from it's
+source Numeric Python array.  There are various levels of caching applied
+in OpenEV, and data is normally only re-read from disk when not available
+in memory.  If a file is updated on disk (or a display numerical python 
+array is updated) the refresh function can be used to force the display
+to be resyncronized from the source data.  Applies to the active raster layer.
+If no raster layer is active nothing happens. 
+<p>
+
+<dt><img src="worldg.gif"> <img src="worldrgb.gif">
+<dd><b>Georeferenced Display</b>:
+When the black and white icon is visible, the view is being displayed
+in raw pixel/line coordinates for the source raster.  When the color icon
+is displayed the view is trying to display in georeferenced units (if
+any are available).  Selecting this icon will toggle the state.  This
+icon is generally used to switch between raw and warped images for images
+that include control points that will define a polynomial warp (such as
+many CEOS images).  The initial state of this icon is determined by the
+"Display Georeferenced" control on the Raster tab of the 
+<a href="preferences.html">Preferences</a> dialog.
+<p>
+
+<dt><img src="help.gif">
+<dd><b>Online Help</b>:
+Launch the OpenEV online help, the same as Help->Help.
+<p>
+
+<dt><img src="help.gif">
+<dd><b>Online Help</b>:
+Launch the OpenEV online help, the same as Help->Help.
+<p>
+
+<dt><img src="busy.gif"> <img src="idle.gif">
+<dd><b>Busy/Idle Indicator</b>:
+The red icon shows when the application is busy, and the green icon
+displays when the application is idle.  Due to the background loading of
+data the application will often be busy for a short time after initially
+displaying a raster.  Pressing the icon has no effect, and even when the
+application is busy it should remain responsive if somewhat more sluggish
+than when idle.
+<p>
+
+</dl>
+
+<h2>Menus</h2>
+
+<h3>File</h3>
+
+<dl compact>
+
+<dt> Import
+<dd> Import raster for optimized access.  The user is prompted to select
+a raster data file.  This is converted into tiled GeoTIFF format in the current
+working directory, overview levels are built, and the imported file is 
+added to the current view (as if it had been opened).<p>
+
+The imported file is optimized for fast access, but may lose some information
+from the source file (such as metadata).  Note that vector files cannot
+be imported.<p>
+
+<dt> Open
+<dd> Open a raster or vector file, and add it to the current view as a new
+layer.  For files with a greater than eight bit data type, min/max values
+will be computed for scaling purposes.  The resulting layer will be assigned
+the name of the source file. <p>
+
+<dt> Open 3D
+<dd> Brings up a dialog for selection of a drape and elevation raster.  They
+are added to the current view as a 3D image, and the view is placed in 3D 
+mode. <p>
+
+<dt> Save Vector Layer
+<dd> Brings up a file selector for entry of a filename to save the active
+vector layer to.  The file saved to will be in ESRI Shapefile format.  ESRI
+Shapefiles can only support one type of geometry per file (point, line, area)
+so if the active layer has a mixture, only one of the types (the first 
+encountered) will be saved.  Feature attributes are only saved if they are
+described by a schema on the layer, as is the case with vectors loaded
+from a shapefile. <p>
+
+<dt> New View
+<dd> Creates a new OpenEV view window with an independent set of data
+layers, and makes it the current view.  <p>
+
+<dt> Print
+<dd> Launches the <a href="gvprint.html">Printing Dialog</a> for printing
+the view contents.<p>
+
+<dt> Recent Filenames
+<dd> A list of recently opened files is maintained in this menu.  Selecting
+one is equivelent to doing a File->Open, and entering the displayed name.<p>
+
+<dt> Close
+<dd> Close the current window.  If this is the last main display window for
+this OpenEV process the user is prompted if they want to exit the
+whole process.<p>
+
+<dt> Exit
+<dd> Close all windows, and terminate OpenEV.  The user is prompted for
+confirmation.<p>
+
+</dl>
+
+<h2>Edit</h2>
+
+<dl compact>
+
+<dt> Undo
+<dd> Undoes the last undoable operation.  Currently only the various 
+vector editing operations are undoable.  View changes, layer property
+changes and addition or deletion of layers are not undable.  The undo 
+stack is not bounded, and so in theory
+many undo steps are available; however, certain operations, like destruction
+of undoable layers results in clearing of the entire undo stack. <p>
+
+<dt> Layers...
+<dd> Launches the <a href="layerdlg.html">Layer Management Dialog</a> for
+manipulating view layers. <p>
+
+<dt> Vector Layer Attributes...
+<dd> Launches the vector features attributes dialog for inspecting, and 
+changing the attributes of the selected feature.<p>
+
+<dt> Edit Toolbar...
+<dd> Launches the <a href="edittools.html">Edit Tools</a> for controlling
+editing mode.<P>
+
+<dt> Go To...
+<dd> Launches a simple dialog for recentering the view around a particular
+georeferenced location.<P>
+
+<dt> Python Shell...
+<dd> Launches a <a href="pyshell.html">Python Shell</a>.  This is a
+dialog for interactively entering Python commands for simple image 
+processing and other scripting needs.<P>
+
+<dt> 3D Position...
+<dd> Launches a dialog for setting the 3D view position and direction.<p>
+
+<dt> Preferences...
+<dd> Launches the application preferences panel for viewing, and modifying
+application wide preferences.<p>
+
+<dt> Python Shell...
+<dd> Launches the python shell for doing numerical python operations.<p>
+
+<dt> 3D Position...
+<dd> Launches the 3D position dialog for viewing, and modifying the 3D view
+position.<p>
+
+</dl>
+
+<h2>Help</h2>
+
+Note that online help is displayed as HTML
+files, and OpenEV needs access to an HTML viewing application.  On Windows
+the default system HTML viewer is used.  On Unix various well known browsers
+are searched for (ie. netscape, Mosaic).  The user can customize this in the
+application preferences panel available under Edit->Preferences.<p>
+
+The help topic for most dialogs can be launched directly by hitting F1 after
+clicking on the dialog.<p>
+
+<dl>
+
+<dt> Help
+<dd> Displays application help.  <p>
+
+<dt> Web Page
+<dd> Displays the OpenEV web page.
+
+<dt> About
+<dd> Display OpenEV information, and credits.<p>
+
+</dl>
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/nonelut.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/nonelut.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/onetoone.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/onetoone.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/open3d.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/open3d.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/open3d.html
===================================================================
--- packages/openev/branches/upstream/current/html/open3d.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/open3d.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,94 @@
+<html>
+<head>
+<title>Open 3D</title>
+</head>
+<body bgcolor="#ffffff">
+
+<h1>Open 3D</h1>
+
+OpenEV is capable of displaying 3D views as well as 2D views.  To setup a
+3D view use the <b>File-&gt;Open 3D</b> option in the menu.  This launches
+the 3D layer loading dialog.<p>
+
+<img src="open3d.gif"><p>
+
+<h2>Selecting Drape, and DEM Rasters</h2>
+
+Displaying a raster in 3D requires two components.  One is the drape image.
+The image to be displayed in 3D.  The other component is the raster used as
+a source of elevation information (the DEM raster).  The Open 3D dialog 
+contains two embedded file selectors, one for selecting the drape image
+and one for selecting the DEM raster.  Any OpenEV supported raster format
+can be used for the drape and the DEM.  Supported raster formats are
+discussed in the <a href="files.html">File Access</a> help topic.<p>
+
+Once selected OpenEV will create a 3D mesh over which to drape the drape
+image, based on elevation values from the DEM image.   The density of the
+mesh is determined by the DEM Level of Detail control described later, but
+it is typically at substantially lower resolution than the drape image.  Each
+mesh location is sampled from the DEM raster.  If the location lies outside
+the DEM raster a value of zero is assumed.<p>
+
+It is not necessary for the drape and DEM images to have the same resolution
+or the same exact region; however, areas of the drape image lying outside
+the DEM will not have proper elevations assigned.  Areas of the DEM outside
+the drape image will not be used.  <b>The coordinate system of the drape and
+DEM images should be compatible.</b>  OpenEV does not check this, so if the 
+coordinate systems are different it is likely that no overlap will occur, or
+the elevations selected will be inappropriate. <p>
+
+<h2>Mesh Level Of Detail</h2>
+
+The <b>Mesh Level of Detail</b> selector allows control over the density of
+the elevation mesh.  A highly refined mesh will result in more accurate 
+geometry, with one mesh point per drape pixel being the most refined the mesh
+can be (at level 8).   The least refined mesh (normally used for 2D display)
+has only one mesh point at each corner of the 256x256 drape tiles, and is
+mesh level of detail 0.<p>
+
+<b>Highly refined meshes take alot more memory, and will generally slow down
+redraws substantially.</b>  The default mesh density (level of detail 3) has
+one mesh vertex every 32 pixels.  Level of details in excess of 5 (one mesh
+vertex every 8 pixels) can become painfully slow even on accelerated systems.
+<p>
+
+<h2>Height Scaling Factor</h2>
+
+The height scaling factor is a multiplier applied to all elevations.  A value
+of one gives a physically realistic display if the elevations are in the
+same units as the image georeferencing (ie. meters).  It may be desirable to
+exaggerate the elevations to make 3D effects more visible by setting a
+scaling factor greater than one.<p>
+
+In some cases elevations are in different units, and the scaling factor can
+be used to correct for this.  For instance, if the georeferenced display is
+in meters, but the elevations are in decimeters, a scaling factor of 0.1 will
+correct things.<p>
+
+<h2>Height Clamping</h2>
+
+The toggles for clamping minimum and maximum height can be used to
+restrict the range of mesh heights, according to the values in their
+corresponding text fields.  This can be useful if the raster
+used as a DEM has invalid points that OpenEV is treating as valid data.
+This clamping is a one-time operation that acts on the data as it is 
+read in to set the mesh values; it does not affect scaling operations 
+that act on the view as a result of key-press events.
+<p>
+
+<h2>3D View Mode</h2>
+
+Once the OK button is hit, a new raster layer will be created, with associated
+3D mesh values.  Depending on the level of detail selected for the mesh this
+may take some time.<p>
+
+Once complete the view is placed into <b>3D Mode</b>, and a different set of
+controls are used to maneuver. 
+Mouse and keyboard controls for 3D mode are described on the
+<a href="viewarea_keys.html">View Area Button/Key Sequences</a> page.<p>
+
+Scrollbars, and the various vector editing controls should not be used in 3D
+mode as they will behave inproperly.<p>
+
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/openevmain.html
===================================================================
--- packages/openev/branches/upstream/current/html/openevmain.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/openevmain.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,37 @@
+<html>
+<head>
+<title>OpenEV</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+
+<p><img SRC="logo.jpg" height=75 width=290 ALT="OpenEV"><p>
+
+OpenEV is an application for viewing and analysing raster and vector
+geospatial data in 2D and 3D.  OpenEV is an Open Source product, more 
+information, and source code can be found at the OpenEV Web Page 
+(<a href="http://openev.sourceforge.net/">http://openev.sourceforge.net/</a>).
+<p>
+
+<h1>OpenEV Online Help Pages</h1>
+
+<ul>
+<li> <a href="viewarea_keys.html">View Area Button/Key Sequences</a>
+<li> <a href="layerdlg.html">Layers Management Dialog</a>
+<li> <a href="gvrasterpropdlg.html">Raster Properties Dialog</a>
+<li> <a href="gvvectorpropdlg.html">Vector Properties Dialog</a>
+<li> <a href="gvpquerypropdlg.html">Point Query Properties Dialog</a>
+<li> <a href="gvprint.html">Printing Dialog</a>
+<li> <a href="edittools.html">Editing Toolbar</a>
+<li> <a href="pyshell.html">Interactive Python Shell</a>
+<li> <a href="mainwindow.html">Main Window</a>
+<li> <a href="open3d.html">Open 3D View</a>
+<li> <a href="preferences.html">Application Preferences</a>
+<li> <a href="files.html">File Access</a>
+<li> <a href="performance.html">Performance Tuning</a>
+<li> <a href="customization.html">Customization</a>
+<li> <a href="developer_info/DEVCOURSE.html">Developer Information</a>
+</ul>
+
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/openfile.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/openfile.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/performance.html
===================================================================
--- packages/openev/branches/upstream/current/html/performance.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/performance.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,132 @@
+<html>
+<head>
+<title>OpenEV Performance Tuning</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>OpenEV Performance Tuning</h1>
+
+OpenEV is designed to be a high performance viewing environment.  Nevertheless
+there are a number of factors to keep in mind to optimize performance. 
+These are three main areas to address to get good performance.<p>
+
+<h2>Accelerated OpenGL Support</h2>
+
+OpenEV is based on OpenGL, a standard for high performance 2D and 3D
+rendering.  Software versions of OpenGL are available which do all operations
+in memory using conventional CPU resources, but these implementations tend to
+be much slower than optimized OpenGL drivers that utilize capabilities of
+accelerated 3D video cards.<p>
+
+In general OpenEV will operate <i>much</i> faster on systems with accelerated
+OpenGL support.  On Windows NT, it is generally sufficient to install 
+vendor provided drivers if you have a 3D video card (most medium to high 
+end video cards).  On Silicon Graphics systems, accelerated support is
+the standard.  On Sun systems it may be available depending on the card
+in use.  On Linux it is often necessary to hunt down and install 3D accelerated
+drivers from one of a number of sources, though software vendors like
+<a href="http://www.xig.com/">Xi Graphics</a> offer good OpenGL drivers for
+many popular cards on Linux. <p>
+
+Without accelerated OpenGL support redraws will be much slower (often 50x 
+slower).  The effect of this on user interaction can be reduced by keeping
+a few things in mind:
+
+<ul>
+<li> Avoid <i>dragging</i> operations that require a redraw after each drag
+increment.  This means some of the vector editing modes will be very
+painful if there is a raster being displayed.  Also, avoid trying to drag
+out a rectangle for zooming.<p>
+
+<li> Avoid continuous zoom and pan.  Use the PageUp/PageDown keys for zooming
+in descrete jumps, and use the arrow keys (with Shift and Control chording)
+for panning. <p>
+
+<li> Avoid having more raster layers displayed than necessary.  Even if drawn
+over one another each will add substantially to redraw time. <p>
+
+<li> Don't make the view window larger than you need, as redraw time will
+be proportional to the number of screen pixels.<p>
+
+<li> Turn off linear interpolation if you don't need it in the raster 
+properties panel, as it adds substantially to the redraw time.<p>
+</ul>
+
+<h2>Texture and File Caching</h2>
+
+OpenEV does two types of caching for raster data, to accelerate redisplay
+speeds.  The first is the caching of raw data from the file, and the second
+is the caching of preprocessed textures.  The application preferences dialog
+can be used to control the cache size for both of these, and appropriate 
+settings primarily depend on the hardware available.<p>
+
+Raw data caching is done on a tiled basis, and holds raw data values, before
+scaling or other preprocessing.  A substantial file cache (raw data cache)
+is most important to accelerate texture regeneration when preprocessing 
+parameters such as scaling values, and color tables are changed.  The disk
+cache is kept in system RAM, and should generally not be larger than 50% of
+system RAM.<p>
+
+Texture data caching involves caching of preprocessed textures.  On accelerated
+systems the textures are kept in video RAM, allowing very rapid redisplay of
+the image.  On software OpenGL systems the textures are cached in main memory.
+Generally speaking the textures cache should be about the size of video
+card RAM less 8MB for frame buffer memory.<p>
+
+It is critical to have enough texture cache memory to hold all the textures
+required for one complete view display or an thrashing condition can occur. 
+In this situation, the display is continually redraw, with different files
+dropping out of the redraw on each refresh.  To correct this problem, go into
+Edit->Preferences, and increase the texture cache size.  
+
+<h2>Disk File Format</h2>
+
+OpenEV is a disk based viewer.  It is intended to operate on images much
+larger than can be kept in physical RAM.  While for small datasets and
+with large file cache sizes it may be practical to hold the entire image in
+memory, that is not the case for large files.  To optimize access to large
+files it is helpful to organize the file for easier access.  Three
+things can help in this regard, building overviews, tiling the data, and 
+ensuring the data is on local (high speed) media.<P>
+
+Having pre-built overviews (pyramids) will ensure that the initial display of 
+large datasets can be very fast, as only a small reduced resolution image
+needs to be read to display the overview on screen.  Overviews can be built
+for most GDAL supported formats with the <i>gdaladdo</i> commandline program,
+and File-&gt;Imported rasters will always have overviews built.  A few 
+applications, notably Atlantis processing tasks, will produce datasets with 
+pre-built overviews for fast initial viewing.<p>
+
+OpenEV naturally accesses data by tiles, typically 256x256 rectangles of the
+source file.  In order to read one tile from a line interleaved raster
+file that is 10Kx10K pixels in size, it is necessary to read 256 full 
+scanlines, or 2.5 million pixels of data, in order to satisfy a request for
+about 64000 pixels.  While OpenEV attempts to cache those scanlines to 
+satisfy other tile requests in the same row, it is often the case that the
+viewer will only need a small portion of the whole scanline for a view.  
+Accessing so much extra disk content can substantiallly slow displays.   By
+reorganizing the data into pre-tiled format, local full resolution access
+can be accomplished much more efficiently.  This can be accomplished by
+translating a file into a tiled file format.  Tiling is available (optionally)
+in the TIFF and MFF formats.  The File-&gt;Import operation converts data into
+a tiled TIFF file.<p>
+
+By default OpenEV uses averaging to compute reduced resolution overviews
+for display.  To display a small overview of a very large image with no
+pre-built overviews it is still necessary to read all the data, and then
+average it down for display.  This can be very slow.  If this is a problem
+the user is encouraged to change the <b>Sample Method</b> on the <b>Raster</b>
+preference tab in the <a href="preferences.html#Raster">Preferences Dialog</a>
+to <b>Decimate</b>.  This will reduce computation effort, and can also 
+substantially reduce the number of disk scanlines that need to be read
+to display an image.<p>
+
+Finally, it is generally prudent to place data files on the highest speed
+media available.  Accessing datasets over slow NFS connections, or from 
+CDROM will of course be much slower than having them on the local disk.  
+The File-&gt;Import operation will put the imported file in the current
+working directory, under the assumption that this will be local and fast.<p>
+
+In summary, importing raster files will often substantially improve
+access time.<p>
+</body>
+</html>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/html/pref_cache.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/pref_cache.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/pref_help.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/pref_help.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/pref_raster.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/pref_raster.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/pref_tracking.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/pref_tracking.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/preferences.html
===================================================================
--- packages/openev/branches/upstream/current/html/preferences.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/preferences.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,145 @@
+<html>
+<head>
+<title>Preferences Dialog</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Preferences Dialog</h1>
+
+
+<h2>Tracking Tool</h2>
+
+<img src="pref_tracking.gif"><p>
+
+The tracking tool preferences are used to control the types of information
+displayed by the tracking tool.  This is the display area at the bottom
+of the <a href="mainwindow.html">Main OpenEV Window</a> that shows 
+information about the location pointed to by the cursor in the window. <p>
+
+<b>Coordinate</b>: Possible values are:<p>
+<ol>
+<li> Off: Do not show the cursor position in the tracking area.<p>
+<li> Raster Pixel/Line: Show the pixel/line (column/row) location of the
+cursor on the top most raster layer under the cursor.<p>
+<li> Georeferenced: Show the cursor position in the current coordinate
+system of the view (which could be pixel/line, UTM, lat/long, etc). <p>
+<li> Geodetic (lat/long): Show the cursor position in geodetic (lat/long)
+coordinates if possible.  If not possible this will operate the same as
+Georeferenced.<p>
+</ol>
+<p>
+
+<b>Lat/Long Format</b>: Determins the format to display Lat/Long values for the
+tracking tool.  Possible formats are:<p>
+<ul>
+<li> ddd:mm:ss.ss  Degree Minute Second notation<p>
+<li> ddd.ddddddd  Degrees to seven decimal places<p>
+</ul>
+<p>
+
+<b>Pixel Value</b>: Determines whether the tracker should report the top
+most raster layers pixel value under the cursor.  Note that fetching the
+pixel value can be expensive for some organizations of raster file, and thus
+can slow down interactive performance. <p>
+
+<h2><a name="Raster">Raster</a></h2>
+
+<img src="pref_raster.gif"><p>
+
+<b>Display Georeferencing:</b>
+Determines whether views loaded with a raster image should operate in
+georeferenced coordinates for that raster, or in raw pixel/line.  A few points
+of note:<p>
+
+<ul>
+<li> The tracking tool can report georeferenced, or geodetic coordinates even 
+if the view is drawn in pixel/line coordinates.<p>
+
+<li> The coordinate system of any vector layers must match the coordinate
+system of the view in order for the vectors to correctly overlay displayed
+rasters.  Thus, only use No if vectors to be overlayed are in pixel/line 
+coordinates, or yes, if they are in the georeferencing system of the raster.<p>
+
+<li> If the raster has GCPs, but no affine transformation, the raster will
+be given a first, or second order warp (depending on the number of GCPs) in
+order to display the raster in the projection of the GCPs.  This can be
+fairly inappropriate for raw scenes with lat/long GCPs (such as most CEOS
+scenes). <p>
+</ul>
+
+<b>Overview Sampling</b>: 
+Determines the sampling method to use when downsampling rasters for displays
+of overview images.  Note that the sample method is a property of a raster,
+and is established at the point the raster is first displayed in a given
+OpenEV session based on the preferences.  Changing this preference will only
+affect subsequently opened rasters.<p>
+
+<ul>
+<li> <b>Average</b>: Average all the pixels contributing to an overview
+pixel.<p>
+<li> <b>Decimate</b>: Just take one of the source pixels when computing
+an overview layer (the top left). <p>
+</ul>
+
+Decimation is the default, as it is less computationally expensive and 
+gives more appropriate results when viewing complex phase images.
+Averaging can give much more viewable results for noisy data such as radar 
+images.  However, it makes loading overviews of large images without 
+pre-computed overviews much more expensive because data must always be
+loaded at full resolution and averaged down to display resolution on the
+fly, whereas decimated displays often only need to load a small portion of
+the total available scanlines in order to compute the decimated display.<p>
+
+<b>Subpixel Interpolation</b>:
+Determines how a pixel is displayed close-up.<p>
+
+<ul>
+<li> <b>Bilinear</b>: The display intensity within a pixel varies from
+its center to its edges, based on the values of surrounding pixels.<p>
+<li> <b>Off (Nearest)</b>: The display intensity within a pixel is 
+constant.<p>
+</ul>
+
+<b>Autoscaling Method</b>:
+Determines the method used to scale an image.
+
+<ul>
+<li> <b>Percent Tail Trim</b>: The intensity mapping is based on the
+minimum and maximum sorted data values with a user-defined percentage 
+of the highest and lowest values not included.<p>
+<li> <b>Standard Deviations</b>: The intensity mapping is based on the
+mean data value plus/minus a user-defined number of standard deviations.<p>
+</ul>
+
+<h2>Caching</h2>
+
+Caching issues are covered in more detail in the 
+<a href="performance.html">Performance Tuning</a> page.<P>
+
+<img src="pref_cache.gif"><p>
+
+
+<b>File Cache</b>: Number of bytes of memory available for caching raw raster
+data from disk.<p>
+
+<b>GL Texture</b>: Number of bytes of video or system memory available
+for caching image texturse.<p>
+
+<h2>Help Browser</h2>
+
+This tab is unavailable on Windows where the system default web/html browser
+is automatically used. <p>
+
+<img src="pref_help.gif"><p>
+
+Enter a command to be used to display a web page containing help (such as
+this one).  If the command includes the value "%s" then the name of the file
+or URL to display will be substituted for the %s.  Otherwise it will be 
+appended to the command.  By default OpenEV attempts to find one of a few
+common web browsers.<P> 
+
+To test a newly entered value, go to the main view window help menu and 
+select a topic, or hit <b>F1</b> over the preferences panel, after clicking
+outside the text box.<p>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/print.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/print.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/pyshell.html
===================================================================
--- packages/openev/branches/upstream/current/html/pyshell.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/pyshell.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,362 @@
+<html>
+<head>
+<title>Interactive Numeric Python Shell</title>
+</head>
+
+<body bgcolor="#ffffff">
+<h1><a name="contents">Interactive Numeric Python Shell</a></h1>
+
+<li><a href="#section1">Introduction</a>
+<li><a href="#section2">Environment</a>
+<li><a href="#section2a">Functions</a>
+<li><a href="#section2b">Commands</a>
+<li><a href="#section3">Examples</a>
+<li><a href="#section4">Python Shell Help</a>
+</ul>
+
+<h1><a name="section1">Introduction</a></h1>
+
+The OpenEV Python Shell provides access to image data in a Python scripting
+environment with built-in mathematical functions (supplied by the Numeric 
+Python module). The OpenEV Python Shell adds a thin extra layer of 
+interpretation to ordinary python, allowing a slightly different syntax to be 
+used for some frequently used functions for typing convenience (no brackets).  
+The core convenience functions (called <I>commands</I>) are registered
+automatically when the Python Shell launches, and have more argument
+checking infrastructure than regular python functions.  
+
+To illustrate the difference between python and command syntax,
+the following example shows two ways of saving an array from the
+Python Shell environment to a file: <p>
+
+<pre>
+Python:
+
+SaveArray(my_array,'my_filename')
+
+Command:
+
+save my_array my_filename
+
+</pre>
+
+Extension modules with other commands may be registered using the core 
+command <I>loadext</I>, but commands require more overhead to implement and 
+in most cases it is preferable to use the ordinary python syntax. <P>
+
+If you're not familiar with Python or Numeric Python (NumPy) here are some
+references:
+<ul>
+<li><a href="http://www.python.org">Python</a>
+<li><a href="http://www.pfdubois.com/numpy/">Numerical Python</a>
+</ul><P>
+
+<h1><a name="section2">Environment:</a></h1>
+At startup (from the Edit/Python Shell menu item) the Python environment
+automatically imports functions from <I>Numeric</I> and <I>gdalnumeric</I>, 
+and registers the <a href="#section2b">core commands</a>.  Numeric
+provides Numeric Python and gdalnumeric provides functions to access OpenEV
+image data.<P>
+
+The shell consists of a command input window (bottom) and an output window
+(top).  Aside from the registered commands, it behaves in the same way as 
+the Python command line interpreter.  Use the
+up and down arrow keys to scroll through previous command history.<P>
+<p>
+<img src="pyshell_default.gif">
+<p>
+
+<h2><a name="section2a">OpenEV Functions:</a></h2>
+In addition to the functions imported from Numeric python, OpenEV's Python 
+shell includes the following functions:
+
+<ul>
+<li><b>LoadFile(<I>filename</I>[,xoff][,yoff][,xsize][,ysize])</b> - returns 
+a numpy array containing the data from <i>filename</i>.  If xoff and yoff
+are specified, <I>filename</I> will be read starting at pixel (column) offset
+<I>xoff</I> and line (row) offset <I>yoff</I>.  
+If <I>xsize</I> and <I>ysize</I> are
+specified, only <I>xsize</I> pixels and <I>ysize</I> lines will be read; 
+otherwise, the remainder of the file will be read in.  The returned
+array will have dimensions <I>ysize</I> x <I>xsize</I> if the raster is
+greyscale; N x <I>ysize</I> x <I>xsize</I> if the raster has N channels.<P>
+
+<li><b>display(<I>array</I>)</b> - appends the array to the current OpenEV view and
+displays it.<P>
+
+<li><b>roi()</b> - returns the Region Of Interest (ROI) drawn out by the ROI tool 
+in the format (x, y, width, height).<P>
+
+<li><b>get_roi(<i>array</i>)</b> - given an <i>array</I>, and an ROI marked by the
+ROI tool, extract that region from <I>array</I> and return it.<P>
+
+<li><b>SaveArray(<i>array</i>,<i>filename</i>[,<i>format</i>])</b>- save <i>array</i>
+to file <i>filename</i>, using <i>format</i>.  <i>format</i> must be a GDAL write-supported
+format, such as 'GTiff' (Geotiff).  If <i>format</i> is not specified, it will default to
+'GTiff'.<P>
+
+<li><b>CopyDatasetInfo(<i>src</i>,<i>dst</i>)</b>- copy metadata and georeferencing
+information from GDAL dataset <i>src</i> to GDAL dataset <i>dst</i>.
+</ul>
+
+<h2><a name="section2b">Core commands:</a></h2>
+
+Commands make use of an infrastructure that simplifies usage and help access for commonly used procedures that interact with the main OpenEV application or describe the local environment.  These commands are intercepted and parsed before being passed to the shell's python interpreter, and are entered with the command and arguments separated by spaces rather than by round brackets and commas.  Commands cannot be loaded or used in a regular python shell; they
+only have meaning within the context of OpenEV.<P>  
+
+Core command list:<P>
+<ol>
+<li><i> newview</i>- create a new OpenEV view.                
+<li><i> view3d</i>- display a dem and drape in 3D mode in the current OpenEV view.
+<li><i> get</i>- grab data from the currently active OpenEV view/layer..
+<li><i> show</i>- display a Python Shell array or GvShapes variable in an OpenEV view.
+<li><i> clearview</i>- clear the current OpenEV view.
+<li><i> save</i>- save a Python Shell array or GvShapes variable to a file.
+<li><i> loadext</i>- register commands from an extension module (command equivalent of python's import keyword).
+<li><i> macro</i>- run a sequence of commands/Python statements from a text file.
+<li><i> journal</i>- save text entered at the Python Shell command line to a text file.
+<li><i> locals</i>- list this session's local variables and their type codes.
+<li><i> help</i>- display help for a function or command.
+<li><i> functions</i>- list loaded python functions, or scan a module for functions.
+<li><i> commands</i>- list registered commands.
+</ol>
+
+<h1><a name="section3">Examples</a></h1>
+Here is a small tutorial that illustrates simple python usage and some
+of OpenEV's python functions; for a more comprehensive general tutorial,
+see the <a href="http://www.python.org/doc/current/tut/tut.html">www.python.org tutorial</a>.
+
+<h2>Example 1:</h2>
+<P><dl><dd>
+
+Load a file into a Numeric python array:
+<pre class="verbatim">
+    array1 = LoadFile(`filename.foo`)
+</pre>
+
+Display it in the current view:
+<pre class="verbatim">
+    display(array1)
+</pre>
+
+
+To get the Region Of Interest (ROI) marked on the image:<P>
+<ol>
+<li>launch the Edit toolbar using the menu entry <i>Edit/Edit toolbar</i>.<P>
+<li>select "Draw ROI" to activate the ROI tool.<P>
+<li>use the ROI tool to mark a region (left click to start drawing, drag, release to finish)<P>
+<li><tt>roi()</tt> returns the ROI in (x, y, width, height)<P>
+</ol>
+
+Use the ROI tool to get a subarray:<P>
+<ol>
+<li>mark out an ROI with the tool on the image<P>
+<li><tt>array2 = get_roi(array1)</tt><P>
+</ol>
+
+To save array2 to a new file:<P>
+<pre class="verbatim">
+SaveArray(array2,'filename2.tif')
+</pre>
+
+</dl>
+
+Numeric python functions such as <i>ones</i> and <i>array</i> can also be 
+used to create and manipulate arrays:
+
+<P><dl><dd>
+
+To create a floating point array of ones with 5 rows and 6 columns:<P>
+<pre class="verbatim">
+    array3=ones([5,6],Float)
+</pre>
+
+Reset rows 1 to 4, columns 1 to 5 of the array with some values:<P>
+<pre class="verbatim">
+    array3[1:5,1:6]=array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20]],Float)
+</pre>
+
+Square it:<P>
+<pre class="verbatim">
+    array3=power(array3,2)
+</pre>
+
+Display it in the current view:<P>
+<pre class="verbatim">
+    display(array3)
+</pre>
+
+Some important points to note are:<P>
+<ol>
+<li> Numeric python arrays are indexed from zero.<P>
+<li> In slicing a Numeric python array using index range N:M, elements N, N+1, ...M-1 are included.<P>
+</ol>
+
+</dl>
+
+<h2>Example 2</h2>
+
+We are starting with Python shell (Edit-&gt;Python Shell... menu item). You
+should load some raster file into OpenEV to work with.<p>
+        
+<ul>
+
+        <li> Get active layer<p>
+
+        <pre>
+layer = gview.app.sel_manager.get_active_layer()
+        </pre>
+
+       ...and load data array into memory:<p>
+
+        <pre>
+ds = layer.get_parent().get_dataset()
+data = DatasetReadAsArray(ds)
+        </pre>
+
+        Note: using the core command <I>get</I> with the argument <I>data</I>
+        (ie. enter "get data" at the command line) will perform these three steps 
+        for you.<p>
+
+        If you don't want open and display your input data with OpenEV, but want
+        load the file(s) in memory, make some calculations and display result,
+        you can use other approach instead of previous steps:<p>
+
+        <pre>
+data = LoadFile('filename.l1b')
+        </pre>
+
+        (In this sample we will work with NOAA AVHRR data in L1B format).<p>
+
+        <li> Now we are ready to operate with loaded data. For example,
+        calculate normalised difference vegetation index (NDVI) with NOAA
+        AVHRR data. In this case NDVI is calculated as
+        (Band2 - Band1)/(Band2 + Band1). We assume we have already calibrated
+        data in our file.<p>
+
+        <pre>
+ndvi = (data[1] - data[0]) / (data[1] + data[0])
+        </pre>
+
+        <li> Display result and, optionally, save it into file:<p>
+
+        <pre>
+display(ndvi)
+SaveArray(ndvi,'ndvi.tif','GTiff')
+        </pre>
+
+        Note, that in all our computations arrays are fully loaded into
+        memory. Be sure you have enough resources for this. For large arrays
+        loading and calculations may take some time.<p>
+
+        <li> Another operation: let's make a RGB composite image. In general
+        case this operation should be interactively controlled by the user,
+        because band colors should be carefully adjusted to produce the best
+        looking results. But we will simplify our task. NOAA AVHRR data has
+        10-bit depth (GDAL stores them in 16-bit integer), so we just scale
+        input data to 8-bit and fill three-component array.<p>
+
+        <pre>
+rgb = zeros((3, ds.RasterYSize, ds.RasterXSize), UnsignedInt8)
+for i in range(3):
+    rgb[i] = (data[i] * 256 / 1024).astype(UnsignedInt8)
+display(rgb)
+        </pre>
+
+        This code produces a RGB composite from the first three channels of
+        the input image, but you can unroll the loop and use channels you
+        want. Following code uses channel 5 as red, 2 as green and 3 as blue:<p>
+
+        <pre>
+rgb[0] = (data[4] * 256 / 1024).astype(UnsignedInt8)
+rgb[1] = (data[1] * 256 / 1024).astype(UnsignedInt8)
+rgb[2] = (data[3] * 256 / 1024).astype(UnsignedInt8)
+        </pre>
+
+        <li> Now we will use other data as a sample. Let load ASTER Level 1B
+        dataset (the second band) and try to calibrate it. This dataset
+        already reprocessed and should be calibrated with the simple linear
+        function. Function coefficients contained in the metadata records with
+        the appropriate number at the end (2 in case of the second band).<p>
+
+        <pre>
+layer = gview.app.sel_manager.get_active_layer()
+ds = layer.get_parent().get_dataset()
+data = DatasetReadAsArray(ds)
+incl = float(ds.GetMetadata()["INCL2"])
+offset = float(ds.GetMetadata()["OFFSET2"])
+data_cal = data * incl + offset
+display(data_cal)
+        </pre>
+
+</ul>
+
+
+<h1><a name="section4">Python Shell Help</a></h1>
+
+The python shell has three commands used to provide help on functions
+and commands: 
+<ol>
+<li><i> help</i>- display help for a function or command.
+<li><i> commands</i>- list registered commands.
+<li><i> functions</i>- list loaded python functions, or scan a module for functions.
+</ol>
+
+These commands make use of python __doc__ strings and any help text
+files that are registered.  Help text files should only be used when
+there is some reason for not updating the python __doc__ string itself
+(eg. if documentation were going to be available in multiple languages,
+or if the package is from a third party and you don't want to use
+the doc string), or if the python documentation is fine but 
+it is desirable to display help for some
+functions/commands that are not loaded so that the user is made
+aware of them.  In the latter case, the help text file should be 
+regenerated from the python documentation each time one of the functions'
+documentation changes (at least for releases).
+  
+Help text files may have entries of the following three formats:
+
+<pre>
+COMMAND_NAME=my_command
+Module: my_module
+Group: my_group
+Html: my_html.html
+
+documentation...
+
+FUNCTION_NAME=my_func
+Module: my_module
+Html: my_html.html
+
+documentation...
+
+BUILTIN_NAME=my_builtin
+Module: my_module
+Html: my_html.html
+
+documentation...
+</pre>
+
+where my_command, my_func, and my_builtin are a user-defined command,
+function, and builtin (bound C) function respectively.  The Html entry
+is used to indicate an html file with more information, and is optional
+(currently, the python shell will not do anything with html information
+other than store the name of the link- this may be used in future though).
+The Module name is required to deal with the case of two modules defining 
+different commands/functions with the same name.  The COMMAND_NAME, 
+FUNCTION_NAME, BUILTIN_NAME, Module, and Html parts of the text file are 
+case-sensitive (ie. an error will occur if the case is not as above). 
+See gvcorecmds.py's RegisterHelp
+function for how to register a help file with the interpreter (this
+function determines the location of gvcorecmds_help.txt assuming that
+it is in the same directory as gvcorecmds.py, and loads the text from
+it if it is present).  The suggested convention
+for text help files is that if they are needed, they be placed
+in the same directory as the commands/functions they relate to, and have the
+same name as the command module, minus the '.py', plus '_help.txt'.
+<p>
+
+<a href="#contents">Top</a><p>
+<a href="openevmain.html>OpenEV Help</a><p>
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/pyshell_default.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/pyshell_default.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/refresh.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/refresh.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/rotatetool.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/rotatetool.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/seeall.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/seeall.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/tool_hist.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/tool_hist.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/tool_roigeneral.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/tool_roigeneral.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/tools_openev.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/tools_openev.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/veclayerselect.html
===================================================================
--- packages/openev/branches/upstream/current/html/veclayerselect.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/veclayerselect.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,62 @@
+<html>
+<head>
+<title>Vector Layer Selection</title>
+</head>
+<body bgcolor="#ffffff">
+<h1>Vector Layer Selection</h1>
+
+The Vector Layer Selection dialog is displayed when the user selects 
+a supported vector file other than a shapefile.  It allows the user to 
+select the layers they want to select out of the file, as well as potentially
+loading only a subarea or subset.
+<p>
+
+<img src="gvogrdlg.gif"><p>
+
+The dialog sections are:<p>
+
+<ul>
+<li> <b>Layers List</b>: Clicking on layers will toggle whether they should
+be loaded or not.  When a layer is to be loaded, the check mark will be
+drawn in black.  By default no layers are loaded.<p>
+
+<li> <b>Clip To View</b>: This toggle button can be enabled to request that
+only vectors in the current view area should be loaded.  Note that any
+vector that appears to intersect the current 2D bounding rectangle will be
+loaded and complete individual vector features are still loaded ... no
+actual clipping occurs within features.  However, any features outside the
+area of the view will not be loaded.<P>
+
+<li> <b>Execute SQL</b>: An SQL statement-like statement can be entered
+in the text area to the left of this button.  Hit execute and the result
+of the query will be returned as a layer.  Note that when accessing a real
+database (such as PostgreSQL or Oracle) this request is passed through to
+the underlying database.  Simple file formats only support a simplified
+SQL WHERE statement.  See the 
+<a href="http://gdal.velocet.ca/projects/opengis/">OGR</a> library web page 
+for further details.<p>
+
+<li> <b>Accept</b>: Loads the currently selected layers (if any) according
+to the current Clip To View setting.<p>
+
+<li> <b>Load All</b>: Loads all layers according to the current Clip To
+View setting.<p>
+
+<li> <b>Cancel</b>: Closes the dataset without any loading.<p>
+
+<li> <b>Help</b>: Displays this page.<P>
+
+</ul>
+
+<hr>
+
+The Vector Layer Selection dialog is not current user accessable when
+reading from Shapefiles, but from Python it can be used even for shapefiles
+by calling GvViewWindow.file_open_ogr_by_name() instead of the higher level
+GvViewArea.file_open_by_name().<p>
+
+</body>
+</html>
+
+
+

Added: packages/openev/branches/upstream/current/html/viewarea_keys.html
===================================================================
--- packages/openev/branches/upstream/current/html/viewarea_keys.html	                        (rev 0)
+++ packages/openev/branches/upstream/current/html/viewarea_keys.html	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,134 @@
+<html>
+<head>
+<title>View Area Button/Key Sequences</title>
+</head>
+<body BGCOLOR="#FFFFFF">
+<h1>View Area Button/Key Sequences</h1>
+
+<h2>2D Mode</h2>
+<ul>
+<li> <b>Control-LeftButton-DoubleClick</b>: Zoom in by one increment (normally
+a factor of two), and recenter on target location.<p>
+
+<li> <b>Control-LeftButton-Drag</b>: Drag out a zoom rectangle, and zoom on release.<p>
+
+<li> <b>Control-LeftButton-PressAndHold</b>: Continuous zoom in.<p>
+
+<li> <b>Control-RightButton-DoubleClick</b>: Zoom out by one increment 
+(normally a factor of two), and recenter on target location.<p>
+
+<li> <b>Control-RightButton-PressAndHold</b>: Continuous zoom out.<p>
+
+<li> <b>Control-MiddleButton-Drag</b>: Drag the image to pan.<p>
+
+<li> <b>Control-F1</b>: Flip image in the horizontal direction.<p>
+
+<li> <b>Control-F2</b>: Flip image in the vertical direction.<p>
+
+<li> <b>Control-Z</b>: Undo one edit operation.<p>
+
+<li> <b>Shift-Leftbutton-Drag</b>: Rotate about center of view.<p>
+
+<li> <b>Escape</b>: Cancels drag and rubber banding operations.<p>
+
+<li> <b>Page Up</b>: Zoom in one increment (normally a factor of two).<p>
+
+<li> <b>Page Down</b>: Zoom out one increment (normally a factor of two).<p>
+
+<li> <b>Home</b>: Zoom/pan such that all layers are visible in the view.<p>
+
+<li> <b>Delete</b>: Delete currently selected vector shapes (if any).<p>
+
+<li> <b>LeftArrow</b>:  move view ten pixels left<p>
+
+<li> <b>RightArrow</b>:  move view ten pixels right<p>
+
+<li> <b>UpArrow</b>:  move view ten pixels up<p>
+
+<li> <b>DownArrow</b>:  move view ten pixels down<p>
+
+<li> <b>Control-LeftArrow</b>:  move view 3/4 of the view width left<p>
+
+<li> <b>Control-RightArrow</b>:  move view 3/4 of the view width right<p>
+
+<li> <b>Control-UpArrow</b>:  move view 3/4 of the view height up<p>
+
+<li> <b>Control-DownArrow</b>:  move view 3/4 of the view height down<p>
+
+<li> <b>Shift-LeftArrow</b>:  move view 3/8 of the view width left<p>
+
+<li> <b>Shift-RightArrow</b>:  move view 3/8 of the view width right<p>
+
+<li> <b>Shift-UpArrow</b>:  move view 3/8 of the view height up<p>
+
+<li> <b>Shift-DownArrow</b>:  move view 3/8 of the view height down<p>
+
+</ul>
+
+<h2>3D Mode</h2>
+<ul>
+      There are two states, <b>Panning</b> which requires no additional key presses,
+        and <b>Translation</b> which requires the <b>Control</b> key to be held down.  Both states
+        share forwards and backwards motion controls.  This allows you to move forward/backwards
+        as well as change your viewing direction or position simultaneously.<p>
+
+        <li>Forward/Backwards (Zooming) Controls:  Common to Both Panning and Translating States.<p>
+        <ul>
+          <li> <b>LeftButton-Hold</b>:  Move forward along current line of sight<p>
+              
+          <li> <b>MiddleButton-Hold</b>:  Nothing<p>
+              
+          <li> <b>RightButton-Hold</b>:  Move backwards along current line of sight<p>
+          
+          <li> <b>LeftButton-DoubleClick</b>:  Move forward along current line of sight by one increment<p>
+          
+          <li> <b>RightButton-DoubleClick</b>:  Move backwards along current line of sight by one increment<p>
+        </ul>
+
+      <li> Pan Mode <b>AnyMouseButton-Hold</b>:  Change viewing direction<p>
+        <ul>
+          <li><b>Mouse Forwards</b>:  look down<p>
+          <li><b>Mouse Backwards</b>:  look up<p>
+          <li><b>Mouse Left</b>:  look right<p>
+          <li><b>Mouse Right</b>:  look left<p>
+        </ul>
+
+      <li> Translate Mode <b>Control-AnyMouseButton-Hold</b>:  Change position<p>
+        <ul>
+          <li><b>Mouse Forwards</b>:  move down<p>
+          <li><b>Mouse Backwards</b>:  move up<p>
+          <li><b>Mouse Left</b>:  move to right<p>
+          <li><b>Mouse Right</b>:  move to left<p>
+        </ul>
+
+<li> <b>LeftArrow</b>:  move left<p>
+
+<li> <b>RightArrow</b>:  move right<p>
+
+<li> <b>UpArrow</b>:  move up<p>
+
+<li> <b>DownArrow</b>:  move down<p>
+
+<li> <b>PageUp</b>: Move forward along current line of sight by 0.1 increment<p>
+
+<li> <b>Shift-PageUp</b>: Move forward along current line of sight by one increment<p>
+
+<li> <b>PageDown</b>: Move backwards along current line of sight by 0.1 increment<p>
+
+<li> <b>Shift-PageDown</b>: Move backwards along current line of sight by one increment<p>
+
+<li> <b>-</b>  decrease height field scaling factor (by 0.01)<p>
+
+<li> <b>_</b>  decrease height field scaling factor (by 0.1)<p>
+
+<li> <b>=</b>  increase height field scaling factor (by 0.01)<p>
+
+<li> <b>+</b>  increase height field scaling factor (by 0.1)<p>
+
+<li> <b>F2</b>:  switch between 2D and 3D mode<p>
+
+<li> <b>Home</b>:  reset viewing position<p>
+</ul>
+
+</body>
+</html>

Added: packages/openev/branches/upstream/current/html/windowed.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/windowed.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/worldg.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/worldg.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/worldrgb.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/worldrgb.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/zoom_control.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/zoom_control.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/zoomin.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/zoomin.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/html/zoomout.gif
===================================================================
(Binary files differ)


Property changes on: packages/openev/branches/upstream/current/html/zoomout.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/openev/branches/upstream/current/install-sh
===================================================================
--- packages/openev/branches/upstream/current/install-sh	                        (rev 0)
+++ packages/openev/branches/upstream/current/install-sh	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,256 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ -x "$src".exe ] ; then
+  src=${src}.exe
+  echo "Cygwin hack - actually installing "$src
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+		chmodcmd=""
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0


Property changes on: packages/openev/branches/upstream/current/install-sh
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/invdistance.c
===================================================================
--- packages/openev/branches/upstream/current/invdistance.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/invdistance.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,164 @@
+/******************************************************************************
+ * $Id: invdistance.c,v 1.3 2001/04/22 17:33:24 pgs Exp $
+ *
+ * Project:  CIETMAP / OpenEV
+ * Purpose:  Weighted Inverse Distance Interpolator (point to raster)
+ *           This implementation is independent of OpenEV, but does depend
+ *           directly on GDAL.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: invdistance.c,v $
+ * Revision 1.3  2001/04/22 17:33:24  pgs
+ * changed WIDInterpolate to take variable exponent for d
+ *
+ * Revision 1.2  2001/02/05 14:45:09  warmerda
+ * make padfWeight optional
+ *
+ * Revision 1.1  2000/09/12 19:17:46  warmerda
+ * New
+ *
+ */
+
+#include "invdistance.h"
+#include "cpl_conv.h"
+
+/************************************************************************/
+/*                           WIDInterpolate()                           */
+/************************************************************************/
+
+/**
+ * Weighted Inverse Distance Interpolation
+ *
+ * This algorithm interpolates the pixel values of a raster (in a GDAL
+ * band) using a simple inverse distance interpolator with an additional
+ * per point weighting factor.  The algorithm support GDALProcessFunc style
+ * progress reporting, and user termination support.
+ *
+ * @param nPoints the number of input sample points (entries in the padfX,
+ * padfY, padfValue and padfWeight arrays).
+ *
+ * @param padfX the X locations of sample points in raster pixel/line
+ * coordinates.
+ *
+ * @param padfY the Y locations of sample points in raster pixel/line
+ * coordinates.
+ *
+ * @param padfValue the data value for each sample point.
+ *
+ * @param padfWeight a relative weighting for each sample point.  Use NULL
+ * to default to equal weighting (implies all are 1.0).
+ *
+ * @param hBand the output GDAL band to which results should be written.
+ *
+ * @param fExponent the exponent to apply to the distance calculate in the
+ * weighting formula
+ *
+ * @param pfnProgress progress function (see GDALDummyProgress() for more
+ * information), use NULL if no progress function desired.
+ *
+ * @param pCBData callback data passed to pfnProgress.
+ *
+ * @return A CPL error number on failure, or CPLE_None on success.  Note that
+ * a user interrupt will result in CPLE_UserInterrupt.
+ */
+
+int WIDInterpolate( int nPoints, double *padfX, double *padfY,
+                    double *padfValue, double *padfWeight,
+                    GDALRasterBandH hBand, double fExponent,
+                    GDALProgressFunc pfnProgress, void * pCBData )
+
+{
+    int		nXSize, nYSize, nError = CPLE_None, iY, iPoint;
+    float       *pafScanline;
+    double      *padfDeltaYSquared;
+
+    if( pfnProgress == NULL )
+        pfnProgress = GDALDummyProgress;
+
+    nXSize = GDALGetRasterBandXSize( hBand );
+    nYSize = GDALGetRasterBandYSize( hBand );
+
+    padfDeltaYSquared = (double *) CPLMalloc(sizeof(double) * nPoints);
+    pafScanline = (float *) CPLMalloc(sizeof(float) * nXSize);
+
+    for( iY = 0; iY < nYSize; iY++ )
+    {
+        int	iX;
+
+        if( !pfnProgress( iY / (double) nYSize, NULL, pCBData ) )
+        {
+            nError = CPLE_UserInterrupt;
+            break;
+        }
+
+        /* Precompute DeltaY Squared for point.  It will remain constant
+           over the scanline. */
+        for( iPoint = 0; iPoint < nPoints; iPoint++ )
+        {
+            padfDeltaYSquared[iPoint] =
+                (padfY[iPoint]-(double)iY) * (padfY[iPoint]-(double)iY);
+        }
+
+        for( iX = 0; iX < nXSize; iX++ )
+        {
+            double	dfNumerator=0.0, dfDenominator = 0.0;
+            double      dfX = iX;
+
+            for( iPoint = 0; iPoint < nPoints; iPoint++ )
+            {
+                double dfDistSquared, dfDeltaX;
+                double dfWeight;
+
+                dfDeltaX = (padfX[iPoint] - dfX);
+
+                dfDistSquared = dfDeltaX*dfDeltaX + padfDeltaYSquared[iPoint];
+
+                if (fExponent != 2.0)
+                {
+					//if the exponent is not 2, use the exponent / 2
+					//as the distance is already squared
+					dfDistSquared = pow(dfDistSquared, fExponent / 2.0);
+				}
+
+                if( padfWeight == NULL )
+                    dfWeight = 1.0 / dfDistSquared;
+                else
+                    dfWeight = padfWeight[iPoint] / dfDistSquared;
+
+                dfDenominator += dfWeight;
+                dfNumerator += dfWeight * padfValue[iPoint];
+            }
+
+            pafScanline[iX] = dfNumerator / dfDenominator;
+        }
+
+        GDALRasterIO( hBand, GF_Write, 0, iY, nXSize, 1,
+                      pafScanline, nXSize, 1, GDT_Float32, 0, 0 );
+    }
+
+    pfnProgress( 1.0, NULL, pCBData );
+
+    CPLFree( pafScanline );
+    CPLFree( padfDeltaYSquared );
+
+    return nError;
+}

Added: packages/openev/branches/upstream/current/invdistance.h
===================================================================
--- packages/openev/branches/upstream/current/invdistance.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/invdistance.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,52 @@
+/******************************************************************************
+ * $Id: invdistance.h,v 1.2 2001/04/22 17:33:24 pgs Exp $
+ *
+ * Project:  CIETMAP / OpenEV
+ * Purpose:  Weighted Inverse Distance Interpolator (point to raster)
+ *           This implementation is independent of OpenEV, but does depend
+ *           directly on GDAL.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: invdistance.h,v $
+ * Revision 1.2  2001/04/22 17:33:24  pgs
+ * changed WIDInterpolate to take variable exponent for d
+ *
+ * Revision 1.1  2000/09/12 19:17:47  warmerda
+ * New
+ *
+ */
+
+#ifndef __INVDISTANCE_H__
+#define __INVDISTANCE_H__
+
+#include "gdal.h"
+
+CPL_C_START
+
+int WIDInterpolate( int nPoints, double *padfX, double *padfY,
+                    double *padfValue, double *padfWeight,
+                    GDALRasterBandH hBand, double fExponent,
+                    GDALProgressFunc pfnProgress, void * pCBData );
+
+CPL_C_END
+
+#endif /*__INVDISTANCE_H__ */

Added: packages/openev/branches/upstream/current/ipgcplayer.c
===================================================================
--- packages/openev/branches/upstream/current/ipgcplayer.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/ipgcplayer.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,414 @@
+/******************************************************************************
+ * $Id: ipgcplayer.c,v 1.15 2004/02/23 20:16:36 gmwalter Exp $
+ *
+ * Project:  InSAR Peppers
+ * Purpose:  Implementation of GCP Layer.
+ *
+ *           This layer is intended specifically for use in InSAR Peppers, and
+ *           should only be changed on behalf of Atlantis, though it can 
+ *           serve as an example of custom layer drawing for others.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: ipgcplayer.c,v $
+ * Revision 1.15  2004/02/23 20:16:36  gmwalter
+ * Checked in changes for insar.
+ *
+ * Revision 1.14  2003/12/10 15:29:42  gmwalter
+ * Undid changes as requested.
+ *
+ * Revision 1.13  2003/12/06 22:23:22  gmwalter
+ * Checked in changes made by insar group.
+ *
+ * Revision 1.12  2003/02/27 03:59:21  warmerda
+ * added view to gv_shapes_layer_get_draw_info
+ *
+ * Revision 1.11  2002/11/05 04:05:32  warmerda
+ * geocoord update
+ *
+ * Revision 1.10  2001/08/08 17:44:12  warmerda
+ * use gv_shape_type() macro
+ *
+ * Revision 1.9  2001/06/15 15:30:29  warmerda
+ * try setting glPointSize to 1 when drawing GCPs
+ *
+ * Revision 1.8  2000/09/18 21:12:43  srawlin
+ * reduced icon size from 10 to 5.5
+ *
+ * Revision 1.7  2000/08/30 14:06:19  warmerda
+ * make it easier to change symbol size
+ *
+ * Revision 1.6  2000/08/16 14:52:43  warmerda
+ * added excluded support
+ *
+ * Revision 1.5  2000/08/15 20:09:31  warmerda
+ * default color properly
+ *
+ * Revision 1.4  2000/08/14 19:45:21  warmerda
+ * use geo_x, geo_y for G point
+ *
+ * Revision 1.3  2000/08/14 15:05:52  warmerda
+ * updated substantially
+ *
+ * Revision 1.2  2000/08/04 14:14:12  warmerda
+ * GvShapes shape ids now persistent
+ *
+ * Revision 1.1  2000/07/14 18:24:57  warmerda
+ * New
+ *
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include "ipgcplayer.h"
+#include "gvutils.h"
+#include <GL/gl.h>
+#include <string.h>
+
+/* Approximate size on screen of symbols in pixels */
+#define SYMBOL_SIZE 5.5
+
+static void ip_gcp_layer_class_init(IpGcpLayerClass *klass);
+static void ip_gcp_layer_init(IpGcpLayer *layer);
+static void ip_gcp_layer_setup(GvLayer *layer, GvViewArea *view);
+static void ip_gcp_layer_draw(GvLayer *layer, GvViewArea *view);
+static void ip_gcp_layer_draw_selected(GvShapeLayer *layer, 
+                                          GvViewArea *view);
+
+GtkType
+ip_gcp_layer_get_type(void)
+{
+    static GtkType gcp_layer_type = 0;
+
+    if (!gcp_layer_type)
+    {
+	static const GtkTypeInfo gcp_layer_info =
+	{
+	    "IpGcpLayer",
+	    sizeof(IpGcpLayer),
+	    sizeof(IpGcpLayerClass),
+	    (GtkClassInitFunc) ip_gcp_layer_class_init,
+	    (GtkObjectInitFunc) ip_gcp_layer_init,
+	    /* reserved_1 */ NULL,
+	    /* reserved_2 */ NULL,
+	    (GtkClassInitFunc) NULL,
+	};
+
+	gcp_layer_type = gtk_type_unique(gv_shapes_layer_get_type(),
+                                         &gcp_layer_info);
+    }
+    return gcp_layer_type;
+}
+
+static void
+ip_gcp_layer_class_init(IpGcpLayerClass *klass)
+{
+    GvLayerClass *layer_class;
+    GvShapeLayerClass *shape_layer_class;
+
+    layer_class = (GvLayerClass*) klass;
+    shape_layer_class = (GvShapeLayerClass*) klass;
+
+    layer_class->setup = ip_gcp_layer_setup;
+    layer_class->draw = ip_gcp_layer_draw;
+    shape_layer_class->draw_selected = ip_gcp_layer_draw_selected;
+}
+
+static void
+ip_gcp_layer_init(IpGcpLayer *layer)
+{
+}
+
+GtkObject *
+ip_gcp_layer_new(void)
+{
+    GvShapes *data;
+    IpGcpLayer *layer = IP_GCP_LAYER(gtk_type_new(
+	ip_gcp_layer_get_type()));
+
+    data = GV_SHAPES(gv_shapes_new());
+    gv_data_set_name( GV_DATA(data), "GCPs" );
+    gv_shapes_layer_set_data( GV_SHAPES_LAYER(layer), data );
+    gv_data_set_name( GV_DATA(layer), "GCPs" );
+    
+    return GTK_OBJECT(layer);
+}
+
+/*******************************************************/
+
+static void
+ip_gcp_layer_setup(GvLayer *rlayer, GvViewArea *view)
+{
+}
+
+static void 
+ip_gcp_draw_circle( gfloat center_x, gfloat center_y, gfloat radius )
+
+{
+    gvfloat angle;
+
+#ifndef PI
+#define PI  3.1415927
+#endif
+
+    glBegin( GL_LINE_LOOP );
+    for( angle = 0.0; angle <= 2.00001*PI; angle += (PI/24.0) )
+    {
+        gfloat x, y;
+
+        x = sin(angle) * radius + center_x;
+        y = cos(angle) * radius + center_y;
+
+        glVertex2f( x, y );
+    }
+    glEnd();
+}
+
+static void 
+ip_gcp_layer_draw_gcp( GvViewArea *view, IpGcpLayer *layer, 
+                       GvShape *shape, int selected )
+
+{
+    GvProperties *properties = gv_shape_get_properties(shape);
+    const char *value;
+    const char * type;
+    gfloat       x, y, geo_x, geo_y, raw_x, raw_y;
+    GLgeocoord   radius_x, radius_y;
+    GvColor      color;
+    GvShapeDrawInfo drawinfo;
+
+    /* get and set color */
+    gv_shapes_layer_get_draw_info( view, GV_SHAPES_LAYER(layer), &drawinfo );
+    gv_color_copy( color, drawinfo.point_color );
+
+    gv_shapes_layer_override_color( shape, color, "_gv_color" );
+
+    glPointSize(1);
+    glColor4fv( color );
+    
+    /* Compute main drawing location */
+    x = gv_shape_get_x(shape, 0, 0 );
+    y = gv_shape_get_y(shape, 0, 0 );
+
+    radius_x = 1.0;
+    radius_y = 0.0;
+    gv_view_area_correct_for_transform( view, 
+                                        radius_x, radius_y,
+                                        &radius_x, &radius_y );
+    radius_x = SYMBOL_SIZE * sqrt(radius_x*radius_x + radius_y*radius_y);
+
+    /* fetch the georeferenced location in view coordinate system */
+    value = gv_properties_get( properties, "geo_x" );
+    if( value != NULL )
+        geo_x = atof(value);
+    else
+        geo_x = x;
+
+    value = gv_properties_get( properties, "geo_y" );
+    if( value != NULL )
+        geo_y = atof(value);
+    else
+        geo_y = y;
+
+    /* fetch the features location in view coordinate system */
+    value = gv_properties_get( properties, "raw_x" );
+    if( value != NULL )
+        raw_x = atof(value);
+    else
+        raw_x = x;
+
+    value = gv_properties_get( properties, "raw_y" );
+    if( value != NULL )
+        raw_y = atof(value);
+    else
+        raw_y = y;
+
+    /* draw based on type */
+    type = gv_properties_get( properties, "gcp_type" );
+    if( type == NULL )
+        type = "F&G";
+
+    value = gv_properties_get( properties, "excluded" );
+    if( value != NULL && atoi(value) > 0 )
+        type = "excluded";
+
+    if( strcmp(type,"excluded") == 0 )
+    {
+        /* draw excluded X */
+        glBegin(GL_LINES);
+        glVertex2f( x-radius_x*0.707, y-radius_x*0.707 );
+        glVertex2f( x+radius_x*0.707, y+radius_x*0.707 );
+        glVertex2f( x+radius_x*0.707, y-radius_x*0.707 );
+        glVertex2f( x-radius_x*0.707, y+radius_x*0.707 );
+        glEnd();
+    }
+
+
+    if( strcmp(type,"G&E") == 0 )
+    {
+        /* draw triangle */
+        glBegin(GL_LINE_LOOP);
+        glVertex2f( geo_x, geo_y+radius_x );
+        glVertex2f( geo_x-radius_x*0.866, geo_y-radius_x*0.5);
+        glVertex2f( geo_x+radius_x*0.866, geo_y-radius_x*0.5);
+        glVertex2f( geo_x, geo_y+radius_x );
+        glEnd();
+
+        ip_gcp_draw_circle( geo_x, geo_y, radius_x );
+    }
+
+    if( strcmp(type,"F&E") == 0 )
+    {
+        /* draw triangle */
+        glBegin(GL_LINE_LOOP);
+        glVertex2f( raw_x, raw_y+radius_x );
+        glVertex2f( raw_x-radius_x*0.866, raw_y-radius_x*0.5);
+        glVertex2f( raw_x+radius_x*0.866, raw_y-radius_x*0.5);
+        glVertex2f( raw_x, raw_y+radius_x );
+        glEnd();
+
+        /* draw cross hair */
+        glBegin(GL_LINES);
+        glVertex2f( raw_x, raw_y-radius_x );
+        glVertex2f( raw_x, raw_y+radius_x );
+        glVertex2f( raw_x+radius_x, raw_y );
+        glVertex2f( raw_x-radius_x, raw_y );
+        glEnd();
+
+    }
+
+    if( strcmp(type,"F&G") == 0 )
+    {
+        /* draw cross hair */
+        glBegin(GL_LINES);
+        glVertex2f( raw_x, raw_y-radius_x );
+        glVertex2f( raw_x, raw_y+radius_x );
+        glVertex2f( raw_x+radius_x, raw_y );
+        glVertex2f( raw_x-radius_x, raw_y );
+        glEnd();
+
+        ip_gcp_draw_circle( geo_x, geo_y, radius_x );
+
+        /* connect with line */
+        glBegin(GL_LINES);
+        glVertex2f( raw_x, raw_y );
+        glVertex2f( geo_x, geo_y );
+        glEnd();
+    }
+
+    if( strcmp(type,"F&G&E") == 0 )
+    {
+        /* draw triangle */
+        glBegin(GL_LINE_LOOP);
+        glVertex2f( raw_x, raw_y+radius_x );
+        glVertex2f( raw_x-radius_x*0.866, raw_y-radius_x*0.5);
+        glVertex2f( raw_x+radius_x*0.866, raw_y-radius_x*0.5);
+        glVertex2f( raw_x, raw_y+radius_x );
+        glEnd();
+
+        /* draw cross hair */
+        glBegin(GL_LINES);
+        glVertex2f( raw_x, raw_y-radius_x );
+        glVertex2f( raw_x, raw_y+radius_x );
+        glVertex2f( raw_x+radius_x, raw_y );
+        glVertex2f( raw_x-radius_x, raw_y );
+        glEnd();
+
+        ip_gcp_draw_circle( geo_x, geo_y, radius_x );
+
+        /* connect with line */
+        glBegin(GL_LINES);
+        glVertex2f( raw_x, raw_y );
+        glVertex2f( geo_x, geo_y );
+        glEnd();
+    }
+
+    /* Draw selection box around base point, if selected */
+    if( selected )
+    {
+        /* draw triangle */
+        glBegin(GL_LINE_LOOP);
+        glVertex2f( x-radius_x*1.25, y-radius_x*1.25 );
+        glVertex2f( x+radius_x*1.25, y-radius_x*1.25 );
+        glVertex2f( x+radius_x*1.25, y+radius_x*1.25 );
+        glVertex2f( x-radius_x*1.25, y+radius_x*1.25 );
+        glVertex2f( x-radius_x*1.25, y-radius_x*1.25 );
+        glEnd();
+    }
+}
+
+static void
+ip_gcp_layer_draw(GvLayer *rlayer, GvViewArea *view)
+{
+    IpGcpLayer *layer = IP_GCP_LAYER(rlayer);
+    gint i, points;
+    gint *selected, presentation;
+    gint hit_selected = FALSE;
+    GvShape           *shape;
+    
+    presentation = GV_LAYER(layer)->presentation;
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+
+    points = gv_shapes_num_shapes(GV_SHAPES_LAYER(layer)->data);
+    
+    for (i=0; i < points; ++i)
+    {
+	if (selected[i] && !presentation)
+	{
+	    hit_selected = 1;
+	    continue;
+	}
+
+        shape = gv_shapes_get_shape(GV_SHAPES_LAYER(layer)->data, i);
+        if( shape != NULL && gv_shape_type(shape) == GVSHAPE_POINT )
+            ip_gcp_layer_draw_gcp( view, layer, shape, FALSE );
+    }
+
+    if (hit_selected && ! GV_SHAPE_LAYER(layer)->flags & GV_DELAY_SELECTED)
+    {
+	ip_gcp_layer_draw_selected(GV_SHAPE_LAYER(layer), view);
+    }     
+}
+
+static void
+ip_gcp_layer_draw_selected(GvShapeLayer *rlayer, GvViewArea *view)
+{
+    IpGcpLayer *layer = IP_GCP_LAYER(rlayer);
+    gint i, points;
+    gint *selected;
+    GvShape           *shape;
+    
+    selected = GV_SHAPE_LAYER_SELBUF(layer);
+
+    points = gv_shapes_num_shapes(GV_SHAPES_LAYER(layer)->data);
+    
+    for (i=0; i < points; ++i)
+    {
+	if ( !selected[i] )
+	    continue;
+
+        shape = gv_shapes_get_shape(GV_SHAPES_LAYER(layer)->data, i);
+        if( shape != NULL && gv_shape_type(shape) == GVSHAPE_POINT )
+            ip_gcp_layer_draw_gcp( view, layer, shape, TRUE );
+    }
+}
+
+

Added: packages/openev/branches/upstream/current/ipgcplayer.h
===================================================================
--- packages/openev/branches/upstream/current/ipgcplayer.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/ipgcplayer.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * $Id: ipgcplayer.h,v 1.1 2000/07/14 18:24:57 warmerda Exp $
+ *
+ * Project:  InSAR Peppers
+ * Purpose:  InSAR Peppers GCP Layer declarations.
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: ipgcplayer.h,v $
+ * Revision 1.1  2000/07/14 18:24:57  warmerda
+ * New
+ *
+ */
+
+#ifndef __IPGCPLAYER_H__
+#define __IPGCPLAYER_H__
+
+#include "gvshapeslayer.h"
+
+#define IP_TYPE_GCP_LAYER            (ip_gcp_layer_get_type ())
+#define IP_GCP_LAYER(obj)            (GTK_CHECK_CAST ((obj), IP_TYPE_GCP_LAYER, IpGcpLayer))
+#define IP_GCP_LAYER_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), IP_TYPE_GCP_LAYER, IpGcpLayerClass))
+#define IP_IS_GCP_LAYER(obj)         (GTK_CHECK_TYPE ((obj),IP_TYPE_GCP_LAYER))
+#define IP_IS_GCP_LAYER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), \
+                                                            IP_TYPE_GCP_LAYER))
+
+typedef struct _IpGcpLayer       IpGcpLayer;
+typedef struct _IpGcpLayerClass  IpGcpLayerClass;
+
+struct _IpGcpLayer
+{
+    GvShapesLayer layer;
+};
+
+struct _IpGcpLayerClass
+{
+    GvShapesLayerClass parent_class;
+};
+
+GtkType ip_gcp_layer_get_type(void);
+GtkObject* ip_gcp_layer_new(void);
+
+#endif /* __IPGCPLAYER_H__ */

Added: packages/openev/branches/upstream/current/llrasterize.c
===================================================================
--- packages/openev/branches/upstream/current/llrasterize.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/llrasterize.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,645 @@
+/******************************************************************************
+ * $Id: llrasterize.c,v 1.9 2004/11/23 06:14:55 warmerda Exp $
+ *
+ * Project:  CIETMAP / OpenEV
+ * Purpose:  Vector rasterization code (initially GvAreaShapes to GDAL raster).
+ * Author:   Frank Warmerdam, warmerda at home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam <warmerda at home.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: llrasterize.c,v $
+ * Revision 1.9  2004/11/23 06:14:55  warmerda
+ * avoid use of rint() - not portable
+ *
+ * Revision 1.8  2004/11/19 23:59:37  gmwalter
+ * Check in Aude's rasterization updates.
+ *
+ * Revision 1.7  2004/10/20 18:33:55  warmerda
+ * Cietmap bug 3233: fix screwup in last scanline under some circumstances.
+ *
+ * Revision 1.6  2001/08/08 04:26:43  warmerda
+ * fixed leak of polyInts array
+ *
+ * Revision 1.5  2001/03/29 14:59:56  warmerda
+ * added fill_short flag to control handling of slivers
+ *
+ * Revision 1.4  2000/09/27 14:04:31  warmerda
+ * removed debugging printf
+ *
+ * Revision 1.3  2000/09/21 02:54:50  warmerda
+ * removed debug stuff
+ *
+ * Revision 1.2  2000/09/15 01:57:43  warmerda
+ * added copyright header
+ *
+ */
+
+/*
+ * NOTE: This code was adapted from the gdImageFilledPolygon() function 
+ * in libgd.  
+ * 
+ * http://www.boutell.com/gd/
+ */
+
+#include <stdlib.h>
+#include "gvrasterize.h"
+
+static int llCompareInt(const void *a, const void *b)
+{
+	return (*(const int *)a) - (*(const int *)b);
+}
+
+/************************************************************************/
+/*                        llImageFilledPolygon()                        */
+/*                                                                      */
+/*      Perform scanline conversion of the passed multi-ring            */
+/*      polygon.  Note the polygon does not need to be explicitly       */
+/*      closed.  The scanline function will be called with              */
+/*      horizontal scanline chunks which may not be entirely            */
+/*      contained within the valid raster area (in the X                */
+/*      direction).                                                     */
+/************************************************************************/
+
+/* KNOWN BUGS:                                                               */
+
+/*  1) Because the nodes" coordinates of the polygons are casted to integers */
+/*  before being passed to the routine, the computation of intersections     */
+/*  between raster lines and the polygon"s segments is not accurate and may  */
+/*  cause pixels to be considered as inside the shape when they don"t even   */
+/*  touch it.                                                                */
+/*  2) Pixels falling at the bottom of a shape on an horizontal segment are  */
+/*  never taken into account.                                                */
+/*  3) When a polygon lies partly outside the extent of the raster file      */
+/*  beyond the last line,  and one of its node falls                         */
+/*  on the last line, wrong pixels are considered outside.                   */
+/*  4) Cones facing down (\./ ) are not filled on the last line.             */
+/*  5) Possible core dumps.                                                  */
+
+void llImageFilledPolygon(int nRasterXSize, int nRasterYSize, 
+                          int nPartCount, int *panPartSize, llPoint *p,
+                          int bFillShort,
+                          llScanlineFunc pfnScanlineFunc, void *pCBData )
+{
+    int i;
+    int y;
+    int miny, maxy;
+    int x1, y1;
+    int x2, y2;
+    int ind1, ind2;
+    int ints, n, part;
+    int *polyInts, polyAllocated;
+
+    if (!nPartCount) {
+        return;
+    }
+
+    n = 0;
+    for( part = 0; part < nPartCount; part++ )
+        n += panPartSize[part];
+
+    polyInts = (int *) malloc(sizeof(int) * n);
+    polyAllocated = n;
+
+    miny = p[0].y;
+    maxy = p[0].y;
+    for (i=1; (i < n); i++) {
+        if (p[i].y < miny) {
+            miny = p[i].y;
+        }
+        if (p[i].y > maxy) {
+            maxy = p[i].y;
+        }
+    }
+    if( miny < 0 )
+        miny = 0;
+    if( maxy >= nRasterYSize )
+        maxy = nRasterYSize-1;
+
+    /* Fix in 1.3: count a vertex only once */
+    for (y=miny; (y <= maxy); y++) {
+        int	partoffset = 0;
+
+        part = 0;
+        ints = 0;
+
+        for (i=0; (i < n); i++) {
+
+            if( i == partoffset + panPartSize[part] ) {
+                partoffset += panPartSize[part];
+                part++;
+            }
+
+            if( i == partoffset ) {
+                ind1 = partoffset + panPartSize[part] - 1;
+                ind2 = partoffset;
+            } else {
+                ind1 = i-1;
+                ind2 = i;
+            }
+            
+            y1 = p[ind1].y;
+            y2 = p[ind2].y;
+            if( (y1 < y && y2 < y) || (y1 > y && y2 > y) )
+                continue;
+
+            if (y1 < y2) {
+                x1 = p[ind1].x;
+                x2 = p[ind2].x;
+            } else if (y1 > y2) {
+                y2 = p[ind1].y;
+                y1 = p[ind2].y;
+                x2 = p[ind1].x;
+                x1 = p[ind2].x;
+            } else {
+                /* skip horizontal segments */
+                continue;
+            }
+
+            /* 
+            ** the commented out test was intended to ensure the last 
+            ** scanline of the polygon gets filled in properly.  Otherwise
+            ** it would often be dropped.  But it doesn't always work, and
+            ** much worse is that sometimes it results in a bogus extra
+            ** transition that screws everything up.
+            ** See CIETMap 3233 at DMSG. 
+            */
+            if( y < y2 /* || y2 == maxy */ )
+                polyInts[ints++] = (y-y1) * (x2-x1) / (y2-y1) + x1;
+        }
+
+        /* 
+         * It would be more efficient to do this inline, to avoid 
+         * a function call for each comparison.
+         */
+        qsort(polyInts, ints, sizeof(int), llCompareInt);
+
+        for (i=0; (i < (ints)); i+=2) {
+            if( !bFillShort && polyInts[i] == polyInts[i+1] )
+                continue;
+
+            if( polyInts[i] < nRasterXSize && polyInts[i+1] >= 0 )
+                pfnScanlineFunc( pCBData, y, polyInts[i], polyInts[i+1] );
+        }
+    }
+
+    free( polyInts );
+}
+
+/************************************************************************/
+/*                        llImageFilledPolygon()                        */
+/*                                                                      */
+/*      Perform scanline conversion of the passed multi-ring            */
+/*      polygon.  Note the polygon does not need to be explicitly       */
+/*      closed.  The scanline function will be called with              */
+/*      horizontal scanline chunks which may not be entirely            */
+/*      contained within the valid raster area (in the X                */
+/*      direction).                                                     */
+
+/*      NEW: Nodes' coordinate are kept as double  in order             */
+/*      to compute accurately the intersections with the lines          */
+      
+/*         Two methods for determining the border pixels:               */
+
+/*            1) method = 0                                             */  
+/*            Inherits algorithm from version above but with several bugs */
+/*              fixed except for the cone facing down.                  */
+/*               A pixel on which a line intersects a segment of a      */
+/*            polygon will always be considered as inside the shape.    */
+/*            Note that we only compute intersections with lines that   */
+/*            passes through the middle of a pixel (line coord = 0.5,   */
+/*            1.5, 2.5, etc.) */
+           
+/*            2) method = 1:                                            */
+/*              A pixel is considered inside a polygon if its center   */
+/*            falls inside the polygon. This is somehow more robust unless */
+/*            the nodes are placed in the center of the pixels in which */
+/*            case, due to numerical inaccuracies, it's hard to predict */
+/*            if the pixel will be considered inside or outside the shape.*/
+
+/************************************************************************/
+
+void ImageFilledPolygon0(int nRasterXSize, int nRasterYSize, 
+                          int nPartCount, int *panPartSize, dllPoint *p,
+                         llScanlineFunc pfnScanlineFunc, void *pCBData);
+
+
+void ImageFilledPolygon1(int nRasterXSize, int nRasterYSize, 
+                         int nPartCount, int *panPartSize, dllPoint *p,
+                         llScanlineFunc pfnScanlineFunc, void *pCBData);
+
+void dllImageFilledPolygon(int nRasterXSize, int nRasterYSize, 
+                           int nPartCount, int *panPartSize, dllPoint *p,
+                           llScanlineFunc pfnScanlineFunc, void *pCBData, 
+                           int method)
+{
+    
+    if ( method == 0 )
+        ImageFilledPolygon0(nRasterXSize,
+                            nRasterYSize,
+                            nPartCount, 
+                            panPartSize, 
+                            p,
+                            pfnScanlineFunc, 
+                            pCBData);
+    
+    else if (method == 1)
+        ImageFilledPolygon1(nRasterXSize,
+                            nRasterYSize,
+                            nPartCount, 
+                            panPartSize, 
+                            p,
+                            pfnScanlineFunc, 
+                            pCBData);
+
+    return;
+    
+}
+
+
+
+
+/***********************************************************************
+Method one
+----------
+Known bugs:
+Cones facing down (\./ ) are not filled on the last line.
+************************************************************************/
+void ImageFilledPolygon0(int nRasterXSize, int nRasterYSize, 
+                          int nPartCount, int *panPartSize, dllPoint *p,
+                         llScanlineFunc pfnScanlineFunc, void *pCBData)
+{
+    int i;
+    int y, y1, y2;
+    int miny, maxy,minx,maxx;
+    double dminy, dmaxy ;
+    double dx1, dy1;
+    double dx2, dy2;
+    double dy;
+    double intersect, slope;
+    
+
+    int ind1, ind2;
+    int ints, n, part;
+    int *polyInts, polyAllocated;
+
+  
+    int horizontal_x1, horizontal_x2;
+
+    int inter, left, right;
+    
+
+    if (!nPartCount) {
+        return;
+    }
+
+    n = 0;
+    for( part = 0; part < nPartCount; part++ )
+        n += panPartSize[part];
+    
+    polyInts = (int *) malloc(sizeof(int) * n);
+    polyAllocated = n;
+    
+    dminy = p[0].y;
+    dmaxy = p[0].y;
+    for (i=1; (i < n); i++) {
+
+        if (p[i].y < dminy) {
+            dminy = p[i].y;
+        }
+        if (p[i].y > dmaxy) {
+            dmaxy = p[i].y;
+        }
+    }
+    miny = (int) dminy;
+    maxy = (int) dmaxy;
+    
+
+    if( miny < 0 )
+        miny = 0;
+    if( maxy >= nRasterYSize )
+        maxy = nRasterYSize-1;
+   
+    
+    minx = 0;
+    maxx = nRasterXSize - 1;
+    
+    
+    /* Fix in 1.3: count a vertex only once */
+    for (y=miny; y <= maxy; y++) {
+        int	partoffset = 0;
+
+        dy = y +0.5; /* center height of line*/
+         
+
+        part = 0;
+        ints = 0;
+
+        /*Initialize polyInts, otherwise it can sometimes causes a seg fault */
+        for (i=0; (i < n); i++) {
+            polyInts[i] = -1;
+        }
+        
+
+        for (i=0; (i < n); i++) {
+        
+            
+            if( i == partoffset + panPartSize[part] ) {
+                partoffset += panPartSize[part];
+                part++;
+            }
+
+            if( i == partoffset ) {
+                ind1 = partoffset + panPartSize[part] - 1;
+                ind2 = partoffset;
+            } else {
+                ind1 = i-1;
+                ind2 = i;
+            }
+	    
+            
+            dy1 = p[ind1].y;
+            dy2 = p[ind2].y;
+            
+
+            y1 = (int) dy1;
+            y2 = (int) dy2;
+
+            
+            if( ( y1 < y && y2 < y) || (y1 > y && y2 > y) )
+                continue;
+
+
+            if (y1 < y2) {
+                dx1 = p[ind1].x;
+                dx2 = p[ind2].x;
+            } else if (y1 > y2) {
+                dy2 = p[ind1].y;
+                dy1 = p[ind2].y;
+                y2 = (int) dy2;
+                y1 = (int) dy1;
+                dx2 = p[ind1].x;
+                dx1 = p[ind2].x;
+            } else /*  (y1 == y2) */
+	    {
+                
+                /*AE: DO NOT skip bottom horizontal segments 
+		  -Fill them separately- 
+		  They are not taken into account twice.*/
+		if (p[ind1].x > p[ind2].x)
+		{
+		    horizontal_x1 = (int) p[ind2].x;
+		    horizontal_x2 = (int) p[ind1].x;
+		
+                    if  ( (horizontal_x1 >  maxx) ||  (horizontal_x2 < minx) )
+                        continue;
+
+		    /*fill the horizontal segment (separately from the rest)*/
+		    pfnScanlineFunc( pCBData, y, horizontal_x1, horizontal_x2);
+		    continue;
+		}
+		else /*skip top horizontal segments (they are already filled in the regular loop)*/
+		    continue;
+
+	    }
+
+           
+            if( y < y2 )
+            {
+
+                intersect = (dy-dy1) * (dx2-dx1) / (dy2-dy1) + dx1;
+                slope = (dx2-dx1) / (dy2-dy1);
+                
+                /*the intersection must fall in the same x-range of the segment*/
+                inter = (int) intersect;
+                if (dx1 <= dx2)
+                {
+                    left = (int)dx1;
+                    right = (int)dx2;
+                }
+                else {
+                    left = (int)dx2;
+                    right = (int)dx1; 
+                }
+                
+                       
+                if (inter < left)
+                    inter  = left;
+                else if ( inter > right )
+                    inter = right;
+                
+                polyInts[ints++] = inter;
+
+	    }
+	}
+        
+
+
+        /*Sort intersections*/
+        qsort(polyInts, ints, sizeof(int), llCompareInt);
+
+
+        for (i=0; (i < (ints)); i+=2) {
+            
+            if( polyInts[i] <= maxx && polyInts[i+1] >= minx )
+            {
+                pfnScanlineFunc( pCBData, y, polyInts[i], polyInts[i+1]);
+	    }
+            
+        }
+    }
+
+    free( polyInts );
+}
+
+/*************************************************************************
+Method 2:
+=========
+No known bug
+*************************************************************************/
+
+void ImageFilledPolygon1(int nRasterXSize, int nRasterYSize, 
+                         int nPartCount, int *panPartSize, dllPoint *p,
+                         llScanlineFunc pfnScanlineFunc, void *pCBData)
+{
+    int i;
+    int y;
+    int miny, maxy,minx,maxx;
+    double dminy, dmaxy;
+    double dx1, dy1;
+    double dx2, dy2;
+    double dy;
+    double intersect;
+    
+
+    int ind1, ind2;
+    int ints, n, part;
+    int *polyInts, polyAllocated;
+
+  
+    int horizontal_x1, horizontal_x2;
+
+
+    if (!nPartCount) {
+        return;
+    }
+
+    n = 0;
+    for( part = 0; part < nPartCount; part++ )
+        n += panPartSize[part];
+    
+    polyInts = (int *) malloc(sizeof(int) * n);
+    polyAllocated = n;
+    
+    dminy = p[0].y;
+    dmaxy = p[0].y;
+    for (i=1; (i < n); i++) {
+
+        if (p[i].y < dminy) {
+            dminy = p[i].y;
+        }
+        if (p[i].y > dmaxy) {
+            dmaxy = p[i].y;
+        }
+    }
+    miny = (int) dminy;
+    maxy = (int) dmaxy;
+    
+    
+    if( miny < 0 )
+        miny = 0;
+    if( maxy >= nRasterYSize )
+        maxy = nRasterYSize-1;
+   
+    
+    minx = 0;
+    maxx = nRasterXSize - 1;
+
+    /* Fix in 1.3: count a vertex only once */
+    for (y=miny; y <= maxy; y++) {
+        int	partoffset = 0;
+
+        dy = y +0.5; /* center height of line*/
+         
+
+        part = 0;
+        ints = 0;
+
+
+        /*Initialize polyInts, otherwise it can sometimes causes a seg fault */
+        for (i=0; (i < n); i++) {
+            polyInts[i] = -1;
+        }
+
+
+        for (i=0; (i < n); i++) {
+        
+            
+            if( i == partoffset + panPartSize[part] ) {
+                partoffset += panPartSize[part];
+                part++;
+            }
+
+            if( i == partoffset ) {
+                ind1 = partoffset + panPartSize[part] - 1;
+                ind2 = partoffset;
+            } else {
+                ind1 = i-1;
+                ind2 = i;
+            }
+	    
+
+            dy1 = p[ind1].y;
+            dy2 = p[ind2].y;
+            
+
+            if( (dy1 < dy && dy2 < dy) || (dy1 > dy && dy2 > dy) )
+                continue;
+
+            
+
+
+            if (dy1 < dy2) {
+                dx1 = p[ind1].x;
+                dx2 = p[ind2].x;
+            } else if (dy1 > dy2) {
+                dy2 = p[ind1].y;
+                dy1 = p[ind2].y;
+                dx2 = p[ind1].x;
+                dx1 = p[ind2].x;
+            } else /* if (fabs(dy1-dy2)< 1.e-6) */
+	    {
+                
+                /*AE: DO NOT skip bottom horizontal segments 
+		  -Fill them separately- 
+		  They are not taken into account twice.*/
+		if (p[ind1].x > p[ind2].x)
+		{
+		    horizontal_x1 = (int) floor(p[ind2].x+0.5);
+		    horizontal_x2 = (int) floor(p[ind1].x+0.5);
+		
+                    if  ( (horizontal_x1 >  maxx) ||  (horizontal_x2 < minx) )
+                        continue;
+
+
+		    /*fill the horizontal segment (separately from the rest)*/
+		    pfnScanlineFunc( pCBData, y, horizontal_x1, horizontal_x2 - 1 );
+		    continue;
+		}
+		else /*skip top horizontal segments (they are already filled in the regular loop)*/
+		    continue;
+
+	    }
+
+           
+
+            if(( dy < dy2 ) && (dy >= dy1))
+            {
+                
+                intersect = (dy-dy1) * (dx2-dx1) / (dy2-dy1) + dx1;
+
+		polyInts[ints++] = (int) floor(intersect+0.5);
+	    }
+	}
+
+
+
+        /* 
+         * It would be more efficient to do this inline, to avoid 
+         * a function call for each comparison.
+         */
+        qsort(polyInts, ints, sizeof(int), llCompareInt);
+
+
+        for (i=0; (i < (ints)); i+=2) {
+
+            if( polyInts[i] <= maxx && polyInts[i+1] >= minx )
+            {
+                pfnScanlineFunc( pCBData, y, polyInts[i], polyInts[i+1] - 1 );
+                
+	    }
+            
+        }
+    }
+
+
+    free( polyInts );
+}
+
+

Added: packages/openev/branches/upstream/current/makefile.vc
===================================================================
--- packages/openev/branches/upstream/current/makefile.vc	                        (rev 0)
+++ packages/openev/branches/upstream/current/makefile.vc	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,94 @@
+GVIEW_ROOT=.
+
+!INCLUDE nmake.opt
+
+GV_INSTALL_HOME = $(NM_BIN_DIR)\..
+GV_INSTALL_HTML = $(GV_INSTALL_HOME)\html
+GV_INSTALL_PICS = $(GV_INSTALL_HOME)\pics
+GV_INSTALL_TOOLS = $(GV_INSTALL_HOME)\tools
+GV_INSTALL_RAMPS = $(GV_INSTALL_HOME)\ramps
+GV_INSTALL_XMLCONFIG = $(GV_INSTALL_HOME)\xmlconfig
+
+CC	=	cl
+CXX	=	cl
+
+INC = /I$(NM_GLIB_DIR) /I$(NM_GLIB_DIR)/gmodule /I$(NM_GTK_DIR) /I$(NM_GTK_DIR)\gdk \
+	/I$(NM_GTKGLAREA_DIR) \
+	/I$(GDAL_HOME)\port /I$(GDAL_HOME)\gcore /I$(GDAL_HOME)\ogr \
+	/I$(GDAL_HOME)\ogr\ogrsf_frmts
+
+CFLAGS = $(INC) $(GV_OPTFLAGS) /DHAVE_OGR
+
+OBJ =	gvviewarea.obj  gvdata.obj gvlayer.obj \
+	gvpolylines.obj  gvlinelayer.obj  gvtool.obj  gvlinetool.obj \
+	gvtoolbox.obj   gvshapelayer.obj  gvselecttool.obj  gextra.obj \
+	gvareas.obj  gvarealayer.obj  gvareatool.obj  gvtess.obj \
+	gvnodetool.obj  gvundo.obj  gvpoints.obj  gvpointlayer.obj \
+  	gvpointtool.obj gvpquerylayer.obj gvviewlink.obj gvroitool.obj \
+	gvmesh.obj gvrasterlayer.obj gvraster.obj gvrasterlut.obj \
+	gvrasteraverage.obj gvrasterconvert.obj gvrastercache.obj \
+	gvtracktool.obj gtkcolorwell.obj gvsymbolmanager.obj gvprint.obj \
+	crs.obj gvproperties.obj gvshapes.obj gvshape.obj gvshapeslayer.obj \
+	gvshapefile.obj shpopen.obj dbfopen.obj gvutils.obj gvtessshape.obj \
+	gvzoompantool.obj gvmanager.obj gvrastersource.obj gvtexturecache.obj \
+	ipgcplayer.obj gvrecttool.obj gvwinprint.obj appcurlayer.obj \
+	invdistance.obj gvrasterize.obj llrasterize.obj gvogr.obj \
+	gvrenderinfo.obj gvskirt.obj gvpoitool.obj gvrecords.obj \
+	gvrotatetool.obj gvautopan.obj
+
+GVLIB = gv.lib
+
+LIBS = $(GDAL_HOME)/gdal_i.lib $(NM_GTKGLAREA_DIR)/gtkglarea.lib \
+	$(NM_GTK_DIR)/gtk/gtk-1.3.lib \
+	$(NM_GTK_DIR)/gdk/gdk-1.3.lib $(NM_GLIB_DIR)/glib-1.3.lib \
+	GLU32.lib opengl32.lib gdi32.lib user32.lib shell32.lib
+
+default: $(GVLIB) gvtest.exe py_modules
+
+py_modules:
+	cd pymod
+	nmake /f makefile.vc 
+	cd ..
+
+.c.obj:	
+	$(CC) $(CFLAGS) /c $*.c
+
+.cpp.obj:	
+	$(CC) $(CFLAGS) /c $*.cpp
+
+$(GVLIB): gv_config.h $(OBJ)
+	if exist $(GVLIB) del $(GVLIB)
+	lib /out:$(GVLIB) $(OBJ)
+
+gvtest.exe:	testmain.obj $(GVLIB)
+	link /debug testmain.obj $(GVLIB) $(LIBS) /out:gvtest.exe
+
+gv_config.h:	gv_config.h_win32
+	copy gv_config.h_win32 gv_config.h
+
+clean:
+	cd pymod
+	nmake /f makefile.vc clean
+	cd ..
+	-del gvtest.exe
+	-del *.obj
+	-del *.lib
+	-del *.dll
+
+full_install:	install
+	if not exist $(GV_INSTALL_HTML) mkdir $(GV_INSTALL_HTML)
+	if not exist $(GV_INSTALL_PICS) mkdir $(GV_INSTALL_PICS)
+	if not exist $(GV_INSTALL_TOOLS) mkdir $(GV_INSTALL_TOOLS)
+	if not exist $(GV_INSTALL_RAMPS) mkdir $(GV_INSTALL_RAMPS)
+	if not exist $(GV_INSTALL_XMLCONFIG) mkdir $(GV_INSTALL_XMLCONFIG)
+	copy html\*.* $(GV_INSTALL_HTML)
+	copy pics\*.* $(GV_INSTALL_PICS)
+	copy tools\*.* $(GV_INSTALL_TOOLS)
+	copy ramps\*.* $(GV_INSTALL_RAMPS)
+	copy xmlconfig\*.* $(GV_INSTALL_XMLCONFIG)
+
+install:	default
+	copy gvtest.exe $(NM_BIN_DIR)
+	cd pymod
+	nmake /f makefile.vc install
+	cd ..

Added: packages/openev/branches/upstream/current/nmake.opt
===================================================================
--- packages/openev/branches/upstream/current/nmake.opt	                        (rev 0)
+++ packages/openev/branches/upstream/current/nmake.opt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,12 @@
+
+GDAL_HOME=$(GVIEW_ROOT)\..\gdal
+
+NM_ROOT=$(GVIEW_ROOT)\..\PKGSRC
+
+!INCLUDE $(NM_ROOT)/nmake.opt
+
+GV_OPTFLAGS = /nologo /Ox
+#GV_OPTFLAGS = /nologo /Zi /MD /Fd$(GVIEW_ROOT)\openev.pdb
+
+#GV_OPTFLAGS = $(NM_OPTFLAGS)
+

Added: packages/openev/branches/upstream/current/pics/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/pics/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,51 @@
+/atlantis_logo.xpm/1.1/Fri Jul 28 20:03:27 2000//
+/busy.xpm/1.1/Thu Jun 29 16:12:31 2000//
+/ck_off_l.xpm/1.1/Tue Aug 13 17:44:36 2002/-kb/
+/ck_on_l.xpm/1.1/Tue Aug 13 17:44:36 2002/-kb/
+/classify.xpm/1.1/Wed Mar 21 05:38:54 2001//
+/delete.xpm/1.1/Thu Jan 20 14:50:07 2000//
+/equalize.xpm/1.4/Thu Aug 10 20:44:43 2000//
+/eye.xpm/1.1/Fri Nov 26 22:34:53 1999//
+/geo_innovation.xpm/1.1/Fri Jul 28 20:03:26 2000//
+/help.xpm/1.2/Mon Jun 26 13:34:06 2000//
+/idle.xpm/1.1/Thu Jun 29 16:12:31 2000//
+/legend.xpm/1.1/Wed Mar 21 05:32:54 2001//
+/linear.xpm/1.1/Thu Mar 22 18:46:32 2001//
+/log.xpm/1.1/Tue Oct 16 18:55:43 2001//
+/lower.xpm/1.1/Thu Jan 20 14:50:07 2000//
+/new.xpm/1.1/Thu Jan 20 14:50:07 2000//
+/node_cursor.xbm/1.1/Thu Nov 11 15:41:21 1999//
+/node_cursor_mask.xbm/1.1/Thu Nov 11 15:41:21 1999//
+/nonelut.xpm/1.4/Thu Aug 10 20:44:44 2000//
+/onetoone.xpm/1.2/Mon Jun 26 13:34:06 2000//
+/openev.xpm/1.2/Fri Jun 16 14:28:25 2000//
+/openfile.xpm/1.2/Mon Jun 26 13:34:06 2000//
+/pan_left.xpm/1.1/Mon Apr  9 18:28:41 2001//
+/pan_rght.xpm/1.1/Mon Apr  9 18:49:33 2001//
+/print.xpm/1.2/Mon Jun 26 13:34:06 2000//
+/raise.xpm/1.1/Thu Jan 20 14:50:07 2000//
+/recenter.xpm/1.1/Thu Sep 11 14:18:38 2003/-kb/
+/refresh.xpm/1.1/Wed Aug  2 19:18:29 2000//
+/root.xpm/1.1/Thu Mar 22 18:46:32 2001//
+/save.xpm/1.1/Mon Jun 26 16:22:45 2000//
+/seeall.xpm/1.1/Thu Jun 15 13:54:27 2000//
+/sym_circle.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_cross.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_filled_circle.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_filled_square.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_filled_star.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_filled_triangle.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_square.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_star.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_triangle.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_vertical.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/sym_x.xpm/1.1/Tue May  8 23:23:50 2001/-kb/
+/vexcel_logo.xpm/1.2/Tue Jun 28 18:14:35 2005/-kb/
+/warning.xpm/1.1/Thu Aug 24 18:02:00 2000//
+/windowed.xpm/1.2/Wed Dec 12 15:15:50 2001//
+/worldg.xpm/1.1/Fri Jul  6 17:05:42 2001//
+/worldrgb.xpm/1.1/Fri Jul  6 17:05:42 2001//
+/zoomin.xpm/1.3/Mon Jun 26 13:34:06 2000//
+/zoomout.xpm/1.3/Mon Jun 26 13:34:06 2000//
+/zoomrect.xpm/1.1/Thu Sep 11 14:18:38 2003/-kb/
+D

Added: packages/openev/branches/upstream/current/pics/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/pics/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/pics

Added: packages/openev/branches/upstream/current/pics/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/pics/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/pics/atlantis_logo.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/atlantis_logo.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/atlantis_logo.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,308 @@
+/* XPM */
+static char * atlantis_logo_xpm[] = {
+"221 55 250 2",
+"  	c None",
+". 	c #000000",
+"+ 	c #800000",
+"@ 	c #008000",
+"# 	c #808000",
+"$ 	c #000080",
+"% 	c #800080",
+"& 	c #008080",
+"* 	c #C0C0C0",
+"= 	c #C0DCC0",
+"- 	c #A6CAF0",
+"; 	c #001031",
+"> 	c #000008",
+", 	c #000808",
+"' 	c #000810",
+") 	c #000818",
+"! 	c #000821",
+"~ 	c #000829",
+"{ 	c #001018",
+"] 	c #001021",
+"^ 	c #001029",
+"/ 	c #001031",
+"( 	c #001039",
+"_ 	c #001821",
+": 	c #001829",
+"< 	c #001831",
+"[ 	c #001842",
+"} 	c #00184A",
+"| 	c #001852",
+"1 	c #002131",
+"2 	c #002139",
+"3 	c #002152",
+"4 	c #00215A",
+"5 	c #002163",
+"6 	c #002173",
+"7 	c #002942",
+"8 	c #00294A",
+"9 	c #00295A",
+"0 	c #00297B",
+"a 	c #00314A",
+"b 	c #003163",
+"c 	c #0031CE",
+"d 	c #00396B",
+"e 	c #003973",
+"f 	c #00398C",
+"g 	c #004A84",
+"h 	c #004A8C",
+"i 	c #00529C",
+"j 	c #005AAD",
+"k 	c #0063B5",
+"l 	c #0063FF",
+"m 	c #006BBD",
+"n 	c #006BC6",
+"o 	c #0073CE",
+"p 	c #0073DE",
+"q 	c #0073E7",
+"r 	c #007BEF",
+"s 	c #008CFF",
+"t 	c #00FFFF",
+"u 	c #080808",
+"v 	c #081010",
+"w 	c #081821",
+"x 	c #082129",
+"y 	c #082131",
+"z 	c #082139",
+"A 	c #08214A",
+"B 	c #082931",
+"C 	c #082939",
+"D 	c #082942",
+"E 	c #08296B",
+"F 	c #083142",
+"G 	c #08314A",
+"H 	c #08316B",
+"I 	c #083952",
+"J 	c #08395A",
+"K 	c #083973",
+"L 	c #101010",
+"M 	c #101021",
+"N 	c #101818",
+"O 	c #101821",
+"P 	c #102939",
+"Q 	c #10314A",
+"R 	c #10398C",
+"S 	c #104252",
+"T 	c #104263",
+"U 	c #10427B",
+"V 	c #104A94",
+"W 	c #181818",
+"X 	c #181821",
+"Y 	c #182129",
+"Z 	c #185273",
+"` 	c #212121",
+" .	c #212929",
+"..	c #212931",
+"+.	c #215273",
+"@.	c #21639C",
+"#.	c #292929",
+"$.	c #293131",
+"%.	c #293942",
+"&.	c #293952",
+"*.	c #313139",
+"=.	c #316B9C",
+"-.	c #3184C6",
+";.	c #393939",
+">.	c #394242",
+",.	c #39424A",
+"'.	c #394263",
+").	c #394A4A",
+"!.	c #394A52",
+"~.	c #394A5A",
+"{.	c #424242",
+"].	c #42424A",
+"^.	c #424A4A",
+"/.	c #424A52",
+"(.	c #42525A",
+"_.	c #426373",
+":.	c #4A4A4A",
+"<.	c #4AB5EF",
+"[.	c #525252",
+"}.	c #525A5A",
+"|.	c #5A8494",
+"1.	c #5A94C6",
+"2.	c #636363",
+"3.	c #636B6B",
+"4.	c #63737B",
+"5.	c #6B737B",
+"6.	c #6BB5E7",
+"7.	c #737373",
+"8.	c #737B7B",
+"9.	c #7B7B7B",
+"0.	c #7B8C94",
+"a.	c #848484",
+"b.	c #848C94",
+"c.	c #8C949C",
+"d.	c #8CCEF7",
+"e.	c #949494",
+"f.	c #94949C",
+"g.	c #949CA5",
+"h.	c #94B5C6",
+"i.	c #9C9CA5",
+"j.	c #9CC6E7",
+"k.	c #9CDEFF",
+"l.	c #A5A5A5",
+"m.	c #A5A5AD",
+"n.	c #A5ADAD",
+"o.	c #A5ADB5",
+"p.	c #A5B5B5",
+"q.	c #A5BDC6",
+"r.	c #A5CEF7",
+"s.	c #ADADAD",
+"t.	c #ADADB5",
+"u.	c #ADB5B5",
+"v.	c #ADBDCE",
+"w.	c #ADC6D6",
+"x.	c #ADCED6",
+"y.	c #ADD6EF",
+"z.	c #ADEFFF",
+"A.	c #B5B5B5",
+"B.	c #B5BDBD",
+"C.	c #B5C6D6",
+"D.	c #BDC6C6",
+"E.	c #BDCECE",
+"F.	c #C6C6C6",
+"G.	c #C6C6CE",
+"H.	c #C6CECE",
+"I.	c #C6CED6",
+"J.	c #C6EFF7",
+"K.	c #C6FFFF",
+"L.	c #CECECE",
+"M.	c #CED6D6",
+"N.	c #CED6DE",
+"O.	c #CEDEDE",
+"P.	c #CEDEE7",
+"Q.	c #CEE7EF",
+"R.	c #CEE7F7",
+"S.	c #CEFFFF",
+"T.	c #D6D6D6",
+"U.	c #D6DEDE",
+"V.	c #D6E7EF",
+"W.	c #D6FFFF",
+"X.	c #DEDEDE",
+"Y.	c #DEE7E7",
+"Z.	c #E7E7E7",
+"`.	c #E7E7EF",
+" +	c #E7EFF7",
+".+	c #E7FFFF",
+"++	c #EFEFEF",
+"@+	c #EFF7FF",
+"#+	c #EFFFFF",
+"$+	c #F7F7F7",
+"%+	c #F7F7FF",
+"&+	c #F7FFFF",
+"*+	c #FFFFFF",
+"=+	c #FFFFFF",
+"-+	c #FFFFFF",
+";+	c #FFFFFF",
+">+	c #FFFFFF",
+",+	c #FFFFFF",
+"'+	c #FFFFFF",
+")+	c #FFFFFF",
+"!+	c #FFFFFF",
+"~+	c #FFFFFF",
+"{+	c #FFFFFF",
+"]+	c #FFFFFF",
+"^+	c #FFFFFF",
+"/+	c #FFFFFF",
+"(+	c #FFFFFF",
+"_+	c #FFFFFF",
+":+	c #FFFFFF",
+"<+	c #FFFFFF",
+"[+	c #FFFFFF",
+"}+	c #FFFFFF",
+"|+	c #FFFFFF",
+"1+	c #FFFFFF",
+"2+	c #FFFFFF",
+"3+	c #FFFFFF",
+"4+	c #FFFFFF",
+"5+	c #FFFFFF",
+"6+	c #FFFFFF",
+"7+	c #FFFFFF",
+"8+	c #FFFFFF",
+"9+	c #FFFFFF",
+"0+	c #FFFFFF",
+"a+	c #FFFFFF",
+"b+	c #FFFFFF",
+"c+	c #FFFFFF",
+"d+	c #FFFFFF",
+"e+	c #FFFFFF",
+"f+	c #FFFFFF",
+"g+	c #FFFFFF",
+"h+	c #FFFFFF",
+"i+	c #FFFFFF",
+"j+	c #FFFFFF",
+"k+	c #FFFFFF",
+"l+	c #FFFFFF",
+"m+	c #FFFFFF",
+"n+	c #FFFFFF",
+"o+	c #FFFFFF",
+"p+	c #FFFFFF",
+"q+	c #FFFFFF",
+"r+	c #FFFFFF",
+"s+	c #FFFFFF",
+"t+	c #FFFFFF",
+"u+	c #FFFFFF",
+"v+	c #FFFFFF",
+"w+	c #FFFFFF",
+"x+	c #FFFFFF",
+"y+	c #FFFBF0",
+"z+	c #A0A0A4",
+"A+	c #808080",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ;.A+L . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . :.l.l.l.l.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L l.l.l.l.l.l.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . l.l.l.l.l.l.l.a.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L l.l.l.e.l.l.l.l.$.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > > > > > > > > > > > > > > > > > > > > > . . . . . . > > > > > > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e.l.l.7.:.. . l.l.a.W . . . . . . . . . . . . c c c c c c . . . . . . > > > > > > > > > > > > > > > > > > > > > . . . . . . > > > > > . . . . . . . . . . . . > ~ ; ; ' . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . c . . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c c c c . . . . . . c c c c c c , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; c . . . . . . . u l.l.7.#.. . . . l.l.#.c . . . . . . . . . . . . c c c c c c . . . . . c c c c c c c c c c c c c c c c c c c c c c . . . . . n c c c c c 5 . . . . . . . . ! c c c c c c c c c c c ! . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c c c c b . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . c c ' . . . . . . l.l.7.. . . . . 2.l.l.c c c . . . . . . . . . . . c c c c c c . . . . . c c c c c c c c c c c c c c c c c c c c c c . . . . . n c c c c c 5 . . . . . . . . c c c c c c c c c c c c c c . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . > c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c c c c b . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 c c c . . . . . l.9.2.. . . . . . l.l.#.c c c c c . . . . . . . . . c c c c c c . . . . . c c c c c c c c c c c c c c c c c c c c c c . . . . . n c c c c c 5 . . . . . . ' c c c c c c c c c c c c c c ] . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . c c c c c . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c c c c b . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . c c c c c . . e.l.7.. . . . . . . #.l.a.. c c c c c c 5 . . . . . . . c c c c c c . . . . . c c c c c c c c c c c c c c c c c c c c c c . . . . . n c c c c c 5 . . . . . . c c c c c c b b b c c c c c c . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . ! c c c c c c . . . . . . . . . . . c c c c c c c c c c c c c c c c c c c c c b . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . c c c c c c : #.l.9.. . . . . . . . l.l.#.. c c c c c c c c . . . . . . c c c c c c . . . . . c c c c c c c c c c c c c c c c c c c c c c . . . . . m c c c c c 5 . . . . . > c c c c b b d d b b b b b c ! . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . ' c c c c c c c . . . . . . . . . . . 5 c c c c c c c c c c c c c c c c c c c c b . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . . c c c c c c c c l.3.. . . . . . . . L l.#.. . c c c c c c c c c ; . . . . c c c c c c . . . . . c c c c c c c c c c c c c c c c c c c c c c . . . . . n c c c c c 5 . . . . . m c c c c . . . . . . > b b c . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . c c c c c c c c c . . . . . . . . . . . b b b b b b b c c c c c c b b b b b b b b . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . . c c c c c c c c c . . . . . . . . . l.f.L . . c c c c c c c c c c c . . . c c c c c c . . . . . c b b b b b b b c c c c c c b b b b b b b . . . . . . n c c c c c 5 . . . . . n c c c c c . . . . . . . . < . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . ; c c c c c c c c c ' . . . . . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . . c c c c c c c c c c . . . . . . . . l.l.$.. . . c c c c c c c c c c c c 5 . c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . n c c c c c c c c ; . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . c c c c c c c c c c c ' . . . . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . c c c c c c c c c c c c . . . . . . {.l.;.. . . . c c c c c c c c c c c c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . ' n n c c c c c c c c c c . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . | c c c c c c c c c c c c . . . . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . . c c c c c c c c c c c c 5 . . . . u l.2.#.. . . . c c c c c c c c c c c c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . n n c c c c c c c c c c c ! . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . } c c c c c c l c c c c c c ! . . . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . . c c c c c c c p c c c c c c . . . . l.e.W . . . . . c c c c c c c c c c c c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . . n n b c c c c c c c c c c ! . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . c c c c c c b b . c c c c c c , . . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . ' c c c c c c 4 . s c c c c c c . . l.a.;.. . . . . . c c c c c c p c c c c c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . . . ' b d b b b c c c c c c c . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . 5 c c c c c b d . . c c c c c c c . . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . . c c c c c c c . . g c c c c c c . u k.{.. . . . . . . c c c c c c s s c c c c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . . . . . > b d d b b c c c c c c . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . c c c c c c b . . . . c c c c c c ; . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c 1 . . . . . . . . . . . . . . . . . . . . . c c c c c c c ' . . . n c c c c c -.k.;.. . . . . . . . c c c c c c . b s c c c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . . . . . . . . . > c c c c c c c . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . c c c c c c b b |.d.<.. . c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . . . . ! c c c c c c b . d.6. at .. c c c c c r.3.. . . . . . . . . c c c c c c . . . s s c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . . . . . . . . . . . . o c c c c . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . c c c c c c b d u p.A+:.<.. 3 c c c c c 4 . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . . . . c c c c c c b . Q.b.8.<.W ] c c c - 0./.. . . . . . . . . c c c c c c . . . . s c c c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . . ] . . . . . . . . . c c c c c . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . c c c c c c b b y.&+a.:.-. at .. c c c c c c } . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . . . ( c c c c c b . . J.s.7.$.-.. c c d.j.U c c . . . . . . . . c c c c c c . . . . . > s c c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . 1 c c c c c c c c c c c c c c . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . c c c c c c b b . v.l.7.{.1.+.. ' c c c c c c . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . . . c c c c c c b . . V.b.2.#.=.. ' 6.r./.c c c . . . . . . . . c c c c c c . . . . . . . s c c c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . c c c c c c c c c c c c c c c . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . c c c c c c c d . . . X.7.a.#.. . . ' c c c c c c . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . . c c c c c c b . . . g.7.:.7.u . _.k.}.c c c c c ' . . . . . . c c c c c c . . . . . . . . < s c c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . . c c c c c c c c c c c c c c . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . > c c c c c c b b . . . . #.#.. . . . . c c c c c c . . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . | c c c c c b . . . . . #.` . . ,.k.[.b c c c c c c . . . . . . c c c c c c . . . . . . . . . . s c c c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . 8 c c c c c c c c c c c c b . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . c c c c c c b b . . . . . . . . . . . . > c c c c c c . . . . . . . . . c c c c c c . . . . . . . . . . . . . c c c c c c c c c c c c c c c c c c . . . . . . c c c c c c b . . . . . . . . Y k.2.. . b c c c c c ; . . . . . c c c c c c . . . . . . . . . . . s s c . . . . . . . . . . . . . c c c c c c . . . . . . . . . . . . . n c c c c c 5 . . . . . c G b b b c c c c c b b b . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . b b d d b d . . . . . . . . . . . . . . ' b d d b b 2 . . . . . . . . b d d b d b . . . . . . . . . . . . . . d b b d b b d b b d b b d b b d b . . . . . . b d d b d b . . . . . . . . O r.4.. . . 8 b b d b b < . . . . . b b d b b b . . . . . . . . . . . . . s . . . . . . . . . . . . . b d d b d d . . . . . . . . . . . . . d b d d b d . . . . . . . . . d b d b d d b . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . l.<.. . . . . . . . . . O k.2.u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b.1.k . . . . . . . . . O k.0.u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > . . . . . W l.` . . . . . . . . . O r.0.u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' . . . . $.l.#.. . . . . . . . . Y r.3.u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . #.$.L . . . . . . . . . . . . . . u #.$.u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' ' . . . . l.i.' . . . . . . . . %.- 5.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . u #.#.W . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . u #.#.W . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . T.L.T.A.b.a.b.* L.L.f.. . . . . . B.L.L.L.e.9.9.f.L.L.L.. . . . . * L.T.. . . . L.L.B.* * * * * B.* * * [.. . . T.L.L.O . . . . . . . L.L.u.> , D.* * * * * L.L.* * * * * A.4.q.7.* L.. . . . L.L.F.* B.* B.B.B.* * B.. . . L.L.... . . . . a.L.L.G.f.9.A+l.L.L.L.u . . . . . . . . . . . . . . . . ++L.L.. . . F.L.L.L.e.. . . . . . . L.L.9.. . . . . A.L.L.B.e.A+A+f.L.L.T.u . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . A.L.F.{.;.;.;.{.{.2.L.L.#.. . . . T.L.L.%.>.{.{.>.{.2.e.L.T.#.. . . * L.L.. . . . L.L.{.{.{.;.{.{.{.{.{.{.{.. . . L.L.L.L.a.. . . . . . L.L.s.' ' :.>.{.>.{.{.L.L.{.{.{.{.{.g.w.7./.L.L.. . . . L.L.7.{.;.>.{.{.{.{.{.{.. . . L.L.$.. . . . A.L.n.>.{.{.>.{.{.2.B.L.L.. . . . . . . . . . . . . . . . ++L.L.. . . &+L.L.L.L.2.. . . . . ` L.L.a.. . . . L.L.L.;.{.;.;.;.:.7.* L.L.. . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . B.L.L.#.. . . . . . . l.s.W . . F.L.9.{.#.. . . . . . . l.7.7.. . . F.L.L.. . . . L.L.` . . . . . . . . . . . . . L.L.L.L.L.L.. . > . . L.L.u., ' , . f.l.u . L.L.' . . . l.l.7.. . L.L.. . . . L.L.:.. . . . . . . . . . . . L.L.#.. . . L.L.f.W . . . . . . . s.f.7.:.. . . . . . . . . . . . . . . ++L.L.. . . &+L.L.Z.X.L.L.. . . . W L.L.7.. . . L.L.a.. . . . . . . $.F.b.7.L . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . b.* T.L.L.L.L.f.:.. . . . . . L.L.{.#.. . . . . . . . . u . . , , F.M.L.. . . . L.L.T.L.L.L.L.L.T.L.L.7.. . ' . L.T.W [.U.L.L.a.. . . L.L.s.' v ' a.l.` . . L.L., . $.l.l.2.. > . L.M.. . . . L.N.L.L.L.L.L.L.L.L.G.. . . ' M.M.;.. . . L.L.. . . . . . . . . . u . . . . . . . . . . . . . . . . . ++L.L.. . . &+L.L.;.X.T.L.L.2.. . ` L.L.a.. . $+L.L.. . . . . . . . . u . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . 3.:.{.7.f.T.L.L.L.L.. . . . L.L.{.. . . . . . . . . . . . . . . G.L.M.' , . . L.M.f.f.f.f.f.e.f.f.e.2.. > . . L.L.M . u F.M.M.{.. . L.M.u.' { ' n.l.` > . L.L.' Y &+l.7.. ' . . L.L.. . . . L.L.u.l.n.l.l.n.n.n.g., . . . L.L.... . Y L.L.. . . . . . . . . . . . . . . . . . . . . . . . . . . . ++L.L.. . . &+L.L.. . L.* L.L.$.. #.L.L.a.. . &+L.L.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . u :.:.. . . . L #.;.:.2.L.L.#.. . B.L.2.. . . . . . . . . . a.2.. . . F.L.T.. . . . M.P.%.#.#.Y Y Y #.#.Y #.. . . . T.M.Y . . . B.* M.G.. L.M.t.' { 4.n.n.. . . L.L.L.Y.l.9.. . ' . . M.L.. . . > L.L.3.....$.$.$...#.W . . . . L.L.$.. . :.L.L.2.. . . . . . . . . . a.:.. . . . . . . . . . . . . . . ++L.L.. . . &+L.L.. . . A.A.* L.T.W L.L.9.. . &+L.L.:.. . . . . . . . . . a.:.. . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . L.L.f.. . . . . . . . L.L.;.. . . L.L.A.. . . . . . . . L.L.e.{.. . * L.L.. . . . L.L.Y . . ' ' { ' , . > ' . . > M.M.M ' , ' , L g.M.L.M.N.t.{ : q.o.a.. ' . u.G.`.9.> ' > ' , . . M.T.. , , ' P.N.[.. . . . . . > . > . . . L.L.... . . L.L.L.:.. . . . . . . [.L.L.{.. . . . . . . . . . . . . . . ++L.L.. . . &+T.L.. . . . . f.e.L.L.L.L.A+. . L.F.L.L.A.. . . . . . . 2.L.L.. . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . {.L.L.L.n.:.$.#.}.F.L.L.[.{.. . . 2.L.L.L.s.:.W O }.L.L.L.[.{.u . . F.L.L.. . > . L.M.b.9.A+9.8.8.A+0.0.0.0., , . M.M.O , , , v {  .c.M.N.P.B.1 P v.q.p.' /.o.o.I.b.Y , ' , , . , ' M.O.] { , . L.M.[.. . . . . . . . . > . . L.L.$.. . . u.l.L.L.L.a.:.M #.:.L.L.L.2.. . . . . . . . . . . . . . . . ++L.L.. . . &+L.L.. . . . . u a.A+L.L.L.7.. . . A.f.L.L.L.7.:.W #.:.L.L.L.:.. . . . L.L.L.. . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . #.3.:.:.7.a.A+2.>.;.>.{.' . . . . L 7.:.}.7.a.A+2.>.>.{.;.. . . . >.}.(.. ' . . [.}.}.[.[.}.[.[.}.[.[.[.[., ' { _.4.P { { { _ : _ 1 0.0.|.4.Q J.- x.w.q.w.w.h.0.< { { ] _ 1 1 { v }.}.> . . . [.[.Y . . ' ' . ' . . ' > . . [.}.> > . . . . 8.2.:.[.7.a.A+2.>.{.{.. . . . . . . . . . . . . . . . . s.[.[.. . . &+:.:.. . . . . . . [.7.2.:.W . . . . :.2.2.:.[.7.a.7.2.{.;.` . . . . &+:.[.:.. . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . L O L > . . . . . . . . . . . . . L N L . . . > . > . . . . . . . > > . . . . . ' ' , ' ' ' ' v v { { _ _ 1 B C S T T T T +.+. at .=.=..+W.S.S.S.J.h.+.+.T S C 1 _ : _ { { ' { ] ] ' ' , . . > . ' . ' > > . . . . . . . . . . . . . . . . ' L N L . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L L L u . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . > > . > . . > > > > > > > > > > > > > > > > > > > > > > > > > > > > > . > > ' ' . ' ' > ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ] ] ] ! ] : 1 z D G I T T +.+.=.1.1.6.6.6.d.&+&+&+&+&+S.1.=.+.+.T S J Q D 2 x 1 ] ] ] ] ' ' ' ' ' ' ' ' ' ' ' . > . > > > > ' . ' > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > . . . . . . > > > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+"' ' ' ' ' ' ! ! ' ' ' ' ' ' ! ' ' ! ! ' ! ! ! ! ! ! ! ! ~ ~ ~ ~ ~ ~ ~ ~ ~ ( ; ; ; ( ; ; ( ( ( [ ( [ [ [ [ [ } } } } } | 4 4 4 4 b 4 E H H K R R V =. at .@.-.-.<.<.d.k.K.#+&+&+&+&+&+&+K.k.d.6.<.-.-. at .@.V V R U K H E E b | 4 4 4 } | | } } } } [ [ [ [ [ ( ( ( ( ( ( ( ( ; ; ; ; ~ ( ~ ~ : ~ ~ ~ ~ ! ! ! ! ! ! ! ! ! ! ' ' ' ! ' ' ' ! ! ' ' ! ' ' ' ' ' ' ' ' ' ' ' ' ' ' > ' > > > > > > > > > > . . > . . > . > . . . > > . . . . . . . . . . . . . . . ",
+"> > > > > . > > > > > > > > > > > > > > > > > > > > > > > > > > > > > . ' . > ' ' ' ' ' . ' ' ' ' ' ' ' ' ' ' ' ' ! ! ' ' ] ' ] ] ! ] ] ~ < z C A Q J T +.V @.=.1.6.6.6.d.k.k.k.d.d.6.6.1.-.+.+.+.T J Q D z y < : ~ ! ! ] ' ' ' ' ! ! ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' > ' . ' ' . > > > > > > > > . . > > > > > > > > > > > . > > > . > > > . > > > > > . . . . . . . . . . . . . . . . . . . > . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > . . . . . . . . ' , , ' , ' , ' ' v v ] { { 1 C F S Q Q S Q S +.S +.+.=.+.+.T +.S Q Q Q S S F B : ] { v v ' ' ' ' ' , ' , ' ' > . ' . ' . . . . > . . . . . . . . . . . . . > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > . . . . . > > ' > . . . > . . . > . > . ' . . > , , ' ' { 1 _ ] ] ' ' { { _ _ 1 : _ x 1 F Q F 2 1 1 1 1 x w { { v v v ] _ 1 _ ' . . ' . > > ' . > . > . . . . . . > ' > > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' ' > ' . . ' { { ' , , , , , , , ' ' { ' ' ' v { { 1 y _ { { v ' ' { { ' ' , , . . , ' , ' ] { ' , . > . ' . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > . . . . . . . . . , ' { ' . . . ' . . ' . . . ' , . , , ' > > > . , ' { _ { ' , ' > . ' ' ' , . . . . . > . > ' . > > . ' { ' , ' > > . . . . . . . > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > . , ' ' , . . . . > . . . . . > . . . . ' . . . . ' . ' . . ' ' v { v , ' ' . . . . ' . . ' . > . > > . . . . . . . > . . > , ' ' , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > ' , , ' > . . . . . . . . . > . . > . . . ' . . . . . . . . > . . . ' ' { , . . . > . . . . . . . . . ' > > . . . . . . . . . > . . . . > . , ' , > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > ' . . > . . . . . . . . . . . . . . . . . . . . . . . . . . . . > . . . . . ' , ' v ' > ' . > . . . . . > . . . . . > . . . . . . . . . . . . . . . . . . . . . > , , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > , > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' , ' , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' , ' , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . > > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "};

Added: packages/openev/branches/upstream/current/pics/busy.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/busy.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/busy.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char *busy {
+/* width height num_colors chars_per_pixel */
+"    22    18        2            1",
+/* colors */
+"a c None",
+". c #ff0000",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaa.aaaaaaaaaaaaaa.aaa",
+"aaaa.aaaaaaaaaaaa.aaaa",
+"aaaaa.aaaaaaaaaa.aaaaa",
+"aaaaaa.aa....aa.aaaaaa",
+"aaaaaaa.......aaaaaaaa",
+"aaaaaa..........aaaaaa",
+"aa..................aa",
+"aaaaaa..........aaaaaa",
+"aaaaaaa........aaaaaaa",
+"aaaaaa.aa....aa.aaaaaa",
+"aaaaa.aaaaaaaaaa.aaaaa",
+"aaaa.aaaaaaaaaaaa.aaaa",
+"aaa.aaaaaaaaaaaaaa.aaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/ck_off_l.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/ck_off_l.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/ck_off_l.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,30 @@
+/* XPM */
+static char *ck_on_l_2[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18      4            2",
+/* colors */
+"`` c #CCCCCC",
+" . c #EEEEEE",
+" , c #444444",
+"`. c None",
+
+/* pixels */
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.```.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.``` ..`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.` ,.`.`.````` ..`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.``` ,.````` ..`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.````````` ..`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.````` ..`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.` ..`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`."
+};
\ No newline at end of file

Added: packages/openev/branches/upstream/current/pics/ck_on_l.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/ck_on_l.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/ck_on_l.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,28 @@
+/* XPM */
+static char *ck_on_l_2[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18      2            2",
+/* colors */
+"`` c #000000",
+"`. c None",
+
+/* pixels */
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.```.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`````.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.```.`.`.```````.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`````.```````.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.```````````.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.```````.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.```.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.",
+"`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`."
+};

Added: packages/openev/branches/upstream/current/pics/classify.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/classify.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/classify.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char * classify_xpm[] = {
+"22 18 6 1",
+" 	c None",
+".	c #000000",
+"+	c #0000FF",
+"@	c #FFFCFC",
+"#	c #FF0000",
+"$	c #008000",
+"                      ",
+"   ................   ",
+"   .++++++++++++++.   ",
+"   ................   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   .@@@@@.....@@@@.   ",
+"   .@@@@.@@@@@.@@@.   ",
+"   .@@@.@@@@@@@@@@.   ",
+"   .@@@.@@@@@@@@@@.   ",
+"   .@@@.@@@@@@@@@@.   ",
+"   .@@@.@@@@@@@@@@.   ",
+"   .@@@.@@@@@@@@@@.   ",
+"   .@@@@.@@@@@.@@@.   ",
+"   .@@@@@.....@@@@.   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   ................   ",
+"                      "};

Added: packages/openev/branches/upstream/current/pics/delete.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/delete.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/delete.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,22 @@
+/* XPM */
+static char * delete_xpm[] = {
+"16 16 3 1",
+" 	c None",
+".	c #000000",
+"+	c #7F7F7F",
+"                ",
+"                ",
+"   .+       .+  ",
+"  ...+     ...+ ",
+"   ...+   ...+  ",
+"    ...+ ...+   ",
+"     ......+    ",
+"      ....+     ",
+"      ....+     ",
+"     ......+    ",
+"    ...++...+   ",
+"   ...+   ...+  ",
+"  ...+     ...+ ",
+"   .+       .+  ",
+"                ",
+"                "};

Added: packages/openev/branches/upstream/current/pics/equalize.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/equalize.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/equalize.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,31 @@
+/* XPM */
+static char *bmp854[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaa.................aa",
+"aaa.#############...aa",
+"aaa.aaaaaaaaaaa.#a#.aa",
+"aaa.aaaaaaaaaa.#aa#.aa",
+"aaa.aaaaaaaaa.#aaa#.aa",
+"aaa.aaaaaaaa.#aaaa#.aa",
+"aaa.aaaaaaa.#aaaaa#.aa",
+"aaa.aaaaaaa.#aaaaa#.aa",
+"aaa.aaaaaa.#aaaaaa#.aa",
+"aaa.aaaaaa.#aaaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa...#aaaaaaaaaaa#.aa",
+"aaa.................aa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};
+
+

Added: packages/openev/branches/upstream/current/pics/eye.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/eye.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/eye.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char * eye_xpm[] = {
+"21 21 3 1",
+" 	c None",
+".	c #000000",
+"+	c #FFFFFF",
+"                     ",
+"                     ",
+"                     ",
+"                     ",
+"       .......       ",
+"     ...........     ",
+"   ...............   ",
+"  .................  ",
+" ....++....+..++.... ",
+"....++.........+++...",
+"..++++.........+++++.",
+"..++++.........++++..",
+" ...+++.......++++.. ",
+"  ...++.......++...  ",
+"   ....+.....+....   ",
+"     ...........     ",
+"       .......       ",
+"                     ",
+"                     ",
+"                     ",
+"                     "};

Added: packages/openev/branches/upstream/current/pics/geo_innovation.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/geo_innovation.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/geo_innovation.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2015 @@
+/* XPM */
+static char * geo_innovation_xpm[] = {
+"113 80 1932 2",
+"  	c None",
+". 	c #FFFFFF",
+"+ 	c #FEFEFE",
+"@ 	c #FDFDFE",
+"# 	c #FDFDFD",
+"$ 	c #FEFEFF",
+"% 	c #FAFAFB",
+"& 	c #EFEFF1",
+"* 	c #E1E0E5",
+"= 	c #D3D1DC",
+"- 	c #C8C5D3",
+"; 	c #BCB9CB",
+"> 	c #B3B1C4",
+", 	c #ADAABF",
+"' 	c #A9A6BB",
+") 	c #A8A5BB",
+"! 	c #AAA6BC",
+"~ 	c #B3B0C3",
+"{ 	c #BBB9CA",
+"] 	c #C6C4D2",
+"^ 	c #D2D0DB",
+"/ 	c #DEDCE4",
+"( 	c #EDEDF0",
+"_ 	c #F9F9F9",
+": 	c #FDFDFF",
+"< 	c #FCFCFD",
+"[ 	c #F9F8FA",
+"} 	c #E6E4EB",
+"| 	c #CAC8D5",
+"1 	c #A9A8BD",
+"2 	c #8C8AA6",
+"3 	c #737192",
+"4 	c #626285",
+"5 	c #57567E",
+"6 	c #515079",
+"7 	c #4A4B74",
+"8 	c #454671",
+"9 	c #40426D",
+"0 	c #3F416C",
+"a 	c #3E3F6B",
+"b 	c #3D3F6B",
+"c 	c #3E3F6A",
+"d 	c #41436E",
+"e 	c #464671",
+"f 	c #505078",
+"g 	c #57577E",
+"h 	c #626186",
+"i 	c #6F6D91",
+"j 	c #8583A2",
+"k 	c #A19FB6",
+"l 	c #C0BECE",
+"m 	c #DCDBE2",
+"n 	c #F4F3F5",
+"o 	c #FCFCFC",
+"p 	c #FDFDFC",
+"q 	c #FBFCFB",
+"r 	c #EDECF0",
+"s 	c #CAC8D7",
+"t 	c #9D99B2",
+"u 	c #747293",
+"v 	c #5A597F",
+"w 	c #3C3E69",
+"x 	c #333762",
+"y 	c #2D305F",
+"z 	c #272C5A",
+"A 	c #242958",
+"B 	c #212756",
+"C 	c #1F2554",
+"D 	c #1E2453",
+"E 	c #1C2251",
+"F 	c #1D2351",
+"G 	c #1C2351",
+"H 	c #1D2452",
+"I 	c #202554",
+"J 	c #232957",
+"K 	c #272C59",
+"L 	c #2C2F5E",
+"M 	c #323562",
+"N 	c #393B67",
+"O 	c #44446F",
+"P 	c #616084",
+"Q 	c #8583A1",
+"R 	c #DCDBE4",
+"S 	c #F9F9FA",
+"T 	c #FEFDFE",
+"U 	c #FBFBFC",
+"V 	c #E1E0E7",
+"W 	c #ADAABD",
+"X 	c #787596",
+"Y 	c #58577F",
+"Z 	c #43446E",
+"` 	c #333562",
+" .	c #282C5B",
+"..	c #212755",
+"+.	c #1D2352",
+"@.	c #1A214F",
+"#.	c #191F4E",
+"$.	c #191D4D",
+"%.	c #191C4B",
+"&.	c #181B4A",
+"*.	c #191B4A",
+"=.	c #191C4A",
+"-.	c #181C4A",
+";.	c #181D4B",
+">.	c #191D4C",
+",.	c #181C4C",
+"'.	c #191D4B",
+").	c #1B204E",
+"!.	c #1C2150",
+"~.	c #1E2352",
+"{.	c #212654",
+"].	c #242856",
+"^.	c #292D5D",
+"/.	c #313561",
+"(.	c #3D3E6B",
+"_.	c #4E4E77",
+":.	c #7B7A98",
+"<.	c #BCBCCA",
+"[.	c #F2F2F5",
+"}.	c #FDFCFC",
+"|.	c #F3F1F4",
+"1.	c #C3C1D1",
+"2.	c #8885A3",
+"3.	c #5D5C83",
+"4.	c #42436E",
+"5.	c #2F3261",
+"6.	c #252A59",
+"7.	c #1E2452",
+"8.	c #1B214F",
+"9.	c #191E4E",
+"0.	c #181D4C",
+"a.	c #181C4B",
+"b.	c #181B4B",
+"c.	c #191B4B",
+"d.	c #181B49",
+"e.	c #272A54",
+"f.	c #484B70",
+"g.	c #6D6F8D",
+"h.	c #8B8CA6",
+"i.	c #A3A4B9",
+"j.	c #B9BAC8",
+"k.	c #CDCCD8",
+"l.	c #D8D8E1",
+"m.	c #E2E2E9",
+"n.	c #E5E6ED",
+"o.	c #E7E7EE",
+"p.	c #E6E6EC",
+"q.	c #E3E3EA",
+"r.	c #D8D9E0",
+"s.	c #CCCCD7",
+"t.	c #BCBCCB",
+"u.	c #AAABBF",
+"v.	c #9696AF",
+"w.	c #7E7F9C",
+"x.	c #706F90",
+"y.	c #9290AA",
+"z.	c #D9D9E1",
+"A.	c #FCFDFD",
+"B.	c #EFEEF2",
+"C.	c #BBB8CB",
+"D.	c #4E4F78",
+"E.	c #363966",
+"F.	c #1A204F",
+"G.	c #191E4D",
+"H.	c #181A4A",
+"I.	c #1A1D4B",
+"J.	c #3B3E66",
+"K.	c #717391",
+"L.	c #A7A9BB",
+"M.	c #D7D7DF",
+"N.	c #F1F0F4",
+"O.	c #F4F1F5",
+"P.	c #EFEDF2",
+"Q.	c #ECE9F0",
+"R.	c #E8E6EE",
+"S.	c #E5E2EB",
+"T.	c #E2DFE8",
+"U.	c #DFDCE5",
+"V.	c #DEDBE4",
+"W.	c #DDD9E4",
+"X.	c #DCD9E4",
+"Y.	c #DDDAE3",
+"Z.	c #E1DEE7",
+"`.	c #E4E2EA",
+" +	c #E8E6ED",
+".+	c #EBE9EF",
+"++	c #EEECF1",
+"@+	c #F3F2F5",
+"#+	c #F2F1F4",
+"$+	c #E8E8EC",
+"%+	c #FCFCFE",
+"&+	c #ECECEF",
+"*+	c #AEABBF",
+"=+	c #6E6C90",
+"-+	c #4A4B75",
+";+	c #303460",
+">+	c #232755",
+",+	c #1B2150",
+"'+	c #191F4D",
+")+	c #2C2F5B",
+"!+	c #56597A",
+"~+	c #A7A8BC",
+"{+	c #DCDCE3",
+"]+	c #F2F0F5",
+"^+	c #EDEBF1",
+"/+	c #E5E3EC",
+"(+	c #DDDAE5",
+"_+	c #D5D1DD",
+":+	c #CCC9D7",
+"<+	c #C3BFD0",
+"[+	c #BAB5C9",
+"}+	c #B3AEC4",
+"|+	c #ADA8C0",
+"1+	c #ACA6BF",
+"2+	c #A49FB7",
+"3+	c #9E99B3",
+"4+	c #9D98B3",
+"5+	c #9E98B3",
+"6+	c #9F9AB4",
+"7+	c #A29DB5",
+"8+	c #A6A1BA",
+"9+	c #ACA7BF",
+"0+	c #BBB6CA",
+"a+	c #C3BFD1",
+"b+	c #CCC8D6",
+"c+	c #D5D1DE",
+"d+	c #DEDBE6",
+"e+	c #E6E4EC",
+"f+	c #EDEBF2",
+"g+	c #F3F1F6",
+"h+	c #F7F7F8",
+"i+	c #B8B5C7",
+"j+	c #706D90",
+"k+	c #44456F",
+"l+	c #2B2F5D",
+"m+	c #1A204E",
+"n+	c #1C204D",
+"o+	c #5C5F7F",
+"p+	c #B9BBCA",
+"q+	c #F1EFF4",
+"r+	c #DAD7E3",
+"s+	c #BEB9CC",
+"t+	c #AEA8C0",
+"u+	c #9F99B5",
+"v+	c #908BA9",
+"w+	c #84809F",
+"x+	c #6F6B90",
+"y+	c #676489",
+"z+	c #605E84",
+"A+	c #5C5A80",
+"B+	c #706D92",
+"C+	c #59597F",
+"D+	c #52527B",
+"E+	c #52527A",
+"F+	c #53547B",
+"G+	c #56567D",
+"H+	c #5B5980",
+"I+	c #666489",
+"J+	c #6E6B8F",
+"K+	c #837F9F",
+"L+	c #A09AB4",
+"M+	c #AEA9BF",
+"N+	c #BCB8CA",
+"O+	c #CBC7D7",
+"P+	c #DBD8E2",
+"Q+	c #EEEBF1",
+"R+	c #F5F4F8",
+"S+	c #FBFAFD",
+"T+	c #D2CFDA",
+"U+	c #83809F",
+"V+	c #515078",
+"W+	c #202655",
+"X+	c #272A57",
+"Y+	c #80829B",
+"Z+	c #DAD9E1",
+"`+	c #F1F0F3",
+" @	c #D9D7E1",
+".@	c #C9C5D5",
+"+@	c #B7B3C6",
+"@@	c #A19CB5",
+"#@	c #8C87A5",
+"$@	c #797697",
+"%@	c #69678B",
+"&@	c #4F4F77",
+"*@	c #464772",
+"=@	c #3E406B",
+"-@	c #343764",
+";@	c #323462",
+">@	c #41426F",
+",@	c #303260",
+"'@	c #3F416D",
+")@	c #292C5C",
+"!@	c #2A2D5C",
+"~@	c #2B2E5D",
+"{@	c #2C2F5D",
+"]@	c #2E315F",
+"^@	c #303361",
+"/@	c #333663",
+"(@	c #383B67",
+"_@	c #3E406C",
+":@	c #454771",
+"<@	c #69678A",
+"[@	c #8A86A3",
+"}@	c #A19CB6",
+"|@	c #B9B5C9",
+"1@	c #D8D5E1",
+"2@	c #E8E5ED",
+"3@	c #F1EFF3",
+"4@	c #F8F7FA",
+"5@	c #F5F4F6",
+"6@	c #ADAAC0",
+"7@	c #5F5E84",
+"8@	c #383C67",
+"9@	c #191B49",
+"0@	c #222551",
+"a@	c #888AA1",
+"b@	c #E8E7EC",
+"c@	c #EEECF0",
+"d@	c #E1DDE6",
+"e@	c #CECAD7",
+"f@	c #B5B0C6",
+"g@	c #8682A1",
+"h@	c #777496",
+"i@	c #5A5981",
+"j@	c #4A4C75",
+"k@	c #363766",
+"l@	c #292C5A",
+"m@	c #252958",
+"n@	c #222755",
+"o@	c #1E2653",
+"p@	c #1F2555",
+"q@	c #2F3460",
+"r@	c #2D315F",
+"s@	c #1B2250",
+"t@	c #1C2250",
+"u@	c #1C2352",
+"v@	c #1F2553",
+"w@	c #2F3160",
+"x@	c #353866",
+"y@	c #55557E",
+"z@	c #656489",
+"A@	c #6F6D90",
+"B@	c #9D96B2",
+"C@	c #B7B2C7",
+"D@	c #CDC9D6",
+"E@	c #DFDDE6",
+"F@	c #EDEBEF",
+"G@	c #F7F5F8",
+"H@	c #FCFBFD",
+"I@	c #D9D8E1",
+"J@	c #8988A4",
+"K@	c #272A56",
+"L@	c #7F809A",
+"M@	c #ECEAF0",
+"N@	c #DAD8E2",
+"O@	c #C5C2D2",
+"P@	c #ABA6BE",
+"Q@	c #8F8AA8",
+"R@	c #726E92",
+"S@	c #5B5981",
+"T@	c #55547D",
+"U@	c #3C3E6A",
+"V@	c #2F3260",
+"W@	c #222756",
+"X@	c #1F2453",
+"Y@	c #1A214E",
+"Z@	c #1E2351",
+"`@	c #282C59",
+" #	c #1B1F4E",
+".#	c #202352",
+"+#	c #232553",
+"@#	c #1A1F4D",
+"##	c #19204F",
+"$#	c #2C305D",
+"%#	c #2F3362",
+"&#	c #2D315E",
+"*#	c #393B68",
+"=#	c #464871",
+"-#	c #5A5880",
+";#	c #737094",
+">#	c #8E89A7",
+",#	c #A9A3BD",
+"'#	c #C4C0D1",
+")#	c #DBD8E3",
+"!#	c #ECEAEF",
+"~#	c #F6F5F6",
+"{#	c #FBFBFD",
+"]#	c #FDFEFE",
+"^#	c #BEBBCB",
+"/#	c #6A678B",
+"(#	c #3B3D69",
+"_#	c #232857",
+":#	c #191F4F",
+"<#	c #191C49",
+"[#	c #525378",
+"}#	c #DCDDE3",
+"|#	c #DDDBE4",
+"1#	c #A19DB6",
+"2#	c #827E9E",
+"3#	c #656387",
+"4#	c #4D4C76",
+"5#	c #393B66",
+"6#	c #363864",
+"7#	c #1A2050",
+"8#	c #181E4D",
+"9#	c #202351",
+"0#	c #292B5A",
+"a#	c #1D204D",
+"b#	c #222554",
+"c#	c #292C59",
+"d#	c #1C224F",
+"e#	c #393C68",
+"f#	c #4B4B75",
+"g#	c #636187",
+"h#	c #807D9D",
+"i#	c #A39EB7",
+"j#	c #C1BDCF",
+"k#	c #DAD8E1",
+"l#	c #F8F8FA",
+"m#	c #B9B6C8",
+"n#	c #5D5C81",
+"o#	c #232753",
+"p#	c #9C9DB1",
+"q#	c #F2F1F3",
+"r#	c #E2DFE9",
+"s#	c #CAC6D5",
+"t#	c #ADA9BE",
+"u#	c #8783A1",
+"v#	c #636087",
+"w#	c #494A73",
+"x#	c #373966",
+"y#	c #2A2E5D",
+"z#	c #232856",
+"A#	c #2C305E",
+"B#	c #19204E",
+"C#	c #1E2250",
+"D#	c #252856",
+"E#	c #2C2E5D",
+"F#	c #181C49",
+"G#	c #1F2350",
+"H#	c #2A2C5A",
+"I#	c #202451",
+"J#	c #292E5C",
+"K#	c #353865",
+"L#	c #484973",
+"M#	c #646288",
+"N#	c #847FA0",
+"O#	c #A7A3BA",
+"P#	c #E3E0E9",
+"Q#	c #F2F1F6",
+"R#	c #FAFAFC",
+"S#	c #F8F8F9",
+"T#	c #AEABC0",
+"U#	c #5D5B81",
+"V#	c #2E315E",
+"W#	c #3F426A",
+"X#	c #D2D2DB",
+"Y#	c #EDEBF0",
+"Z#	c #D9D6E2",
+"`#	c #B6B1C7",
+" $	c #9390AC",
+".$	c #6F6D8E",
+"+$	c #363866",
+"@$	c #282D5A",
+"#$	c #202653",
+"$$	c #1A1E4E",
+"%$	c #212351",
+"&$	c #262857",
+"*$	c #252756",
+"=$	c #1C1F4D",
+"-$	c #222553",
+";$	c #1A2250",
+">$	c #1F2653",
+",$	c #282C5A",
+"'$	c #4B4C75",
+")$	c #68678B",
+"!$	c #8F8BA8",
+"~$	c #B8B3C7",
+"{$	c #D7D4E0",
+"]$	c #ECEAF1",
+"^$	c #F4F3F7",
+"/$	c #9F9DB3",
+"($	c #53537C",
+"_$	c #2B2F5E",
+":$	c #686B89",
+"<$	c #E1E1E7",
+"[$	c #E4E1EA",
+"}$	c #A29EB6",
+"|$	c #747093",
+"1$	c #5A5A81",
+"2$	c #292D5C",
+"3$	c #191F4C",
+"4$	c #1F2451",
+"5$	c #1E214F",
+"6$	c #212452",
+"7$	c #292B59",
+"8$	c #1E2150",
+"9$	c #232653",
+"0$	c #292C5B",
+"a$	c #373967",
+"b$	c #757294",
+"c$	c #9D99B3",
+"d$	c #C6C3D2",
+"e$	c #E3E1E9",
+"f$	c #F5F4F7",
+"g$	c #FBFBFB",
+"h$	c #A3A0B7",
+"i$	c #50507A",
+"j$	c #8487A0",
+"k$	c #DEDAE4",
+"l$	c #B9B5C8",
+"m$	c #8E8BA7",
+"n$	c #3B3D6B",
+"o$	c #222856",
+"p$	c #242855",
+"q$	c #252857",
+"r$	c #1D214E",
+"s$	c #1C1F4E",
+"t$	c #343564",
+"u$	c #232654",
+"v$	c #414370",
+"w$	c #616085",
+"x$	c #908BA8",
+"y$	c #BCB7CB",
+"z$	c #DDDAE4",
+"A$	c #F2F0F4",
+"B$	c #BFBDCD",
+"C$	c #58577E",
+"D$	c #9495AB",
+"E$	c #DBD9E3",
+"F$	c #B3AFC3",
+"G$	c #837E9E",
+"H$	c #58587F",
+"I$	c #3A3C69",
+"J$	c #2E3160",
+"K$	c #212553",
+"L$	c #222655",
+"M$	c #1A1D4A",
+"N$	c #262956",
+"O$	c #1A1C4C",
+"P$	c #272A58",
+"Q$	c #272A59",
+"R$	c #383A67",
+"S$	c #57567C",
+"T$	c #8B86A5",
+"U$	c #D9D5E1",
+"V$	c #F0EFF3",
+"W$	c #D5D3DD",
+"X$	c #6A698C",
+"Y$	c #303360",
+"Z$	c #1B2151",
+"`$	c #82839C",
+" %	c #F0EFF2",
+".%	c #D9D6E0",
+"+%	c #B2AEC2",
+"@%	c #7F7A9B",
+"#%	c #343664",
+"$%	c #222757",
+"%%	c #202453",
+"&%	c #222654",
+"*%	c #282B59",
+"=%	c #1B1E4C",
+"-%	c #1C204E",
+";%	c #262A58",
+">%	c #5C5A81",
+",%	c #7D7899",
+"'%	c #D6D2DD",
+")%	c #F1F0F5",
+"!%	c #FDFCFE",
+"~%	c #E2E1EA",
+"{%	c #777595",
+"]%	c #757693",
+"^%	c #EBEAEF",
+"/%	c #D6D3DE",
+"(%	c #AAA5BE",
+"_%	c #4B4B74",
+":%	c #202654",
+"<%	c #202552",
+"[%	c #202553",
+"}%	c #1B1E4D",
+"|%	c #262856",
+"1%	c #1A1E4D",
+"2%	c #1D2150",
+"3%	c #2A2D5A",
+"4%	c #41426C",
+"5%	c #535279",
+"6%	c #7D799A",
+"7%	c #A9A4BD",
+"8%	c #F6F6F8",
+"9%	c #918EAA",
+"0%	c #42456E",
+"a%	c #545879",
+"b%	c #EFEFF2",
+"c%	c #DAD7E2",
+"d%	c #ACA7BE",
+"e%	c #767496",
+"f%	c #4C4C76",
+"g%	c #2D2F5F",
+"h%	c #1F2352",
+"i%	c #202452",
+"j%	c #1A1E4C",
+"k%	c #1B1D4D",
+"l%	c #242755",
+"m%	c #292B58",
+"n%	c #1D204E",
+"o%	c #252855",
+"p%	c #191D4A",
+"q%	c #262B58",
+"r%	c #282D59",
+"s%	c #383C69",
+"t%	c #454770",
+"u%	c #716E91",
+"v%	c #ACA8BE",
+"w%	c #F2F1F5",
+"x%	c #C4C1D1",
+"y%	c #2F325C",
+"z%	c #E2E2E8",
+"A%	c #E3E0E8",
+"B%	c #7D789A",
+"C%	c #1B204F",
+"D%	c #1A1E4B",
+"E%	c #212554",
+"F%	c #1D214F",
+"G%	c #171C4A",
+"H%	c #2A2D5B",
+"I%	c #1F2250",
+"J%	c #272957",
+"K%	c #1F2452",
+"L%	c #2B305E",
+"M%	c #484972",
+"N%	c #7E799A",
+"O%	c #B6B1C5",
+"P%	c #DFDDE5",
+"Q%	c #F6F5F8",
+"R%	c #F3F2F6",
+"S%	c #7F7C9D",
+"T%	c #353766",
+"U%	c #B9B9C7",
+"V%	c #EAE7ED",
+"W%	c #C5C0D2",
+"X%	c #8A85A5",
+"Y%	c #525279",
+"Z%	c #1C204F",
+"`%	c #272B5A",
+" &	c #1C1E4D",
+".&	c #272B58",
+"+&	c #212451",
+"@&	c #1D1F4E",
+"#&	c #1E204E",
+"$&	c #282A58",
+"%&	c #282C58",
+"&&	c #232655",
+"*&	c #212352",
+"=&	c #1A1C4A",
+"-&	c #52537B",
+";&	c #8884A4",
+">&	c #C0BCCE",
+",&	c #E7E5EC",
+"'&	c #FAF9FC",
+")&	c #FCFDFC",
+"!&	c #B7B4C6",
+"~&	c #4D4C75",
+"{&	c #626685",
+"]&	c #F0EEF3",
+"^&	c #D0CCDA",
+"/&	c #9995B0",
+"(&	c #5C5A82",
+"_&	c #1A2150",
+":&	c #282B58",
+"<&	c #1A1C4B",
+"[&	c #1C1E4C",
+"}&	c #2B2E5C",
+"|&	c #333664",
+"1&	c #938FAB",
+"2&	c #CBC8D7",
+"3&	c #EEEDF1",
+"4&	c #7A7697",
+"5&	c #D1D2DC",
+"6&	c #AEAAC1",
+"7&	c #3E3F6C",
+"8&	c #232858",
+"9&	c #181E4C",
+"0&	c #1A1D4C",
+"a&	c #1B1F4F",
+"b&	c #1F2351",
+"c&	c #242756",
+"d&	c #262957",
+"e&	c #1D204F",
+"f&	c #3D3F6A",
+"g&	c #6D698E",
+"h&	c #A9A6BD",
+"i&	c #E0DEE7",
+"j&	c #BFBCCC",
+"k&	c #53527A",
+"l&	c #6E718F",
+"m&	c #EFECF3",
+"n&	c #C6C2D3",
+"o&	c #8985A5",
+"p&	c #505079",
+"q&	c #212450",
+"r&	c #282B57",
+"s&	c #1E2151",
+"t&	c #1C1E4E",
+"u&	c #181F4D",
+"v&	c #84819F",
+"w&	c #C8C4D5",
+"x&	c #F7F6F9",
+"y&	c #8A86A4",
+"z&	c #3A3C68",
+"A&	c #A7A3BB",
+"B&	c #67658A",
+"C&	c #393A67",
+"D&	c #191C4C",
+"E&	c #191E4C",
+"F&	c #1E224F",
+"G&	c #262A59",
+"H&	c #222453",
+"I&	c #363765",
+"J&	c #ABA6BD",
+"K&	c #DEDBE5",
+"L&	c #D8D6E0",
+"M&	c #615F85",
+"N&	c #616384",
+"O&	c #F0EEF2",
+"P&	c #C9C6D4",
+"Q&	c #8581A1",
+"R&	c #4A4A73",
+"S&	c #1E2251",
+"T&	c #222452",
+"U&	c #252956",
+"V&	c #8985A3",
+"W&	c #C7C3D4",
+"X&	c #EEECF2",
+"Y&	c #B1AEC1",
+"Z&	c #494972",
+"`&	c #B2B2C2",
+" *	c #E6E3EB",
+".*	c #69688C",
+"+*	c #202755",
+"@*	c #202251",
+"#*	c #262A57",
+"$*	c #2A2C5B",
+"%*	c #363865",
+"&*	c #6C6B8E",
+"**	c #B0ACC1",
+"=*	c #E2E0E8",
+"-*	c #FBFAFC",
+";*	c #242753",
+">*	c #EAE9EE",
+",*	c #D8D5E0",
+"'*	c #9C98B2",
+")*	c #53547C",
+"!*	c #2D305E",
+"~*	c #212653",
+"{*	c #363F68",
+"]*	c #323A64",
+"^*	c #1B1F4D",
+"/*	c #1B1E4E",
+"(*	c #181B4C",
+"_*	c #31335D",
+":*	c #4E5074",
+"<*	c #383A63",
+"[*	c #1E204F",
+"}*	c #1F224F",
+"|*	c #7B7D98",
+"1*	c #353860",
+"2*	c #9894AF",
+"3*	c #D4D1DE",
+"4*	c #EBEAEE",
+"5*	c #6F6C8F",
+"6*	c #585B7D",
+"7*	c #F1EEF2",
+"8*	c #8681A1",
+"9*	c #434470",
+"0*	c #272F5A",
+"a*	c #7C95AC",
+"b*	c #B1D5E3",
+"c*	c #A3C5D6",
+"d*	c #ACCFDD",
+"e*	c #444F75",
+"f*	c #363862",
+"g*	c #B3B5C2",
+"h*	c #F1F2F3",
+"i*	c #D5D6DE",
+"j*	c #BFC0CC",
+"k*	c #1E224E",
+"l*	c #232754",
+"m*	c #1F2251",
+"n*	c #272B59",
+"o*	c #333761",
+"p*	c #8C8FA6",
+"q*	c #CDCFD7",
+"r*	c #4E5274",
+"s*	c #19204D",
+"t*	c #817E9E",
+"u*	c #EEEDF2",
+"v*	c #DDDCE4",
+"w*	c #5D5C82",
+"x*	c #252B59",
+"y*	c #191A4A",
+"z*	c #8B8EA5",
+"A*	c #E9E7ED",
+"B*	c #BAB5CA",
+"C*	c #737092",
+"D*	c #7A93AB",
+"E*	c #A9CADA",
+"F*	c #38436A",
+"G*	c #1B204D",
+"H*	c #637796",
+"I*	c #7992AA",
+"J*	c #3C486E",
+"K*	c #354067",
+"L*	c #7790A8",
+"M*	c #7189A4",
+"N*	c #2A325D",
+"O*	c #1B1E4B",
+"P*	c #C9CBD4",
+"Q*	c #D8D9DF",
+"R*	c #343862",
+"S*	c #8486A0",
+"T*	c #A4A6B7",
+"U*	c #5F6283",
+"V*	c #535679",
+"W*	c #71728D",
+"X*	c #8789A0",
+"Y*	c #9D9FB1",
+"Z*	c #2A2D58",
+"`*	c #4E5174",
+" =	c #82859C",
+".=	c #727490",
+"+=	c #9FA2B3",
+"@=	c #2F325B",
+"#=	c #6A6D8C",
+"$=	c #6A6C8A",
+"%=	c #898CA3",
+"&=	c #A7A8BA",
+"*=	c #4A4D72",
+"==	c #B8BBC7",
+"-=	c #EEEEF0",
+";=	c #7A7C96",
+">=	c #9698AB",
+",=	c #272C57",
+"'=	c #3E426A",
+")=	c #999CAE",
+"!=	c #9C9FB1",
+"~=	c #333660",
+"{=	c #1B1D4C",
+"]=	c #9092A6",
+"^=	c #686988",
+"/=	c #A1A4B5",
+"(=	c #676989",
+"_=	c #585A7C",
+":=	c #A4A7B9",
+"<=	c #8B8DA4",
+"[=	c #1E2553",
+"}=	c #6F6C90",
+"|=	c #B4AFC5",
+"1=	c #FCFBFC",
+"2=	c #D2D1DC",
+"3=	c #51517A",
+"4=	c #B2B2C3",
+"5=	c #AFA9C2",
+"6=	c #656589",
+"7=	c #313362",
+"8=	c #1E2552",
+"9=	c #B1D5E1",
+"0=	c #5D6F8E",
+"a=	c #6D819F",
+"b=	c #6D849F",
+"c=	c #2D355F",
+"d=	c #8CA9BE",
+"e=	c #8EACC0",
+"f=	c #86A2B9",
+"g=	c #9EBFCF",
+"h=	c #343D67",
+"i=	c #AFD2E0",
+"j=	c #7187A3",
+"k=	c #92B0C3",
+"l=	c #FCFAF9",
+"m=	c #6B6F8C",
+"n=	c #B7B9C7",
+"o=	c #DADBE1",
+"p=	c #A6A7B9",
+"q=	c #F8F9F9",
+"r=	c #42466C",
+"s=	c #A0A1B4",
+"t=	c #EFF0F1",
+"u=	c #A2A4B5",
+"v=	c #F9FAF9",
+"w=	c #737593",
+"x=	c #9597AB",
+"y=	c #F7F8F7",
+"z=	c #F0F0F3",
+"A=	c #7D7E9A",
+"B=	c #878AA1",
+"C=	c #DDDDE2",
+"D=	c #989AAE",
+"E=	c #353964",
+"F=	c #CCCED7",
+"G=	c #D6D7DE",
+"H=	c #9092A7",
+"I=	c #41446A",
+"J=	c #D5D7DD",
+"K=	c #CECFD7",
+"L=	c #6B6C8A",
+"M=	c #777997",
+"N=	c #F4F4F6",
+"O=	c #44486E",
+"P=	c #EFF0F2",
+"Q=	c #A9AABB",
+"R=	c #C4C6CF",
+"S=	c #C7C8D4",
+"T=	c #30345D",
+"U=	c #CBCDD6",
+"V=	c #D0D2D8",
+"W=	c #E4E4EB",
+"X=	c #2C2F5A",
+"Y=	c #F2F4F3",
+"Z=	c #8E91A7",
+"`=	c #6E718D",
+" -	c #323563",
+".-	c #626187",
+"+-	c #A7A1BC",
+"@-	c #E0DEE8",
+"#-	c #C5C3D3",
+"$-	c #CECDD9",
+"%-	c #DFDCE6",
+"&-	c #A59FB9",
+"*-	c #5C5B81",
+"=-	c #2C3560",
+"--	c #B7DCE6",
+";-	c #46537A",
+">-	c #222955",
+",-	c #839EB6",
+"'-	c #B6DDE7",
+")-	c #48567A",
+"!-	c #B7DDE8",
+"~-	c #90AFC1",
+"{-	c #A9CCDA",
+"]-	c #819CB4",
+"^-	c #768EA7",
+"/-	c #A1C1D0",
+"(-	c #6F87A3",
+"_-	c #AACDDC",
+":-	c #474C6F",
+"<-	c #505376",
+"[-	c #42456A",
+"}-	c #636787",
+"|-	c #383C65",
+"1-	c #FCFBFA",
+"2-	c #646785",
+"3-	c #CACBD5",
+"4-	c #9397AC",
+"5-	c #2F325E",
+"6-	c #F9FAF8",
+"7-	c #636685",
+"8-	c #BFC0CD",
+"9-	c #C0C2CD",
+"0-	c #E1E2E6",
+"a-	c #757893",
+"b-	c #ECECF0",
+"c-	c #D5D6DD",
+"d-	c #D3D5DD",
+"e-	c #DBDCE2",
+"f-	c #6F718E",
+"g-	c #FAFCFA",
+"h-	c #474970",
+"i-	c #202250",
+"j-	c #CCCCD6",
+"k-	c #797B97",
+"l-	c #181D4A",
+"m-	c #A3A4B7",
+"n-	c #CED0D7",
+"o-	c #9D9EB2",
+"p-	c #E0E2E5",
+"q-	c #71738E",
+"r-	c #585C7C",
+"s-	c #464A6F",
+"t-	c #9FA2B4",
+"u-	c #282B56",
+"v-	c #E6E7EB",
+"w-	c #CACAD5",
+"x-	c #3B4066",
+"y-	c #59587E",
+"z-	c #9D97B2",
+"A-	c #F9F8FB",
+"B-	c #BAB8C9",
+"C-	c #E1E1E8",
+"D-	c #1D2350",
+"E-	c #A6C8D8",
+"F-	c #8BA6BD",
+"G-	c #333D67",
+"H-	c #5F7192",
+"I-	c #A2C3D5",
+"J-	c #455378",
+"K-	c #B7DDE9",
+"L-	c #5D718F",
+"M-	c #475479",
+"N-	c #232956",
+"O-	c #829DB3",
+"P-	c #9ABACB",
+"Q-	c #242B56",
+"R-	c #A1C2D2",
+"S-	c #829DB4",
+"T-	c #32355F",
+"U-	c #F5F5F6",
+"V-	c #B7BAC6",
+"W-	c #3B3E65",
+"X-	c #3E4169",
+"Y-	c #525678",
+"Z-	c #FBFBF9",
+"`-	c #5C5D80",
+" ;	c #82859D",
+".;	c #393C64",
+"+;	c #F1F1F3",
+"@;	c #55587A",
+"#;	c #5A5D7E",
+"$;	c #393D64",
+"%;	c #EAEAEE",
+"&;	c #81859C",
+"*;	c #4C4F73",
+"=;	c #F6F7F6",
+"-;	c #A3A6B6",
+";;	c #373B62",
+">;	c #797D97",
+",;	c #FAFCF9",
+"';	c #494D71",
+");	c #323660",
+"!;	c #F5F6F8",
+"~;	c #656788",
+"{;	c #D1D3DB",
+"];	c #9FA0B3",
+"^;	c #AFB1BE",
+"/;	c #D6D6DD",
+"(;	c #BFC1CC",
+"_;	c #AFB0BF",
+":;	c #E8E9EC",
+"<;	c #CCCDD6",
+"[;	c #A7A7B9",
+"};	c #393C63",
+"|;	c #DEDFE5",
+"1;	c #AFB1C0",
+"2;	c #191B4C",
+"3;	c #2A2E5C",
+"4;	c #525379",
+"5;	c #9690AD",
+"6;	c #D6D2DF",
+"7;	c #F7F7FA",
+"8;	c #B6B4C6",
+"9;	c #42446E",
+"0;	c #EBEAF1",
+"a;	c #D9D6E1",
+"b;	c #9A94B0",
+"c;	c #53537A",
+"d;	c #272B5B",
+"e;	c #47557A",
+"f;	c #A8C9D9",
+"g;	c #B5DDE7",
+"h;	c #B5D9E6",
+"i;	c #758CA7",
+"j;	c #95B4C8",
+"k;	c #B5DAE6",
+"l;	c #B1D4E1",
+"m;	c #3E4B71",
+"n;	c #3F4C72",
+"o;	c #ADD0DC",
+"p;	c #B4D7E4",
+"q;	c #92B1C3",
+"r;	c #28305B",
+"s;	c #6E738F",
+"t;	c #EBEDEE",
+"u;	c #FAFAFA",
+"v;	c #F5F6F6",
+"w;	c #2F335B",
+"x;	c #C2C3CF",
+"y;	c #676B8A",
+"z;	c #43476E",
+"A;	c #44476D",
+"B;	c #8688A0",
+"C;	c #E8EAED",
+"D;	c #2B2E59",
+"E;	c #595C7C",
+"F;	c #9FA1B4",
+"G;	c #F4F5F5",
+"H;	c #8588A1",
+"I;	c #DBDDE3",
+"J;	c #F7F8F8",
+"K;	c #B1B1C1",
+"L;	c #E7E8EC",
+"M;	c #E5E6EB",
+"N;	c #262A55",
+"O;	c #747694",
+"P;	c #525578",
+"Q;	c #EDEDEF",
+"R;	c #F3F4F4",
+"S;	c #D7D8DE",
+"T;	c #30345F",
+"U;	c #B2B5C1",
+"V;	c #BDBDCA",
+"W;	c #EEEFF2",
+"X;	c #7B7C97",
+"Y;	c #9EA0B3",
+"Z;	c #EEF0F2",
+"`;	c #606381",
+" >	c #1C1F4C",
+".>	c #2B2E5B",
+"+>	c #292D5B",
+"@>	c #505077",
+"#>	c #938DAB",
+"$>	c #B8B6C8",
+"%>	c #43446F",
+"&>	c #191A4B",
+"*>	c #E9E9EF",
+"=>	c #D9D5E0",
+"->	c #9B95B1",
+";>	c #262C5A",
+">>	c #1E2450",
+",>	c #2B315C",
+"'>	c #1F2551",
+")>	c #171C49",
+"!>	c #1E2350",
+"~>	c #29305C",
+"{>	c #181D4D",
+"]>	c #343861",
+"^>	c #252A54",
+"/>	c #1C214E",
+"(>	c #3A3D68",
+"_>	c #212552",
+":>	c #242854",
+"<>	c #252754",
+"[>	c #2F335C",
+"}>	c #2E315D",
+"|>	c #1F2252",
+"1>	c #383A65",
+"2>	c #272B57",
+"3>	c #2E325C",
+"4>	c #252954",
+"5>	c #212453",
+"6>	c #343962",
+"7>	c #242754",
+"8>	c #252957",
+"9>	c #343762",
+"0>	c #202353",
+"a>	c #948FAC",
+"b>	c #C0BECF",
+"c>	c #474971",
+"d>	c #E0DFE7",
+"e>	c #56577C",
+"f>	c #1A1F4C",
+"g>	c #212854",
+"h>	c #313A64",
+"i>	c #4C5072",
+"j>	c #262955",
+"k>	c #53537B",
+"l>	c #9691AC",
+"m>	c #D7D3DF",
+"n>	c #CECCD9",
+"o>	c #4E4F77",
+"p>	c #CBCBD6",
+"q>	c #A6A1B9",
+"r>	c #5E5D82",
+"s>	c #202754",
+"t>	c #7892AB",
+"u>	c #B2D6E4",
+"v>	c #B3D9E4",
+"w>	c #B4DAE5",
+"x>	c #3C476E",
+"y>	c #92AFC6",
+"z>	c #5C6E8D",
+"A>	c #787B94",
+"B>	c #ECEDED",
+"C>	c #F4F5F7",
+"D>	c #F6F6F6",
+"E>	c #3E4167",
+"F>	c #EAECED",
+"G>	c #636584",
+"H>	c #5A5980",
+"I>	c #F9F9FB",
+"J>	c #595980",
+"K>	c #AEAEC0",
+"L>	c #B0AAC2",
+"M>	c #686589",
+"N>	c #313461",
+"O>	c #8DA8BF",
+"P>	c #ADD0DE",
+"Q>	c #46557A",
+"R>	c #272D59",
+"S>	c #37416A",
+"T>	c #445177",
+"U>	c #6E83A0",
+"V>	c #2D3661",
+"W>	c #4D5B7E",
+"X>	c #526183",
+"Y>	c #666A89",
+"Z>	c #F6F6F7",
+"`>	c #7B7D99",
+" ,	c #2A2C58",
+".,	c #33365F",
+"+,	c #6F728E",
+"@,	c #5D607F",
+"#,	c #707290",
+"$,	c #3D4068",
+"%,	c #464A6E",
+"&,	c #636684",
+"*,	c #535578",
+"=,	c #737591",
+"-,	c #2D305B",
+";,	c #33355F",
+">,	c #5A5E7E",
+",,	c #3D4168",
+"',	c #5A5D7D",
+"),	c #2C305A",
+"!,	c #676C88",
+"~,	c #5D6080",
+"{,	c #6C6F8B",
+"],	c #484B6F",
+"^,	c #40446A",
+"/,	c #656786",
+"(,	c #595D7D",
+"_,	c #646786",
+":,	c #2B305A",
+"<,	c #646289",
+"[,	c #E1DFE8",
+"},	c #E6E5EA",
+"|,	c #69668A",
+"1,	c #8586A0",
+"2,	c #ECE8EF",
+"3,	c #BDB8CC",
+"4,	c #767295",
+"5,	c #383A68",
+"6,	c #3F4A71",
+"7,	c #B7DEE9",
+"8,	c #556689",
+"9,	c #232A55",
+"0,	c #455175",
+"a,	c #445277",
+"b,	c #7B95AE",
+"c,	c #A3C5D4",
+"d,	c #94B1C5",
+"e,	c #99B7C9",
+"f,	c #222754",
+"g,	c #9BBBCD",
+"h,	c #A2C1D3",
+"i,	c #A2C6D3",
+"j,	c #91ADC2",
+"k,	c #CFD0D9",
+"l,	c #A9ABBB",
+"m,	c #565779",
+"n,	c #C2C6CF",
+"o,	c #F2F3F5",
+"p,	c #787994",
+"q,	c #353761",
+"r,	c #DADBE3",
+"s,	c #D7D9DF",
+"t,	c #CBCCD6",
+"u,	c #E3E5E8",
+"v,	c #D9DAE1",
+"w,	c #FAFBFA",
+"x,	c #4F5278",
+"y,	c #A5A7B7",
+"z,	c #DDDFE4",
+"A,	c #C8C9D4",
+"B,	c #C5C7D0",
+"C,	c #F2F3F4",
+"D,	c #E2E2E7",
+"E,	c #8789A1",
+"F,	c #E8E8EA",
+"G,	c #222653",
+"H,	c #DFE0E4",
+"I,	c #D7D9DD",
+"J,	c #D0D1DB",
+"K,	c #D2D3DC",
+"L,	c #ECEDEE",
+"M,	c #DBDBE2",
+"N,	c #FAFAF8",
+"O,	c #3B3F67",
+"P,	c #ACAEBD",
+"Q,	c #DADCE1",
+"R,	c #BABBCA",
+"S,	c #383C64",
+"T,	c #3B3D6A",
+"U,	c #726F92",
+"V,	c #B7B2C8",
+"W,	c #E9E7EE",
+"X,	c #F6F5F9",
+"Y,	c #7E7B9B",
+"Z,	c #54567A",
+"`,	c #F0EEF4",
+" '	c #8884A2",
+".'	c #444670",
+"+'	c #5B6C8C",
+"@'	c #B7DEE7",
+"#'	c #303963",
+"$'	c #435074",
+"%'	c #A3C7D3",
+"&'	c #B4D9E4",
+"*'	c #344068",
+"='	c #7289A5",
+"-'	c #8AA6BB",
+";'	c #94B3C4",
+">'	c #637896",
+",'	c #B1D5E2",
+"''	c #272E59",
+")'	c #627395",
+"!'	c #282E59",
+"~'	c #8486A1",
+"{'	c #C5C6D2",
+"]'	c #B9BCC8",
+"^'	c #ACADBD",
+"/'	c #B4B6C2",
+"('	c #5E6181",
+"_'	c #545779",
+":'	c #A3A5B8",
+"<'	c #202253",
+"['	c #EEF0F1",
+"}'	c #7A7D97",
+"|'	c #FBFBFA",
+"1'	c #FAFAF9",
+"2'	c #979AAE",
+"3'	c #B9BCC7",
+"4'	c #BFC0CB",
+"5'	c #171C4B",
+"6'	c #A0A1B5",
+"7'	c #A4A5B7",
+"8'	c #DADAE2",
+"9'	c #757793",
+"0'	c #F5F6F7",
+"a'	c #373A63",
+"b'	c #6D6F8E",
+"c'	c #353862",
+"d'	c #F3F3F5",
+"e'	c #8B8DA3",
+"f'	c #5B5E81",
+"g'	c #FBFCFA",
+"h'	c #3A3E65",
+"i'	c #BEBFCA",
+"j'	c #D0D1D9",
+"k'	c #595C7F",
+"l'	c #1A1F4F",
+"m'	c #474871",
+"n'	c #EFEEF3",
+"o'	c #A5A2B9",
+"p'	c #44456E",
+"q'	c #E6E6EB",
+"r'	c #DBD7E3",
+"s'	c #9F9CB4",
+"t'	c #57567D",
+"u'	c #4A567C",
+"v'	c #B7DEE8",
+"w'	c #5C718F",
+"x'	c #758DA6",
+"y'	c #9BBACC",
+"z'	c #3C466F",
+"A'	c #B6DCE5",
+"B'	c #667C99",
+"C'	c #536486",
+"D'	c #232B56",
+"E'	c #7E98B0",
+"F'	c #9FC0CF",
+"G'	c #7189A3",
+"H'	c #A0C0D2",
+"I'	c #BDBECA",
+"J'	c #222552",
+"K'	c #D7D8E0",
+"L'	c #9192A9",
+"M'	c #8E90A6",
+"N'	c #8E90A7",
+"O'	c #E5E6E9",
+"P'	c #CBCCD5",
+"Q'	c #ACACBD",
+"R'	c #42456C",
+"S'	c #2E315C",
+"T'	c #7F829A",
+"U'	c #F1F2F4",
+"V'	c #7A7C98",
+"W'	c #F9FAFA",
+"X'	c #8A8CA1",
+"Y'	c #242853",
+"Z'	c #292B57",
+"`'	c #F1F1F4",
+" )	c #C5C7D2",
+".)	c #AFB2BF",
+"+)	c #999AB0",
+"@)	c #E4E5E9",
+"#)	c #86889F",
+"$)	c #4C5073",
+"%)	c #494D70",
+"&)	c #8587A0",
+"*)	c #EDEEF0",
+"=)	c #2D305C",
+"-)	c #B4B7C6",
+";)	c #2A2D57",
+">)	c #2D2F5E",
+",)	c #9C97B2",
+"')	c #D7D3E0",
+"))	c #F5F5F7",
+"!)	c #CECCD8",
+"~)	c #A6A7BA",
+"{)	c #6D6C8E",
+"])	c #1C1F4F",
+"^)	c #89A6BB",
+"/)	c #B7DBE8",
+"()	c #A3C4D4",
+"_)	c #B3D7E4",
+":)	c #7D96AE",
+"<)	c #262D5A",
+"[)	c #B0D6E2",
+"})	c #9BBCCC",
+"|)	c #91AFC2",
+"1)	c #B6DCE9",
+"2)	c #90ADC0",
+"3)	c #B0D4E0",
+"4)	c #485579",
+"5)	c #616483",
+"6)	c #F4F4F4",
+"7)	c #E5E8EB",
+"8)	c #787B96",
+"9)	c #AEB2C0",
+"0)	c #EAEAED",
+"a)	c #E4E6EB",
+"b)	c #C0C1CC",
+"c)	c #1F234F",
+"d)	c #F2F2F4",
+"e)	c #80829C",
+"f)	c #E1E3E8",
+"g)	c #AAACBB",
+"h)	c #43466D",
+"i)	c #F1F2F2",
+"j)	c #D3D4DB",
+"k)	c #C5C6D0",
+"l)	c #505378",
+"m)	c #BDBECB",
+"n)	c #C7C9D4",
+"o)	c #7D8198",
+"p)	c #E3E4E9",
+"q)	c #EFEFF0",
+"r)	c #84889F",
+"s)	c #F7F8F9",
+"t)	c #C5C5D0",
+"u)	c #676A88",
+"v)	c #727691",
+"w)	c #B1B1C0",
+"x)	c #C4C6D1",
+"y)	c #C3C5CE",
+"z)	c #D3D4DC",
+"A)	c #D8DAE0",
+"B)	c #717093",
+"C)	c #FAF9FB",
+"D)	c #787696",
+"E)	c #333563",
+"F)	c #535579",
+"G)	c #F1F0F2",
+"H)	c #CECAD8",
+"I)	c #4F4E76",
+"J)	c #3D4870",
+"K)	c #506082",
+"L)	c #434F74",
+"M)	c #2F3A62",
+"N)	c #566689",
+"O)	c #4C5B7F",
+"P)	c #222854",
+"Q)	c #404D73",
+"R)	c #5D6E8F",
+"S)	c #333C65",
+"T)	c #3C3F67",
+"U)	c #6E728F",
+"V)	c #6A6C8B",
+"W)	c #30345E",
+"X)	c #646886",
+"Y)	c #6A6D8B",
+"Z)	c #535777",
+"`)	c #44486D",
+" !	c #3F416B",
+".!	c #525579",
+"+!	c #4D5175",
+"@!	c #484B71",
+"#!	c #6F728F",
+"$!	c #626584",
+"%!	c #5F6282",
+"&!	c #31345F",
+"*!	c #242953",
+"=!	c #5E6281",
+"-!	c #2E305C",
+";!	c #5E6280",
+">!	c #31355D",
+",!	c #727491",
+"'!	c #41466B",
+")!	c #505474",
+"!!	c #45496E",
+"~!	c #252854",
+"{!	c #686A89",
+"]!	c #696C8A",
+"^!	c #353962",
+"/!	c #CBC7D6",
+"(!	c #F0EFF4",
+"_!	c #C9C9D4",
+":!	c #ABA7BF",
+"<!	c #6C698D",
+"[!	c #3C3D6A",
+"}!	c #AFAAC0",
+"|!	c #E5E4EB",
+"1!	c #595C7E",
+"2!	c #CCC8D8",
+"3!	c #918DA9",
+"4!	c #56557D",
+"5!	c #1D2353",
+"6!	c #1A1F4E",
+"7!	c #282A59",
+"8!	c #1E2353",
+"9!	c #515179",
+"0!	c #8D88A6",
+"a!	c #CECBD9",
+"b!	c #A7A4B9",
+"c!	c #454570",
+"d!	c #C0C1CD",
+"e!	c #B6B0C5",
+"f!	c #7A7597",
+"g!	c #292C58",
+"h!	c #2B2E5A",
+"i!	c #737093",
+"j!	c #B1ACC2",
+"k!	c #E4E2EB",
+"l!	c #E4E3EA",
+"m!	c #6A688D",
+"n!	c #D5D2DF",
+"o!	c #A29DB6",
+"p!	c #646187",
+"q!	c #222857",
+"r!	c #1A1D4D",
+"s!	c #1B1D4F",
+"t!	c #232652",
+"u!	c #383B66",
+"v!	c #9B97B0",
+"w!	c #D2CEDC",
+"x!	c #4D4D76",
+"y!	c #A2A4B7",
+"z!	c #928DA9",
+"A!	c #353764",
+"B!	c #232555",
+"C!	c #1A1D4E",
+"D!	c #2C305C",
+"E!	c #323461",
+"F!	c #595981",
+"G!	c #908CA8",
+"H!	c #EAE9ED",
+"I!	c #817D9C",
+"J!	c #3B3C69",
+"K!	c #E7E4EC",
+"L!	c #BFBBCD",
+"M!	c #53527B",
+"N!	c #343665",
+"O!	c #1A204D",
+"P!	c #181B48",
+"Q!	c #272958",
+"R!	c #1B1F4C",
+"S!	c #2F3361",
+"T!	c #8683A1",
+"U!	c #BEB9CB",
+"V!	c #E5E3EA",
+"W!	c #F9F7FA",
+"X!	c #D1CFDB",
+"Y!	c #E9E8ED",
+"Z!	c #E0DDE8",
+"`!	c #B4B0C5",
+" ~	c #807C9D",
+".~	c #232554",
+"+~	c #1B1D4B",
+"@~	c #393D68",
+"#~	c #343663",
+"$~	c #4C4D76",
+"%~	c #7B7798",
+"&~	c #B6B2C6",
+"*~	c #B3B1C2",
+"=~	c #272C5B",
+"-~	c #57597C",
+";~	c #EDECF1",
+">~	c #DCD9E1",
+",~	c #B3AEC5",
+"'~	c #837F9E",
+")~	c #313462",
+"!~	c #1C2151",
+"~~	c #252A57",
+"{~	c #212656",
+"]~	c #5A5A82",
+"^~	c #B2ADC3",
+"/~	c #A7A4BA",
+"(~	c #686A88",
+"_~	c #F0EFF5",
+":~	c #BBB7CA",
+"<~	c #8884A3",
+"[~	c #282B5A",
+"}~	c #232958",
+"|~	c #373A66",
+"1~	c #8682A0",
+"2~	c #DCD9E2",
+"3~	c #F5F3F6",
+"4~	c #F4F3F6",
+"5~	c #8F8CA7",
+"6~	c #727591",
+"7~	c #E2DFE7",
+"8~	c #BDBACD",
+"9~	c #626086",
+"0~	c #41426D",
+"a~	c #252A58",
+"b~	c #1D1F4F",
+"c~	c #3F406C",
+"d~	c #616086",
+"e~	c #9490AB",
+"f~	c #E8E8ED",
+"g~	c #5E6080",
+"h~	c #9D98B4",
+"i~	c #323561",
+"j~	c #6D6A8E",
+"k~	c #C5C1D1",
+"l~	c #E4E1E9",
+"m~	c #8987A4",
+"n~	c #494973",
+"o~	c #4C4F74",
+"p~	c #E9E6EE",
+"q~	c #ADA8BF",
+"r~	c #7F7B9B",
+"s~	c #636088",
+"t~	c #40406D",
+"u~	c #212655",
+"v~	c #1B224F",
+"w~	c #202454",
+"x~	c #3D3F6C",
+"y~	c #807D9C",
+"z~	c #A8A3BB",
+"A~	c #CFCBDA",
+"B~	c #E8E7ED",
+"C~	c #FDFCFD",
+"D~	c #9E9BB3",
+"E~	c #504F78",
+"F~	c #B8B8C8",
+"G~	c #E1DEE8",
+"H~	c #A09BB4",
+"I~	c #57577F",
+"J~	c #2C305F",
+"K~	c #242655",
+"L~	c #292D5A",
+"M~	c #232757",
+"N~	c #747193",
+"O~	c #9B97B2",
+"P~	c #A29FB6",
+"Q~	c #787A95",
+"R~	c #EBEBEF",
+"S~	c #D3D0DC",
+"T~	c #9490AC",
+"U~	c #6E6B8E",
+"V~	c #2F325F",
+"W~	c #2D315D",
+"X~	c #1F2454",
+"Y~	c #908CA9",
+"Z~	c #D2CFDB",
+"`~	c #BAB8C8",
+" {	c #3D3F69",
+".{	c #353961",
+"+{	c #C5C5D1",
+"@{	c #E5E1EB",
+"#{	c #CECAD9",
+"${	c #8F8BA9",
+"%{	c #716F91",
+"&{	c #303461",
+"*{	c #41436D",
+"={	c #55557B",
+"-{	c #6E6C8F",
+";{	c #AFABC1",
+">{	c #D8D6DF",
+",{	c #8B88A6",
+"'{	c #4C4C78",
+"){	c #6E708E",
+"!{	c #CBCAD6",
+"~{	c #E1DDE9",
+"{{	c #CDC9D8",
+"]{	c #9994AF",
+"^{	c #7C779A",
+"/{	c #5D5B82",
+"({	c #1B2251",
+"_{	c #303462",
+":{	c #2E325F",
+"<{	c #4E4E76",
+"[{	c #9893AE",
+"}{	c #CCC8D7",
+"|{	c #B5B2C5",
+"1{	c #6E6C8E",
+"2{	c #5A5D7F",
+"3{	c #E8E6EC",
+"4{	c #ACA8BF",
+"5{	c #9691AE",
+"6{	c #8581A0",
+"7{	c #68668A",
+"8{	c #57577D",
+"9{	c #2A2D5D",
+"0{	c #242757",
+"a{	c #323662",
+"b{	c #242957",
+"c{	c #1D2252",
+"d{	c #494B73",
+"e{	c #616186",
+"f{	c #717091",
+"g{	c #7E7B9A",
+"h{	c #E7E4EB",
+"i{	c #E1E0E8",
+"j{	c #9A96B2",
+"k{	c #5F5F83",
+"l{	c #393C67",
+"m{	c #5D5F80",
+"n{	c #BBBCCB",
+"o{	c #EDEAF0",
+"p{	c #D3CFDD",
+"q{	c #C2BED0",
+"r{	c #767395",
+"s{	c #66658A",
+"t{	c #5A5A80",
+"u{	c #474872",
+"v{	c #40416E",
+"w{	c #464872",
+"x{	c #363965",
+"y{	c #42446F",
+"z{	c #2F3360",
+"A{	c #41426E",
+"B{	c #474873",
+"C{	c #4F5078",
+"D{	c #9993AF",
+"E{	c #AFAAC2",
+"F{	c #C4C0D2",
+"G{	c #D3CFDC",
+"H{	c #E0DDE7",
+"I{	c #D0CDD9",
+"J{	c #85819F",
+"K{	c #9797AF",
+"L{	c #EBE9F0",
+"M{	c #BBB6C9",
+"N{	c #ADA7C0",
+"O{	c #9F99B4",
+"P{	c #928DAB",
+"Q{	c #7B7899",
+"R{	c #737193",
+"S{	c #6D6A8D",
+"T{	c #6A668B",
+"U{	c #7A7798",
+"V{	c #5D5D83",
+"W{	c #5C5C82",
+"X{	c #5E5E83",
+"Y{	c #626287",
+"Z{	c #68658A",
+"`{	c #7B7898",
+" ]	c #918CAA",
+".]	c #9E99B4",
+"+]	c #ADA9C0",
+"@]	c #C9C5D6",
+"#]	c #ECEBF2",
+"$]	c #CDCBD7",
+"%]	c #9795AE",
+"&]	c #636186",
+"*]	c #4F5374",
+"=]	c #85879F",
+"-]	c #BABAC9",
+";]	c #E9EAEE",
+">]	c #EBE9EE",
+",]	c #D5D2DE",
+"']	c #C5C1D2",
+")]	c #BEBACD",
+"!]	c #B6B2C7",
+"~]	c #AFAABF",
+"{]	c #AAA6BD",
+"]]	c #A8A4BC",
+"^]	c #A9A5BC",
+"/]	c #AAA7BD",
+"(]	c #BDBACC",
+"_]	c #CDC9D7",
+":]	c #E5E3E9",
+"<]	c #ECEBEF",
+"[]	c #FBFCFD",
+"}]	c #A8A5B9",
+"|]	c #757393",
+"1]	c #4E5075",
+"2]	c #85869E",
+"3]	c #B5B5C5",
+"4]	c #E7E6EB",
+"5]	c #E5E3EB",
+"6]	c #E3E1E8",
+"7]	c #E3E2E8",
+"8]	c #E6E4EA",
+"9]	c #EBE8EE",
+"0]	c #B8B8B9",
+"a]	c #999999",
+"b]	c #A9A9A9",
+"c]	c #929292",
+"d]	c #7F7F80",
+"e]	c #BEA6AC",
+"f]	c #DD748B",
+"g]	c #ECB0C5",
+"h]	c #D95E7D",
+"i]	c #F8E9F0",
+"j]	c #EEEEF2",
+"k]	c #C8C6D2",
+"l]	c #938FA9",
+"m]	c #6B698D",
+"n]	c #3E406A",
+"o]	c #484C71",
+"p]	c #80839C",
+"q]	c #9698AE",
+"r]	c #AAABBE",
+"s]	c #B6B8C6",
+"t]	c #C0C0CD",
+"u]	c #C5C5D2",
+"v]	c #C8C8D4",
+"w]	c #C7C7D4",
+"x]	c #C2C2D1",
+"y]	c #BABBCB",
+"z]	c #AFB1C1",
+"A]	c #A2A4B9",
+"B]	c #9495AD",
+"C]	c #8E8EA8",
+"D]	c #AFAEC2",
+"E]	c #BFBFC0",
+"F]	c #404040",
+"G]	c #848484",
+"H]	c #7D7D7D",
+"I]	c #A8888D",
+"J]	c #D3445F",
+"K]	c #DD7F93",
+"L]	c #CD2148",
+"M]	c #F8E2EB",
+"N]	c #E7E6EC",
+"O]	c #C9C7D5",
+"P]	c #A09DB5",
+"Q]	c #777495",
+"R]	c #5F5E83",
+"S]	c #262B59",
+"T]	c #1B2050",
+"U]	c #1C2350",
+"V]	c #616184",
+"W]	c #9A99B2",
+"X]	c #C5C4D1",
+"Y]	c #626262",
+"Z]	c #8B8B8C",
+"`]	c #949494",
+" ^	c #ABABAC",
+".^	c #969696",
+"+^	c #DBDBDC",
+"@^	c #575758",
+"#^	c #727272",
+"$^	c #DFDFDF",
+"%^	c #868686",
+"&^	c #909090",
+"*^	c #E6E6E6",
+"=^	c #AAAAAB",
+"-^	c #B4B4B4",
+";^	c #555555",
+">^	c #B1B1B2",
+",^	c #7F7F7F",
+"'^	c #B1B1B1",
+")^	c #AEAEAF",
+"!^	c #CBC9D8",
+"~^	c #AFACC1",
+"{^	c #8E8CA8",
+"]^	c #767495",
+"^^	c #4F5079",
+"/^	c #333662",
+"(^	c #57557F",
+"_^	c #606084",
+":^	c #717191",
+"<^	c #9090A9",
+"[^	c #CDCCD7",
+"}^	c #676768",
+"|^	c #8E8E8F",
+"1^	c #F2F2F3",
+"2^	c #CDCDCD",
+"3^	c #A8A8A9",
+"4^	c #424242",
+"5^	c #F4F4F5",
+"6^	c #353535",
+"7^	c #363636",
+"8^	c #EEEEEE",
+"9^	c #B5B5B5",
+"0^	c #5B5B5B",
+"a^	c #B4B4B5",
+"b^	c #323232",
+"c^	c #777777",
+"d^	c #C4C4C5",
+"e^	c #A6A6A6",
+"f^	c #656565",
+"g^	c #F7F6F8",
+"h^	c #C9C6D5",
+"i^	c #A19EB6",
+"j^	c #9794AF",
+"k^	c #908EAA",
+"l^	c #8D8AA7",
+"m^	c #8B88A5",
+"n^	c #9390AB",
+"o^	c #9B98B1",
+"p^	c #A5A3B9",
+"q^	c #B2AFC3",
+"r^	c #C4C4C4",
+"s^	c #464646",
+"t^	c #A4A4A5",
+"u^	c #3A3A3A",
+"v^	c #C3C3C4",
+"w^	c #434343",
+"x^	c #383838",
+"y^	c #A8A8A8",
+"z^	c #666667",
+"A^	c #818181",
+"B^	c #B6B6B7",
+"C^	c #2F2F2F",
+"D^	c #797979",
+"E^	c #AAAAAA",
+"F^	c #2D2D2D",
+"G^	c #D9D9DA",
+"H^	c #636364",
+"I^	c #C1C1C1",
+"J^	c #A0A0A0",
+"K^	c #BEBEBF",
+"L^	c #DDDDDE",
+"M^	c #B0B0B0",
+"N^	c #C6C6C7",
+"O^	c #888888",
+"P^	c #D1D1D1",
+"Q^	c #9A9A9A",
+"R^	c #F5F5F5",
+"S^	c #D4D4D4",
+"T^	c #ADADAD",
+"U^	c #BDBDBE",
+"V^	c #ABABAB",
+"W^	c #8B8B8B",
+"X^	c #B9B9BA",
+"Y^	c #A2A2A2",
+"Z^	c #C7C7C7",
+"`^	c #EAEAEA",
+". + . + . . + . + . . + . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ",
+". + + + . . + + + . . + . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ",
+"+ . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ",
+"+ + + + + + @ + @ @ @ @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @ @ @ @ @ @ @ @ # @ @ # @ @ @ @ # # @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ . + @ @ @ @ + @ @ @ @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ @ @ @ % & * = - ; > , ' ) ! , ~ { ] ^ / ( _ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : @ : @ : : @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + < [ } | 1 2 3 4 5 6 7 8 9 0 a b c 0 d e 7 f g h i j k l m n o + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ : @ @ @ @ @ @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + + + + p q r s t u v 7 w x y z A B C D E F E E E G H D I B J K L M N O 6 P Q > R S p T + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : $ @ $ : : : @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + @ @ # U V W X Y Z `  ...+. at .#.$.$.%.%.%.&.&.*.=.%.%.%.=.-.;.>.,.'.$.).!.~.{.].^./.(._.:.<.[.}.@ @ + + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ + + + @ + @ @ $ $ $ $ $ $ $ $ $ $ $ $ + + + @ @ |.1.2.3.4.5.6.7.8.9.0.a.b.c.*.d.&.*.&.*.c.b.-.a.%.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.@ + + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + $ $ $ $ $ $ $ @ $ $ $ $ $ $ $ $ $ $ $ $ @ @ @ B.C.X D.E.z D F.G.>.*.&.*.H.H.H.H.c.c.*.*.&.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.X.Y.U.Z.`. +.+++ at +#+R $+U %++ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : @ @ @ : : @ @ $ $ $ $ $ $ $ $ $ $ @ + @ &+*+=+-+;+>+,+'+;.%.c.*.c.*.*.H.H.*.*.*.a.)+!+~+{+]+^+/+(+_+:+<+[+}+|+1+2+2+3+4+5+6+7+8+9+}+0+a+b+c+d+e+f+g+h+% # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ + @ @ @ @ $ $ $ $ $ $ $ $ $ + # @+i+j+k+l+I m+>.c.*.*.*.*.*.c.c.&.*.*.&.n+o+p+q+++S.r+:+s+t+u+v+w+X x+y+z+A+B+C+D+6 E+F+G+H+z+I+J+X K+v+L+M+N+O+P+e+Q+R+S+# @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ $ : @ $ : : : @ $ $ $ $ $ $ + + + o T+U+V+M W+m+>.c.&.*.*.c.c.c.c.*.&.*.c.X+Y+Z+`+e+ @. at +@@@#@$@%@v &@*@=@N -@;@>@,@'@l+)@!@~@{@]@^@/@(@_@:@_.H+<@X [@}@|@. at 1@2 at 3@4@< @ + + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + + : + @ + + @ + @ $ $ $ $ $ @ @ < 5 at 6@7 at 8@J 8.$.c.*.*.c.c.c.c.*.*.*.*.9 at 0@a at b@c at d@e at f@3+g at h@i at j@0 k@]@l at m@n at o@p at q@F 8.F r@@.F. at .s@t at u@~.v at B m@ .w at x@=@y at z@A at w+B@C at D@E at F@G at H@< + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + $ $ $ $ : $ $ @ $ $ $ $ $ @ # I at J@_.l++.9.%.*.*.c.c.c.c.c.c.*.*.b.K at L@m M at N@O at P@Q at R@S at T@U at V@ .W at X@t at Y@F.#.Z@`@ #'.'.'..#+#>.;.;.0.0.0.G.@###Y at +.$#%#&#V@*#=#-#;#>#,#'#)#!#~#{#@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ . + @ : : @ @ : : @ @ $ $ $ $ ]#o ^#/#(#_#:#a.*.*.*.c.c.c.c.c.c.*.=.<#[#}#++|#'#1#2#3#4#5#6#]@v at s@7##.8#;.a.b.9#X+c.*.c.*.*.-.0#a#*.*.*.*.c.*.=.c.b#c#!.#.7#d#v at A y e#f#g#h#i#j#k#^+l#< @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ + @ @ @ @ $ $ @ + % m#n#` v@'+&.*.*.c.c.c.c.c.c.*.*.*.o#p#q#r#s#t#u#v#w#x#y#z#A#B#'+;.c.*.*.*.*.C#D#*.*.H.H.H.*.*.%.E#F#&.*.*.*.%.G#H#I#=.*.b.a.0.'+F.G ..J#K#L#M#N#O#. at P#Q#R## + + @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : : $ : : : @ $ + + S#T#U#V#F >.*.*.c.c.c.c.c.c.*.*.*.=.W#X#Y#Z#`# $.$_.+$@$#$E  .$$%.*.*.H.&.*.*.%$&$&.*.H.H.H.H.H.c.b.9#*$*.*.a.=$c#-$*.*.*.*.*.*.*.*.%.>.#.;$>$,$E.'$)$!$~${$]$S @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + + $ $ + + @ + @ @ + + ^$/$($_$8.>.*.*.c.c.c.c.c.c.*.*.*.%.:$<$[$.@}$|$1$N 2$v@@.3$4$5$c.*.H.H.H.*.*.D#6$*.b.H.H.H.H.H.H.c.*.c.7$=$8$X+9$%.*.*.H.H.H.H.H.H.H.H.c.=.0.'+ at .v@0$a$6 b$c$d$e$f$< @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + $ $ $ $ $ $ $ @ + g$h$i$2$,+;.*.c.c.c.c.c.c.c.c.*.*.'.j$]+k$l$m$%@7 n$o$s@'+0.'.p$=.*.H.H.H.H.*.q$r$%.H.H.H.H.H.H.H.H.*.c.a.s$t$u$%.&.*.H.H.H.H.H.H.H.H.H.H.H.&.9@*.>.3$s at W+A#v$w$x$y$z$A${#@ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : @ : : : @ @ p B$C$~@,+;.*.c.c.c.c.c.c.c.c.*.*.I.D$3 at E$F$G$H$I$J$K$L${.%.%.n at M$9@*.H.*.*.*.N$5$b.&.H.H.H.H.H.H.9@*.=.O$P$Q$P$>.b.&.*.H.H.H.H.H.H.H.H.H.H.H.&.&.*.*.%.>.#.H z R$S$T$}+U$V${#@ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ + @ @ @ @ W$X$Y$Z$>.*.*.c.c.c.c.c.c.c.*.*.-.`$ %.%+%@%f #%A $%'+'.=.%%&%8$-.c.c.*.*.%.P$=$9@*.H.H.H.H.H.*.*.*.a.u$*%=%a.-%;%-.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.&.*.*.%.'+s at W@;@>%,%|+'%)%!%@ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : : $ : + # ~%{%R$+.>.*.*.c.c.c.c.c.c.*.*.*.b.]%^%/%(%$@_%]@:%F.n+c.c.*.I.<%[%}%*.*.=.s$|%1%*.*.H.H.H.H.&.*.*.=.N$D#2%b.*.*.*.3%%.*.&.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.*.*.*.;.m+v at 4%5%6%7%/%N.{#+ @ @ @ @ @ @ @ + + + + ",
+"+ . + @ $ @ @ + @ + 8%9%0%..>.d.*.c.c.c.c.c.c.c.*.*.F#a%b%c%d%e%f%g%7.B#,+%.*.*.<#h%%.%.i%j%%.k%l%%.*.*.H.H.H.H.&.*.c.-$m%=$*.*.*.H.H.*.n%o%&.*.H.H.H.H.H.H.H.H.H.H.H.H.&.*.*.*.*.&.p%9#q%r%s%#%t%u%v%c%w%%+@ @ @ @ @ @ @ + + + + ",
+". . + + $ $ $ $ + # x%Y ,$F.=.*.*.c.c.c.c.c.c.c.*.*.y%z%A%+ at B%f%^@D #.;.C%&.*.&.=%D%*.*.=.E%F%&%=.*.c.c.H.H.H.H.G%9#`@9#c.*.*.H.H.H.H.&.c.H%-.*.H.H.H.H.H.H.H.H.H.H.H.H.&.&.=.I%J%P$;%I%=.%.K%L%A#M%N%O%P%Q%%++ @ @ @ @ @ + + + + ",
+". . + @ : : @ : @ R%S%T%E >.*.*.*.c.c.c.c.c.c.c.&.D%U%V%W%X%Y%^@W at G.%.%.1%c.*.%.Z%9@&.*.<#a#`%p%c.c.c.c.H.b.*. &.&+&*.c.*.*.*.H.H.H.H.H.*.&$@&*.H.H.H.H.H.H.H.*.a.%.%.#&$&%&&&*&=&d.&.*.*.&.%.$#X at V@-&;&>&,&'&@ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ + )&!&~&B >.c.*.c.c.c.c.c.c.c.c.c.*.{&]&^&/&(&/@:%_&%.*.1%c.*.*. #b.&.*.*.n+-%-.&%=.*.H.*.*.-%;%*&*.c.*.*.H.H.H.H.H.H.H.H.*.I.:&&.*.H.*.&.b.b.<&-$.&X+N$[&*.a.c.*.*.*.*.*.&.*.-.}&:#C |&1$1&2&3@< @ @ @ @ @ + + + + ",
+". . + @ : : @ + 3&4&M ,+%.*.c.c.c.c.c.c.c.c.c.b.n%5&d+6&R at 7&8&@.'+*.&.9&*.*.0&>.&.&.&.a&n+*.-.1%b&*.*.I.c&-$%.*.*.*.H.H.H.H.H.H.H.H.H.H.*.b.%&&.*.c.*.6$X+X+d&e&a.c.*.*.*.*.*.&.H.H.H.H.H.*.a.H%>.F.A f&g&h&i&S#%+@ @ @ @ + + + + ",
+"+ + + @ $ + + + j&k&_#G.&.c.c.c.c.c.c.c.c.c.c.&.l&m&n&o&p&l+d#8#%.*.;.;.*.%.G.*.*.*. #1%*.*.*.=.b&D%&%[%=.*.*.H.H.H.H.H.H.H.H.H.*.*.c.*.*.*.P$q&H#r&u$s&&.*.*.*.*.c.&.H.H.H.H.H.H.H.H.H.H.*.c.P$t&u&d#l+'$v&w&f+U @ @ @ @ + + + + ",
+". . + + $ $ @ x&y&z&,+%.*.c.c.c.c.c.c.c.c.c.c.e&X#U.A&B&C&B B#%.c.c.D&*.9 at E&%.c.%.@#0&*.H.H.c.%.F&G&I.*.*.H.H.H.H.H.H.H.H.H.H.H.*.*.j%H&9#d&%&3%=.*.*.9@*.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.*..#9$%.B#W+I&g#J&K&h+%++ @ @ + + + + ",
+"+ . + @ : @ @ L&M& .G.*.c.c.c.c.c.c.c.c.c.c.c.N&O&P&Q&R&2$,+>.9@*.c.c.*.c.D&c.-.E&%.c.*.*.<#F%S&'.I%k%*.c.H.H.H.H.H.H.&.&.a.=.T&X+X+U&@&-.=.&.J%*.*.*.*.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.c.I.l@*.>._&`%=#V&W&X&< @ @ @ + + + + ",
+"+ + + @ @ @ @ Y&Z&C >.c.c.c.c.c.c.c.c.c.c.c.a.`& *F$.*R$+*#.%.*.*.*.*.c.*.*.*.G.c.c.*.*.1%Z%'.&.*.=.%%&.&.H.H.H.c.&.%.@*d&#*d&5$&.*.%.&.*.*.=.*&c.*.H.H.H.&.-.&.*.*.H.H.H.H.H.H.H.H.H.H.H.H.*.-.$**.%.#.I %*&***=*S @ @ @ + + + + ",
+". + + @ : @ -*T$R$t@%.*.c.c.c.c.c.c.c.c.c.c.;*>*,*'*)*!*G >.c.~*{*]*^*c.*.c.c.c.d.*.%./*>.-.(*_*:*<*i%a.&.b.b.[*>+D#D#}*=.-.*.*.*.&.*.&.H.*.-.u$a.*.&.&.*.%.|*1**.*.H.H.H.H.H.H.H.H.H.H.H.H.&.-.H#*.c.>.t@}&g 2*3*f$# @ @ + + + + ",
+"+ + + @ + # 4*5*y #.*.*.c.c.c.c.c.c.c.c.c.b.6*7*. at 8*9*m at m+0*a*b*c*d*e*&.c.c.&.&.%.>.;.-.c.f*g*h*i*j*k*i%K%F&l*m*'.-.b.*.&.&.d.c.&.*.&.&.c.&.*.n*b.d.o*p*=%+&q*r*9@&.b.b.*.&.b.b.&.&.&.b.&.&.&.=.H#*.*.c.s*z#8 t*'#u*p @ @ + + + + ",
+". . + + + @ v*w*x*G.y*H.c.c.c.c.c.c.c.c.c.&.z*A*B*C*R$C u&D*E*F*a.G*j%v at H*I*J*D&K*L*M*N*O*P*Q*R*2%Z@*%S*T*U*,.V*W*X*Y*Z*`* =.=+=@=I.#=T*$==.;*%=&=*===-=;=J.>=,='=)=!=~={=]=^=/=(=a._=:=<=D&&.c.H#*.H.c.$.[=e#}=|=e+1=@ @ + + + + ",
+"+ + + + $ # 2=3=B %.y*H.c.c.c.c.c.c.c.c.c.%.4=e$5=6=7=E 8=9=0='.a=b=c=d=e=f=g=h=i=j=g=k='=l=m=0&=.0&n=o=p=q=r=s=t=u=v=w=x=y=:=z=A=B=C=D=_ E=F=G=H=I=J=K=L=M=N=O=P=Q=R=S=T=% U=V=W=X=Y=Z=`=&.&.*.H#*.H.*.8#E  -.-+- at -R#@ @ + + + + ",
+"+ + + @ + @ #-w#C %.y*H.c.c.c.c.c.c.c.c.c.,.$-%-&-*-{@,+=---;->-,-'-)-!-~-{-]-^-/-a.(-_-:-q <-%.F#[-% }-|-1-2-3-4-5-6-7-8-9--.0-a-b-c-d-e-f-g-h-i-a.j-k-l-m-n-o-p-t&q-b-r-q s-t-i*u-v-w-x-*.*.=.H#c.c.&.;.,+!*y-z-)#A-@ @ + + + + ",
+". . + @ @ # B-k++.=.b.H.c.c.c.c.c.c.c.c.c.F#C-)#5+G+0$@.D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z-`- ;@+.;+;@;#;q $;%;&;y%v=*;=;-;#;;;>;,;';);8$!;~;%.{;];^;/;;*(;_;a@:;=&<;[;l@};|;1;a.*.-.l at 2;*.&.;.m+3;4;5;6;7;@ + + + + + ",
+"+ + + @ @ # 8;9;+.=.c.H.c.c.c.c.c.c.c.c.c.=.0;a;b;c;d;B#D&e;f;g;h;i;h%j;k;l;m;n;o;p;q;r;'.s;t;u;v;w;x;=;%;y;z;A-A;B;C;D;_ 6*E;+;}*F;S#G;H;);I;J;K;K$L;M;N;h*O;P;Q;R;S;T;U;V;+#W;X;Y;Z;& `;^* & >.>0&<&=.'.C%+>@>#>3*7;< + + + + + ",
+". . + @ + # $>%>+.%.&>H.c.c.c.c.c.c.c.c.c.*.*>=>->c; .##%.-.E&;>>>a.a.E&,>'>)>&.!>~>;.0.;.{>I%]>^> #/>(>o%8$m*o#m*_>l*S&:>.#-$<>b&b&[>}>|>b&-$1>{@h%2>3>i%4>5>G##*6>K$%%D#7>%%8>&%H&9>;%0>0>.#.#]@5$a#n+-%~.r at -&a>3*4@< @ + + + + ",
+"+ + + + $ @ b>c>D %.y*H.c.c.c.c.c.c.c.c.c.&.d>)#3+e>+>F.D&&.*.*.&.*.*.*.*.;.f>F#a.*.=.g>h>%.*.*.*.&.*.b&a.a.0>F%*.*.*.*.*.&.*.&.*.*.*.*.*.*.*.5$b#*.c.*.*.&.&.*.*.&.i>j>&.c.&.*.&.*.*.*.*.b.&.9@$*c.*.*.;.F.~@k>l>m>4@@ @ + + + + ",
+". . + @ + + n>o>W+D&*.H.c.c.c.c.c.c.c.c.c.b.p>P%q>r>A#s@'.*.c.c.c.*.-.s>t>u>v>w>x>D&m+y>z>=.*.*.*.*.*.i%A>B>C>D>E>-.*.*.*.*.*.*.*.*.*.*.*.&.*.6$m**.&.*.*.*.*.*.*.b&F>G>*.*.&.*.*.*.*.*.*.*.*.*.H#*.*.*.'. at .J$H>3+)#I>@ @ + + + + ",
+". . + @ @ # E$J>A >.*.H.c.c.c.c.c.c.c.c.c.c.K>`.L>M>N>+.0.*.c.c.c.*.'.O>P>Q>R>S>f>'.T>U>V> #<%W>X>'>a.Y>Z>`> ,.,=%T-+,@,%.j%#;<*#,$,a.%,$;&,o+7>n%*,=,-,;,>,o#,,',),!,-%C#~,{,4>&.],^,/,(,a.+&_,|*:,&.*.>.G /@<,7%[,-*@ @ + + + + ",
+"+ + + @ @ @ },|,3;G.*.&.c.c.c.c.c.c.c.c.c.c.1,2,3,4,5,W+'+%.c.c.c.*.6,7,8,9,0,a,v at b,c,d,e,f,g,h,i,j,!>k,l,D&*.*.m,& n,o,p,q,U r,&+s,%.t,u,v,w,x,y,z,A,B,T=C,Y+D,E,:$F,G,3-H,I,J,j%K,L,M,N,O,P,Q,R,S,d.%.9.C T,U,V,W,H@@ @ + + + + ",
+". . + @ : @ X,Y,#%@.=.*.c.c.c.c.c.c.c.c.c.c.Z,`,2& '.'6. at .'.c.c.c.*.+'@'#'$'%'&'*'g;='-';'>',''')'v>!'o,~'9#S&@&{']'{>^'/'('Z-_':'v,<'['}'!+|'|*1'2'3'4'5'6'v;7'0&x=8'9'0'a'b'Z-c'd'e'f'g'h'i'j'k',.*.a.l'A m'w+n&n'@ @ @ + + + + ",
+". + + + + $ @ o'p'7.%.*.c.c.c.c.c.c.c.c.c.c.9#q'r's't'J$+.E&c.c.c.*.u'v'w'b.x'y'z'A'B'C'D'E'F'a.G'H'[%8'I'8$0.J'K'L'.&Q,M'N'O'j%P'Q'R'U S'T'U'V'W'X'#=Y'Z'G=`'B;,. ).)+)@)'.#)v-$)g$%)&)*)^*=)-)0';)*.0.E >)v ,)')))@ @ @ + + + + ",
+". . + @ : : @ !)-#6.$.b.c.c.c.c.c.c.c.c.c.c.b.~)2 at C@{)(#o$#.c.*.*.*.])^)/)()_):)<)[)})|)8,8,1)2)3)4)9#5)6)7)Q*8)9)0)K=a)E=V;b)c)d)e){,f)D%g)P'h)i)j)k)l)m)n)o)p)-$q)r)o+s)t)['u)v)U-7>w)x)A;y)z)A)k*%.#.W+R$B)|=`.C)@ @ @ + + + + ",
+". + + @ : : @ b%D)E)F.*.*.c.c.c.c.c.c.c.c.c.&.F)G)H)T$I)}&!.>.2;*.*.a.I.J)K)L)K$G.M)N)O)P)F#Q)R)S)i%=.a.T)U)V)W)I#X)Y);)*.Z)`)l at Y) !%,.!-.+!@!*%h'#!$!a'%!&!*!=!-!;!>!c.%),!'!'.,,r-%.)!!!~!{!]!^!=.E&,+!@-+m$/!(!%+@ + @ + + + + ",
+"+ + + @ @ @ @ p *+_%:%0.*.c.c.c.c.c.c.c.c.c.b.}%_!Z.:!<![!W@#.>.c.9 at c.&.a.E&a.-.=.1%>.=.*.=.=.I.b&m@'.b.-.-.b.c.&.*.*.&.*.c.=.b.&.=.=$l%l%6$H%;%F#=.*.b.<#b.%.*.=.c.*.&.-.*.=.b.&.*.c.c.b.-.*.J'b#'.#.B 5,B&}!i&A-@ @ @ @ + + + + ",
+". . + @ : : : + |!A at r@l'%.c.c.c.c.c.c.c.c.c.c.&.1!O&2!3!4!V at 5!G.'.*.'.a.c.%.>.*.&.b.6!D%*.c.H.&.G#I.~*K$=.*.H.H.H.H.H.H.H.H.H.H.H.H.*.&.&.a.*%b#P$7!D#@&*.-.-.*.H.H.H.H.H.H.H.H.H.H.H.H.H.*.*.`@1%8#8!]@9!0!a!O&< @ @ @ @ + + + + ",
+"+ + + + $ $ + + U b!c!K%>.*.c.c.c.c.c.c.c.c.c.b.j%d!r#e!f!O G&@.'+*.=.E&*.*.G.'.&.*.*.-%s$*.*.^*C#*.-.=%].I#c.*.c.H.H.H.H.H.H.H.H.H.*.*.*.=.g!a.-.b.0&u$X+X+:>O*c.&.=.*.H.H.H.H.H.H.H.H.H.*.&.h!E&@.z 4.i!j!k!A-@ @ @ @ @ + + + + ",
+". . + + $ $ $ + @ l!m!$#F.c.b.c.c.c.c.c.c.c.c.&.*.Y-N.n!o!p!E.q!s@>.=.r!c.*.*. #c.*.*.*.n+Z%I.E%&.*.*.*.%.5$X+G#*.*.*.H.H.H.H.H.H.H.H.H.*.}%J%*.*.*.*.*.&.a.s!*$X+X+t!p%*.*.*.*.*.H.H.H.&.*.%.}&m+g>u!w$v!w!g+< + @ @ @ @ + + + + ",
+". . + @ : : @ : + p +%x!J @#&.c.c.c.c.c.c.c.c.c.H.D%y!]$/!z!g A!N-##;.>.G.*.*.%.Z%&.&.*.&.F%;%%.*.&.H.H.*.*.*.S&N$9#&.*.*.c.H.H.H.H.H.*.*.X+ &*.*.H.H.H.&.&.*.*.*.-.])B!6$P$U&r$<#*.*.*.*.c.C!D!s>E!F!G!n&H!U + + @ @ @ @ + + + + ",
+"+ + + @ @ @ @ @ + @ u*I!J!X at 9&*.c.c.c.c.c.c.c.c.&.*.t!K,K!L!Q&M!N![%O!;.F.*.*.b.}%I.*.*.p%E%n+&&%.*.&.&.H.H.&.*.=.*&0#8$P!*.*.c.H.H.H.*.a.$**.*.H.H.H.H.H.H.H.H.H.*.*.*.*.*.0&b#$&Q!D#R!<#;.n at l+S!&@T!U!V!W!@ + @ @ @ @ @ + + + + ",
+". . + @ : : : $ : + p X!z@;+,+%.*.c.c.c.c.c.c.c.c.c.=.W#Y!Z!`! ~k&N>>$m+,+%.*.*.=.h%F#M$K$=.*.}%l**.&.&.H.H.H.H.*.c.c.9$:&j%c.&.H.H.H.*.6$.~c.*.H.H.H.H.H.H.H.H.H.H.H.H.H.H.&.=.=.*.+~l%n*+>@~#~$~%~&~Z!f$< + @ @ @ @ @ @ + + + + ",
+"+ + + + + $ @ + + + # H@*~t'=~F.=.*.c.c.c.c.c.c.c.c.c.c.-~;~>~,~'~9!)~W@!~8.=.c.*.D%n at i%=.&.*.*.s$~~%.*.*.H.H.H.*.*.c.=.I.q$*%I.&.*.*.d.3%=.*.&.H.H.H.H.H.H.H.H.H.H.H.H.H.H.&.*.*.*.=.9&7#{~=#]~2#^~z$n < @ @ @ @ @ @ @ @ + + + + ",
+". . + @ $ $ $ $ $ + + @ S /~x!G&F.a.*.c.c.c.c.c.c.c.c.*.&.(~_~U.:~<~H$*#[~}~#.>.^*K$i%r$9 at H.H.c.c.>.q%=%*.H.H.H.H.c.c.*.c.a.I.D#;%0&&.I%7>a.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.&.c.>.#.G ;%|~.-1~`#2~3~o @ + @ @ @ @ @ @ @ + + + + ",
+". + + @ : : @ : : : @ + @ 4~5~:@A #.%.*.c.c.c.c.c.c.c.c.*.%.6~]&7~8~>#9~0~M a~J v at a.*.>+<&*.b.H.*.*.%.P$b~*.H.H.H.H.H.c.c.*.*.a.}%*%o%J%a.&.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.&.*.c.G. at .C l+c~d~e~; U. at +< @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ @ @ @ @ @ + + f~1~t%a~6!%.*.*.c.c.c.c.c.c.c.c.*.g~*)[$'#h~e%p&U at m@+.##>.M$>+c.*.c.H.*.*.%.d&n%&.*.*.H.H.H.H.H.c.*.*.a.I#i~.~*.c.*.c.H.H.H.H.H.H.H.H.H.*.*.*.%.E&#.E J  -f#j~,)k~l~R+U @ + @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : : $ : : : @ $ @ @ e+m~n~a~8.;.c.c.c.c.c.c.c.c.*.*.=.o~R p~^&q~r~s~t~r at u~v~'+w~n%9 at c.H.H.H.*.&.#*m*=.*.H.H.H.H.H.H.H.*.c.H%0&i%:&.#%.*.H.H.H.H.H.H.H.H.H.&.%.0.#.v~B A#x~F!y~z~A~B~7;C~@ + @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + + + $ @ + + + + @ $ $ + # w%D~E~l+G G.c.c.c.c.c.c.c.*.*.*.=.X=F~z=G~j#H~4&I~U at J~8&+.2$6!0.%.*.H.*.c.d.G,D#a.b.H.H.H.H.H.*.c.K~6$=.b.c.5$L~9#-.*.H.H.H.&.*.%.>.'+ at .G M~A#a T at N~O~a+d+V$U @ + @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ $ $ $ $ $ $ $ @ $ $ + + # x&P~J>i~X at G.a.c.c.c.c.c.c.c.*.*.*.^*Q~R~ +S~~$T~U~D+a V~z W~s at O!$.0.%.&.*.d.5>&&&.*.H.H.H.*.&.{=H%&.*.*.*.a.=..~l@[*c.b.%.0.E&B#,+X~6.]@[!3=}=Y~F$Z~W,R+< @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". + + @ : @ @ : @ : : @ $ $ $ $ + # l#`~X$ {o$F.D&&.&.*.c.c.c.c.c.*.-.-..{+{`+@{#{}!${%{t'4%U@&{o$7.,+B#'+8#>.>.b#N$-.&.H.*.*.-.}&0&*.*.H.&.*.*.a.;.8>g!C%#.8.D n at 0$E)*{={-{Q@;{2!G~`+'&%+@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ @ @ @ @ @ $ $ $ $ $ + @ C~>{,{'{&#v@#.D&*.*.c.c.c.c.c.c.*.&. >){!{]&~{{{`!]{^{g#/{0~/@l+a~W+~.({F.6!#$0$l'8#9&'.u$4$>.>.9&8#8#G.#.B#F.E :%)~_{:{/@c~<{M&B%[{^~}{G~P.4@< @ + + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : : $ : : : @ $ $ $ $ $ $ $ $ $ x&|{1{O 3;+.#.>.*.c.c.c.c.c.c.c.c.*.D%2{{;`'3{U$a+4{5{6{7{8{w#x~A!]@9{;%_#0{a{X at u@b{A#E !.c{+.7.v at u~_#;%!@V at K#x~d{e{f{g{a>P@<+{$h{]+C)@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + + @ $ @ + @ @ + @ $ $ $ $ $ $ $ $ $ @ # i{j{k{l{n*+.9.%.*.*.c.c.c.c.c.c.*.*.}%m{n{A$o{Z.p{q{|+]{ 'r{s{t{p&u{v{(#*#w{x{y{z{V@, at Y$N>A!x#J!A{B{C{J>B&r{1~D{E{F{G{H{M at R+R#!%@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + $ $ $ $ $ $ : @ $ $ $ $ $ $ $ $ $ $ # @ u;I{J{I~e#z +.6!>.a.&.*.c.c.c.c.H.*.&.=.J.K{0- at +L{[,/%. at M{N{O{P{Q&Q{R{S{T{U{9~V{W{w*X{Y{Z{j~R{`{8* ].]+]y$@]/%P##]w%S#< @ @ @ @ @ @ @ @ @ + + + ]#]#@ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ . + + $ + + + + $ + @ $ $ $ $ $ $ $ $ $ $ $ $ @ # ))$]%]&]9;z{_#E l'$.a.*.*.*.*.*.&.&.F#a.}%*]=]-];]`+>][$z$,]D@'])]l$!]~]**{]]]^]/], F$+@(]k~_],]z$:]<]#+Q%[ R#@ @ + @ @ @ @ @ @ @ @ + @ @ < []< []@ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ @ @ @ @ @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + # o H{}]|]4!U at L _#+.l'>.%.%.*.*.*.*.c.&.*.<#(*H&1]2]3]R w%@+]&++^%4]} 5]`.6]7]`.8] +9]^+3 at 4~3~G at U < 0]a]b]c]@ + + @ + + @ + @ + + @ U d]e]f]g]h]i]@ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : : $ : : : @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ @ o j]k]l]m]9!n]V at G&I E F.E&9&%.%.&.*.*.c.c.b.b.b.0&X+o]Y>p]q]r]s]t]u]v]w]x]y]z]A]B]C]D] @S @ E]F]S#U G]@ @ @ + + + + + + + @ + @ H]I]J]K]L]M]@ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ : : @ + @ @ + @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + + + < N]O]P]Q]R]$~=@a{l+S]W at X@G t at T]:#m+'+'+'+#.B#F.8.,+U]D u~A +>V##%U at B{V]W]X]4~T ]#+ %+Y]Z]+ + q)`] ^.^+^@^E]#^$^%^&^*^=^-^;^>^,^'^)^@ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + $ $ $ $ $ $ $ @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + @ @ # @ ]+V !^~^{^]^B&i@^^c>*{U@(@x{-@/@/^A!x#l{f&*{=#D.(^_^:^<^D][^q.R#p @ # T @ + < }^|^+ + 1^2^3^4^5^6^o 7^8^9^0^a^b^U c^>^d^e^f^+ + + @ @ @ @ @ @ @ + + + + ",
+"+ . + + $ + + + + + $ @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ + @ @ # # < g^Y!c%h^B-, i^j^k^l^m^m^m$n^o^p^q^b>X!d>)%{## p @ @ + @ @ @ @ @ @ @ r^s^S < t^u^v^w^5^x^U 7^y^z^A^B^C^u;D^E^F^G^H^< @ + @ @ @ @ @ @ @ + + + + ",
+". . + @ : : : : : : : @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ @ @ @ @ @ # # p p # @ @ @ @ @ # @ @ # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ I^J^K^L^M^N^O^P^Q^R^Q^S^E^T^E^U^V^W^X^Y^Z^c]`^@ @ @ @ @ @ @ @ @ + + + + ",
+". . + @ : : @ : : : : @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ < @ @ @ @ < < @ @ @ @ < < < < < {#{#< @ < @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ @ @ @ + @ @ @ @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + @ @ @ + @ @ @ @ @ @ + + + + + + + + + @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + $ $ : $ $ $ : @ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+"+ + + @ + + + + @ + + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + + + ",
+". . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ",
+". . . + . . + . . . . + . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "};

Added: packages/openev/branches/upstream/current/pics/help.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/help.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/help.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,29 @@
+/* XPM */
+static char *help[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c None",
+"a c #ffff00",
+"b c #ffffff",
+/* pixels */
+"######################",
+"######################",
+"###....###........####",
+"##.aaaa.##.bbbbbb..###",
+"#.aa..aa.#.bbbbbb.b.##",
+"#.a.##.a.#.bbbbbb....#",
+"#...##.a.#.bbbbbbbbb.#",
+"#####.aa.#.bb...bbbb.#",
+"####.aa.##.bbbbbbbbb.#",
+"###.aa.###.bb...bbbb.#",
+"###.a.####.bbbbbbbbb.#",
+"###...####.bb...bbbb.#",
+"##########.bbbbbbbbb.#",
+"###...####.bbbbbbbbb.#",
+"###.a.####...........#",
+"###...################",
+"######################",
+"######################"
+};

Added: packages/openev/branches/upstream/current/pics/idle.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/idle.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/idle.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char *idle {
+/* width height num_colors chars_per_pixel */
+"    22    18        2            1",
+/* colors */
+"a c None",
+". c #00ff00",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaa....aaaaaaaaa",
+"aaaaaaa.......aaaaaaaa",
+"aaaaaa..........aaaaaa",
+"aaaaa...........aaaaaa",
+"aaaaaa..........aaaaaa",
+"aaaaaaa........aaaaaaa",
+"aaaaaaaaa....aaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/legend.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/legend.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/legend.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char * legend_xpm[] = {
+"22 18 6 1",
+" 	c None",
+".	c #000000",
+"+	c #0000FF",
+"@	c #FFFCFC",
+"#	c #FF0000",
+"$	c #008000",
+"                      ",
+"   ................   ",
+"   .++++++++++++++.   ",
+"   ................   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   .@##@@@@@@@@@@@.   ",
+"   .@##@@........ at .   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   .@$$@@@@@@@@@@@.   ",
+"   .@$$@@........ at .   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   . at ++@@@@@@@@@@@.   ",
+"   . at ++@@........ at .   ",
+"   .@@@@@@@@@@@@@@.   ",
+"   ................   ",
+"                      "};

Added: packages/openev/branches/upstream/current/pics/linear.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/linear.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/linear.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,31 @@
+/* XPM */
+static char *linear[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaa.................aa",
+"aaa.###############.aa",
+"aaa.aaaaaaaaa.#aaa#.aa",
+"aaa.aaaaaaaaa.#aaa#.aa",
+"aaa.aaaaaaaa.#aaaa#.aa",
+"aaa.aaaaaaaa.#aaaa#.aa",
+"aaa.aaaaaaa.#aaaaa#.aa",
+"aaa.aaaaaaa.#aaaaa#.aa",
+"aaa.aaaaaa.#aaaaaa#.aa",
+"aaa.aaaaaa.#aaaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa.................aa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};
+
+

Added: packages/openev/branches/upstream/current/pics/log.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/log.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/log.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,31 @@
+/* XPM */
+static char *linear[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaa.................aa",
+"aaa.############..#.aa",
+"aaa.aaaaaaaaa...###.aa",
+"aaa.aaaaaaa..##aaa#.aa",
+"aaa.aaaaaa.##aaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.a.#aaaaaaaaaaa#.aa",
+"aaa.a.#aaaaaaaaaaa#.aa",
+"aaa.a.#aaaaaaaaaaa#.aa",
+"aaa..#aaaaaaaaaaaa#.aa",
+"aaa..#aaaaaaaaaaaa#.aa",
+"aaa.................aa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};
+
+

Added: packages/openev/branches/upstream/current/pics/lower.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/lower.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/lower.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,24 @@
+/* XPM */
+static char * lower_xpm[] = {
+"22 18 3 1",
+" 	c None",
+".	c #000000",
+"+	c #7F7F7F",
+"                      ",
+"                      ",
+"                      ",
+"                      ",
+"                      ",
+"                      ",
+"    ..............    ",
+"     ............+    ",
+"      ..........+     ",
+"       ........+      ",
+"        ......+       ",
+"         ....+        ",
+"          ..+         ",
+"           +          ",
+"                      ",
+"                      ",
+"                      ",
+"                      "};

Added: packages/openev/branches/upstream/current/pics/new.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/new.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/new.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* XPM */
+static char * new_xpm[] = {
+"16 16 4 1",
+" 	c None",
+".	c #000000",
+"+	c #FFFFFF",
+"@	c #7F7F7F",
+"                ",
+"       ......   ",
+"      ..++++.@  ",
+"     .+.++++.@  ",
+"    .++.++++.@  ",
+"   .....++++.@  ",
+"   .++++++++.@  ",
+"   .++++++++.@  ",
+"   .++++++++.@  ",
+"   .++++++++.@  ",
+"   .++++++++.@  ",
+"   .++++++++.@  ",
+"   .++++++++.@  ",
+"   ..........@  ",
+"    @@@@@@@@@@  ",
+"                "};

Added: packages/openev/branches/upstream/current/pics/node_cursor.xbm
===================================================================
--- packages/openev/branches/upstream/current/pics/node_cursor.xbm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/node_cursor.xbm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+#define node_cursor_width 16
+#define node_cursor_height 16
+#define node_cursor_x_hot 3
+#define node_cursor_y_hot 1
+static unsigned char node_cursor_bits[] = {
+   0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00,
+   0xf8, 0x01, 0xf8, 0x03, 0xf8, 0x07, 0xf8, 0x0f, 0x78, 0x1f, 0x38, 0x38,
+   0x38, 0x00, 0x18, 0x00, 0x18, 0x00, 0x08, 0x00};

Added: packages/openev/branches/upstream/current/pics/node_cursor_mask.xbm
===================================================================
--- packages/openev/branches/upstream/current/pics/node_cursor_mask.xbm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/node_cursor_mask.xbm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,6 @@
+#define node_cursor_mask_width 16
+#define node_cursor_mask_height 16
+static unsigned char node_cursor_mask_bits[] = {
+    0x0c, 0x00, 0x1c, 0x00, 0x3c, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xfc, 0x01,
+    0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, 0xfc, 0x1f, 0xfc, 0x3f, 0x7c, 0x7e,
+    0x7c, 0x70, 0x3c, 0x00, 0x3c, 0x00, 0x1c, 0x00, };

Added: packages/openev/branches/upstream/current/pics/nonelut.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/nonelut.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/nonelut.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,28 @@
+/* XPM */
+static char *nonelut[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        3            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaa.................aa",
+"aaa.############....aa",
+"aaa.aaaaaaaaaaaa.##.aa",
+"aaa.aaaaaaaaaaa.#a#.aa",
+"aaa.aaaaaaaaaa.#aa#.aa",
+"aaa.aaaaaaaaa.#aaa#.aa",
+"aaa.aaaaaaaa.#aaaa#.aa",
+"aaa.aaaaaaa.#aaaaa#.aa",
+"aaa.aaaaaa.#aaaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.a.#aaaaaaaaaaa#.aa",
+"aaa..#aaaaaaaaaaaa#.aa",
+"aaa.................aa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/onetoone.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/onetoone.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/onetoone.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char *onetoone[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        2            1",
+/* colors */
+". c #000000",
+"# c None",
+/* pixels */
+"######################",
+"######################",
+"######################",
+"######################",
+"####..###########..###",
+"##....#########....###",
+"##....####..###....###",
+"####..####..#####..###",
+"####..###########..###",
+"####..###########..###",
+"####..####..#####..###",
+"####..####..#####..###",
+"####..###########..###",
+"####..###########..###",
+"######################",
+"######################",
+"######################",
+"######################"
+};

Added: packages/openev/branches/upstream/current/pics/openev.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/openev.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/openev.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2520 @@
+/* XPM */
+static char * openev_xpm[] = {
+"250 32 2485 2",
+"  	c None",
+". 	c #FFFFFF",
+"+ 	c #FEFEF8",
+"@ 	c #FDFEFB",
+"# 	c #FCFEFE",
+"$ 	c #FDFFFF",
+"% 	c #FEFFFE",
+"& 	c #FEFEFC",
+"* 	c #FCFCFA",
+"= 	c #FDFDFB",
+"- 	c #FCFBF9",
+"; 	c #FAF9F7",
+"> 	c #FAFAF8",
+", 	c #FBFAFA",
+"' 	c #FDFAFD",
+") 	c #FCFBFC",
+"! 	c #F8F8F9",
+"~ 	c #F9F9FB",
+"{ 	c #FDFAFA",
+"] 	c #FCF7F8",
+"^ 	c #FDFAF8",
+"/ 	c #FCFEF8",
+"( 	c #FCFFFD",
+"_ 	c #FCFFFF",
+": 	c #FFFFFE",
+"< 	c #FFFEFA",
+"[ 	c #FFFDFB",
+"} 	c #FEFDFE",
+"| 	c #FEFEFE",
+"1 	c #FDFEFE",
+"2 	c #FFFDFF",
+"3 	c #FEFAFA",
+"4 	c #FEFAFB",
+"5 	c #F8FBFE",
+"6 	c #F3F8FC",
+"7 	c #FBFDFC",
+"8 	c #FEFCF9",
+"9 	c #FDFCFB",
+"0 	c #FBFDFB",
+"a 	c #FBFDF9",
+"b 	c #FBFEF8",
+"c 	c #FBFDF8",
+"d 	c #FCFDF9",
+"e 	c #FCFEF9",
+"f 	c #FBFEFB",
+"g 	c #FDFCFA",
+"h 	c #FBFCFB",
+"i 	c #FCFCFD",
+"j 	c #FAFCFD",
+"k 	c #F9FCFD",
+"l 	c #FBFBFC",
+"m 	c #FDFBFB",
+"n 	c #FDFBF8",
+"o 	c #FAFEF9",
+"p 	c #FBFEFC",
+"q 	c #FEFBFE",
+"r 	c #FCFAFB",
+"s 	c #FBFCFA",
+"t 	c #F9FBFA",
+"u 	c #FAFDFC",
+"v 	c #F9FDFC",
+"w 	c #F7FCFD",
+"x 	c #F6FBFC",
+"y 	c #FEFDFB",
+"z 	c #FDFEFD",
+"A 	c #FDFDFE",
+"B 	c #FEFFFD",
+"C 	c #FEFEFF",
+"D 	c #FFFEFD",
+"E 	c #FEFDF9",
+"F 	c #FDFCF7",
+"G 	c #FBFCF9",
+"H 	c #F6FBFA",
+"I 	c #F2FCFD",
+"J 	c #EDFCFC",
+"K 	c #E8FAFD",
+"L 	c #E2F5FA",
+"M 	c #E2F3FA",
+"N 	c #E6F8FC",
+"O 	c #EDFCFE",
+"P 	c #EEFCFD",
+"Q 	c #F4FCFD",
+"R 	c #F8FAFC",
+"S 	c #FBFCFC",
+"T 	c #FDFFFD",
+"U 	c #FFFEFE",
+"V 	c #FFFEFB",
+"W 	c #FFFEFC",
+"X 	c #F0F3F7",
+"Y 	c #E0EFF5",
+"Z 	c #DDF4F9",
+"` 	c #E0F6F9",
+" .	c #E2F6FA",
+"..	c #DFF4FB",
+"+.	c #E0F2FB",
+"@.	c #E7EFFA",
+"#.	c #E8F0FB",
+"$.	c #E9F2FB",
+"%.	c #EAF2FC",
+"&.	c #E9F1FB",
+"*.	c #E5F4FA",
+"=.	c #E1F7F9",
+"-.	c #E2F6F9",
+";.	c #E2F6F8",
+">.	c #E2F5F7",
+",.	c #E3F5F7",
+"'.	c #E5F5F7",
+").	c #EDF8F9",
+"!.	c #FBFDFD",
+"~.	c #FEFBFB",
+"{.	c #FDFDFA",
+"].	c #FAFEFA",
+"^.	c #FBFEFD",
+"/.	c #FBFBFB",
+"(.	c #F3F9FB",
+"_.	c #E4F1F5",
+":.	c #DEF4F9",
+"<.	c #DCF3F8",
+"[.	c #DDF1F3",
+"}.	c #E8F6F8",
+"|.	c #F7FDFD",
+"1.	c #FDFCFD",
+"2.	c #FEFDFD",
+"3.	c #FBFEFE",
+"4.	c #FFFEFF",
+"5.	c #FFFDFC",
+"6.	c #FDFCF9",
+"7.	c #F9FDFB",
+"8.	c #F0FCFB",
+"9.	c #E2F4F8",
+"0.	c #ABCCD6",
+"a.	c #7DA7B7",
+"b.	c #5A8CA0",
+"c.	c #55889E",
+"d.	c #58889E",
+"e.	c #548496",
+"f.	c #578392",
+"g.	c #6F96A2",
+"h.	c #92B0BB",
+"i.	c #CCE2E9",
+"j.	c #EFFCFE",
+"k.	c #FAFDFF",
+"l.	c #FDFDFF",
+"m.	c #FEFCFF",
+"n.	c #FEFCFE",
+"o.	c #FFFDFD",
+"p.	c #FEFFFC",
+"q.	c #B2D0D4",
+"r.	c #588894",
+"s.	c #4E8B9D",
+"t.	c #478797",
+"u.	c #468595",
+"v.	c #428299",
+"w.	c #42829B",
+"x.	c #49849B",
+"y.	c #4C879E",
+"z.	c #4E89A0",
+"A.	c #4A859C",
+"B.	c #49869D",
+"C.	c #45839B",
+"D.	c #45849B",
+"E.	c #46839A",
+"F.	c #498298",
+"G.	c #4E8296",
+"H.	c #4D7D8E",
+"I.	c #8AA5AC",
+"J.	c #FDFAFB",
+"K.	c #FCFEFC",
+"L.	c #FEFAFE",
+"M.	c #FDF9F9",
+"N.	c #E3F3F8",
+"O.	c #7E9EAF",
+"P.	c #5C869A",
+"Q.	c #548399",
+"R.	c #4D7C90",
+"S.	c #4F777F",
+"T.	c #89A6AA",
+"U.	c #F3FCFE",
+"V.	c #FDFAFE",
+"W.	c #F8FEFE",
+"X.	c #FEFBFD",
+"Y.	c #F8FBFD",
+"Z.	c #E0F5FA",
+"`.	c #A8C6D2",
+" +	c #5B869B",
+".+	c #477F9A",
+"++	c #4483A3",
+"@+	c #3D82A5",
+"#+	c #3F85A7",
+"$+	c #3F83A3",
+"%+	c #3F83A2",
+"&+	c #3F819D",
+"*+	c #3D7993",
+"=+	c #3E748D",
+"-+	c #3F6E86",
+";+	c #7496A9",
+">+	c #D6E2E7",
+",+	c #F5FAFD",
+"'+	c #FBF9FC",
+")+	c #FEFCFD",
+"!+	c #FEFDFC",
+"~+	c #FEFEFD",
+"{+	c #FDFDFD",
+"]+	c #F5FEFE",
+"^+	c #B2D8E1",
+"/+	c #518EA1",
+"(+	c #3C8AA3",
+"_+	c #32839D",
+":+	c #307E97",
+"<+	c #2F7B9A",
+"[+	c #30799A",
+"}+	c #367B99",
+"|+	c #387D9B",
+"1+	c #3A7F9D",
+"2+	c #3B809E",
+"3+	c #3C819F",
+"4+	c #377C9A",
+"5+	c #367B9B",
+"6+	c #2B7295",
+"7+	c #317AA0",
+"8+	c #317CA3",
+"9+	c #357CA3",
+"0+	c #3E7EA0",
+"a+	c #2E6885",
+"b+	c #628390",
+"c+	c #F4FBFD",
+"d+	c #FEFCFC",
+"e+	c #FDFEFC",
+"f+	c #F4FCFE",
+"g+	c #CFE9F3",
+"h+	c #6393AE",
+"i+	c #4C87A5",
+"j+	c #43809D",
+"k+	c #336C84",
+"l+	c #25525D",
+"m+	c #95B3B8",
+"n+	c #EFF9FC",
+"o+	c #FBFAFD",
+"p+	c #F8FEFD",
+"q+	c #FEFAFD",
+"r+	c #FDFBFE",
+"s+	c #F6FAFC",
+"t+	c #E5F3F7",
+"u+	c #88AFC3",
+"v+	c #5E8DA7",
+"w+	c #4D83A3",
+"x+	c #3F7C9E",
+"y+	c #397599",
+"z+	c #316B8E",
+"A+	c #2E6889",
+"B+	c #336D8E",
+"C+	c #357191",
+"D+	c #367595",
+"E+	c #3B7B9A",
+"F+	c #3C7A9B",
+"G+	c #3B7698",
+"H+	c #3F6F8A",
+"I+	c #637D8A",
+"J+	c #E7F4F8",
+"K+	c #F6F8FA",
+"L+	c #FEFCFB",
+"M+	c #FCFFFE",
+"N+	c #FCFDFE",
+"O+	c #FCFDFD",
+"P+	c #FCFEFD",
+"Q+	c #F9FCFE",
+"R+	c #ADBDD2",
+"S+	c #6186A6",
+"T+	c #457CA0",
+"U+	c #3F7697",
+"V+	c #3F6C8C",
+"W+	c #2F5576",
+"X+	c #345678",
+"Y+	c #365774",
+"Z+	c #355673",
+"`+	c #375875",
+" @	c #345673",
+".@	c #385A70",
+"+@	c #325869",
+"@@	c #55839A",
+"#@	c #477A97",
+"$@	c #437695",
+"%@	c #49748F",
+"&@	c #33576B",
+"*@	c #4E6770",
+"=@	c #F1FCFE",
+"-@	c #FBFBFE",
+";@	c #FAFBFD",
+">@	c #ECFAFD",
+",@	c #95B9CD",
+"'@	c #578FB0",
+")@	c #4582A4",
+"!@	c #3C7590",
+"~@	c #1D4C5E",
+"{@	c #46656D",
+"]@	c #DFF1F4",
+"^@	c #F2F8FB",
+"/@	c #FBFAFC",
+"(@	c #FAFEFE",
+"_@	c #F9FDFE",
+":@	c #EFFBFC",
+"<@	c #A0BAC5",
+"[@	c #5F8FAA",
+"}@	c #5285A4",
+"|@	c #457898",
+"1@	c #427190",
+"2@	c #315671",
+"3@	c #385469",
+"4@	c #445A6D",
+"5@	c #415A70",
+"6@	c #45657C",
+"7@	c #466E87",
+"8@	c #497995",
+"9@	c #437896",
+"0@	c #457B9D",
+"a@	c #427693",
+"b@	c #416877",
+"c@	c #91A8AE",
+"d@	c #F6FCFC",
+"e@	c #FEFDFA",
+"f@	c #F9FEFE",
+"g@	c #F8FDFE",
+"h@	c #FBFDFE",
+"i@	c #FAFDFE",
+"j@	c #F8FCFE",
+"k@	c #95A2BB",
+"l@	c #6F91B3",
+"m@	c #4F7FA2",
+"n@	c #497491",
+"o@	c #3E596F",
+"p@	c #525F73",
+"q@	c #758191",
+"r@	c #728590",
+"s@	c #748892",
+"t@	c #768A94",
+"u@	c #788C96",
+"v@	c #758993",
+"w@	c #7A898E",
+"x@	c #859596",
+"y@	c #A0BCC8",
+"z@	c #557D93",
+"A@	c #4E7994",
+"B@	c #567C94",
+"C@	c #3F5E70",
+"D@	c #506971",
+"E@	c #EEFCFE",
+"F@	c #F9FAFE",
+"G@	c #CDE1E9",
+"H@	c #6E97B1",
+"I@	c #5D94B8",
+"J@	c #497FA1",
+"K@	c #3E677A",
+"L@	c #2C444B",
+"M@	c #B6BCC1",
+"N@	c #F6F8FB",
+"O@	c #F7F9FB",
+"P@	c #FEFEFB",
+"Q@	c #C3DAE3",
+"R@	c #7598A9",
+"S@	c #5F91AB",
+"T@	c #4D7D99",
+"U@	c #4C758D",
+"V@	c #314E62",
+"W@	c #495765",
+"X@	c #BABBC1",
+"Y@	c #E7E4E7",
+"Z@	c #E8E7ED",
+"`@	c #DBE1E9",
+" #	c #A0B0BC",
+".#	c #5C798C",
+"+#	c #577E96",
+"@#	c #4D7B98",
+"##	c #4D7F9B",
+"$#	c #396776",
+"%#	c #648188",
+"&#	c #F1FAF9",
+"*#	c #F2F5F7",
+"=#	c #D3E1E6",
+"-#	c #CBDFE6",
+";#	c #CDDFE5",
+">#	c #CEDCE3",
+",#	c #D1DDE4",
+"'#	c #F1FAFD",
+")#	c #F3FCFD",
+"!#	c #D9E7EC",
+"~#	c #CCE0E7",
+"{#	c #C9DEE6",
+"]#	c #CCE1E8",
+"^#	c #C9DDE5",
+"/#	c #D4E6EB",
+"(#	c #E8F7F9",
+"_#	c #F6FDFE",
+":#	c #FAFCFC",
+"<#	c #F7FDFE",
+"[#	c #F0F8F9",
+"}#	c #DEEEF2",
+"|#	c #D2E6ED",
+"1#	c #CADEE4",
+"2#	c #CBDEE4",
+"3#	c #CBDFE5",
+"4#	c #CFE3E8",
+"5#	c #D9E8EC",
+"6#	c #E2EEF0",
+"7#	c #F0F9FB",
+"8#	c #FCFCFB",
+"9#	c #FAF9F8",
+"0#	c #D9E1E7",
+"a#	c #C3CFD6",
+"b#	c #BDCCD5",
+"c#	c #BDCDD8",
+"d#	c #BDCBD7",
+"e#	c #DAE4ED",
+"f#	c #F2FBFE",
+"g#	c #E4EEF4",
+"h#	c #C6D3DC",
+"i#	c #C0CDD6",
+"j#	c #C0CFD7",
+"k#	c #BFCED7",
+"l#	c #C1CED6",
+"m#	c #D7DFE3",
+"n#	c #F3F8FA",
+"o#	c #7DA0B5",
+"p#	c #6498B1",
+"q#	c #49869E",
+"r#	c #447988",
+"s#	c #35585E",
+"t#	c #B1C1C3",
+"u#	c #F6FDFD",
+"v#	c #F9FEFD",
+"w#	c #FAFEFD",
+"x#	c #F5FDFD",
+"y#	c #EDFDFE",
+"z#	c #ACCEDA",
+"A#	c #5B8CA6",
+"B#	c #5087A8",
+"C#	c #5083A2",
+"D#	c #3B6680",
+"E#	c #4D6A79",
+"F#	c #F9FBFE",
+"G#	c #FEFBFC",
+"H#	c #ECF8FC",
+"I#	c #95AFBE",
+"J#	c #7099B4",
+"K#	c #5688A8",
+"L#	c #517D98",
+"M#	c #334D5A",
+"N#	c #73797B",
+"O#	c #F9F4F8",
+"P#	c #E1F2F6",
+"Q#	c #8FB0C2",
+"R#	c #6D9BB5",
+"S#	c #5A8DA3",
+"T#	c #578293",
+"U#	c #3C5862",
+"V#	c #4F5D62",
+"W#	c #DFE4E6",
+"X#	c #FCFCFC",
+"Y#	c #FCFDFC",
+"Z#	c #F4FAFC",
+"`#	c #ACBDCA",
+" $	c #65859A",
+".$	c #5783A0",
+"+$	c #5687A2",
+"@$	c #47707F",
+"#$	c #5A7179",
+"$$	c #E2E9E9",
+"%$	c #F0FAFD",
+"&$	c #94B0BD",
+"*$	c #6F9AAF",
+"=$	c #6F93A7",
+"-$	c #6F8C9F",
+";$	c #6B8393",
+">$	c #C1D4DA",
+",$	c #C3D7DE",
+"'$	c #819EAD",
+")$	c #7296AF",
+"!$	c #6F97B1",
+"~$	c #6E96B1",
+"{$	c #6B8FA8",
+"]$	c #69879A",
+"^$	c #839BA9",
+"/$	c #D0DEE2",
+"($	c #F9FBF9",
+"_$	c #FDF9F1",
+":$	c #FDFCF6",
+"<$	c #F0FAFA",
+"[$	c #C8E1E7",
+"}$	c #8CACB5",
+"|$	c #729AAD",
+"1$	c #6A93AA",
+"2$	c #6F93A8",
+"3$	c #7395A8",
+"4$	c #7698AB",
+"5$	c #7299AC",
+"6$	c #6D98AC",
+"7$	c #6F93A5",
+"8$	c #7593A1",
+"9$	c #8BA1AB",
+"0$	c #C6D2D7",
+"a$	c #FCFAF7",
+"b$	c #FCFBFD",
+"c$	c #D5E2EA",
+"d$	c #7E9CAC",
+"e$	c #749EB5",
+"f$	c #7099B5",
+"g$	c #6D8EAB",
+"h$	c #627A94",
+"i$	c #D3E6F5",
+"j$	c #94ACBD",
+"k$	c #7998AD",
+"l$	c #7498B1",
+"m$	c #7299B3",
+"n$	c #6D92A8",
+"o$	c #6C8797",
+"p$	c #96A5B0",
+"q$	c #F8F8FC",
+"r$	c #CEE5EC",
+"s$	c #799FB4",
+"t$	c #6A94AB",
+"u$	c #60879D",
+"v$	c #4E6D7E",
+"w$	c #475862",
+"x$	c #E7ECED",
+"y$	c #FDFCFC",
+"z$	c #B9D1D8",
+"A$	c #688F9E",
+"B$	c #5F8CA0",
+"C$	c #5E869A",
+"D$	c #49697B",
+"E$	c #596C7A",
+"F$	c #F3FDFE",
+"G$	c #FDFAF5",
+"H$	c #FBFDFA",
+"I$	c #F5FDFE",
+"J$	c #C3DBE5",
+"K$	c #81A3B8",
+"L$	c #6C93AA",
+"M$	c #648CA1",
+"N$	c #456575",
+"O$	c #3A4B55",
+"P$	c #D5D8DA",
+"Q$	c #F5FCFD",
+"R$	c #BED0D8",
+"S$	c #88A9BC",
+"T$	c #6892AB",
+"U$	c #628BA2",
+"V$	c #48697A",
+"W$	c #3F525B",
+"X$	c #C6CFD2",
+"Y$	c #F8FBFB",
+"Z$	c #F8F9FA",
+"`$	c #F7FDFF",
+" %	c #CEDDE4",
+".%	c #7290A3",
+"+%	c #628DA6",
+"@%	c #5D8BA4",
+"#%	c #517685",
+"$%	c #556B72",
+"%%	c #D8DDDD",
+"&%	c #F2FDFE",
+"*%	c #9EBAC9",
+"=%	c #749EB3",
+"-%	c #6D91A6",
+";%	c #648396",
+">%	c #5C7587",
+",%	c #B8CED9",
+"'%	c #9DB4BF",
+")%	c #87A4B5",
+"!%	c #7195AD",
+"~%	c #6B92AD",
+"{%	c #6B93B0",
+"]%	c #688DA7",
+"^%	c #68899E",
+"/%	c #5F7989",
+"(%	c #85949C",
+"_%	c #F4F8F8",
+":%	c #FDFBF7",
+"<%	c #D4E3EB",
+"[%	c #8DACB9",
+"}%	c #7CA0AF",
+"|%	c #6F98AE",
+"1%	c #688FA9",
+"2%	c #618299",
+"3%	c #5F7D92",
+"4%	c #607F94",
+"5%	c #64889E",
+"6%	c #6C96AB",
+"7%	c #6F95A9",
+"8%	c #7292A4",
+"9%	c #6B8593",
+"0%	c #697A83",
+"a%	c #ECF3F5",
+"b%	c #F9F9F9",
+"c%	c #FAF8F7",
+"d%	c #D6E4EB",
+"e%	c #85A4B4",
+"f%	c #749DB5",
+"g%	c #6891AB",
+"h%	c #64859F",
+"i%	c #637A93",
+"j%	c #A0B9CA",
+"k%	c #7B99AA",
+"l%	c #7194A9",
+"m%	c #6E95AC",
+"n%	c #6A94AC",
+"o%	c #6A96AD",
+"p%	c #6992A7",
+"q%	c #60808F",
+"r%	c #5B6C77",
+"s%	c #D4D5D9",
+"t%	c #F5FCFE",
+"u%	c #BBD0DA",
+"v%	c #81A6BD",
+"w%	c #6D93AB",
+"x%	c #66899F",
+"y%	c #526D7E",
+"z%	c #5B6A72",
+"A%	c #BFD3DB",
+"B%	c #7296A5",
+"C%	c #6993A5",
+"D%	c #688DA0",
+"E%	c #537080",
+"F%	c #5F707B",
+"G%	c #EFF7FA",
+"H%	c #E7F2F7",
+"I%	c #99B4C5",
+"J%	c #7DA0B6",
+"K%	c #6E93A9",
+"L%	c #5B7D91",
+"M%	c #354E5C",
+"N%	c #869298",
+"O%	c #F7F7F8",
+"P%	c #EBF4F7",
+"Q%	c #A0B4BE",
+"R%	c #89A8BA",
+"S%	c #6D91A9",
+"T%	c #68849A",
+"U%	c #485C6C",
+"V%	c #7D8890",
+"W%	c #F8FBFC",
+"X%	c #F7F7F7",
+"Y%	c #F7F8FA",
+"Z%	c #FAFDFD",
+"`%	c #F9FEFF",
+" &	c #DCE9F0",
+".&	c #86A0B1",
+"+&	c #6F97AD",
+"@&	c #6791A8",
+"#&	c #567786",
+"$&	c #5A6E75",
+"%&	c #DBE1E0",
+"&&	c #FAFAF7",
+"*&	c #FBFEFF",
+"=&	c #EBF7FB",
+"-&	c #99B4C3",
+";&	c #7BA1B6",
+">&	c #6C8DA1",
+",&	c #5F7B8F",
+"'&	c #5D7585",
+")&	c #A9BFCE",
+"!&	c #8197A4",
+"~&	c #67808F",
+"{&	c #638095",
+"]&	c #6889A1",
+"^&	c #6A8FA9",
+"/&	c #668AA3",
+"(&	c #6F8FA4",
+"_&	c #5A7687",
+":&	c #677780",
+"<&	c #DCE0E2",
+"[&	c #F9F9FA",
+"}&	c #ECF4F8",
+"|&	c #A4BACA",
+"1&	c #86A7BD",
+"2&	c #6A8FA4",
+"3&	c #69899E",
+"4&	c #4C687D",
+"5&	c #465C6E",
+"6&	c #5B6D7E",
+"7&	c #617585",
+"8&	c #637C8D",
+"9&	c #6A8999",
+"0&	c #6D8FA2",
+"a&	c #7192A4",
+"b&	c #648190",
+"c&	c #566C77",
+"d&	c #B9C5CA",
+"e&	c #FBFAFB",
+"f&	c #C4D3D9",
+"g&	c #8EAAB9",
+"h&	c #7499AE",
+"i&	c #6A8DA4",
+"j&	c #6E899E",
+"k&	c #617588",
+"l&	c #657D8B",
+"m&	c #5B7485",
+"n&	c #557385",
+"o&	c #67899D",
+"p&	c #6E91A5",
+"q&	c #7095A9",
+"r&	c #6D92A6",
+"s&	c #648492",
+"t&	c #53656F",
+"u&	c #ABAFB3",
+"v&	c #F2F8FD",
+"w&	c #ACBECC",
+"x&	c #89ACC5",
+"y&	c #7296AE",
+"z&	c #6C8CA1",
+"A&	c #5A7383",
+"B&	c #86959E",
+"C&	c #F4F9FA",
+"D&	c #FDFDFC",
+"E&	c #C6D8DE",
+"F&	c #7A9BA9",
+"G&	c #7298A9",
+"H&	c #7192A3",
+"I&	c #5B7583",
+"J&	c #617078",
+"K&	c #E9F0F1",
+"L&	c #F9FBF8",
+"M&	c #BED0DF",
+"N&	c #89A7BB",
+"O&	c #7B9CB2",
+"P&	c #7090A5",
+"Q&	c #4C6878",
+"R&	c #51636E",
+"S&	c #E3E8EB",
+"T&	c #FBF9F8",
+"U&	c #FCFBFA",
+"V&	c #F6FBFD",
+"W&	c #D3DFE5",
+"X&	c #9CB2BD",
+"Y&	c #84A1B1",
+"Z&	c #7896AA",
+"`&	c #637688",
+" *	c #485460",
+".*	c #D0D4D9",
+"+*	c #FDFFFE",
+"@*	c #F8FDFF",
+"#*	c #DBE6EA",
+"$*	c #8CA3B1",
+"%*	c #7799AD",
+"&*	c #5B7686",
+"**	c #63737A",
+"=*	c #E5EAE9",
+"-*	c #DEECF2",
+";*	c #95AEBE",
+">*	c #7FA3B5",
+",*	c #7794A7",
+"'*	c #687F91",
+")*	c #687B89",
+"!*	c #728390",
+"~*	c #62747D",
+"{*	c #687A85",
+"]*	c #687D8A",
+"^*	c #698396",
+"/*	c #7392A8",
+"(*	c #7292A8",
+"_*	c #7593A8",
+":*	c #668092",
+"<*	c #606F79",
+"[*	c #C7CDD0",
+"}*	c #CCD8E3",
+"|*	c #96AFC5",
+"1*	c #7B9CB5",
+"2*	c #7191A6",
+"3*	c #556977",
+"4*	c #444F58",
+"5*	c #B8BFC6",
+"6*	c #DEE1E5",
+"7*	c #DBE0E3",
+"8*	c #CFD8DC",
+"9*	c #9DACB3",
+"0*	c #7592A0",
+"a*	c #7393A4",
+"b*	c #6D8A9A",
+"c*	c #546B79",
+"d*	c #9EACB1",
+"e*	c #B5C5CC",
+"f*	c #90ABB8",
+"g*	c #7495A7",
+"h*	c #7896A8",
+"i*	c #617786",
+"j*	c #45535F",
+"k*	c #7D8895",
+"l*	c #9DA8B2",
+"m*	c #9FADB9",
+"n*	c #8294A4",
+"o*	c #7A8FA1",
+"p*	c #7E96A9",
+"q*	c #7C97A9",
+"r*	c #63808D",
+"s*	c #51636B",
+"t*	c #C8CCCE",
+"u*	c #FBFAFE",
+"v*	c #EFF4FA",
+"w*	c #A3B3C2",
+"x*	c #8BA9C0",
+"y*	c #7796AD",
+"z*	c #7591A5",
+"A*	c #6D8393",
+"B*	c #9CACB4",
+"C*	c #E2EBEF",
+"D*	c #E2EAEE",
+"E*	c #E3ECF0",
+"F*	c #E6EDF0",
+"G*	c #E6ECEE",
+"H*	c #E6EAEB",
+"I*	c #E9EAEB",
+"J*	c #E8E8E8",
+"K*	c #FBFBFA",
+"L*	c #D1E0E6",
+"M*	c #85A2AF",
+"N*	c #7A9BAB",
+"O*	c #7A97A6",
+"P*	c #627985",
+"Q*	c #667377",
+"R*	c #E9EEED",
+"S*	c #FCFCF8",
+"T*	c #FDFCF8",
+"U*	c #E1EEF7",
+"V*	c #9AB1C6",
+"W*	c #8BA9BE",
+"X*	c #7B99AC",
+"Y*	c #607B8C",
+"Z*	c #435562",
+"`*	c #A6AFB7",
+" =	c #F8F9FB",
+".=	c #FEFEFA",
+"+=	c #F6FEFE",
+"@=	c #B6C6D0",
+"#=	c #9BB1BE",
+"$=	c #84A0AE",
+"%=	c #7C96A6",
+"&=	c #5C6A77",
+"*=	c #676F78",
+"==	c #F4F5F7",
+"-=	c #FBFFFE",
+";=	c #D7E1E6",
+">=	c #92A6B2",
+",=	c #82A0B0",
+"'=	c #7A9AAC",
+")=	c #5D7281",
+"!=	c #647178",
+"~=	c #F2F6F6",
+"{=	c #FCFEFB",
+"]=	c #CDDBE3",
+"^=	c #99B2C1",
+"/=	c #7E9EB0",
+"(=	c #6F8899",
+"_=	c #546674",
+":=	c #79858E",
+"<=	c #CCD4DA",
+"[=	c #E6EDEF",
+"}=	c #E7EEF0",
+"|=	c #E5EDF0",
+"1=	c #B4C3CD",
+"2=	c #788FA0",
+"3=	c #7994A8",
+"4=	c #7D95A8",
+"5=	c #718798",
+"6=	c #67767F",
+"7=	c #C4CBCD",
+"8=	c #F1F9FC",
+"9=	c #ACBBC9",
+"0=	c #98B2C7",
+"a=	c #7E9AB0",
+"b=	c #6A8291",
+"c=	c #525D63",
+"d=	c #C8CBCE",
+"e=	c #F7F6F7",
+"f=	c #FCF9F8",
+"g=	c #FCFBF8",
+"h=	c #F7F8F6",
+"i=	c #EDF5F5",
+"j=	c #91A9B2",
+"k=	c #86A3B0",
+"l=	c #728E9C",
+"m=	c #5C7280",
+"n=	c #98A5AB",
+"o=	c #EEF8F9",
+"p=	c #A7B9BF",
+"q=	c #93ACB9",
+"r=	c #7D99AA",
+"s=	c #718897",
+"t=	c #4E5E66",
+"u=	c #A7AFB3",
+"v=	c #F7F7FB",
+"w=	c #FAF9FC",
+"x=	c #E2E8EF",
+"y=	c #95A1AD",
+"z=	c #8D9FAE",
+"A=	c #8198A9",
+"B=	c #607884",
+"C=	c #53626A",
+"D=	c #E7ECEE",
+"E=	c #FAFBFE",
+"F=	c #E4EAF1",
+"G=	c #A2B1C1",
+"H=	c #8DA5BA",
+"I=	c #839BAF",
+"J=	c #8197A9",
+"K=	c #8094A4",
+"L=	c #8395A1",
+"M=	c #96A7B1",
+"N=	c #98A8B1",
+"O=	c #9BAAB7",
+"P=	c #9EADB8",
+"Q=	c #9EA9B2",
+"R=	c #A5ADB4",
+"S=	c #979DA2",
+"T=	c #9A9FA3",
+"U=	c #F7F8F8",
+"V=	c #D5E3E8",
+"W=	c #8EA6B3",
+"X=	c #829FAE",
+"Y=	c #8099A6",
+"Z=	c #6A7D86",
+"`=	c #6B7679",
+" -	c #EBEEED",
+".-	c #F9F8F6",
+"+-	c #B7CAD7",
+"@-	c #94B1C8",
+"#-	c #83A1B3",
+"$-	c #78909E",
+"%-	c #546672",
+"&-	c #6A747C",
+"*-	c #EEF1F5",
+"=-	c #FBFAF7",
+"--	c #FEFCF8",
+";-	c #EDF8FB",
+">-	c #ABBEC9",
+",-	c #9CB3C0",
+"'-	c #8AA3B0",
+")-	c #7A929E",
+"!-	c #586870",
+"~-	c #98A1A5",
+"{-	c #D2DADF",
+"]-	c #9EB0B9",
+"^-	c #85A1AD",
+"/-	c #7B97A7",
+"(-	c #5F7080",
+"_-	c #788288",
+":-	c #BBCAD5",
+"<-	c #9CB4C4",
+"[-	c #809EAF",
+"}-	c #6D8392",
+"|-	c #53616B",
+"1-	c #D0D5D9",
+"2-	c #F9F9F8",
+"3-	c #F9F7F3",
+"4-	c #F8F4F0",
+"5-	c #FAFAFA",
+"6-	c #F2F9FC",
+"7-	c #AAB9C5",
+"8-	c #8CA1B1",
+"9-	c #869AAA",
+"0-	c #768997",
+"a-	c #65757B",
+"b-	c #C3CCCD",
+"c-	c #DFE9EC",
+"d-	c #A8BAC7",
+"e-	c #8EA7BA",
+"f-	c #839BAB",
+"g-	c #697880",
+"h-	c #818C8F",
+"i-	c #EBF1F3",
+"j-	c #F5F7F8",
+"k-	c #FAF9F9",
+"l-	c #F1FBFB",
+"m-	c #A7BCC2",
+"n-	c #93ACB7",
+"o-	c #77909D",
+"p-	c #5B707D",
+"q-	c #9EABB2",
+"r-	c #FCF9F7",
+"s-	c #E2EDED",
+"t-	c #A8BAC0",
+"u-	c #95ACB8",
+"v-	c #88A0AE",
+"w-	c #6C7F8A",
+"x-	c #545F63",
+"y-	c #DEE2E1",
+"z-	c #FCFBF5",
+"A-	c #F8F9F4",
+"B-	c #EBF2F1",
+"C-	c #ACBBC0",
+"D-	c #98B0BA",
+"E-	c #809BA9",
+"F-	c #617381",
+"G-	c #727D86",
+"H-	c #F8FCFC",
+"I-	c #D0DBE3",
+"J-	c #A6B6C4",
+"K-	c #97A8BA",
+"L-	c #8EA0B0",
+"M-	c #899BAB",
+"N-	c #899CAB",
+"O-	c #8399A7",
+"P-	c #859DAC",
+"Q-	c #88A0AF",
+"R-	c #8CA1B2",
+"S-	c #8FA2B3",
+"T-	c #90A1AF",
+"U-	c #8F9CA6",
+"V-	c #657078",
+"W-	c #9CA4AB",
+"X-	c #DBE6E9",
+"Y-	c #98ACB9",
+"Z-	c #8AA2B2",
+"`-	c #899FAB",
+" ;	c #788990",
+".;	c #747D80",
+"+;	c #DDE6EA",
+"@;	c #A3B9C7",
+"#;	c #8DAFC5",
+"$;	c #7F9BAA",
+"%;	c #687B84",
+"&;	c #4D5860",
+"*;	c #CACED2",
+"=;	c #F9F8FB",
+"-;	c #DEEAEF",
+";;	c #B0C3D0",
+">;	c #A0B5C3",
+",;	c #95A8B6",
+"';	c #758790",
+");	c #5A676D",
+"!;	c #CCD4D6",
+"~;	c #FAFCFA",
+"{;	c #C6CED5",
+"];	c #A7B6C0",
+"^;	c #8CA3B0",
+"/;	c #8097A5",
+"(;	c #64727F",
+"_;	c #949BA1",
+":;	c #F8FCFA",
+"<;	c #F1FAFB",
+"[;	c #A2B8C8",
+"};	c #839DAE",
+"|;	c #687A88",
+"1;	c #606A73",
+"2;	c #FBFAF8",
+"3;	c #FDF9F5",
+"4;	c #FEF9F3",
+"5;	c #FCFBF7",
+"6;	c #C2D1D8",
+"7;	c #9CAFBC",
+"8;	c #91A1AF",
+"9;	c #808E9C",
+"0;	c #707B82",
+"a;	c #D3DDDE",
+"b;	c #CCD7DC",
+"c;	c #ADBCC8",
+"d;	c #99ADBC",
+"e;	c #8B9FAB",
+"f;	c #7E8B91",
+"g;	c #ABB7BC",
+"h;	c #E0EAF0",
+"i;	c #E3EBEF",
+"j;	c #E9EFF2",
+"k;	c #EAF0F3",
+"l;	c #E7F1F3",
+"m;	c #E3F1F4",
+"n;	c #B8CCD4",
+"o;	c #9FB6BE",
+"p;	c #7B8E98",
+"q;	c #5B6A73",
+"r;	c #C1C8CB",
+"s;	c #FAFBFB",
+"t;	c #D8E3E6",
+"u;	c #B1C2C8",
+"v;	c #9AAEB9",
+"w;	c #90A5B0",
+"x;	c #68787F",
+"y;	c #727D7D",
+"z;	c #F2F6F3",
+"A;	c #FDFCF5",
+"B;	c #FCFDF5",
+"C;	c #FBFDF6",
+"D;	c #E2EBE8",
+"E;	c #B1C1C4",
+"F;	c #9EB5BE",
+"G;	c #869EAB",
+"H;	c #63717C",
+"I;	c #929BA2",
+"J;	c #F9FDFD",
+"K;	c #C4CFD7",
+"L;	c #B0BDCA",
+"M;	c #A0ADBC",
+"N;	c #96A4B0",
+"O;	c #8B9CA4",
+"P;	c #75878E",
+"Q;	c #687C86",
+"R;	c #647B87",
+"S;	c #627786",
+"T;	c #637684",
+"U;	c #637380",
+"V;	c #63727E",
+"W;	c #5B6771",
+"X;	c #5B676F",
+"Y;	c #B6BFC6",
+"Z;	c #DFE8EA",
+"`;	c #9FB0BB",
+" >	c #95ABB7",
+".>	c #93A7AF",
+"+>	c #809093",
+"@>	c #7C8485",
+"#>	c #DDDDDF",
+"$>	c #F5F9FA",
+"%>	c #C5D0D6",
+"&>	c #A8BDC9",
+"*>	c #8CA8BA",
+"=>	c #8498A4",
+"->	c #535F66",
+";>	c #858C92",
+">>	c #FCFCF7",
+",>	c #F8FDFD",
+"'>	c #D5E3EA",
+")>	c #B7C9D6",
+"!>	c #A6B7C5",
+"~>	c #A2ADB8",
+"{>	c #798289",
+"]>	c #747D81",
+"^>	c #EBF0F2",
+"/>	c #FCFDFB",
+"(>	c #FFFFFB",
+"_>	c #F3F8FB",
+":>	c #C4CED6",
+"<>	c #B0BDC8",
+"[>	c #96A6B4",
+"}>	c #8797A3",
+"|>	c #69737A",
+"1>	c #B1B6B8",
+"2>	c #EAF3F6",
+"3>	c #B8C9D0",
+"4>	c #A8BBC9",
+"5>	c #8E9FB0",
+"6>	c #65737F",
+"7>	c #818C91",
+"8>	c #FDFBF9",
+"9>	c #F8FDF9",
+"0>	c #F2FDFD",
+"a>	c #C1D4D9",
+"b>	c #A9BCC5",
+"c>	c #99A9B5",
+"d>	c #89929E",
+"e>	c #767C83",
+"f>	c #C3CCD2",
+"g>	c #B6C1CA",
+"h>	c #A0ADB7",
+"i>	c #A3B0BB",
+"j>	c #97A4AE",
+"k>	c #9CA7B0",
+"l>	c #B0BAC3",
+"m>	c #B2BCC5",
+"n>	c #B9C2CB",
+"o>	c #BCC6CF",
+"p>	c #BFC9D2",
+"q>	c #BFCAD3",
+"r>	c #B7CAD5",
+"s>	c #9EB0B8",
+"t>	c #747F84",
+"u>	c #606568",
+"v>	c #E7E8E9",
+"w>	c #FBFCFD",
+"x>	c #D4DBE2",
+"y>	c #BCC7D0",
+"z>	c #A6B4BC",
+"A>	c #97A6AD",
+"B>	c #697679",
+"C>	c #919A99",
+"D>	c #F9FDFA",
+"E>	c #DDE3E5",
+"F>	c #C0C9D0",
+"G>	c #ABB5C0",
+"H>	c #8A95A1",
+"I>	c #666F73",
+"J>	c #C2C7C9",
+"K>	c #F9FBFD",
+"L>	c #C9CDD5",
+"M>	c #B9C1CD",
+"N>	c #A4AFBB",
+"O>	c #9DAAAF",
+"P>	c #73817F",
+"Q>	c #53615B",
+"R>	c #899795",
+"S>	c #8C999E",
+"T>	c #919CA3",
+"U>	c #959BA0",
+"V>	c #979EA3",
+"W>	c #999FA5",
+"X>	c #9BA1A6",
+"Y>	c #A2A8AD",
+"Z>	c #DAE0E4",
+"`>	c #E5EDEE",
+" ,	c #ADBCC2",
+".,	c #A3B5BD",
+"+,	c #9DAEB3",
+"@,	c #8D9A9C",
+"#,	c #858C8D",
+"$,	c #DADCDD",
+"%,	c #E2E9EF",
+"&,	c #BFC9D1",
+"*,	c #A6B2BD",
+"=,	c #6F7981",
+"-,	c #585F64",
+";,	c #E2E7EA",
+">,	c #D4DEE4",
+",,	c #C1CED8",
+"',	c #ADB8C2",
+"),	c #AAB1B8",
+"!,	c #797E84",
+"~,	c #848A8D",
+"{,	c #FFFFFC",
+"],	c #EDF1F4",
+"^,	c #C8D1D7",
+"/,	c #B3BFC6",
+"(,	c #9FAFB7",
+"_,	c #7E8A93",
+":,	c #696D74",
+"<,	c #E0E1E4",
+"[,	c #E3E9ED",
+"},	c #AABAC6",
+"|,	c #919FAB",
+"1,	c #66717A",
+"2,	c #ABB3B7",
+"3,	c #F0FAFB",
+"4,	c #C4D4D8",
+"5,	c #B3C4CA",
+"6,	c #9FADB5",
+"7,	c #87939B",
+"8,	c #858A90",
+"9,	c #EDF0F3",
+"0,	c #C9CDD3",
+"a,	c #BCC2C8",
+"b,	c #ACB3BB",
+"c,	c #A3ADB6",
+"d,	c #8B96A1",
+"e,	c #8A949C",
+"f,	c #9099A1",
+"g,	c #929BA3",
+"h,	c #8F989F",
+"i,	c #96A0A7",
+"j,	c #959EA6",
+"k,	c #818F98",
+"l,	c #748088",
+"m,	c #60676C",
+"n,	c #797C7D",
+"o,	c #F5F6F6",
+"p,	c #CED5DB",
+"q,	c #C2CAD2",
+"r,	c #AEB8BF",
+"s,	c #959FA5",
+"t,	c #6D7578",
+"u,	c #BFC3C4",
+"v,	c #D8DDDE",
+"w,	c #C9D0D4",
+"x,	c #B1B8C2",
+"y,	c #868E98",
+"z,	c #72767A",
+"A,	c #E2E3E6",
+"B,	c #F3F3F7",
+"C,	c #CCCFD6",
+"D,	c #BFC4CE",
+"E,	c #AFB7C0",
+"F,	c #929A9E",
+"G,	c #697271",
+"H,	c #CBD2CF",
+"I,	c #F2F9F8",
+"J,	c #F5FBFC",
+"K,	c #F6F9F9",
+"L,	c #EAF0F2",
+"M,	c #BBC7CD",
+"N,	c #B2BFC8",
+"O,	c #A9B4BC",
+"P,	c #9CA4AA",
+"Q,	c #8F9497",
+"R,	c #D6DADB",
+"S,	c #D5DCE1",
+"T,	c #A8B1BA",
+"U,	c #8D98A2",
+"V,	c #5C646B",
+"W,	c #A6ABAE",
+"X,	c #F4F8FA",
+"Y,	c #D2D5D8",
+"Z,	c #C7CBCF",
+"`,	c #B1B4B8",
+" '	c #AAACAD",
+".'	c #808081",
+"+'	c #AAACAC",
+"@'	c #FBFBF9",
+"#'	c #FDFCFE",
+"$'	c #E3E3E5",
+"%'	c #D0D5D5",
+"&'	c #B2BABB",
+"*'	c #A7B4B6",
+"='	c #7D868A",
+"-'	c #79797E",
+";'	c #FAF8FA",
+">'	c #DEE0E4",
+",'	c #C9CFD6",
+"''	c #AEB7BE",
+")'	c #8F979D",
+"!'	c #6D7379",
+"~'	c #D6DADC",
+"{'	c #EAF0F1",
+"]'	c #CDD6D8",
+"^'	c #BAC5C8",
+"/'	c #A0ACAF",
+"('	c #879195",
+"_'	c #9CA1A4",
+":'	c #E9E8EA",
+"<'	c #C7C5C7",
+"['	c #BBB8BB",
+"}'	c #AFAEB2",
+"|'	c #898E95",
+"1'	c #68707A",
+"2'	c #7F868D",
+"3'	c #83898E",
+"4'	c #868C91",
+"5'	c #8B9296",
+"6'	c #8A9095",
+"7'	c #8A9195",
+"8'	c #899095",
+"9'	c #84898E",
+"0'	c #8D9093",
+"a'	c #B9BBBB",
+"b'	c #FAFBF9",
+"c'	c #F2F4F1",
+"d'	c #CFD2D4",
+"e'	c #BEC1C5",
+"f'	c #8C8F93",
+"g'	c #787A7C",
+"h'	c #DEDEDE",
+"i'	c #FAFBF8",
+"j'	c #D3D4D0",
+"k'	c #C7C9C8",
+"l'	c #B0B3B8",
+"m'	c #82858B",
+"n'	c #858688",
+"o'	c #F4F4F5",
+"p'	c #FAF8F9",
+"q'	c #EBEBED",
+"r'	c #CFD0D4",
+"s'	c #BCBEC3",
+"t'	c #B5B7BB",
+"u'	c #8A8D8E",
+"v'	c #7C7D7C",
+"w'	c #F1F2F0",
+"x'	c #EEF1F4",
+"y'	c #C7CDD4",
+"z'	c #BBC1C9",
+"A'	c #B0B3BB",
+"B'	c #A7A6AC",
+"C'	c #939495",
+"D'	c #D8DAD9",
+"E'	c #E6E9E9",
+"F'	c #D0D4D7",
+"G'	c #B9BDC2",
+"H'	c #A9AEB6",
+"I'	c #70767E",
+"J'	c #6E7378",
+"K'	c #E9EDEE",
+"L'	c #F8FAFB",
+"M'	c #FAFBFC",
+"N'	c #FBFBF8",
+"O'	c #CFCBC7",
+"P'	c #C3BDB7",
+"Q'	c #AEA7A0",
+"R'	c #A29B93",
+"S'	c #848079",
+"T'	c #B8B7B3",
+"U'	c #FAF5F2",
+"V'	c #D7D2CC",
+"W'	c #CBC7C0",
+"X'	c #B7B8B2",
+"Y'	c #A0A7A3",
+"Z'	c #757B7A",
+"`'	c #9E9BA0",
+" )	c #FDF9FC",
+".)	c #FBF7F7",
+"+)	c #FAF7F5",
+"@)	c #CDC8C6",
+"#)	c #C6C3C1",
+"$)	c #B2B1B1",
+"%)	c #888888",
+"&)	c #7D7D7D",
+"*)	c #EDEDEE",
+"=)	c #FBFBF7",
+"-)	c #DAD8D4",
+";)	c #D2D1CF",
+">)	c #B6B8B6",
+",)	c #9DA2A0",
+"')	c #7B8382",
+"))	c #D2D4D5",
+"!)	c #DED9D9",
+"~)	c #C0B7B3",
+"{)	c #B7ACA5",
+"])	c #A09692",
+"^)	c #7D7B7D",
+"/)	c #C3C7CD",
+"()	c #F2F7F8",
+"_)	c #F4F7F8",
+":)	c #F3F7F8",
+"<)	c #F3F6F8",
+"[)	c #F6F6F8",
+"})	c #FAFCFB",
+"|)	c #FDFEF9",
+"1)	c #E8E8E2",
+"2)	c #CBC6C1",
+"3)	c #B5AFAA",
+"4)	c #ABA6A1",
+"5)	c #827C7A",
+"6)	c #84807E",
+"7)	c #F7F5F6",
+"8)	c #F7FCFE",
+"9)	c #EFEDE5",
+"0)	c #CDC7BD",
+"a)	c #BAB4AC",
+"b)	c #A8A3A0",
+"c)	c #787475",
+"d)	c #9F9D9E",
+"e)	c #DDDAD6",
+"f)	c #D2CDC8",
+"g)	c #B4AFA9",
+"h)	c #AAA6A0",
+"i)	c #7A7772",
+"j)	c #908D8B",
+"k)	c #CCCDD0",
+"l)	c #C0BEC0",
+"m)	c #B4AFAF",
+"n)	c #ADA5A2",
+"o)	c #9B968F",
+"p)	c #CDCAC1",
+"q)	c #D4D2CB",
+"r)	c #BFBDBB",
+"s)	c #B3B2B3",
+"t)	c #909095",
+"u)	c #64656B",
+"v)	c #C0C1C4",
+"w)	c #F8F9F9",
+"x)	c #F9F6EE",
+"y)	c #C5BCB0",
+"z)	c #B9AB9A",
+"A)	c #A79886",
+"B)	c #9B907D",
+"C)	c #877D6E",
+"D)	c #BFBBB4",
+"E)	c #FDFBF6",
+"F)	c #FCFCF9",
+"G)	c #FEFDF8",
+"H)	c #FEFAF3",
+"I)	c #E2D6CC",
+"J)	c #C8B9AC",
+"K)	c #B7AA9B",
+"L)	c #B1AA9E",
+"M)	c #8B8A82",
+"N)	c #686865",
+"O)	c #D6D5D8",
+"P)	c #F9F6F6",
+"Q)	c #FBF7F4",
+"R)	c #EFE9E2",
+"S)	c #BEB2A8",
+"T)	c #BDB0A2",
+"U)	c #ABA195",
+"V)	c #7D7771",
+"W)	c #95918D",
+"X)	c #FCFAF9",
+"Y)	c #FEFEF7",
+"Z)	c #F2EDE1",
+"`)	c #C8BFB4",
+" !	c #C0B4AB",
+".!	c #B1A8A1",
+"+!	c #8B8983",
+"@!	c #717370",
+"#!	c #F9F8F5",
+"$!	c #C7BFB8",
+"%!	c #B7A89B",
+"&!	c #AC9887",
+"*!	c #948476",
+"=!	c #706861",
+"-!	c #EFF1F1",
+";!	c #FCFCFE",
+">!	c #F9F9F7",
+",!	c #D7D1C8",
+"'!	c #C3B5A5",
+")!	c #AC9C8C",
+"!!	c #A09183",
+"~!	c #786D62",
+"{!	c #A49E99",
+"]!	c #FCFAFA",
+"^!	c #F7FBFC",
+"/!	c #FAF9F4",
+"(!	c #E1D9CC",
+"_!	c #C5B7A5",
+":!	c #AFA090",
+"<!	c #9B8D82",
+"[!	c #726862",
+"}!	c #BCBBB9",
+"|!	c #F8F7F0",
+"1!	c #CDC5B9",
+"2!	c #C4B8A8",
+"3!	c #ADA08C",
+"4!	c #9A907D",
+"5!	c #756C60",
+"6!	c #AFA9A4",
+"7!	c #F4F3F1",
+"8!	c #C5C2BB",
+"9!	c #B9B1A6",
+"0!	c #ADA293",
+"a!	c #A99C89",
+"b!	c #A69783",
+"c!	c #B5A693",
+"d!	c #BAAE9F",
+"e!	c #A9A096",
+"f!	c #A19A97",
+"g!	c #726D70",
+"h!	c #838086",
+"i!	c #FAFBF6",
+"j!	c #FBF8EC",
+"k!	c #C1B5A0",
+"l!	c #BDAA8F",
+"m!	c #AF9C7F",
+"n!	c #A5957A",
+"o!	c #8E816B",
+"p!	c #C1BAB1",
+"q!	c #FEFBF5",
+"r!	c #F9EFE1",
+"s!	c #D5C5B3",
+"t!	c #C0AC97",
+"u!	c #AB9782",
+"v!	c #A69787",
+"w!	c #6E665B",
+"x!	c #7D7B76",
+"y!	c #F5F7F7",
+"z!	c #FBF9F1",
+"A!	c #E8DED1",
+"B!	c #C3B19F",
+"C!	c #B39D86",
+"D!	c #A18E7A",
+"E!	c #7D7368",
+"F!	c #B2ABA4",
+"G!	c #FEFAF9",
+"H!	c #FDFCF2",
+"I!	c #E1D8C4",
+"J!	c #C4B5A2",
+"K!	c #B2A08F",
+"L!	c #9C8B7F",
+"M!	c #756B64",
+"N!	c #A4A29E",
+"O!	c #FAF8F2",
+"P!	c #C8BFB2",
+"Q!	c #BEAB97",
+"R!	c #B59C83",
+"S!	c #96816C",
+"T!	c #706659",
+"U!	c #FEFDF7",
+"V!	c #FBFAF5",
+"W!	c #F9F6F0",
+"X!	c #C9BEB4",
+"Y!	c #C3AE95",
+"Z!	c #B29A80",
+"`!	c #9C866F",
+" ~	c #756655",
+".~	c #C0BAB1",
+"+~	c #F9FAFA",
+"@~	c #F8FBFA",
+"#~	c #FDFAF1",
+"$~	c #D2C6B3",
+"%~	c #C6B29A",
+"&~	c #8F7A68",
+"*~	c #76665B",
+"=~	c #D9DAD6",
+"-~	c #FAFCF8",
+";~	c #F8F9F2",
+">~	c #F2F0E3",
+",~	c #C4BAA7",
+"'~	c #BBA98E",
+")~	c #AE9A7C",
+"!~	c #9B8C6F",
+"~~	c #7F7360",
+"{~	c #CBC4BD",
+"]~	c #F7FEFD",
+"^~	c #FBFCFE",
+"/~	c #F6F4ED",
+"(~	c #BFB9AB",
+"_~	c #B6AA94",
+":~	c #AB9D80",
+"<~	c #AF9E7C",
+"[~	c #B29C7C",
+"}~	c #B49C81",
+"|~	c #B09C84",
+"1~	c #AA9A8B",
+"2~	c #81756F",
+"3~	c #5D5656",
+"4~	c #D8D3D7",
+"5~	c #FEFCFA",
+"6~	c #FCFAF8",
+"7~	c #FAFBF5",
+"8~	c #C8BAA4",
+"9~	c #CCB698",
+"0~	c #BFA785",
+"a~	c #BDA480",
+"b~	c #A79172",
+"c~	c #B0A294",
+"d~	c #F6EEE5",
+"e~	c #FBF7F2",
+"f~	c #FEF5EC",
+"g~	c #EDDAC0",
+"h~	c #DDC6A8",
+"i~	c #C1A98B",
+"j~	c #B49D83",
+"k~	c #8F7D6B",
+"l~	c #61554B",
+"m~	c #C4BFBB",
+"n~	c #F8F9F8",
+"o~	c #FDFAF0",
+"p~	c #DECFBA",
+"q~	c #D7BFA1",
+"r~	c #BDA380",
+"s~	c #A38E72",
+"t~	c #7B6F5F",
+"u~	c #D5CEC7",
+"v~	c #FCF9FB",
+"w~	c #FDFEF7",
+"x~	c #F8F1DC",
+"y~	c #DECAAC",
+"z~	c #CBB599",
+"A~	c #B29C84",
+"B~	c #857263",
+"C~	c #685D55",
+"D~	c #E6E4E1",
+"E~	c #FBF7ED",
+"F~	c #D1C3B1",
+"G~	c #CAB198",
+"H~	c #C4A486",
+"I~	c #B19478",
+"J~	c #8D7A67",
+"K~	c #F4F0E5",
+"L~	c #FEFCF6",
+"M~	c #FEFCF7",
+"N~	c #FEFBF6",
+"O~	c #FEFAF5",
+"P~	c #F7F1E7",
+"Q~	c #E7E4DA",
+"R~	c #F6F9F1",
+"S~	c #F7FBF4",
+"T~	c #FDFCF3",
+"U~	c #F7EDE1",
+"V~	c #CEB6A5",
+"W~	c #CFB394",
+"X~	c #BDA384",
+"Y~	c #967F67",
+"Z~	c #776657",
+"`~	c #EFE7DF",
+" {	c #F7FAFB",
+".{	c #FDF8E9",
+"+{	c #D4C0A4",
+"@{	c #D3B693",
+"#{	c #BFA483",
+"${	c #89745B",
+"%{	c #837769",
+"&{	c #F6F6F3",
+"*{	c #F8FBF8",
+"={	c #FCFCF4",
+"-{	c #E9E3CE",
+";{	c #CABA9C",
+">{	c #BFA984",
+",{	c #BAA680",
+"'{	c #9E8E6F",
+"){	c #80725E",
+"!{	c #F2EAE3",
+"~{	c #FEF9FA",
+"{{	c #FCFBFE",
+"]{	c #F9FEFC",
+"^{	c #F8F9F7",
+"/{	c #F8F4EB",
+"({	c #C4B9A2",
+"_{	c #BFAF8A",
+":{	c #B6A276",
+"<{	c #BCA67C",
+"[{	c #C0A67F",
+"}{	c #B59D81",
+"|{	c #A08E7A",
+"1{	c #66594F",
+"2{	c #9E9796",
+"3{	c #FCFAFD",
+"4{	c #F9FBF7",
+"5{	c #FBFAF0",
+"6{	c #DCD1BD",
+"7{	c #C7B397",
+"8{	c #C5AA87",
+"9{	c #C6A881",
+"0{	c #C3A47E",
+"a{	c #B29C83",
+"b{	c #C1B09B",
+"c{	c #EADFCE",
+"d{	c #F5EEDF",
+"e{	c #F7F1E2",
+"f{	c #EFE7D6",
+"g{	c #E7D8C2",
+"h{	c #E4CBA9",
+"i{	c #CCB28F",
+"j{	c #C1A886",
+"k{	c #AA9478",
+"l{	c #695947",
+"m{	c #8A7F78",
+"n{	c #F6F1F0",
+"o{	c #F9F6E9",
+"p{	c #DAC8AC",
+"q{	c #D5BA92",
+"r{	c #C2A97F",
+"s{	c #B09A77",
+"t{	c #A28F76",
+"u{	c #E9DECF",
+"v{	c #F1E7DF",
+"w{	c #F3EAE3",
+"x{	c #F3EADD",
+"y{	c #F1E8D2",
+"z{	c #E6D5B4",
+"A{	c #DAC19B",
+"B{	c #C3AB88",
+"C{	c #AA9579",
+"D{	c #71624F",
+"E{	c #9E988F",
+"F{	c #FDF9F0",
+"G{	c #D0C1AD",
+"H{	c #D2B79B",
+"I{	c #C9A582",
+"J{	c #C4A280",
+"K{	c #B59B81",
+"L{	c #C1B19D",
+"M{	c #DCD2BE",
+"N{	c #E5DCC8",
+"O{	c #E6DDC9",
+"P{	c #E8DFCB",
+"Q{	c #E9E0CC",
+"R{	c #E3D9C4",
+"S{	c #E0D4C1",
+"T{	c #CABCAD",
+"U{	c #C9C3B7",
+"V{	c #F9FCF5",
+"W{	c #F9FEF8",
+"X{	c #FCFBEE",
+"Y{	c #F3E4CF",
+"Z{	c #DFC0A3",
+"`{	c #CDAE89",
+" ]	c #BDA282",
+".]	c #8C7660",
+"+]	c #87786C",
+"@]	c #FDF8F2",
+"#]	c #FAF9F3",
+"$]	c #F7EEDA",
+"%]	c #DBC29F",
+"&]	c #D7B58A",
+"*]	c #BA9D76",
+"=]	c #8C7A5D",
+"-]	c #999282",
+";]	c #FCFCF3",
+">]	c #E6D9C0",
+",]	c #D5BE97",
+"']	c #C1A97D",
+")]	c #BDA87E",
+"!]	c #AD9D78",
+"~]	c #97886D",
+"{]	c #F8EFDE",
+"]]	c #F8F0E6",
+"^]	c #F8F1E8",
+"/]	c #F7F3E6",
+"(]	c #F8F3E8",
+"_]	c #F8F2E6",
+":]	c #F7F1E5",
+"<]	c #F6F0E4",
+"[]	c #E2DFD6",
+"}]	c #F1F1EB",
+"|]	c #FBF4EB",
+"1]	c #CFC0A5",
+"2]	c #CEB98A",
+"3]	c #C1A875",
+"4]	c #C2A57A",
+"5]	c #C4A680",
+"6]	c #C1A782",
+"7]	c #BBA486",
+"8]	c #867460",
+"9]	c #71665C",
+"0]	c #EBE8E7",
+"a]	c #F5F9F8",
+"b]	c #FCFCF6",
+"c]	c #ECE4D6",
+"d]	c #B4A38C",
+"e]	c #B69E80",
+"f]	c #BCA07C",
+"g]	c #C4A781",
+"h]	c #BBA17C",
+"i]	c #C2AB87",
+"j]	c #C4B08E",
+"k]	c #CAB997",
+"l]	c #D2C29E",
+"m]	c #D2C19D",
+"n]	c #D1BF9B",
+"o]	c #C5AD8C",
+"p]	c #B59D7D",
+"q]	c #988364",
+"r]	c #68583F",
+"s]	c #6B6052",
+"t]	c #EBE6E1",
+"u]	c #FBF9F9",
+"v]	c #F5F5F7",
+"w]	c #F0E7D9",
+"x]	c #D2BB9C",
+"y]	c #CAAC84",
+"z]	c #BFA67D",
+"A]	c #C0A780",
+"B]	c #C1A481",
+"C]	c #C4AA8D",
+"D]	c #CAB39A",
+"E]	c #CDB69D",
+"F]	c #CFB798",
+"G]	c #D4BC95",
+"H]	c #D1B88F",
+"I]	c #BEA680",
+"J]	c #B7A07F",
+"K]	c #7C6A50",
+"L]	c #746958",
+"M]	c #E7E4DC",
+"N]	c #FDFCF4",
+"O]	c #D2C6B4",
+"P]	c #C3AC90",
+"Q]	c #BF9E7A",
+"R]	c #C1A17A",
+"S]	c #BEA37F",
+"T]	c #BBA88A",
+"U]	c #C7B690",
+"V]	c #D2C097",
+"W]	c #CEBC93",
+"X]	c #D0BE95",
+"Y]	c #CCB991",
+"Z]	c #C8B58D",
+"`]	c #B8A67F",
+" ^	c #9B8B77",
+".^	c #DBD3CB",
+"+^	c #FEFCEF",
+"@^	c #E5D4B8",
+"#^	c #DBBE96",
+"$^	c #C3A67E",
+"%^	c #B09675",
+"&^	c #7F6B54",
+"*^	c #A69A8D",
+"=^	c #FDFBF4",
+"-^	c #F8FCFB",
+";^	c #FCFBF3",
+">^	c #ECE0CD",
+",^	c #DAC1A0",
+"'^	c #C9AA82",
+")^	c #A88D68",
+"!^	c #7A694E",
+"~^	c #B7B09F",
+"{^	c #FCF8ED",
+"]^	c #E0CFB3",
+"^^	c #DABE97",
+"/^	c #C3A579",
+"(^	c #BEA579",
+"_^	c #B7A376",
+":^	c #B09C75",
+"<^	c #C8B594",
+"[^	c #CFBB9E",
+"}^	c #D0BFA0",
+"|^	c #D2C39F",
+"1^	c #D4C29E",
+"2^	c #D5C1A0",
+"3^	c #CFBA9B",
+"4^	c #C9B69A",
+"5^	c #BAAB95",
+"6^	c #AFA797",
+"7^	c #F8F6EB",
+"8^	c #F7F8F4",
+"9^	c #F9F2EA",
+"0^	c #CFC0A7",
+"a^	c #CDB88B",
+"b^	c #C0A775",
+"c^	c #C0A177",
+"d^	c #BEA07B",
+"e^	c #BCA17F",
+"f^	c #A08A6F",
+"g^	c #695B4A",
+"h^	c #B2ABA3",
+"i^	c #F9F8F9",
+"j^	c #F7FAFD",
+"k^	c #F8F8F8",
+"l^	c #FBF9F0",
+"m^	c #BDB1A1",
+"n^	c #928169",
+"o^	c #958063",
+"p^	c #A98F6E",
+"q^	c #B59973",
+"r^	c #B79B75",
+"s^	c #BDA47D",
+"t^	c #C0A980",
+"u^	c #C0AA7F",
+"v^	c #B7A176",
+"w^	c #A18C65",
+"x^	c #867257",
+"y^	c #726046",
+"z^	c #63553D",
+"A^	c #89806E",
+"B^	c #E0DCD2",
+"C^	c #E9E8E6",
+"D^	c #E1E0E4",
+"E^	c #DBDADD",
+"F^	c #DCD8D5",
+"G^	c #D2C3B2",
+"H^	c #CAAE8E",
+"I^	c #BF9F77",
+"J^	c #B59C76",
+"K^	c #B69F7B",
+"L^	c #B89C77",
+"M^	c #BFA582",
+"N^	c #BBA381",
+"O^	c #BBA380",
+"P^	c #BCA079",
+"Q^	c #B89A6E",
+"R^	c #AA9067",
+"S^	c #9F8A69",
+"T^	c #7A684C",
+"U^	c #5C503A",
+"V^	c #C1BBAD",
+"W^	c #EFEFE8",
+"X^	c #EEF0EE",
+"Y^	c #F4F2ED",
+"Z^	c #D7CDBF",
+"`^	c #AB9880",
+" /	c #AF9571",
+"./	c #B1956D",
+"+/	c #B69D75",
+"@/	c #B49F7A",
+"#/	c #BAA37E",
+"$/	c #BCA47E",
+"%/	c #BCA37D",
+"&/	c #7D6541",
+"*/	c #776755",
+"=/	c #F2E9E4",
+"-/	c #F5F1F4",
+";/	c #F5F3F6",
+">/	c #D1C2A2",
+",/	c #CBB586",
+"'/	c #B19872",
+")/	c #957E61",
+"!/	c #6E5D47",
+"~/	c #CCC2B6",
+"{/	c #F5F4ED",
+"]/	c #EBF0ED",
+"^/	c #EAEFEE",
+"//	c #F3EEE6",
+"(/	c #D3C6B5",
+"_/	c #CEB99C",
+":/	c #B19675",
+"</	c #8E7658",
+"[/	c #6E5F48",
+"}/	c #DDD5C8",
+"|/	c #EEEBE7",
+"1/	c #F2F0EC",
+"2/	c #EDE5D8",
+"3/	c #D2BBA1",
+"4/	c #CEAE88",
+"5/	c #B69770",
+"6/	c #B29973",
+"7/	c #AD966F",
+"8/	c #AE9671",
+"9/	c #B9A07C",
+"0/	c #BFA781",
+"a/	c #C5AD84",
+"b/	c #C8B182",
+"c/	c #C8AD80",
+"d/	c #C9A97F",
+"e/	c #C5A681",
+"f/	c #B19676",
+"g/	c #86715A",
+"h/	c #AB9E90",
+"i/	c #EFEAE4",
+"j/	c #EDEDEB",
+"k/	c #EDEEEC",
+"l/	c #EEE9E2",
+"m/	c #C1B39F",
+"n/	c #C0AD87",
+"o/	c #B79D73",
+"p/	c #B19470",
+"q/	c #AF9272",
+"r/	c #A88E72",
+"s/	c #715E48",
+"t/	c #786D61",
+"u/	c #ECE9E5",
+"v/	c #F3F4F5",
+"w/	c #F5F5F5",
+"x/	c #F1F1F1",
+"y/	c #ECECEC",
+"z/	c #E7E7E7",
+"A/	c #E4E4E4",
+"B/	c #E2E2E2",
+"C/	c #E5E5E5",
+"D/	c #EBEBEB",
+"E/	c #F2EFE5",
+"F/	c #B6AC9D",
+"G/	c #8C7F69",
+"H/	c #827059",
+"I/	c #836D53",
+"J/	c #8B7459",
+"K/	c #897256",
+"L/	c #877052",
+"M/	c #816C4E",
+"N/	c #756143",
+"O/	c #69573D",
+"P/	c #655847",
+"Q/	c #7A6F5F",
+"R/	c #B6B0A1",
+"S/	c #E0DED3",
+"T/	c #DDDCD7",
+"U/	c #CDCDCD",
+"V/	c #B9B9BB",
+"W/	c #A6A6A5",
+"X/	c #A49E95",
+"Y/	c #B6A28C",
+"Z/	c #C2A17E",
+"`/	c #B08F67",
+" (	c #AA9371",
+".(	c #8C7B60",
+"+(	c #8D7A61",
+"@(	c #86755C",
+"#(	c #7D6E53",
+"$(	c #7D6E51",
+"%(	c #7A684A",
+"&(	c #766243",
+"*(	c #6F5D41",
+"=(	c #665740",
+"-(	c #625745",
+";(	c #AAA396",
+">(	c #E0DDD5",
+",(	c #D5D4CF",
+"'(	c #D2D3D0",
+")(	c #CFCCC9",
+"!(	c #D0C8C0",
+"~(	c #9D927F",
+"{(	c #807154",
+"](	c #816F4E",
+"^(	c #877453",
+"/(	c #887559",
+"((	c #8A735E",
+"_(	c #8C7361",
+":(	c #886F5D",
+"<(	c #88705E",
+"[(	c #7E6554",
+"}(	c #745B49",
+"|(	c #5F4836",
+"1(	c #8C8076",
+"2(	c #E0DAD7",
+"3(	c #D8D4D7",
+"4(	c #D8D5D7",
+"5(	c #D9D3CA",
+"6(	c #ADA187",
+"7(	c #92825B",
+"8(	c #7A6749",
+"9(	c #695741",
+"0(	c #685B4C",
+"a(	c #DAD3CB",
+"b(	c #D6D4D0",
+"c(	c #C9CDCB",
+"d(	c #C3C9C6",
+"e(	c #D2CCC5",
+"f(	c #B7AB9E",
+"g(	c #9B8A75",
+"h(	c #78664C",
+"i(	c #66553E",
+"j(	c #6E6251",
+"k(	c #CDC7BC",
+"l(	c #C7C4C1",
+"m(	c #C7C5C2",
+"n(	c #C6BDB3",
+"o(	c #B49F89",
+"p(	c #987C5E",
+"q(	c #8A7055",
+"r(	c #867159",
+"s(	c #836E56",
+"t(	c #826D55",
+"u(	c #866F57",
+"v(	c #887257",
+"w(	c #8B7354",
+"x(	c #8C7451",
+"y(	c #8E7451",
+"z(	c #896A4D",
+"A(	c #84664B",
+"B(	c #715641",
+"C(	c #675346",
+"D(	c #B4A8A4",
+"E(	c #DDD8DB",
+"F(	c #DBDCDE",
+"G(	c #D9DCD9",
+"H(	c #E1DED9",
+"I(	c #B5AA9C",
+"J(	c #98876B",
+"K(	c #836F4E",
+"L(	c #806B51",
+"M(	c #7E6651",
+"N(	c #725C48",
+"O(	c #584A3C",
+"P(	c #9E9892",
+"Q(	c #D5D5D5",
+"R(	c #E7E9EB",
+"S(	c #EEF1F1",
+"T(	c #ECEDEE",
+"U(	c #E9E9E9",
+"V(	c #E1E1E1",
+"W(	c #DBDBDB",
+"X(	c #C8C8C8",
+"Y(	c #C7C7C7",
+"Z(	c #CBCBCB",
+"`(	c #D4D4D4",
+" _	c #DFDFDF",
+"._	c #F4F4F4",
+"+_	c #EAEAE8",
+"@_	c #DFDED7",
+"#_	c #BDB9AD",
+"$_	c #928A7C",
+"%_	c #786F61",
+"&_	c #746859",
+"*_	c #766758",
+"=_	c #78695A",
+"-_	c #7A6D5F",
+";_	c #85796E",
+">_	c #988F87",
+",_	c #C2BDB6",
+"'_	c #D5D4CC",
+")_	c #DEE0D9",
+"!_	c #D4D7D2",
+"~_	c #CBCDCA",
+"{_	c #B2B2B1",
+"]_	c #979695",
+"^_	c #7D7C76",
+"/_	c #807769",
+"(_	c #AE957B",
+"__	c #B9936E",
+":_	c #A47F5B",
+"<_	c #8E775D",
+"[_	c #605244",
+"}_	c #766A61",
+"|_	c #72695C",
+"1_	c #716C5B",
+"2_	c #716F5E",
+"3_	c #7A7466",
+"4_	c #847C70",
+"5_	c #8D847A",
+"6_	c #9E978D",
+"7_	c #C1BEB6",
+"8_	c #CFCEC9",
+"9_	c #C0BFBD",
+"0_	c #B3B2AF",
+"a_	c #9F9B98",
+"b_	c #9F9A9A",
+"c_	c #A8A1A0",
+"d_	c #A49D94",
+"e_	c #928978",
+"f_	c #827765",
+"g_	c #786C5C",
+"h_	c #736559",
+"i_	c #74655F",
+"j_	c #766763",
+"k_	c #756662",
+"l_	c #776864",
+"m_	c #746561",
+"n_	c #70615D",
+"o_	c #887A76",
+"p_	c #BCB5B3",
+"q_	c #C5C3C1",
+"r_	c #BBBAB8",
+"s_	c #B5B4B0",
+"t_	c #BAB6AD",
+"u_	c #948D7D",
+"v_	c #857A67",
+"w_	c #7D7160",
+"x_	c #756A5D",
+"y_	c #786F67",
+"z_	c #ACA7A4",
+"A_	c #A8A7A6",
+"B_	c #9D9F9D",
+"C_	c #9EA19E",
+"D_	c #B1ADA9",
+"E_	c #9D948D",
+"F_	c #877D6F",
+"G_	c #796F5F",
+"H_	c #736A5C",
+"I_	c #716A60",
+"J_	c #908B85",
+"K_	c #91918F",
+"L_	c #969695",
+"M_	c #A19A94",
+"N_	c #998779",
+"O_	c #8A7462",
+"P_	c #807164",
+"Q_	c #756A62",
+"R_	c #72655B",
+"S_	c #6E6257",
+"T_	c #6F6257",
+"U_	c #6D6053",
+"V_	c #6E5F51",
+"W_	c #716353",
+"X_	c #766759",
+"Y_	c #79685A",
+"Z_	c #7B6A5D",
+"`_	c #78685D",
+" :	c #867A75",
+".:	c #D3CDCC",
+"+:	c #D4D2D7",
+"@:	c #D2D5D5",
+"#:	c #D7DAD6",
+"$:	c #E3E1DE",
+"%:	c #BAB4AB",
+"&:	c #887E6B",
+"*:	c #756A54",
+"=:	c #65594B",
+"-:	c #605045",
+";:	c #5C4C42",
+">:	c #5D554F",
+",:	c #939392",
+"':	c #B9BABC",
+"):	c #D1D3D2",
+"!:	c #D4D3D0",
+"~:	c #D0CFCD",
+"{:	c #CACACA",
+"]:	c #C4C4C4",
+"^:	c #C0C0C0",
+"/:	c #BEBEBE",
+"(:	c #BFBFBF",
+"_:	c #C1C1C1",
+"::	c #C5C5C5",
+"<:	c #D1D1D1",
+"[:	c #DDDDDD",
+"}:	c #EEEEEE",
+"|:	c #F5F7FB",
+"1:	c #DEE2E5",
+"2:	c #B5B9B9",
+"3:	c #9C9F9A",
+"4:	c #878881",
+"5:	c #797D75",
+"6:	c #818279",
+"7:	c #9D9A91",
+"8:	c #B7B2AB",
+"9:	c #C9C5C2",
+"0:	c #D5D4D6",
+"a:	c #DBDCDF",
+"b:	c #D7DADA",
+"c:	c #D6DBDB",
+"d:	c #C9D1CF",
+"e:	c #C5CCCB",
+"f:	c #BBBEBD",
+"g:	c #ABAAA8",
+"h:	c #96938F",
+"i:	c #89887D",
+"j:	c #968B79",
+"k:	c #A98D70",
+"l:	c #A77E59",
+"m:	c #9A7454",
+"n:	c #755E4B",
+"o:	c #60534D",
+"p:	c #9B9091",
+"q:	c #938C88",
+"r:	c #8E8E85",
+"s:	c #9A9B93",
+"t:	c #B1B2AF",
+"u:	c #C6C5C8",
+"v:	c #D5D4D5",
+"w:	c #D8D8D8",
+"x:	c #C9CBCC",
+"y:	c #BBBDC1",
+"z:	c #ADAEB1",
+"A:	c #9C9898",
+"B:	c #9B9391",
+"C:	c #9C9497",
+"D:	c #A29CA0",
+"E:	c #A09C9A",
+"F:	c #A29F99",
+"G:	c #9A9692",
+"H:	c #928C8C",
+"I:	c #91888C",
+"J:	c #95908E",
+"K:	c #A2A09A",
+"L:	c #AEABA6",
+"M:	c #B8B5B0",
+"N:	c #C5C2BD",
+"O:	c #C1BFBA",
+"P:	c #C6C4BF",
+"Q:	c #C2C2C2",
+"R:	c #C0C1BF",
+"S:	c #C0C1BA",
+"T:	c #BFC1B6",
+"U:	c #BFBFB6",
+"V:	c #C5C1BC",
+"W:	c #C9C3C2",
+"X:	c #CBC5BF",
+"Y:	c #C2BBB7",
+"Z:	c #ACA7A6",
+"`:	c #989496",
+" <	c #939192",
+".<	c #9B9A99",
+"+<	c #ADADAA",
+"@<	c #C3C0BF",
+"#<	c #D3CFCB",
+"$<	c #D6D1CA",
+"%<	c #CCC8BE",
+"&<	c #B2B0A6",
+"*<	c #9E9D97",
+"=<	c #858481",
+"-<	c #868788",
+";<	c #949597",
+"><	c #B1AEAB",
+",<	c #C7BBB3",
+"'<	c #D1C3BB",
+")<	c #C9C4C4",
+"!<	c #B6B7B9",
+"~<	c #A8A49F",
+"{<	c #8F8B86",
+"]<	c #83807A",
+"^<	c #84837D",
+"/<	c #93908D",
+"(<	c #A6A5A2",
+"_<	c #B2B2AF",
+":<	c #C0C0BC",
+"<<	c #C8C6C1",
+"[<	c #D2CFC9",
+"}<	c #D7D5D0",
+"|<	c #D5D7D2",
+"1<	c #D9DDDB",
+"2<	c #E0E6E1",
+"3<	c #E6EAE3",
+"4<	c #EBEBE9",
+"5<	c #EAE6E2",
+"6<	c #C4C0B5",
+"7<	c #969186",
+"8<	c #6C6864",
+"9<	c #605653",
+"0<	c #615653",
+"a<	c #6E6B6B",
+"b<	c #8B8D90",
+"c<	c #A9ACAD",
+"d<	c #B9B9B6",
+"e<	c #C4C0B8",
+"f<	c #C0BEBA",
+"g<	c #E6E6E6",
+"h<	c #EDEDED",
+"i<	c #F3F3F3",
+"j<	c #FFFDFE",
+"k<	c #F5F6F7",
+"l<	c #E1E3E4",
+"m<	c #C4C6C6",
+"n<	c #ADAEAD",
+"o<	c #9E9F9D",
+"p<	c #9B9E9B",
+"q<	c #A6A8A5",
+"r<	c #BDBDBA",
+"s<	c #CECECB",
+"t<	c #D9D9D9",
+"u<	c #E1E1E3",
+"v<	c #DFE0E3",
+"w<	c #DDDFE0",
+"x<	c #D5D7D8",
+"y<	c #CCCFCF",
+"z<	c #C7CACA",
+"A<	c #C3C5C4",
+"B<	c #C3C3C2",
+"C<	c #C1BFBD",
+"D<	c #AE9B89",
+"E<	c #A07F63",
+"F<	c #997151",
+"G<	c #856248",
+"H<	c #604939",
+"I<	c #70655F",
+"J<	c #9B9798",
+"K<	c #A5A3A1",
+"L<	c #AEAEAB",
+"M<	c #BBBBB9",
+"N<	c #CDCECE",
+"O<	c #D9D9DB",
+"P<	c #D9DADB",
+"Q<	c #CECFD1",
+"R<	c #C7C7CA",
+"S<	c #BFBFC1",
+"T<	c #BEBDBD",
+"U<	c #C0BEBC",
+"V<	c #C8C5C6",
+"W<	c #CCCACC",
+"X<	c #C4C3C3",
+"Y<	c #B2B2B2",
+"Z<	c #AAAAAA",
+"`<	c #A5A2A5",
+" [	c #A9A7AA",
+".[	c #B3B2B2",
+"+[	c #BFC0BD",
+"@[	c #CACAC7",
+"#[	c #CECFCC",
+"$[	c #D1D1CE",
+"%[	c #D4D4D2",
+"&[	c #D5D6D3",
+"*[	c #D6D7D6",
+"=[	c #D7D8D7",
+"-[	c #DDDDDB",
+";[	c #DEDFDA",
+">[	c #E2E2DF",
+",[	c #DEDEDD",
+"'[	c #D6D4D6",
+")[	c #CFCDCC",
+"![	c #BEBCBB",
+"~[	c #B2B0B1",
+"{[	c #B0AFB0",
+"][	c #B6B5B6",
+"^[	c #C7C6C6",
+"/[	c #D8D8D7",
+"([	c #E5E4E4",
+"_[	c #EAE9E8",
+":[	c #E2E1DE",
+"<[	c #D0CFCC",
+"[[	c #AFAEAD",
+"}[	c #AEAEAD",
+"|[	c #B5B6B6",
+"1[	c #C8C9C9",
+"2[	c #D7D6D6",
+"3[	c #DFDBD9",
+"4[	c #D9D5D3",
+"5[	c #CAC9CA",
+"6[	c #BABBBD",
+"7[	c #AEADAC",
+"8[	c #A3A3A1",
+"9[	c #A5A5A3",
+"0[	c #BDBDBD",
+"a[	c #CCCDCE",
+"b[	c #D4D5D5",
+"c[	c #D6D8D8",
+"d[	c #DBDBDA",
+"e[	c #DCDCDA",
+"f[	c #E2E3E0",
+"g[	c #E8EAE8",
+"h[	c #EEF1EE",
+"i[	c #F7F6F6",
+"j[	c #ECEBEA",
+"k[	c #CFCECB",
+"l[	c #AFAFAC",
+"m[	c #9A9A9A",
+"n[	c #979594",
+"o[	c #989594",
+"p[	c #9F9F9F",
+"q[	c #ACADAF",
+"r[	c #B9BABB",
+"s[	c #C9C9C8",
+"t[	c #D1CFCC",
+"u[	c #D7D7D5",
+"v[	c #DCDCDC",
+"w[	c #EAEAEA",
+"x[	c #EFEFEF",
+"y[	c #F2F2F2",
+"z[	c #D7D7D7",
+"A[	c #D6D6D6",
+"B[	c #DADADA",
+"C[	c #E9E9E6",
+"D[	c #EBE5DA",
+"E[	c #8F7155",
+"F[	c #826042",
+"G[	c #6C4F35",
+"H[	c #4F3D29",
+"I[	c #A49C93",
+"J[	c #E3E3E3",
+"K[	c #F6F6F6",
+"L[	c #F0F0F0",
+"M[	c #F9F9F6",
+"N[	c #F4F1E6",
+"O[	c #807562",
+"P[	c #5D492F",
+"Q[	c #4B3417",
+"R[	c #422F17",
+"S[	c #605542",
+"T[	c #DDD9D0",
+"U[	c #FAFAF6",
+"V[	c #C7C2B9",
+"W[	c #82786B",
+"X[	c #7A6E5F",
+"Y[	c #7A7165",
+"Z[	c #8E8A80",
+"`[	c #F8F7F2",
+" }	c #FFFCFB",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # # $ % & * = - - ; > , ' ) ! ~ { ] ^ / ( _ _ : < [ } } | | 1 # | } } } } } } } | | | | | | } } } } } } } } 2 } } | | | 1 1 1 1 1 1 1 1 1 1 1 1 1 | } } } } } } 3 4 5 6 7 8 9 0 a b b c d e f 0 = g h i j k l m n o p q r = s t u v v w x t * y z A | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ B | | C D E F G H I J K L M N O P Q R S T | C C U V W } } } | | | | | | | | | | | | | | | | | | | | | | | | } 2 } | | | | | | | | | | | | | | | | | | } } } } } } ) X Y Z `  ...+. at .#.$.%.&.&.*.=.-.;.>.,.'.).!.~.{.].^.q 4 /.(._.L :.<.[.}.|.1.2.z 3.| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # | 4.} 2 5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.& | | | | | | | 1 1 1 1 1 1 | | | | | | | 1 1 1 1 1 1 1 | 4.| | | | | | | | | | | | | | | | | | | | | | | | | |.q.r.s.t.u.v.w.x.y.z.z.y.A.B.C.D.E.F.G.H.I.w J.y K.^.L.M.j N.O.P.Q.R.S.T.U.V.n.z W.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # C } q X.} Y.Z.`. +.+++ at +#+$+%+&+*+=+-+;+>+,+'+)+!+~+z & & & & & & & ~+~+& & & ~+| | | | | | | ~+& ~+& ~+& & | | | | | | | & & & & & & & & & !+& & & ~+| | {+| | | ]+^+/+(+_+:+<+[+}+|+1+2+3+4+5+6+7+8+9+0+a+b+c+1.d+e+# q d+f+g+h+i+j+k+l+m+n+o+n.z p+1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 C } q+r+s+t+u+v+w+x+y+z+A+B+C+D+E+F+G+H+I+J+K+L+y 1 3.3.3.# 1 {+| } } )+} )+} } } | | | | | | } )+)+n.)+} } 1 M+1 1 | | | 1 N+1 S O+i A {+1 O+O+1 1 1 1 1 P+1 | | Q+R+S+T+U+V+W+X+Y+Y+Z+`+Y+ @. at +@@@#@$@%@&@*@=@-@)+z 1 } ;@>@,@'@)@!@~@{@]@^@/@n.z (@1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | U !+2._@:@<@[@}@|@1 at 2@3 at 4@5 at 6@7 at 8@9 at 0@a at b@c at d@e at 8 !.f at W.W.f@g at 3.3.(@(@(@f@(@(@(@1 | | | | | | h@(@f at f@(@f at f@(@3.# 1 | {+} _ at i@j@(@(@(@j at 3.(@3.(@(@(@(@!.O+1 1 1 1 Q+k at l@m at n@o at p@q at r@s at t@t at u@v at w@x at y@z at A@B at C@D at E@F at n.| 1 } ,+G at H@I at J@K at L@M at N@O at S z 1 A | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B P@[ S I Q at R@S at T@U at V@W at X@Y at Z@`@ #.#+#@###$#%#&#E y *#=#-#;#>#,#'#)#!#~#{#]#^#/#(#_#h 6.{.:#<#[#}#|#1#;#2#3#4#5#6#7#x :#8#9#_ at 0#a#b#c#d#e#f#g#h#i#j#k#l#m#n#i 1 z 3._.o#p#q#r#s#t#u#f at v#v#w#v#w#x#y#z#A#B#C#D#E#j.F#G#!+e+h at H#I#J#K#L#M#N#O#L.) e+K.| n.| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . M+e+P at v P#Q#R#S#T#U#V#W#! X#= Y#Z#`# $.$+$@$#$$$e@!.%$&$*$=$-$;$>$,$'$)$!$~${$]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$7$8$9$0$s+9#a$b$c$d$e$f$g$h$i$j$k$l$m$m$n$o$p$q$q+n.]+r$s$t$u$v$w$x$& 1 1 | | {+z y$f at z$A$B$C$D$E$F$O+8 G$H$I$J$K$L$M$N$O$P$| | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.~+@ Q$R$S$T$U$V$W$X$Y$Z$/.@ O+`$ %.%+%@%#%$%%%{.i@&%*%=%-%;%>%,%'%)%!%~%{%]%^%/%(%_%:%t <%[%}%|%1%2%3%4%5%6%7%8%9%0%a%b%c%R d%e%f%g%h%i%j%k%l%m%n%o%p%q%r%s%q+n.t%u%v%w%x%y%z%u & | 1 | | ~+& g i at A%B%C%D%E%F%G%t 8 E _ at H%I%J%K%L%M%N%O%| | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i | 3.P%Q%R%S%T%U%V%W%X%Y%Z%& S `% &.&+&@&#&$&%&&&*&=&-&;&>&,&'&)&!&~&{&]&^&/&(&_&:&<&[&}&|&1&2&3&4&5&6&7&8&9&0&a&b&c&d&k e&Q+f&g&h&i&j&k&l&m&n&o&p&q&r&s&t&u&X.q v&w&x&y&z&A&B&C&P+(@3.# | D&& g k E&F&G&H&I&J&K&L&F D&f+M&N&O&P&Q&R&S&T&& | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . U&S V&W&X&Y&Z&`& *.*~+!+N+Z%K.+*@*#*$*%*q&&***=*G f at -*;*>*,*'*)*!*~*{*]*^*/*(*_*:*<*[*j@}*|*1*2*3*4*5*6*7*8*9*0*a*b*c*d*f at l Q$e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*)+u*v*w*x*y*z*A*B*C*D*E*F*G*H*I*J*K*3.L*M*N*O*P*Q*R*S*T*W%U*V*W*X*Y*Z*`* =8 & | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .=O++=@=#=$=%=&=*===y y 1 -=3.1 *&;=>=,='=)=!=~={=|.]=^=/=(=_=:=<=[=}=|=1=2=3=4=5=6=7=8=9=0=a=b=c=d=e=f=g=h=i=j=k=l=m=n=g at m o=p=q=r=s=t=u=v=w=Q+x=y=z=A=B=C=D=i E=F=G=H=I=J=K=L=M=N=O=P=Q=R=S=T=U=3.V=W=X=Y=Z=`= -{..-C&+- at -#-$-%-&-*-1.=-~+| | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . --!.;->-,-'-)-!-~-:#< < z *&3.M+*&{-]-^-/-(-_-Z%p )#:-<-[-}-|-1-2-3-4-5-6-7-8-9-0-a-b-c-d-e-f-g-h-i-j-k-b%H l-m-n-o-p-q-!.r-s-t-u-v-w-x-y-z-F A-B-C-D-E-F-G-H-l _ at I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-i@h at X-Y-Z-`- ;.;I** s +;@;#;$;%;&;*;=;r 8#| | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . :%v -;;;>;,;';);!;~;E < 1 3.u 1 j@{;];^;/;(;_;^.:;<;@=[;};|;1;*#2;3;4;5;w 6;7;8;9;0;a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;9 t;u;v;w;x;y;z;A;B;C;D;E;F;G;H;I;W%S J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z%s Z;`; >.>+>@>#>s;$>%>&>*>=>->;>j 1.) D&| | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . >>,>'>)>!>~>{>]>^>/>P@(>| P+D&N+_>:><>[>}>|>1>P+3.2>3>4>5>6>7>|.Y#G#8>9>0>a>b>c>d>e>D=f>g>h>i>j>k>l>m>n>o>p>q>r>s>t>u>v>S w>x>y>z>A>B>C>D>y & # E>F>G>H>I>J>w>A K>L>M>N>O>P>Q>R>S>T>U>V>W>X>Y>Z>K.>>`> ,.,+,@,#,$,j@%,&,*,N;=,-,;,3.| ~+P@| | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ _@>,,,',),!,~,k />.={,B P+| A ],^,/,(,_,:,<,e&l [,i#},|,1,2,(@{+)+, H-3,4,5,6,7,8,9,0,a,b,c,d,e,f,g,h,g,i,j,k,l,m,n,o,h W%p,q,r,s,t,u,K.{+z ^.v,w,x,y,z,A,O+A B,C,D,E,F,G,H,I,J,j@# i at Y.Y.K,j h = L,M,N,O,P,Q,R,n#S,F>T,U,V,W,X,w>| & P@| | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . X#j Y,Z,`, '.'+'h @'& {,& {+#'r+$'%'&'*'='-';'J.;'>',''')'!'~'# 2.2.1.# {']'^'/'('_':'<'['}'|'1'2'3'4'5'6'7'8';>9'0'a'($b'c'd'e'`,f'g'h') j u i'j'k'l'm'n'o'p'X#q'r's't'u'v'w'($!.{+~+~+~+D&K*y$= {+x'y'z'A'B'C'D'E'F'G'H'I'J'K'L'M'| & & | | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . P+N'O'P'Q'R'S'T'g=- = & & !+~.U'V'W'X'Y'Z'`' ).)+)@)#)$)%)&)*)| | | & =)-);)>),)')))!)~){)])^)/)()_%_)_%_):)<)[)e=O%})@ |)1)2)3)4)5)6)7)b$8)s 9)0)a)b)c)d)y$- - e)f)g)h)i)j)!+& ~+~+~+~+~+& ~+& X#| ==k)l)m)n)o)p)q)r)s)t)u)v)w)X#X#| & & | | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ x)y)z)A)B)C)D)E)F)z % & G)H)I)J)K)L)M)N)O)P)Q)R)S)T)U)V)W)X)| | 1 Y)Z)`) !.!+!@!#!$!%!&!*!=!-!h at N+N+N+A N+;!e@; >!* P@:$,!'!)!!!~!{!]!h@^!/!(!_!:!<![!}!* &&|!1!2!3!4!5!6!d+1 P+z | | | | | } ;!O+7!8!9!0!a!b!c!d!e!f!g!h!p'; g g & & & | | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i!j!k!l!m!n!o!p!q!* !.1 = q!r!s!t!u!v!w!x!y!; z!A!B!C!D!E!F!G!} 1 # H!I!J!K!L!M!N!O!P!Q!R!S!T!d z | | | ~+~+D&U!V!d g E W!X!Y!Z!`! ~.~+~f@@~#~$~%~C!&~*~=~-~;~>~,~'~)~!~~~{~!+# ]~p+1 1 1 1 N+^~N+8#/~(~_~:~<~[~}~|~1~2~3~4~J.5~6~& & & & ~+| | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7~j!8~9~0~a~b~c~d~e~^ 8>8 f~g~h~i~j~k~l~m~n~s o~p~q~r~s~t~u~] v~* w~x~y~z~A~B~C~D~E~F~G~H~I~J~K~L~M~M~M~N~O~O~P~Q~R~S~T~U~V~W~X~Y~Z~`~@' {d .{+{@{#{${%{&{*{={-{;{>{,{'{){!{~{{{,>]{{+2.9 &&^{U=1 * /{({_{:{<{[{r~}{|{1{2{3{2.!+2.D&~+~+~+{+| | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4{5{6{7{8{9{0{a{b{c{d{e{f{g{h{i{j{k{l{m{n{5-s o{p{q{r{s{t{u{v{w{x{y{z{A{B{C{D{E{>!F{G{H{I{J{K{L{M{N{O{P{Q{R{S{T{U{V{W{X{Y{Z{`{ ].]+]@]8>Y##]$]%]&]*]=]-]F)^.;]>],]'])]!]~]{]]]^]/]/](]_]:]<][]}]/>2.|]1]2]3]4]5]6]7]8]9]0];!A | | | | | | | | | | | {+| | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a]b]c]d]e]f]g]h]i]j]k]l]m]n]o]p]q]r]s]t]u]v]&{w]x]y]z]A]B]C]D]E]F]G]H]I]J]K]L]M]^{N]O]P]Q]R]S]T]U]V]W]X]Y]Z]`] ^.^> 7 +^@^#^$^%^&^*^=^L&-^;^>^,^'^)^!^~^T*F){^]^^^/^(^_^:^<^[^}^|^1^2^3^4^5^6^7^8^8#9^0^a^b^c^d^e^f^g^h^i^j^N+{+{+{+{+/.5-b%X%k^5-X#| | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . g at 7 l^m^n^o^p^q^r^s^t^u^v^w^x^y^z^A^B^C^D^E^F^G^H^I^J^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^`^ /./+/@/#/$/%/$/J^J^&/*/=/-/;/P~>/,/'/)/!/~/{/]/^///(/_/:/</[/}/|/1/2/3/4/5/6/7/8/9/0/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/s+j 5-X%w/x/y/z/A/B/C/D/x/b%X#| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . *&# {.E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/`/ (.(+(@(#($(%(&(*(=(-(;(>(,('()(!(~({(](^(/(((_(:(<([(}(|(1(2(3(4(5(6(7(8(9(0(a(b(c(d(e(f(g(h(i(j(k(l(m(n(o(p(q(r(s(t(u(v(w(x(y(z(A(B(C(D(E(F(G(H(I(J(K(L(M(N(O(P(Q(R(S(T(U(V(W(Q(U/X(Y(Z(`( _U(._/.{+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . #'l.Z$+_ at _#_$_%_&_*_=_-_;_>_,_'_)_!_~_{_]_^_/_(___:_<_[_}_|_1_2_3_4_5_6_7_8_9_0_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F_G_H_I_J_K_L_M_N_O_P_Q_R_S_T_U_V_W_X_Y_Z_`_ :.:+:@:#:$:%:&:*:=:-:;:>:,:':):!:~:{:]:^:/:(:_:::<:[:C/}:b%{+| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . G#r+|:1:2:3:4:5:6:7:8:9:0:a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:`: <.<+<@<#<$<%<&<*<=<-<;<><,<'<)<!<~<{<]<^</<(<_<:<<<[<}<|<1<2<3<4<5<6<7<8<9<0<a<b<c<d<e<f<_:Y(Z(<:w: _g<h<i<X%X#| | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . j<} k<l<m<n<o<p<q<r<s<t<u<v<w<x<y<z<A<B<C<.~D<E<F<G<H<I<J<K<L<M<N<O<#>P<Q<R<S<T<U<V<W<X<Y<Z<`< [.[+[@[#[$[%[&[*[=[-[;[>[,['[)[![~[{[][^[/[([_[:[<[r<[[}[|[1[2[3[4[5[6[7[8[9[n<0[a[b[c[d[e[-[f[g[h[>!i[j[k[l[m[n[o[p[q[r[s[t[u[v[C/w[x[i<X%k^5-X#| {+| | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | | 5-y[C/[:z[A[B[B/U(y/}:h<D/z/B/B/C/J*C[D[D!E[F[G[H[I[{:A[[:J[U(y/y/U(A/V(V(J[U(}:}:z/h't<z[B[ _C/J*J*w[D/D/D/}:i<K[K[i<h<C/[:B[[:C/x[._b%k^L[g< _W( _C/}:i<y[h<J[[:w:Q(t<h'C/J*w[y/h<h<}:x/K[5-{+{+X%z/v[z[A[`(A[W(V(w[x[y[w/b%/.5-X#{+| | | | {+{+{+| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | | | X#5-b%X%X%k^b%b%b%b%b%5-b%X%k^b%5-M[N[O[P[Q[R[S[T[w/K[k^5-5-/./.5-b%b%5-/.X#{+X#5-k^X%X%k^k^b%/./.5-5-5-/.X#| | | {+X#b%k^X%k^/.| | | {+| 5-k^X%b%5-{+| X#b%k^b%k^X%X%b%/.b%5-5-b%b%b%/.{+| {+| {+5-k^X%X%K[X%b%b%/.{+{+| | | | | | | {+{+{+{+{+{+| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | | | | | | | X#X#{+{+{+{+X#| | {+{+| | Y#U[V[W[X[Y[Z[`[{+{+{+| | | | | {+{+| | | | | | | {+X#X#{+{+| | | | | | {+X#/.X#| {+| | | | X#{+X#| {+{+{+| | | | | {+{+{+{+| | | | | | {+| | | | {+X#| | | | | {+X#{+{+{+{+| | {+{+| | | | | | | | | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | . . | . . | {+| {+| | {+{+| {+| . . . C A j<5. }o.U U | | | | | | {+{+{+| | | | | | | | | {+X#| | | | | | {+X#X#{+X#X#{+{+| | | | /.| X#| {+{+{+| | {+{+{+X#{+{+{+| | | | | | | | {+{+{+| {+{+| | | | {+| {+/././.{+| {+X#| . | | | | | | | | | | | | | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "};

Added: packages/openev/branches/upstream/current/pics/openfile.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/openfile.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/openfile.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,29 @@
+/* XPM */
+static char *openfile[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c #808000",
+"a c None",
+"b c #ffff00",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaa...aaaaaaa",
+"aaaaaaaaaaa.aaa.a.aaaa",
+"aaaaaaaaaaaaaaaa..aaaa",
+"aaaa...aaaaaaaa...aaaa",
+"aaa.bbb.......aaaaaaaa",
+"aaa.bbbbbbbbb.aaaaaaaa",
+"aaa.bbbbbbbbb.aaaaaaaa",
+"aaa.bbbb...........aaa",
+"aaa.bbb.#########.aaaa",
+"aaa.bb.#########.aaaaa",
+"aaa.b.#########.aaaaaa",
+"aaa..#########.aaaaaaa",
+"aaa...........aaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/pan_left.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/pan_left.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/pan_left.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,28 @@
+/* XPM */
+static char * pan_left_xpm[] = {
+"22 18 7 1",
+" 	c None",
+".	c #000000",
+"+	c #333366",
+"@	c #6666CC",
+"#	c #9999FF",
+"$	c #CCCCFF",
+"%	c #FFFFFF",
+"                      ",
+"          .+          ",
+"         . at +          ",
+"        .@@+          ",
+"       .@@#+          ",
+"      .@@##+++++++    ",
+"     .@@##$$$$$$$+    ",
+"    .@@@#$$$$$$$%+    ",
+"   .@@@##$$$%%%%%+    ",
+"   .@@@##$$$%%%%%+    ",
+"    .@@@#$$$$$$$%+    ",
+"     .@@##$$$$$$$+    ",
+"      .@@##+++++++    ",
+"       .@@#+          ",
+"        .@@+          ",
+"         . at +          ",
+"          .+          ",
+"                      "};

Added: packages/openev/branches/upstream/current/pics/pan_rght.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/pan_rght.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/pan_rght.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,28 @@
+/* XPM */
+static char * pan_rght_xpm[] = {
+"22 18 7 1",
+" 	c None",
+".	c #333366",
+"+	c #000000",
+"@	c #6666CC",
+"#	c #9999FF",
+"$	c #CCCCFF",
+"%	c #FFFFFF",
+"                      ",
+"          .+          ",
+"          . at +         ",
+"          .@@+        ",
+"          .#@@+       ",
+"    .......##@@+      ",
+"    .$$$$$$$##@@+     ",
+"    .%$$$$$$$#@@@+    ",
+"    .%%%%%$$$##@@@+   ",
+"    .%%%%%$$$##@@@+   ",
+"    .%$$$$$$$#@@@+    ",
+"    .$$$$$$$##@@+     ",
+"    .......##@@+      ",
+"          .#@@+       ",
+"          .@@+        ",
+"          . at +         ",
+"          .+          ",
+"                      "};

Added: packages/openev/branches/upstream/current/pics/print.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/print.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/print.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,30 @@
+/* XPM */
+static char *print[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        5            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ff0000",
+"c c #ffffff",
+/* pixels */
+"aaaaa............aaaaa",
+"aaaaa.cccccccccc.aaaaa",
+"aaaaa.c..ccccccc.aaaaa",
+"aaaaa.cccccccccc.aaaaa",
+"aaaaa.c........c.aaaaa",
+"aaaaa.cccccccccc.aaaaa",
+"aaaaa.c........c.aaaaa",
+"aaaa..cccccccccc..aaaa",
+"aaaa..............aaaa",
+"aa...#ccc#ccc#ccc...aa",
+"a.#c#c#c#c#c#c#c#c#c.a",
+"a.cbbbcccbbbbcccbbc#.a",
+"a.#c#c#c#c#c#c#c#c#c.a",
+"a.ccc#ccc#ccc#ccc#cc.a",
+"a....................a",
+"aa...aaaaaaaaaaaa...aa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/raise.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/raise.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/raise.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,22 @@
+/* XPM */
+static char * raise_xpm[] = {
+"16 16 3 1",
+" 	c None",
+".	c #000000",
+"+	c #7F7F7F",
+"                ",
+"                ",
+"                ",
+"                ",
+"                ",
+"       ..       ",
+"      ....+     ",
+"     ......+    ",
+"    ........+   ",
+"   ..........+  ",
+"  ............+ ",
+"   ++++++++++++ ",
+"                ",
+"                ",
+"                ",
+"                "};

Added: packages/openev/branches/upstream/current/pics/recenter.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/recenter.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/recenter.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,281 @@
+/* XPM */
+static char *recenter[] = {
+/* width height num_colors chars_per_pixel */
+"    20    18      256            2",
+/* colors */
+"`` c None",
+"`. c #003163",
+"`# c #000000",
+"`a c #000000",
+"`b c #000000",
+"`c c #000000",
+"`d c #000000",
+"`e c #000000",
+"`f c #000000",
+"`g c #000000",
+"`h c #000000",
+"`i c #000000",
+"`j c #000000",
+"`k c #000000",
+"`l c #000000",
+"`m c #000000",
+"`n c #000000",
+"`o c #000000",
+"`p c #000000",
+"`q c #000000",
+"`r c #000000",
+"`s c #000000",
+"`t c #000000",
+"`u c #000000",
+"`v c #000000",
+"`w c #000000",
+"`x c #000000",
+"`y c #000000",
+"`z c #000000",
+"`A c #000000",
+"`B c #000000",
+"`C c #000000",
+"`D c #000000",
+"`E c #000000",
+"`F c #000000",
+"`G c #000000",
+"`H c #000000",
+"`I c #000000",
+"`J c #000000",
+"`K c #000000",
+"`L c #000000",
+"`M c #000000",
+"`N c #000000",
+"`O c #000000",
+"`P c #000000",
+"`Q c #000000",
+"`R c #000000",
+"`S c #000000",
+"`T c #000000",
+"`U c #000000",
+"`V c #000000",
+"`W c #000000",
+"`X c #000000",
+"`Y c #000000",
+"`Z c #000000",
+"`0 c #000000",
+"`1 c #000000",
+"`2 c #000000",
+"`3 c #000000",
+"`4 c #000000",
+"`5 c #000000",
+"`6 c #000000",
+"`7 c #000000",
+"`8 c #000000",
+".` c #000000",
+".. c #000000",
+".# c #000000",
+".a c #000000",
+".b c #000000",
+".c c #000000",
+".d c #000000",
+".e c #000000",
+".f c #000000",
+".g c #000000",
+".h c #000000",
+".i c #000000",
+".j c #000000",
+".k c #000000",
+".l c #000000",
+".m c #000000",
+".n c #000000",
+".o c #000000",
+".p c #000000",
+".q c #000000",
+".r c #000000",
+".s c #000000",
+".t c #000000",
+".u c #000000",
+".v c #000000",
+".w c #000000",
+".x c #000000",
+".y c #000000",
+".z c #000000",
+".A c #000000",
+".B c #000000",
+".C c #000000",
+".D c #000000",
+".E c #000000",
+".F c #000000",
+".G c #000000",
+".H c #000000",
+".I c #000000",
+".J c #000000",
+".K c #000000",
+".L c #000000",
+".M c #000000",
+".N c #000000",
+".O c #000000",
+".P c #000000",
+".Q c #000000",
+".R c #000000",
+".S c #000000",
+".T c #000000",
+".U c #000000",
+".V c #000000",
+".W c #000000",
+".X c #000000",
+".Y c #000000",
+".Z c #000000",
+".0 c #000000",
+".1 c #000000",
+".2 c #000000",
+".3 c #000000",
+".4 c #000000",
+".5 c #000000",
+".6 c #000000",
+".7 c #000000",
+".8 c #000000",
+"#` c #000000",
+"#. c #000000",
+"## c #000000",
+"#a c #000000",
+"#b c #000000",
+"#c c #000000",
+"#d c #000000",
+"#e c #000000",
+"#f c #000000",
+"#g c #000000",
+"#h c #000000",
+"#i c #000000",
+"#j c #000000",
+"#k c #000000",
+"#l c #000000",
+"#m c #000000",
+"#n c #000000",
+"#o c #000000",
+"#p c #000000",
+"#q c #000000",
+"#r c #000000",
+"#s c #000000",
+"#t c #000000",
+"#u c #000000",
+"#v c #000000",
+"#w c #000000",
+"#x c #000000",
+"#y c #000000",
+"#z c #000000",
+"#A c #000000",
+"#B c #000000",
+"#C c #000000",
+"#D c #000000",
+"#E c #000000",
+"#F c #000000",
+"#G c #000000",
+"#H c #000000",
+"#I c #000000",
+"#J c #000000",
+"#K c #000000",
+"#L c #000000",
+"#M c #000000",
+"#N c #000000",
+"#O c #000000",
+"#P c #000000",
+"#Q c #000000",
+"#R c #000000",
+"#S c #000000",
+"#T c #000000",
+"#U c #000000",
+"#V c #000000",
+"#W c #000000",
+"#X c #000000",
+"#Y c #000000",
+"#Z c #000000",
+"#0 c #000000",
+"#1 c #000000",
+"#2 c #000000",
+"#3 c #000000",
+"#4 c #000000",
+"#5 c #000000",
+"#6 c #000000",
+"#7 c #000000",
+"#8 c #000000",
+"a` c #000000",
+"a. c #000000",
+"a# c #000000",
+"aa c #000000",
+"ab c #000000",
+"ac c #000000",
+"ad c #000000",
+"ae c #000000",
+"af c #000000",
+"ag c #000000",
+"ah c #000000",
+"ai c #000000",
+"aj c #000000",
+"ak c #000000",
+"al c #000000",
+"am c #000000",
+"an c #000000",
+"ao c #000000",
+"ap c #000000",
+"aq c #000000",
+"ar c #000000",
+"as c #000000",
+"at c #000000",
+"au c #000000",
+"av c #000000",
+"aw c #000000",
+"ax c #000000",
+"ay c #000000",
+"az c #000000",
+"aA c #000000",
+"aB c #000000",
+"aC c #000000",
+"aD c #000000",
+"aE c #000000",
+"aF c #000000",
+"aG c #000000",
+"aH c #000000",
+"aI c #000000",
+"aJ c #000000",
+"aK c #000000",
+"aL c #000000",
+"aM c #000000",
+"aN c #000000",
+"aO c #000000",
+"aP c #000000",
+"aQ c #000000",
+"aR c #000000",
+"aS c #000000",
+"aT c #000000",
+"aU c #000000",
+"aV c #000000",
+"aW c #000000",
+"aX c #000000",
+"aY c #000000",
+"aZ c #000000",
+"a0 c #000000",
+"a1 c #000000",
+"a2 c #000000",
+"a3 c #000000",
+"a4 c #000000",
+"a5 c #000000",
+"a6 c #000000",
+"a7 c #000000",
+"a8 c #000000",
+/* pixels */
+"````````````````````````````````````````",
+"```````````````````.````````````````````",
+"```````````````````.````````````````````",
+"`````````````````.`.`.``````````````````",
+"`````````````````.`.`.``````````````````",
+"```````````````.`.`.`.`.````````````````",
+"```````````````````.````````````````````",
+"```````````.```````.```````.````````````",
+"```````.`.`.```````.```````.`.`.````````",
+"```.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.````",
+"```````.`.`.```````.```````.`.`.````````",
+"```````````.```````.```````.````````````",
+"```````````````````.````````````````````",
+"```````````````.`.`.`.`.````````````````",
+"`````````````````.`.`.``````````````````",
+"`````````````````.`.`.``````````````````",
+"```````````````````.````````````````````",
+"```````````````````.````````````````````"
+};

Added: packages/openev/branches/upstream/current/pics/refresh.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/refresh.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/refresh.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,30 @@
+/* XPM */
+static char *refresh[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        5            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ff0000",
+"c c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaa.aaaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaa.....aaaaaaaaa",
+"aaaaaaa.......aaaaaaaa",
+"aaaaaa.........aaaaaaa",
+"aaaaa..a.....a..aaaaaa",
+"aaaa..aaa...aaa..aaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaa...aaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/root.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/root.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/root.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,31 @@
+/* XPM */
+static char *linear[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaa.................aa",
+"aaa.###############.aa",
+"aaa.aaaaaaaaa.##aa#.aa",
+"aaa.aaaaaaa.##aaaa#.aa",
+"aaa.aaaaaa.#aaaaaa#.aa",
+"aaa.aaaaa.#aaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaaa.#aaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa.aaa.#aaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.aa.#aaaaaaaaaa#.aa",
+"aaa.................aa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};
+
+

Added: packages/openev/branches/upstream/current/pics/save.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/save.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/save.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,28 @@
+/* XPM */
+static char *save[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        3            1",
+/* colors */
+". c #000000",
+"# c #c0c0c0",
+"a c #0000ff",
+/* pixels */
+"######################",
+"######################",
+"####..............####",
+"####.a.########.#.####",
+"####.a.########...####",
+"####.a.########.a.####",
+"####.a.########.a.####",
+"####.a.########.a.####",
+"####.aa........aa.####",
+"####.aaaaaaaaaaaa.####",
+"####.aaaaaaaaaaaa.####",
+"####.aa.........a.####",
+"####.aa......##.a.####",
+"####.aa......##.a.####",
+"####.aa......##.a.####",
+"#####...........a.####",
+"######################",
+"######################"
+};

Added: packages/openev/branches/upstream/current/pics/seeall.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/seeall.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/seeall.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,27 @@
+/* XPM */
+static char *seeall[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        2            1",
+/* colors */
+". c #000000",
+"a c None",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"a....................a",
+"a.aaaaaaaaaaaaaaaaaa.a",
+"a.a...aaaaaaaaaa...a.a",
+"a.a..aaaaaaaaaaaa..a.a",
+"a.a.a.aaaaaaaaaa.a.a.a",
+"a.aaaa.aaaaaaaa.aaaa.a",
+"a.aaaaaaaaaaaaaaaaaa.a",
+"a.aaaaaaaaaaaaaaaaaa.a",
+"a.aaaaaaaaaaaaaaaaaa.a",
+"a.aaaaaaaaaaaaaaaaaa.a",
+"a.aaaa.aaaaaaaa.aaaa.a",
+"a.a.a.aaaaaaaaaa.a.a.a",
+"a.a..aaaaaaaaaaaa..a.a",
+"a.a...aaaaaaaaaa...a.a",
+"a.aaaaaaaaaaaaaaaaaa.a",
+"a....................a",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/sym_circle.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_circle.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_circle.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,21 @@
+/* XPM */
+static char * sym_circle_xpm[] = {
+"16 16 2 1",
+" 	c None",
+".	c #0B76B9",
+"    ........    ",
+"  ...      ...  ",
+" ..          .. ",
+" .            . ",
+"..            ..",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+"..            ..",
+" .            . ",
+" ..          .. ",
+"  ...      ...  ",
+"    ........    "};

Added: packages/openev/branches/upstream/current/pics/sym_cross.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_cross.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_cross.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,21 @@
+/* XPM */
+static char * sym_cross_xpm[] = {
+"15 16 2 1",
+" 	c None",
+".	c #0B76B9",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"...............",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       ",
+"       .       "};

Added: packages/openev/branches/upstream/current/pics/sym_filled_circle.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_filled_circle.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_filled_circle.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,21 @@
+/* XPM */
+static char * sym_filled_circle_xpm[] = {
+"16 16 2 1",
+" 	c None",
+".	c #0B76B9",
+"    ........    ",
+"  ............  ",
+" .............. ",
+" .............. ",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+" .............. ",
+" .............. ",
+"  ............  ",
+"    ........    "};

Added: packages/openev/branches/upstream/current/pics/sym_filled_square.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_filled_square.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_filled_square.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,19 @@
+/* XPM */
+static char * sym_filled_square_xpm[] = {
+"16 14 2 1",
+" 	c None",
+".	c #0B76B9",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................",
+"................"};

Added: packages/openev/branches/upstream/current/pics/sym_filled_star.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_filled_star.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_filled_star.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* XPM */
+static char * sym_filled_star_xpm[] = {
+"18 18 2 1",
+" 	c None",
+".	c #0B76B9",
+"                  ",
+"        ..        ",
+"        ...       ",
+"       ....       ",
+"       .....      ",
+"      ......      ",
+"      ......      ",
+" ................ ",
+" ................ ",
+"  ..............  ",
+"    ...........   ",
+"   ............   ",
+"   ............   ",
+"  ..............  ",
+"  .....    .....  ",
+" ....        .... ",
+" ...          ... ",
+"                  "};

Added: packages/openev/branches/upstream/current/pics/sym_filled_triangle.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_filled_triangle.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_filled_triangle.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* XPM */
+static char * sym_filled_triangle_xpm[] = {
+"18 18 2 1",
+" 	c None",
+".	c #0B76B9",
+"                  ",
+"        ...       ",
+"       ....       ",
+"       .....      ",
+"      ......      ",
+"      .......     ",
+"     ........     ",
+"     .........    ",
+"    ..........    ",
+"    ..........    ",
+"   ............   ",
+"   ............   ",
+"  ..............  ",
+"  ..............  ",
+" ................ ",
+" .................",
+"..................",
+"                  "};

Added: packages/openev/branches/upstream/current/pics/sym_square.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_square.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_square.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,19 @@
+/* XPM */
+static char * sym_square_xpm[] = {
+"16 14 2 1",
+" 	c None",
+".	c #0B76B9",
+"................",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+".              .",
+"................"};

Added: packages/openev/branches/upstream/current/pics/sym_star.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_star.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_star.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* XPM */
+static char * sym_star_xpm[] = {
+"18 18 2 1",
+" 	c None",
+".	c #0B76B9",
+"                  ",
+"        ..        ",
+"        ...       ",
+"       .  .       ",
+"       .  .       ",
+"      .    .      ",
+"      .    .      ",
+" ................ ",
+" ..  .      .  .. ",
+"  ...        ...  ",
+"    ..      ...   ",
+"   .  ..  ..  .   ",
+"   .    ..    .   ",
+"  .   ......   .  ",
+"  .  ..    ... .  ",
+" ....        .... ",
+" ...          ... ",
+"                  "};

Added: packages/openev/branches/upstream/current/pics/sym_triangle.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_triangle.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_triangle.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* XPM */
+static char * sym_triangle_xpm[] = {
+"18 18 2 1",
+" 	c None",
+".	c #0B76B9",
+"                  ",
+"        ...       ",
+"       ....       ",
+"       .....      ",
+"      ..  ..      ",
+"      ..  ...     ",
+"     ..    ..     ",
+"     ..    ...    ",
+"    ..      ..    ",
+"    ..      ..    ",
+"   ..        ..   ",
+"   ..        ..   ",
+"  ..          ..  ",
+"  ..          ..  ",
+" ...          ... ",
+" .................",
+"..................",
+"                  "};

Added: packages/openev/branches/upstream/current/pics/sym_vertical.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_vertical.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_vertical.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,18 @@
+/* XPM */
+static char * sum_vertical2_xpm[] = {
+"2 13 2 1",
+" 	c None",
+".	c #0B76B9",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" .",
+" ."};

Added: packages/openev/branches/upstream/current/pics/sym_x.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/sym_x.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/sym_x.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/* XPM */
+static char * sym_x_xpm[] = {
+"18 18 2 1",
+" 	c None",
+".	c #0B76B9",
+"                  ",
+" .              . ",
+"  .            .  ",
+"   .          .   ",
+"    .        .    ",
+"     .      .     ",
+"      .    .      ",
+"       .  .       ",
+"        ..        ",
+"        ..        ",
+"       .  .       ",
+"      .    .      ",
+"     .      .     ",
+"    .        .    ",
+"   .          .   ",
+"  .            .  ",
+" .              . ",
+"                  "};

Added: packages/openev/branches/upstream/current/pics/vexcel_logo.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/vexcel_logo.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/vexcel_logo.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,313 @@
+/* XPM */
+static char * vexcel_logo_xpm[] = {
+"234 53 257 2",
+"  	c None",
+". 	c #D6D6D6",
+"+ 	c #005495",
+"@ 	c #005595",
+"# 	c #AABBC8",
+"$ 	c #81C150",
+"% 	c #015695",
+"& 	c #015595",
+"* 	c #8AA8BF",
+"= 	c #035696",
+"- 	c #D0D2D4",
+"; 	c #B1C0CB",
+"> 	c #035796",
+", 	c #D5D5D5",
+"' 	c #C9CED2",
+") 	c #236AA0",
+"! 	c #2369A0",
+"~ 	c #0D5C99",
+"{ 	c #D3D4D5",
+"] 	c #D3D5D1",
+"^ 	c #B1CD9B",
+"/ 	c #0D5D99",
+"( 	c #256AA0",
+"_ 	c #ACBDC9",
+": 	c #D1D5CE",
+"< 	c #A1C981",
+"[ 	c #C4D2BA",
+"} 	c #9EB4C5",
+"| 	c #256BA0",
+"1 	c #598BB0",
+"2 	c #B9C5CD",
+"3 	c #8CC462",
+"4 	c #BACFAA",
+"5 	c #BCC6CE",
+"6 	c #AECC96",
+"7 	c #84C255",
+"8 	c #6B95B5",
+"9 	c #CDD0D3",
+"0 	c #22699F",
+"a 	c #CCD4C7",
+"b 	c #C2D1B6",
+"c 	c #6D97B6",
+"d 	c #85C257",
+"e 	c #C0C9CF",
+"f 	c #D4D6D3",
+"g 	c #B7CEA4",
+"h 	c #C0D0B3",
+"i 	c #A4C986",
+"j 	c #457EAA",
+"k 	c #CACFD2",
+"l 	c #96C671",
+"m 	c #80A2BC",
+"n 	c #91ACC1",
+"o 	c #D5D6D4",
+"p 	c #065897",
+"q 	c #8DA9C0",
+"r 	c #D1D3D4",
+"s 	c #D5D5D6",
+"t 	c #9EC87D",
+"u 	c #A8CB8E",
+"v 	c #C4CBD1",
+"w 	c #AFBECA",
+"x 	c #B2C0CB",
+"y 	c #5587AF",
+"z 	c #045796",
+"A 	c #286CA1",
+"B 	c #0A5B98",
+"C 	c #266CA0",
+"D 	c #C6CCD1",
+"E 	c #427CA9",
+"F 	c #12609B",
+"G 	c #3D7AA8",
+"H 	c #92C56B",
+"I 	c #A1B6C6",
+"J 	c #D5D6D5",
+"K 	c #94C66E",
+"L 	c #045896",
+"M 	c #B4CEA0",
+"N 	c #D6D6D5",
+"O 	c #D4D5D2",
+"P 	c #2C6FA3",
+"Q 	c #A4B8C7",
+"R 	c #CED1D3",
+"S 	c #CFD4CB",
+"T 	c #A8BAC8",
+"U 	c #90C568",
+"V 	c #82C150",
+"W 	c #A2C985",
+"X 	c #82C152",
+"Y 	c #7B9EBA",
+"Z 	c #C7D2BF",
+"` 	c #98B1C3",
+" .	c #D3D5D2",
+"..	c #A6CA8A",
+"+.	c #AABCC9",
+"@.	c #9AC777",
+"#.	c #D2D5D0",
+"$.	c #C8D3C0",
+"%.	c #CED1D4",
+"&.	c #3273A4",
+"*.	c #C1C9D0",
+"=.	c #1A649D",
+"-.	c #4C83AC",
+";.	c #B7CEA6",
+">.	c #9EC87E",
+",.	c #7199B7",
+"'.	c #C6D2BC",
+").	c #AFCC98",
+"!.	c #86C258",
+"~.	c #D4D5D5",
+"{.	c #A5CA88",
+"].	c #4980AB",
+"^.	c #BDD0AE",
+"/.	c #3C79A7",
+"(.	c #91C569",
+"_.	c #6793B4",
+":.	c #98C774",
+"<.	c #81C151",
+"[.	c #7EA1BB",
+"}.	c #095A98",
+"|.	c #025696",
+"1.	c #8AC35F",
+"2.	c #A9BBC8",
+"3.	c #A5CA89",
+"4.	c #8EC464",
+"5.	c #AACB91",
+"6.	c #BFC8CF",
+"7.	c #5E8EB2",
+"8.	c #D2D3D5",
+"9.	c #9BC778",
+"0.	c #B8CFA7",
+"a.	c #B5CEA3",
+"b.	c #B7C3CD",
+"c.	c #88A7BE",
+"d.	c #95AFC2",
+"e.	c #266BA0",
+"f.	c #5487AE",
+"g.	c #3474A5",
+"h.	c #89C35D",
+"i.	c #B3CD9F",
+"j.	c #17629C",
+"k.	c #94C66D",
+"l.	c #105F9A",
+"m.	c #A9CB90",
+"n.	c #779CB9",
+"o.	c #AABBC9",
+"p.	c #BED0B0",
+"q.	c #9BB2C4",
+"r.	c #0E5E99",
+"s.	c #8FC466",
+"t.	c #025596",
+"u.	c #B2CD9D",
+"v.	c #1F679E",
+"w.	c #2A6DA2",
+"x.	c #D0D4CC",
+"y.	c #93C56C",
+"z.	c #B4C1CC",
+"A.	c #6994B5",
+"B.	c #CBD3C4",
+"C.	c #ADCC95",
+"D.	c #105E9A",
+"E.	c #82C151",
+"F.	c #3172A4",
+"G.	c #83C153",
+"H.	c #CCD0D3",
+"I.	c #618FB2",
+"J.	c #5386AE",
+"K.	c #3776A6",
+"L.	c #6491B3",
+"M.	c #B1CD9C",
+"N.	c #5287AE",
+"O.	c #C5CCD1",
+"P.	c #407BA8",
+"Q.	c #87C259",
+"R.	c #3977A6",
+"S.	c #075997",
+"T.	c #18639C",
+"U.	c #6B95B6",
+"V.	c #C0D1B3",
+"W.	c #2F71A3",
+"X.	c #9CB3C4",
+"Y.	c #2B6FA2",
+"Z.	c #D1D5CF",
+"`.	c #14619B",
+" +	c #6290B3",
+".+	c #AFBFCA",
+"++	c #D2D5CF",
+"@+	c #B5C2CC",
+"#+	c #085998",
+"$+	c #A4B7C7",
+"%+	c #5185AD",
+"&+	c #8AC35D",
+"*+	c #CDD4C7",
+"=+	c #CED4C9",
+"-+	c #C9D3C2",
+";+	c #CAD3C3",
+">+	c #3675A5",
+",+	c #0B5C98",
+"'+	c #C8D2BF",
+")+	c #BACFA9",
+"!+	c #A9BAC8",
+"~+	c #B9CFA8",
+"{+	c #AEBECA",
+"]+	c #A4CA87",
+"^+	c #8CC461",
+"/+	c #B3C1CB",
+"(+	c #115F9A",
+"_+	c #759BB9",
+":+	c #ABCB92",
+"<+	c #6F98B7",
+"[+	c #B5CEA1",
+"}+	c #5286AE",
+"|+	c #83C254",
+"1+	c #5387AE",
+"2+	c #B0CD9B",
+"3+	c #5689AF",
+"4+	c #C8CDD2",
+"5+	c #B6CEA3",
+"6+	c #8DC463",
+"7+	c #125F9B",
+"8+	c #ADBDCA",
+"9+	c #4780AB",
+"0+	c #1D669E",
+"a+	c #5B8CB1",
+"b+	c #4B81AC",
+"c+	c #83A4BD",
+"d+	c #9FC880",
+"e+	c #91C56A",
+"f+	c #C3D1B8",
+"g+	c #9BC87A",
+"h+	c #9CC87B",
+"i+	c #246AA0",
+"j+	c #246BA0",
+"k+	c #085A97",
+"l+	c #99C776",
+"m+	c #6893B5",
+"n+	c #81C050",
+"o+	c #B0BFCB",
+"p+	c #A7CA8C",
+"q+	c #5186AE",
+"r+	c #789DB9",
+"s+	c #3775A6",
+"t+	c #B0CD9A",
+"u+	c #4F84AD",
+"v+	c #86C256",
+"w+	c #97B0C3",
+"x+	c #8BA9BF",
+"y+	c #ACCB92",
+"z+	c #83C151",
+"A+	c #81C051",
+"B+	c #115E9A",
+"C+	c #739AB8",
+"D+	c #3071A4",
+"E+	c #1C659D",
+"F+	c #1C669D",
+"G+	c #BBCFAC",
+"H+	c #B4CEA1",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . =+h g ;.)+O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  .'.H+i ..0.-+#.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . S 4 ]+9.{.[+Z J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . *+;.W e+ at .5.-+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . #.G+ at .Q.6+u $.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . J h < h.|+t ^.#.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a i.k.$ h...'+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -+3.d $ &+M.: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b t 7 $ 6+0.: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . '+W E.$ 1.).: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . ++:+7 $ $ i *+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . h H $ $ U ^.. . . . . . . . . . . . . . . w 2.# # +.r . . . . . . . . . . . . . . . . . . . . _ # # T b.. x # # # # # # # # # # # # # # # # # # # # # # o.9 . . v I 2.# # _ . . . . . . . . . . . . . . . . . e # # !+I 5 . . . . . . . . 8.e T q * n.8 Y * n w k . . . . . . . . . . { _ # # # # # # # # # # # # # # # # # # # # # # _ { . . . D # # # +.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . o t+X $ $ 9.a . . . . . . . . . . . . . . . . c+z > = L a+. . . . . . . . . . . . . . . . . . . Y z > = p n s A > > > = & & % & % % % % % % % % % % % & % % L # . . 6.].& % > p ].e . . . . . . . . . . . . . . n v.= & + R.w . . . . . /+c+3+Y.}.+ @ + + + @ @ + > `./.U.} %.. . . . . . ' D.> > > > % & & & % & & % & % & & % & % & & % / ' . . . q = > = S.+.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . < $ $ $ ..N . . . . . . . . . . . . . . . . . . 7.@ @ + @ n . . . . . . . . . . . . . . . . . T B + + + L.. r ) @ + @ + & % & & % & & % & % % & % & % % % & z # . . . s _+p + + @ E+X.. . . . . . . . . . . v N.@ + @ & L.- . . . . $+E #++ @ + + k+0 F.b+-.E Y.`.+ + + + + P * . . . . . ' B @ @ + + % & & & % & & & & % % & % & % & % % / ' . . . * + + + & 2.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . o ]+$ $ $ ]+. . . . . . . . . . . . . . . . . . . . D e.+ @ @ 0+5 . . . . . . . . . . . . . . . R P @ @ + F.9 . - ( + + @ E # # # # # # # # # # # # # # # # # # o.9 . . . . . } A + @ @ B _+~.. . . . . . . . } A @ @ + C X.. . . . *.3+= + @ + j+7.d.6.- ~.. . . { k } m+( + @ @ + 1+v . . . ' / + @ + y # # # # # # # # # # # # # # # # # # _ { . . . * @ + @ > # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . 2+$ $ $ K  .. . . . . . . . . . . . . . . . . . . . . x / @ + @ P.. . . . . . . . . . . . . . . }++ @ + =.6.. . - | @ + @ 3+. . . . . . . . . . . . . . . . . . . . . . . . . . ' -.+ + + + G 6.. . . . . k _.t.+ @ @ a+' . . . . b.0 + + + ,+r+%.. . . . . . . . . . . . '  ++ + + @ F.k . . ' ~ + + + c . . . . . . . . . . . . . . . . . . . . . . . * @ @ @ > # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . '.7 $ $ d $.. . . . . . . . . . . . . . . . . . . . . . . c++ + @ @ n.. . . . . . . . . . . . . x++ + + % q.. . . - | @ + @ J.. . . . . . . . . . . . . . . . . . . . . . . . . . . . c.F + + @ 0+n . . . Q P + @ + 0+n . . . . . O.w.+ + + / I . . . . . . . . . . . . . . . . c + = ,+T.c.. . ' ~ + + @ 8 . . . . . . . . . . . . . . . . . . . . . . . * + @ + = # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . < $ $ $ h+. . . . . . . . . . . . . . . . . . . . . . . . . f.+ @ @ k++.. . . . . . . . . . . x `.+ + @ 8 . . . . - | + + @ f.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . o+g.+ @ + z c D [.}.+ + @ ].5 . . . . . . 8 + @ + |.c.. . . . . . . . . . . . . . . . . . ` !+v R %.. . ' / @ + @ c . . . . . . . . . . . . . . . . . . . . . . . * + + @ > # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . ^+$ $ $ 6 . . . . . . . . . . . . . . . . . . . . . . . . . k ( + + @ W.H.. . . . . . . . . { W.+ + + P.. . . . . - | + + @ %+- - - - - - - - - - - - - - - - - r . . . . . . . . . . . . 8._.% + + @ =.+ + @ l.c.. . . . . . . { C @ + + A s . . . . . . . . . . . . . . . . . . . . . . . . . ' / @ @ @ _.- - - - - - - - - - - - - - - - - 8.. . . . . * @ @ @ = # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . d $ $ $ u.. . . . . . . . . . . . . . . . . . . . . . . . . . ; D.+ + @ 1 . . . . . . . . . 7.@ + + 0 6.. . . . . - ( @ @ + ~ ) ) ! ! ) ) ) ) ! ) ) ) ! ! ) ! ! E r . . . . . . . . . . . . . } C + + + + @ -. at +. . . . . . . . *.}.@ @ @ }+. . . . . . . . . . . . . . . . . . . . . . . . . . ' ~ + @ @ F ) ) ) ! ) ! ! ! ! ) ! ! ! ) ! ! )  +. . . . . * + @ @ = # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . Q.$ $ $ 3.. . . . . . . . . . . . . . . . . . . . . . . . . . . [.+ + @ = ` . . . . . . . d.@ @ + p I . . . . . . - ( @ @ + + + @ @ + @ @ + @ + @ + @ + @ @ + + =.- . . . . . . . . . . . . . ' j + + + + % N.v . . . . . . . . .+z + @ @ 7.. . . . . . . . . . . . . . . . . . . . . . . . . . ' ~ + + + + @ @ + + @ + @ @ + + + + + @ + + @ j . . . . . * + + @ = # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . H V $ $ 6+] . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 + + + j.e . . . . . 2 T.@ + + ,.. . . . . . . - ( + @ + >+* * * * * * * * * * * * * * * * * ` ~.. . . . . . . . . . . . w+E++ + @ @ @ + + F+} . . . . . . . 4+B + + + %+. . . . . . . . . . . . . . . . . . . . . . . . . . ' ~ + @ + j * * * * * * * * * * * * * * * * * Q . . . . . * + + @ = # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . C.$ $ $ $ p+. . . . . . . . . . . . . . . . . . . . . . . . . . . ' A + + + 9+. . . . s K.+ + + E . . . . . . . . - | + @ + 3+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . H.1 p + + + I.w+w.+ @ @ S._.R . . . . . { D++ @ @ e.s . . . . . . . . . . . . . . . . . . 8.9 . . . . . ' ~ + @ + ,.. . . . . . . . . . . . . . . . . . . . . . . * + + + = # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . Z.H $ $ $ $ a.. . . . . . . . . . . . . . . . . . . . . . . . . . . {+r.+ + + Y . . . c @ @ + | e . . . . . . . . - | + + @ 1+. . . . . . . . . . . . . . . . . . . . . . . . . . . . $+F.@ @ + 7+Y . . v ].@ @ @ + K.w . . . . . n.@ @ @ |.q . . . . . . . . . . . . . . . . . . q+=./.f.U.k . ' ~ + @ @ 8 . . . . . . . . . . . . . . . . . . . . . . . * + + + > # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . '.d $ $ $ <.).. . . . . . . . . . . . . . . . . . . . . . . . . . . [.+ @ + ~ b.. q.+ + @ = T . . . . . . . . . - ( @ + @ J.. . . . . . . . . . . . . . . . . . . . . . . . . . { <+L + + + &./+. . . . . m B + + + / * . . . . r K.+ + + (+x . . . . . . . . . . . . . . . . q t.+ @ + g.- . ' / + + @ 8 . . . . . . . . . . . . . . . . . . . . . . . * + + @ > # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . b h.$ $ $ $ >.a . . . . . . . . . . . . . . . . . . . . . . . . . . 1 @ + + &.w v.+ + + [.. . . . . . . . . . - ( @ + @ 3+. . . . . . . . . . . . . . . . . . . . . . . . . {+>++ @ @ = A.R . . . . . . . T &.@ @ @ @ u+v . . . 6.w.+ + @ j.Y r . . . . . . . . . . . . ' A.S.@ @ + F.v . . ' ~ @ + + c . . . . . . . . . . . . . . . . . . . . . . . * + + + = w . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . [ k.V $ $ $ 6+M ] . . . . . . . . . . . . . . . . . . . . . . . . 4+C + @ @ F @ + + j . . . . . . . . . . . - | + + @ j ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; z.{ . . . m F @ + + 0 w+. . . . . . . . . . H.3+|.@ + @ e.X.. . . 6./.@ + @ @ P C+I e - s . . . - *.Q ,.i++ @ @ @ j e . . . ' ~ @ @ @ 1 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; 5 . . . * + + @ = q ; ; ; ; ; ; ; ; ; ; ; ; ; ; z.. . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . #.u G.$ $ $ $ y.^.o . . . . . . . . . . . . . . . . . . . . . . . x ~ + @ @ + @ A O.. . . . . . . . . . . - ) + + + + = = = = = > > = = = > = = > = > > = = l.' . 2 E @ + + + j D . . . . . . . . . . . . . n r.@ @ @ @ A.R . . . * A % @ + + @ #+0 g.-.-.s+( }.@ + + + = P * . . . . . ' }.+ @ @ % > = > > = > > = > > > > = > = = = > = G . . . * @ @ @ @ > > = > = > > = > > = > = = = D.. . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . b g+$ $ $ $ $ K u.S . . . . . . . . . . . . . . . . . . . . . . c+p = = > B++.. . . . . . . . . . . . - A > > = > % & % & & % % & % % % & % & & % % & & r.9 q 0 + > = (+m ~.. . . . . . . . . . . . . . . x /.L = t.@ G z.. . . - } c /.T.z @ + + @ + + @ + = F P.<+Q r . . . . . . ' l.> > > = & & & % & & % % % % % % & % & & & & & /.. . . q = > = > |.& & % % % % & % % & % & & & ~ . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . ;.l <.$ V $ $ ^+m.f+f . . . . . . . . . . . . . . . . . . . . _ # # # 2 . . . . . . . . . . . . . s ; # # # # # # # # # # # # # # # # # # # # # # # 8+{ _ Q # # # 5 . . . . . . . . . . . . . . . . . . - o.# # 2.I 2 . . . . . . . 9 @+} * * ,.8 m * d..+k . . . . . . . . . . { _ # # # # # # # # # # # # # # # # # # # # # # # b.. . . D # # # # # # # # # # # # # # # # # # # _ . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . J V.t 7 $ $ $ $ X k.).;+N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Z C.(.$ $ $ n+$ v+l 5.f+O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . O ^.i 4.A+$ $ $ V E.U ..~+$. .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ] V.m.s.G.$ $ $ $ $ 7 s.3.~+*+f . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  .'.M W 6+X $ $ $ $ $ d (.>.6 h S N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ] f+).d+H d $ $ $ $ $ $ Q.l p+i.h B.] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . f *+p.5.l+1.|+$ $ $ $ $ $ 7 3 9.u 5+[ : f . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ++Z G+M.3.l 1.X $ $ V $ $ $ Q.U @.]+6 0.[ =+O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N #.Z 4 6 W @.(.Q.X $ $ $ $ $ $ 7 4.:.W C.a.p.f+B.: f . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . f Z.-+h a.:+< l 6+d E.$ $ $ $ $ $ $ G.4.l >.5.^ ;.b Z : ] O J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  .: ;+f+p.;.i.y+]+t l U &+z+$ $ $ $ <.<.!.1.U K 9.t {.u 6 [+5+h b [ =+S : ] ] ] J . . . . . . . . . . . . . . . . . . . . o o o f : x.x.*+*+N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . o ] : B.[ b ;.g ).m.m.< >.t @.k.y.K K 6+U (.H K H 4.:.:.:.d+]+{.{.{.6 ^ ^ ^ ^ ^ ^ ^ ^ M 4 4 4 4 )+^ g g g g u.M M m.a.J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . , , , , , , , , , , , , , , , , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . , , , , , , , . . . . . . . . . . . . . . . . , , , , , , , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "};

Added: packages/openev/branches/upstream/current/pics/warning.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/warning.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/warning.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,53 @@
+/* XPM */
+static char *warning[] = {
+/* width height num_colors chars_per_pixel */
+"    27    43        2            1",
+/* colors */
+". c #000000",
+"# c None",
+/* pixels */
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########..##############",
+"##########..#.#############",
+"#########..#.#.############",
+"#########.#.#.#############",
+"#########..#.#.############",
+"#########.#.#.#############",
+"#########..#.#.############",
+"##########..#.#############",
+"##########.#.##############",
+"##########..#.#############",
+"##########.#.##############",
+"##########..#.#############",
+"##########.#.##############",
+"###########################",
+"###########################",
+"##########....#############",
+"##########.#.##############",
+"##########..#.#############",
+"##########.#.##############",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################",
+"###########################"
+};
+

Added: packages/openev/branches/upstream/current/pics/windowed.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/windowed.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/windowed.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,29 @@
+/* XPM */
+static char *windowed[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        4            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aa..................aa",
+"aaa.##############.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa................aaa",
+"aaa.##############.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aaa.aaaaaaaaaaaaa#.aaa",
+"aa..................aa",
+"a....................a",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/worldg.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/worldg.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/worldg.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,36 @@
+/* XPM */
+static char *world[] = {
+/* width height num_colors chars_per_pixel */
+"    27    23        6            1",
+/* colors */
+". c None",
+"# c None",
+"a c None",
+"b c #282828",
+"c c #ffffff",
+"d c #ffffff",
+/* pixels */
+"...........................",
+".##########################",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa",
+".#aaaaaaaddaaaabdcadaaaaaaa",
+".#aaaaaaccbcaabcccbbbbcaaaa",
+".#aaaaabcbbcaacccbbbbbbcaaa",
+".#aaaaccbbbbccccbbbbbbcccca",
+".#aaacccbbbcccccccbbbbbcccc",
+".#aaccccbbbccccbbbbbbbbcccc",
+".#aaccccbcccccbbbbbbbcbcccc",
+".#accccccbccccbbbbbcbcccbcc",
+".#acccccccbbbcccbbbccccbccc",
+".#acccccccbbbcccbbccccbbbcc",
+".#accccccccbbcccbbccccbbbbc",
+".#aacccccccbcacccbccccbcbcc",
+".#aacccccccbcaacccccacccccb",
+".#aaccccaccccaacccccaacccca",
+".#aaacccaacccaaccccaaacccca",
+".#aaacccaacccaaacccaaaaccaa",
+".#aaaaddaaaddaaaaddaaaadaaa",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/worldrgb.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/worldrgb.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/worldrgb.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,36 @@
+/* XPM */
+static char *world[] = {
+/* width height num_colors chars_per_pixel */
+"    27    23        6            1",
+/* colors */
+". c None",
+"# c None",
+"a c None",
+"b c #00ff00",
+"c c #0000ff",
+"d c #ffffff",
+/* pixels */
+"...........................",
+".##########################",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa",
+".#aaaaaaaddaaaabdcadaaaaaaa",
+".#aaaaaaccbcaabcccbbbbcaaaa",
+".#aaaaabcbbcaacccbbbbbbcaaa",
+".#aaaaccbbbbccccbbbbbbcccca",
+".#aaacccbbbcccccccbbbbbcccc",
+".#aaccccbbbccccbbbbbbbbcccc",
+".#aaccccbcccccbbbbbbbcbcccc",
+".#accccccbccccbbbbbcbcccbcc",
+".#acccccccbbbcccbbbccccbccc",
+".#acccccccbbbcccbbccccbbbcc",
+".#accccccccbbcccbbccccbbbbc",
+".#aacccccccbcacccbccccbcbcc",
+".#aacccccccbcaacccccacccccb",
+".#aaccccaccccaacccccaacccca",
+".#aaacccaacccaaccccaaacccca",
+".#aaacccaacccaaacccaaaaccaa",
+".#aaaaddaaaddaaaaddaaaadaaa",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa",
+".#aaaaaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/zoomin.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/zoomin.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/zoomin.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,30 @@
+/* XPM */
+static char *zoomin[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        5            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ff0000",
+"c c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaa...aaaaaaaaaaaa",
+"aaaaa..aaa..aaaaaaaaaa",
+"aaaa.aaabaaa.aaaaaaaaa",
+"aaaa.aaabaaa.aaaaaaaaa",
+"aaa.aaaabaaaa.aaaaaaaa",
+"aaa.abbbbbbba.aaaaaaaa",
+"aaa.aaaabaaaa.aaaaaaaa",
+"aaaa.aaabaaa..aaaaaaaa",
+"aaaa..aabaaa.aaaaaaaaa",
+"aaaaa..aaa....#aaaaaaa",
+"aaaaaaa....a...#aaaaaa",
+"aaaaaaaaaaaa#...#aaaaa",
+"aaaaaaaaaaaaa#...caaaa",
+"aaaaaaaaaaaaaa#.ccaaaa",
+"aaaaaaaaaaaaaaaccaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/zoomout.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/zoomout.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/zoomout.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,30 @@
+/* XPM */
+static char *zoomout[] = {
+/* width height num_colors chars_per_pixel */
+"    22    18        5            1",
+/* colors */
+". c #000000",
+"# c #808080",
+"a c None",
+"b c #ff0000",
+"c c #ffffff",
+/* pixels */
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa",
+"aaaaaaa...aaaaaaaaaaaa",
+"aaaaa..aaa..aaaaaaaaaa",
+"aaaa.aaaaaaa.aaaaaaaaa",
+"aaaa.aaaaaaa.aaaaaaaaa",
+"aaa.aaaaaaaaa.aaaaaaaa",
+"aaa.abbbbbbba.aaaaaaaa",
+"aaa.aaaaaaaaa.aaaaaaaa",
+"aaaa.aaaaaaa..aaaaaaaa",
+"aaaa..aaaaaa.aaaaaaaaa",
+"aaaaa..aaa....#aaaaaaa",
+"aaaaaaa....a...#aaaaaa",
+"aaaaaaaaaaaa#...#aaaaa",
+"aaaaaaaaaaaaa#...caaaa",
+"aaaaaaaaaaaaaa#.ccaaaa",
+"aaaaaaaaaaaaaaaccaaaaa",
+"aaaaaaaaaaaaaaaaaaaaaa"
+};

Added: packages/openev/branches/upstream/current/pics/zoomrect.xpm
===================================================================
--- packages/openev/branches/upstream/current/pics/zoomrect.xpm	                        (rev 0)
+++ packages/openev/branches/upstream/current/pics/zoomrect.xpm	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,281 @@
+/* XPM */
+static char *zoomrect[] = {
+/* width height num_colors chars_per_pixel */
+"    20    18      256            2",
+/* colors */
+"`` c #033365",
+"`. c #002a55",
+"`# c #cdf1f1",
+"`a c #1f4a76",
+"`b c #bfd0de",
+"`c c #cdb5b5",
+"`d c #d8f4f4",
+"`e c #d4f0f2",
+"`f c #a5dde2",
+"`g c #a9b8d0",
+"`h c #acc9d0",
+"`i c #caedef",
+"`j c #a5cacd",
+"`k c #cbb2b3",
+"`l c #fbfefe",
+"`m c #032648",
+"`n c #b0cfd6",
+"`o c #b5afaf",
+"`p c #31587f",
+"`q c #25496e",
+"`r c #22476c",
+"`s c #274f78",
+"`t c #41668a",
+"`u c #89949f",
+"`v c #163a60",
+"`w c #a8c9d2",
+"`x c #a3c4cb",
+"`y c #003163",
+"`z c #2d4d6d",
+"`A c #003061",
+"`B c #123c68",
+"`C c #013264",
+"`D c #69747f",
+"`E c #002c5a",
+"`F c #41668b",
+"`G c #2d4f72",
+"`H c #adadad",
+"`I c #b3d2db",
+"`J c #ade9e9",
+"`K c #cc0000",
+"`L c None",
+"`M c #000000",
+"`N c #000000",
+"`O c #000000",
+"`P c #000000",
+"`Q c #000000",
+"`R c #000000",
+"`S c #000000",
+"`T c #000000",
+"`U c #000000",
+"`V c #000000",
+"`W c #000000",
+"`X c #000000",
+"`Y c #000000",
+"`Z c #000000",
+"`0 c #000000",
+"`1 c #000000",
+"`2 c #000000",
+"`3 c #000000",
+"`4 c #000000",
+"`5 c #000000",
+"`6 c #000000",
+"`7 c #000000",
+"`8 c #000000",
+".` c #000000",
+".. c #000000",
+".# c #000000",
+".a c #000000",
+".b c #000000",
+".c c #000000",
+".d c #000000",
+".e c #000000",
+".f c #000000",
+".g c #000000",
+".h c #000000",
+".i c #000000",
+".j c #000000",
+".k c #000000",
+".l c #000000",
+".m c #000000",
+".n c #000000",
+".o c #000000",
+".p c #000000",
+".q c #000000",
+".r c #000000",
+".s c #000000",
+".t c #000000",
+".u c #000000",
+".v c #000000",
+".w c #000000",
+".x c #000000",
+".y c #000000",
+".z c #000000",
+".A c #000000",
+".B c #000000",
+".C c #000000",
+".D c #000000",
+".E c #000000",
+".F c #000000",
+".G c #000000",
+".H c #000000",
+".I c #000000",
+".J c #000000",
+".K c #000000",
+".L c #000000",
+".M c #000000",
+".N c #000000",
+".O c #000000",
+".P c #000000",
+".Q c #000000",
+".R c #000000",
+".S c #000000",
+".T c #000000",
+".U c #000000",
+".V c #000000",
+".W c #000000",
+".X c #000000",
+".Y c #000000",
+".Z c #000000",
+".0 c #000000",
+".1 c #000000",
+".2 c #000000",
+".3 c #000000",
+".4 c #000000",
+".5 c #000000",
+".6 c #000000",
+".7 c #000000",
+".8 c #000000",
+"#` c #000000",
+"#. c #000000",
+"## c #000000",
+"#a c #000000",
+"#b c #000000",
+"#c c #000000",
+"#d c #000000",
+"#e c #000000",
+"#f c #000000",
+"#g c #000000",
+"#h c #000000",
+"#i c #000000",
+"#j c #000000",
+"#k c #000000",
+"#l c #000000",
+"#m c #000000",
+"#n c #000000",
+"#o c #000000",
+"#p c #000000",
+"#q c #000000",
+"#r c #000000",
+"#s c #000000",
+"#t c #000000",
+"#u c #000000",
+"#v c #000000",
+"#w c #000000",
+"#x c #000000",
+"#y c #000000",
+"#z c #000000",
+"#A c #000000",
+"#B c #000000",
+"#C c #000000",
+"#D c #000000",
+"#E c #000000",
+"#F c #000000",
+"#G c #000000",
+"#H c #000000",
+"#I c #000000",
+"#J c #000000",
+"#K c #000000",
+"#L c #000000",
+"#M c #000000",
+"#N c #000000",
+"#O c #000000",
+"#P c #000000",
+"#Q c #000000",
+"#R c #000000",
+"#S c #000000",
+"#T c #000000",
+"#U c #000000",
+"#V c #000000",
+"#W c #000000",
+"#X c #000000",
+"#Y c #000000",
+"#Z c #000000",
+"#0 c #000000",
+"#1 c #000000",
+"#2 c #000000",
+"#3 c #000000",
+"#4 c #000000",
+"#5 c #000000",
+"#6 c #000000",
+"#7 c #000000",
+"#8 c #000000",
+"a` c #000000",
+"a. c #000000",
+"a# c #000000",
+"aa c #000000",
+"ab c #000000",
+"ac c #000000",
+"ad c #000000",
+"ae c #000000",
+"af c #000000",
+"ag c #000000",
+"ah c #000000",
+"ai c #000000",
+"aj c #000000",
+"ak c #000000",
+"al c #000000",
+"am c #000000",
+"an c #000000",
+"ao c #000000",
+"ap c #000000",
+"aq c #000000",
+"ar c #000000",
+"as c #000000",
+"at c #000000",
+"au c #000000",
+"av c #000000",
+"aw c #000000",
+"ax c #000000",
+"ay c #000000",
+"az c #000000",
+"aA c #000000",
+"aB c #000000",
+"aC c #000000",
+"aD c #000000",
+"aE c #000000",
+"aF c #000000",
+"aG c #000000",
+"aH c #000000",
+"aI c #000000",
+"aJ c #000000",
+"aK c #000000",
+"aL c #000000",
+"aM c #000000",
+"aN c #000000",
+"aO c #000000",
+"aP c #000000",
+"aQ c #000000",
+"aR c #000000",
+"aS c #000000",
+"aT c #000000",
+"aU c #000000",
+"aV c #000000",
+"aW c #000000",
+"aX c #000000",
+"aY c #000000",
+"aZ c #000000",
+"a0 c #000000",
+"a1 c #000000",
+"a2 c #000000",
+"a3 c #000000",
+"a4 c #000000",
+"a5 c #000000",
+"a6 c #000000",
+"a7 c #000000",
+"a8 c #000000",
+/* pixels */
+"`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L",
+"`L`K`K`K`K`K`K`K`K`K`K`L`L`L`L`L`L`L`L`L",
+"`L`K`L`L`L`L`L`L`L`L`K`L`L`L`L`L`L`L`L`L",
+"`L`K`L`L`L`L`L`L`L`L`K`L`L`L`L`L`L`L`L`L",
+"`L`K`L`L`L`L`L`L`L`L`K`L`L`L`L`L`L`L`L`L",
+"`L`K`L`L`L`L`L`b```.`.```b`L`L`L`L`L`L`L",
+"`L`K`L`L`L`L`g`a`j`e`k`x`a`g`L`L`L`L`L`L",
+"`L`K`L`L`L`b`a`f`J`d`c`J`f`a`b`L`L`L`L`L",
+"`L`K`L`L`L```j`J`d`l`c`#`J`I```L`L`L`L`L",
+"`L`K`L`L`L`.`e`d`l`#`c`J`J`i`.`L`L`L`L`L",
+"`L`K`K`K`K`.`k`c`c`c`o`J`#`e`.`L`L`L`L`L",
+"`L`L`L`L`L```w`J`#`J`J`J`#`h```L`L`L`L`L",
+"`L`L`L`L`L`b`a`f`J`J`#`#`d`B`L`L`L`L`L`L",
+"`L`L`L`L`L`L`g`a`n`i`e`h`v`E`s`L`L`L`L`L",
+"`L`L`L`L`L`L`L`b```.`.```L`z`A`p`L`L`L`L",
+"`L`L`L`L`L`L`L`L`L`L`L`L`L`L`G`y`F`L`L`L",
+"`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`q`C`t`L`L",
+"`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`L`r`m`u`L"
+};

Added: packages/openev/branches/upstream/current/pymod/.cvsignore
===================================================================
--- packages/openev/branches/upstream/current/pymod/.cvsignore	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/.cvsignore	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,7 @@
+*.pyc
+Makefile
+*.dll
+*.exp
+*.lib
+*.pdb
+*.ilk

Added: packages/openev/branches/upstream/current/pymod/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/pymod/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,71 @@
+/.cvsignore/1.5/Thu Feb 15 16:37:38 2001//
+/Makefile.in/1.10/Tue Sep 28 19:44:17 2004//
+/_gtkmissing.def/1.1/Sun Apr 16 21:45:27 2000//
+/_gview.def/1.1/Sun Apr 16 21:45:27 2000//
+/filedlg.py/1.9/Mon Aug 16 16:47:24 2004//
+/generate.py/1.7/Tue Jul 26 18:26:24 2005//
+/gtkmissing.c/1.8/Thu Sep 30 21:12:07 2004//
+/gtkmissing.py/1.8/Wed Jan 22 19:54:36 2003//
+/gv.defs/1.87/Mon Sep 12 15:33:10 2005//
+/gv_ciet.c/1.12/Wed Jul 27 18:31:50 2005//
+/gvalg.py/1.7/Fri Nov 19 23:59:37 2004//
+/gvbitlayerlut.py/1.4/Mon Mar 19 21:57:14 2001//
+/gvclassification.py/1.30/Tue Jun 29 16:06:15 2004//
+/gvclassifydlg.py/1.31/Fri Aug 27 17:11:06 2004//
+/gvcommand.py/1.10/Fri Oct 15 13:25:26 2004//
+/gvconst.py/1.12/Wed Aug  6 17:09:14 2003//
+/gvcorecmds.py/1.6/Thu Jun  3 20:26:07 2004//
+/gvhtml.py/1.14/Thu Nov  4 14:32:33 2004//
+/gview.py/1.212/Mon Oct 17 19:19:15 2005//
+/gviewapp.py/1.29/Mon Oct 17 18:21:13 2005//
+/gvlabeledit.py/1.8/Tue Dec 10 15:49:11 2002//
+/gvlegenddlg.py/1.21/Tue Jan 20 19:45:51 2004//
+/gvmaptools.py/1.1/Wed Jan  7 20:41:48 2004//
+/gvmodule.c/1.120/Mon Oct 17 19:19:15 2005//
+/gvmodule_defs.c/1.57/Mon Sep 12 15:33:10 2005//
+/gvmodule_impl.c/1.67/Mon Sep 12 15:33:10 2005//
+/gvogrdlg.py/1.5/Tue Sep  9 15:18:46 2003//
+/gvogrfs.py/1.7/Thu May  8 16:19:39 2003//
+/gvogrfsgui.py/1.6/Mon Sep 17 15:27:03 2001//
+/gvplot.py/1.21/Thu Aug 18 19:56:15 2005//
+/gvpquerypropdlg.py/1.11/Wed Aug 23 14:32:10 2000//
+/gvprint.py/1.16/Sat Jul 24 14:35:04 2004//
+/gvrasterpropdlg.py/1.57/Mon Jun 27 19:37:24 2005//
+/gvsdsdlg.py/1.4/Tue Sep  9 15:18:46 2003//
+/gvselbrowser.py/1.4/Fri Nov  9 15:41:49 2001//
+/gvshell.py/1.5/Wed Jul 30 15:31:12 2003//
+/gvsignaler.py/1.6/Tue Jan  7 18:44:32 2003//
+/gvutils.py/1.29/Fri Oct 15 16:52:20 2004//
+/gvvectorpropdlg.py/1.23/Fri Feb 14 23:03:35 2003//
+/gvviewwindow.py/1.96/Thu Oct 13 21:21:51 2005//
+/ibrowse.py/1.3/Thu Feb  6 08:18:29 2003//
+/layerdlg.py/1.33/Tue Sep  9 15:18:46 2003//
+/makefile.vc/1.9/Wed Apr 21 10:09:38 2004//
+/mkgv.py/1.1/Wed Nov 24 21:06:55 1999//
+/nls.py/1.6/Wed Mar 16 18:57:04 2005//
+/oe_about.py/1.1/Sun Aug  7 18:22:57 2005//
+/oeattedit.py/1.9/Thu Feb 20 19:27:23 2003//
+/openev.py/1.46/Fri Jan 14 17:17:18 2005//
+/pathutils.py/1.1/Fri Jul  2 16:40:51 2004//
+/pgu.py/1.1/Mon Jul 10 19:58:51 2000/-ko/
+/pgucolor.py/1.12/Tue Sep  9 15:18:46 2003//
+/pgucolorsel.py/1.4/Wed May  2 03:30:38 2001/-ko/
+/pgucolourswatch.py/1.3/Tue Sep  9 15:18:46 2003//
+/pgucombo.py/1.7/Tue Nov  8 15:51:44 2005//
+/pguentry.py/1.1/Mon Jun 11 23:19:00 2001//
+/pgufilesel.py/1.7/Wed Sep  1 15:46:52 2004/-ko/
+/pgufont.py/1.4/Tue Jul 20 12:26:43 2004//
+/pgugrid.py/1.17/Mon Oct 17 14:32:28 2005//
+/pgumenu.py/1.3/Tue Aug 13 16:09:29 2002//
+/pguprogress.py/1.1/Mon Jul 10 19:58:51 2000/-ko/
+/pgushapesgrid.py/1.10/Sun Jul 27 04:58:44 2003//
+/pgutextarea.py/1.5/Fri Feb 14 17:59:14 2003//
+/pgutogglebutton.py/1.1/Tue Aug 13 16:07:17 2002//
+/pyshell.py/1.14/Tue Sep  9 15:18:46 2003//
+/scmexpr.py/1.2/Mon Mar 19 21:57:14 2001//
+/testmain.py/1.14/Fri Apr  7 00:49:56 2000//
+/toolexample.py/1.2/Fri Apr  5 19:06:52 2002//
+/toolfile_example.txt/1.2/Fri Apr  5 19:06:52 2002//
+/vecplot.py/1.7/Sun Jun 27 20:18:14 2004//
+/vrtutils.py/1.17/Thu Jul  7 21:36:06 2005//
+D

Added: packages/openev/branches/upstream/current/pymod/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/pymod/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/pymod

Added: packages/openev/branches/upstream/current/pymod/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/pymod/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/pymod/Makefile.in
===================================================================
--- packages/openev/branches/upstream/current/pymod/Makefile.in	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/Makefile.in	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,44 @@
+all: _gvmodule.so _gtkmissing.so
+
+CC	=	@CC@
+CXX	=	@CXX@
+
+OPTFLAGS = @OPTFLAGS@
+CFLAGS = $(OPTFLAGS) @C_WFLAGS@ -I.. @CFLAGS@ @PYTHON_INCLUDES@
+
+LIBS = @LDFLAGS@ @LIBS@ 
+
+PYLINK = @PYTHON_LINK@
+
+%.o : %.c
+	$(CC) $(CFLAGS) -c $<
+
+gvmodule.o: gvmodule.c gvmodule_impl.c gv_ciet.c
+
+_gvmodule.so: gvmodule.o ../libgv.a 
+	$(CXX) -shared -o $@ $^ $(LIBS)
+
+_gtkmissing.so: gtkmissing.o
+	$(CXX) -shared -o $@ $^ $(LIBS)
+
+gvmodule_impl.c: gv.defs mkgv.py
+	python mkgv.py
+
+clean:
+	rm -f *.o gvmodule_*.c *.so *.pyc
+
+doc:
+	@if test \! -d html ; then \
+	  mkdir html; \
+	else \
+	  rm -f html/*; \
+	fi 
+	pythondoc -f HTML4 -i -d html _gv gview gvbitlayerlut
+
+install-doc: doc
+	@if test -d ~/wwwgv/gv_python ; then \
+	  cp html/* ~/wwwgv/gv_python; \
+	  echo cp html/* ~/wwwgv/gv_python; \
+	else \
+	  echo "~/wwwgv/gv_python doesn't exist ... install fails."; \
+	fi

Added: packages/openev/branches/upstream/current/pymod/_gtkmissing.def
===================================================================
--- packages/openev/branches/upstream/current/pymod/_gtkmissing.def	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/_gtkmissing.def	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+LIBRARY		_gtkmissing
+VERSION		1.0
+EXPORTS
+	init_gtkmissing	@1

Added: packages/openev/branches/upstream/current/pymod/_gview.def
===================================================================
--- packages/openev/branches/upstream/current/pymod/_gview.def	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/_gview.def	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+LIBRARY		_gv
+VERSION		1.0
+EXPORTS
+	init_gv	@1

Added: packages/openev/branches/upstream/current/pymod/filedlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/filedlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/filedlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,685 @@
+###############################################################################
+# $Id: filedlg.py,v 1.9 2004/08/16 16:47:24 pgs Exp $
+#
+# Project:  CIET Map
+# Purpose:  Multi-purpose file selection dialog
+# Author:   Paul Spencer, spencer at dmsolutions.ca
+#
+# Developed by DM Solutions Group (www.dmsolutions.ca) for CIETcanada
+#
+###############################################################################
+# Copyright (c) 2000-2002, CIETcanada
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: filedlg.py,v $
+#  Revision 1.9  2004/08/16 16:47:24  pgs
+#  modified handling of filters to respect order in which they are specified when populating the dropdown list
+#
+#  Revision 1.8  2004/07/20 12:22:18  pgs
+#  added patch from Zak James for handling manual input of filenames correctly
+#
+#  Revision 1.7  2003/09/13 22:09:41  pgs
+#  changed add_with_viewport to add to prevent problem with many entries
+#
+#  Revision 1.6  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.5  2003/04/03 21:56:34  pgs
+#  added localization support
+#
+#  Revision 1.4  2003/04/03 15:06:27  pgs
+#  made filedlg safe if pygtkextra not installed
+#
+#  Revision 1.3  2003/02/14 17:58:35  pgs
+#  added ability to multi-select files
+#
+#  Revision 1.2  2002/11/06 16:38:10  warmerda
+#  minor fix to pref handling
+#
+#  Revision 1.1  2002/11/06 16:05:51  pgs
+#  ported from CIETmap
+#
+#  Revision 1.38  2002/08/27 19:01:37  pspencer
+#  made the filename widget type dependent on dialog type.
+#
+#  Revision 1.37  2002/08/15 18:01:49  pspencer
+#  changed prints for ciet_utils.debug_msg()
+#
+#  Revision 1.36  2002/08/01 18:14:12  pspencer
+#  updated copyright text
+#
+#  Revision 1.35  2002/08/01 15:24:03  warmerda
+#  added fallback if pref_manager not available
+#
+#  Revision 1.34  2002/08/01 13:44:09  pspencer
+#  put keypresses into the filename box
+#
+#  Revision 1.33  2002/07/30 02:53:38  pspencer
+#  modified to use the new CmTopmostWindow base class to prevent conflicts
+#  with message boxes that fight to be on top
+#
+#  Revision 1.32  2002/07/12 22:39:13  pspencer
+#  added filters to file dialogs
+#
+#  Revision 1.31  2001/10/02 00:02:24  pspencer
+#  modified for new preference manager architecture
+#
+#  Revision 1.30  2001/09/27 02:30:43  pspencer
+#  changed Save to OK for FILE_SAVE
+#
+#  Revision 1.29  2001/09/21 21:30:51  pspencer
+#  changed DIRECTORY_SELECT mode to display OK instead of Open
+#
+#  Revision 1.28  2001/08/29 20:27:49  pspencer
+#  removed dependence on cmnormpath and modified directory listing
+#  to not have '..' when at the root of a drive.
+#
+#  Revision 1.27  2001/08/29 18:46:32  pspencer
+#  use cmnormpath to avoid  bug in ntpath.py
+#
+#  Revision 1.26  2001/07/01 03:15:12  pspencer
+#  misc bug fixes
+#
+#  Revision 1.25  2001/06/06 02:51:56  pspencer
+#  use pguentry
+#
+#  Revision 1.24  2001/05/24 14:03:44  pspencer
+#  added case-insensitive filtering
+#
+#  Revision 1.23  2001/05/14 22:05:50  pspencer
+#  added expanduser to file name expansion for linux users
+#
+#  Revision 1.22  2001/05/08 23:15:15  pspencer
+#  minor change to update gui before loading files to allow for slow network
+#  connections.
+#
+#  Revision 1.21  2001/05/05 17:35:26  pspencer
+#  modifications to allow modal state to work properly under certain circumstances
+#
+#  Revision 1.20  2001/04/13 18:50:17  pspencer
+#  fixed bug in help topic
+#
+#  Revision 1.19  2001/04/12 18:14:24  pspencer
+#  implemented nls'd helpfiles and preferences.
+#
+#  Revision 1.18  2001/03/23 02:18:18  pspencer
+#  fixed conflicts from merging v_1_1_4
+#
+#  Revision 1.17  2001/03/05 03:20:24  pspencer
+#  fixed a bug in cwd not existing that was fixed earlier but got lost in a merge.
+#
+#  Revision 1.16  2001/03/01 14:04:16  pspencer
+#  fixed conflicts during merge of bug fixes from v_1_1_3
+#
+#  Revision 1.15  2001/02/26 18:52:04  pspencer
+#  fixed bug when gview working directory doesn't exist
+#
+#  Revision 1.14  2001/02/23 17:12:52  pspencer
+#  Merged bug fixes from v_1_1_2
+#
+#  Revision 1.13.2.2  2001/02/28 18:11:43  pspencer
+#  fixed a bug in the file dialog code (need to destroy it
+#
+#  Revision 1.13.2.1  2001/02/23 02:12:49  pspencer
+#  fixed cwd, made dialog 'stay' on top.
+#
+#  Revision 1.13  2001/02/10 18:13:32  pspencer
+#  added combo box for rfl and made filename editable.  when a path
+#  is entered, the dialog switches to that path.
+#
+#  Revision 1.12  2001/02/08 17:41:44  pspencer
+#  fixed (I hope) a bug in determining drive letters
+#
+#  Revision 1.11  2001/01/29 15:26:14  pspencer
+#  missing import fixed
+#
+#  Revision 1.10  2001/01/24 18:54:07  warmerda
+#  fixed FALSE and TRUE constants
+#
+#  Revision 1.9  2001/01/22 19:48:54  fredrock
+#  adjusted self.set_policy(as=FALSE, ag=FALSE, autos=TRUE)
+#
+#  Revision 1.8  2001/01/20 20:00:53  pspencer
+#  fixed directory sorting and added missing drive letters
+#
+#  Revision 1.7  2001/01/17 03:08:21  pspencer
+#  editorial change
+#
+#  Revision 1.6  2000/10/20 16:23:08  pspencer
+#  fixed bug in get_directory()
+#
+#  Revision 1.5  2000/10/19 22:46:26  pspencer
+#  Modified to use os module for os specific path separators.  Removed
+#  os.chdir() code so it no longer changes the working directory.
+#
+#  Revision 1.4  2000/10/19 05:15:38  warmerda
+#  fixed some DOS path specific issues
+#
+#  Revision 1.3  2000/10/17 19:00:38  pspencer
+#  made this a modal dialog
+#
+#  Revision 1.2  2000/10/09 14:02:59  pspencer
+#  added comments and regular expressions for filter matching.
+#
+#  Revision 1.1  2000/10/08 17:17:55  pspencer
+#  initial version
+#
+#
+
+import os
+import gtk
+from gtk import TRUE, FALSE
+from gvsignaler import Signaler
+import gview
+import string
+from pguentry import pguEntry
+try:
+    from pgucombo import pguCombo
+except:
+    pguCombo = gtk.GtkCombo
+
+#pguCombo = gtk.GtkCombo
+
+import gdal
+import nls
+import re
+import string
+
+"""
+This module contains a single class, FileDialog that presents a common interface
+to several directory/file operations.
+
+The class is initialized for file saving, file opening, or directory selection
+through the dialog_type parameter of __init__.
+
+FILE_OPEN -- allows the user to select an existing file
+
+FILE_SAVE -- allows the user to pick an existing file or type a new one
+
+DIRECTORY_SELECT -- allows the user to pick an existing directory
+
+Usage -- connect signals to FileDialog.ok_button and FileDialog.cancel_button
+      -- FileDialog.get_filename() returns the file name
+      -- FileDialog.get_directory() returns the directory
+      -- FileDialog.set_filter() sets a filter for file name that limits the
+         display of files in the file list.
+         
+Filter specifications
+
+A file filter is specified as a text string containing the filter name and 
+filter items.  Filter items are separated by commas.  The filter name and
+filter items are separated by a vertical bar |
+
+Multiple filters are separated by a vertical bar
+
+i.e. DBF (*.dbf)|*.dbf|REC (*.rec)|*.rec|All Files (*.*)|*.*
+"""
+
+#dialog_types
+FILE_OPEN = 0
+FILE_SAVE = 1
+DIRECTORY_SELECT = 2
+
+
+class FilterSpec:
+
+    """
+    implements a single filter
+    """
+    
+    def __init__(self, filterspec):
+        """
+        Initialize the filter based on the spec string
+        """
+        #parse the filterspec
+        parts = filterspec.split("|" )
+        if len(parts) != 2:
+            gdal.Debug( "OpenEV", "an error occurred parsing the FilterSpec %s" % filterspec )
+            pass
+        
+        
+        self.name = parts[0].strip()
+        filters = parts[1].split( "," )
+        
+        self.filters = []
+        for i in range(len(filters)):
+            re_filter = string.replace(string.strip(filters[i]), "\\", "\\\\")
+            re_filter = string.replace(re_filter, ".", "\.")
+            re_filter = string.replace(re_filter, "*", ".*?")
+            re_comp = re.compile(re_filter, re.I)
+            self.filters.append( re_comp )
+        
+    def match(self, filename):
+        """
+        compare the filename to the filter spec and return None if not matched,
+        otherwise the result of the match is returned
+        """
+        for i in range(len(self.filters)):
+            result = self.filters[i].match( filename )
+            if result is not None:
+                return result
+        return None
+
+
+class FileDialog(gtk.GtkWindow, Signaler):
+    """FileDialog provides a multipurpose file selection dialog."""
+
+    def __init__(self, title=None, cwd=None, dialog_type=FILE_OPEN, filter=None, app=None, multiselect=0):
+        gtk.GtkWindow.__init__(self)
+
+        if dialog_type >= FILE_OPEN and dialog_type <= DIRECTORY_SELECT:
+            self.dialog_type = dialog_type
+        else:
+            self.dialog_type = FILE_OPEN
+
+        self.filter = None #current filter
+        self.filters = {} #active filter objects
+        self.filter_keys = [] #ordered list of the names of the filters
+        
+        self.file_selection = []
+        
+        self.multiselect = multiselect
+                
+        self.set_border_width(5)
+        self.set_policy(as=gtk.FALSE, ag=gtk.FALSE, autos=gtk.TRUE)
+        self.drives = None
+
+        if title == None:
+            if dialog_type == FILE_OPEN:
+                title = nls.get('filedlg-title-open-file', 'Open File ...')
+            elif dialog_type == FILE_SAVE:
+                title = nls.get('filedlg-title-save-file', 'Save File ...')
+            elif dialog_type == DIRECTORY_SELECT:
+                title = nls.get('filedlg-title-select-directory', 'Select Directory ...')
+        self.set_title(title)
+
+        #setup the current working directory
+        if cwd is None or not os.path.exists(cwd):
+            cwd = gview.get_preference('working-directory')
+            if cwd is None:
+                cwd = os.getcwd()
+        self.cwd = cwd
+        
+        #widgets
+        vbox = gtk.GtkVBox(spacing=5)
+        if dialog_type == FILE_OPEN or dialog_type == DIRECTORY_SELECT:
+            lbl = gtk.GtkLabel(nls.get('filedlg-label-open-from', 'Open From:'))
+        elif dialog_type == FILE_SAVE:
+            lbl = gtk.GtkLabel(nls.get('filedlg-label-save-in', 'Save In:'))
+        self.opt_menu = gtk.GtkOptionMenu()
+        self.opt_menu.set_menu(gtk.GtkMenu())
+        hbox = gtk.GtkHBox()
+        hbox.pack_start(lbl, expand=gtk.FALSE)
+        hbox.pack_start(self.opt_menu)
+        vbox.pack_start(hbox, expand = gtk.FALSE)
+
+        self.list_directory = gtk.GtkCList()
+        scr_directories = gtk.GtkScrolledWindow()
+        scr_directories.add(self.list_directory)
+        self.list_directory.connect('button-press-event', self.directory_selected_cb)
+
+        if dialog_type == DIRECTORY_SELECT:
+            self.list_files = None
+            vbox.pack_start(scr_directories)
+        else:
+            self.list_files = gtk.GtkCList()
+            if self.multiselect:
+                self.list_files.set_selection_mode( gtk.SELECTION_EXTENDED )
+            scr_files = gtk.GtkScrolledWindow()
+            scr_files.add(self.list_files)
+            self.list_files.connect('button-press-event', self.file_clicked_cb)
+            self.list_files.connect('select-row', self.file_selected_cb )
+            self.list_files.connect('unselect-row', self.file_unselected_cb )
+            pane = gtk.GtkHPaned()
+            scr_directories.set_usize(100, -1)
+            scr_files.set_usize(100, -1)
+            pane.add1(scr_directories)
+            pane.add2(scr_files)
+            pane.set_position(200)
+            vbox.pack_start(pane)
+
+        widget = None
+        if dialog_type == FILE_SAVE:
+            self.txt_filename = gtk.GtkEntry()
+            widget = self.txt_filename            
+        
+        elif dialog_type == FILE_OPEN:
+            combo = gtk.GtkCombo()
+            combo.set_value_in_list(gtk.FALSE, gtk.FALSE)
+            combo.disable_activate()
+            if app is not None:
+                rfl = app.get_rfl()
+                rfl.insert(0, '')
+                combo.set_popdown_strings( rfl )
+            self.txt_filename = combo.entry
+            widget = combo
+            
+        if widget is not None:
+            table = gtk.GtkTable(rows=2, cols=2)
+            lbl = gtk.GtkLabel(nls.get('filedlg-label-file-name', 'File Name:'))
+            self.txt_filename.connect('focus-out-event', self.map_path_cb)
+            self.txt_filename.connect('key-press-event', self.map_path_cb)
+
+            table.attach(lbl, 0, 1, 0, 1)
+            table.attach(widget, 1, 2, 0, 1)
+            lbl = gtk.GtkLabel(nls.get('filedlg-label-filter-extension', 'Filter extension:'))
+            self.cmb_filter = pguCombo()
+            self.set_filter(filter)
+            self.cmb_filter.entry.connect('changed', self.filter_cb)
+            table.attach(lbl, 0, 1, 1, 2)
+            table.attach(self.cmb_filter, 1, 2, 1, 2)
+            vbox.pack_start(table, expand=gtk.FALSE)
+
+        if dialog_type == FILE_SAVE:
+            self.ok_button = gtk.GtkButton(nls.get('filedlg-button-ok', 'OK'))
+        elif dialog_type == FILE_OPEN:
+            self.ok_button = gtk.GtkButton(nls.get('filedlg-button-open', 'Open'))
+        elif dialog_type == DIRECTORY_SELECT:
+            self.ok_button = gtk.GtkButton(nls.get('filedlg-button-ok', 'OK'))
+
+        self.cancel_button = gtk.GtkButton(nls.get('filedlg-button-cancel', 'Cancel'))
+
+        self.ok_button.connect('clicked', self.remove_grab)
+        self.ok_button.connect('clicked', self.update_cwd)
+        self.cancel_button.connect('clicked', self.remove_grab)
+        btn_box = gtk.GtkHButtonBox()
+        btn_box.pack_start(self.ok_button)
+        btn_box.pack_start(self.cancel_button)
+        vbox.pack_start(btn_box, expand=gtk.FALSE)
+
+        self.add(vbox)
+        self.show_all()
+        
+        #make modal
+        gtk.grab_add(self)
+
+
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.ok_button.grab_default()
+
+        self.set_usize(400, 400)
+        self.menu_update = gtk.FALSE
+
+        while gtk.events_pending():
+            gtk.mainiteration(FALSE)
+
+        self.refresh_directory()
+        self.connect('delete-event', self.quit)
+        self.ok_button.connect('clicked', self.quit)
+        self.cancel_button.connect('clicked', self.quit)
+        self.publish('quit')
+        
+        self.add_events(gtk.GDK.KEY_PRESS_MASK)
+        self.connect('key-press-event', self.key_press_cb)
+
+        self.result = 'cancel'
+
+    def key_press_cb( self, widget, event ):
+        """
+        process key presses
+        """
+        #focus on cmdline and disable further processing of this signal
+        self.txt_filename.grab_focus()    
+        return FALSE
+        
+    def update_cwd(self, *args):
+        #gview.set_preference('working-directory', self.cwd)
+        pass
+
+    def remove_grab(self, widget, *args):
+        if widget == self.ok_button:
+            self.result = 'ok'
+        gtk.grab_remove(self)
+
+    def refresh_directory(self, *args):
+        """refresh the directory menu and cause a rebuild of the
+        file/directory lists"""
+        self.menu_update = gtk.TRUE
+        self.opt_menu.remove_menu()
+        paths = []
+        drive, head = os.path.splitdrive(self.cwd)
+        while head <> os.sep and head <> "":
+            paths.append(drive + head)
+            head, tail = os.path.split(head)
+
+        paths.append(drive + os.sep)
+
+        menu = gtk.GtkMenu()
+        for path in paths:
+            item = gtk.GtkMenuItem(path)
+            item.show()
+            item.connect('activate', self.directory_cb, path)
+            menu.append(item)
+
+        self.opt_menu.set_menu(menu)
+        self.opt_menu.set_history(len(paths))
+        self.refresh_files()
+        self.menu_update = gtk.FALSE
+
+    def map_path_cb(self, entry, event):
+        """user has entered a value into txt_filename.  If it maps to
+           a path, change to it"""
+        if event.type == gtk.GDK.KEY_PRESS:
+            if event.keyval == gtk.GDK.Return:
+                path = os.path.normpath(os.path.join(self.cwd, entry.get_text()))
+                path = os.path.expanduser(path)
+                if os.path.isdir(path):
+                    self.cwd = path
+                    self.refresh_directory()
+                    entry.set_text('')
+            else:
+                """for text entry deselect in list"""
+                if len(self.file_selection) > 0:
+                    self.file_selection = []
+                    self.refresh_directory()
+            
+        elif event.type == gtk.GDK.FOCUS_CHANGE:
+            if os.path.isdir(entry.get_text()):
+                self.cwd = entry.get_text()
+                self.refresh_directory()
+                entry.set_text('')
+            elif os.path.isfile(entry.get_text()):
+                self.update_filename_box()
+
+
+
+    def directory_cb(self, widget, path, *args):
+        """called when the directory menu changes"""
+        if not self.menu_update:
+            self.cwd = os.path.normpath(path)
+            self.refresh_directory()
+
+    def refresh_files(self, *args):
+        """rebuild the file and directory lists.  Use regular expressions to
+        filter."""
+        files = os.listdir(self.cwd)
+        files.sort(self.cmp_dir)
+        if self.list_files <> None:
+            self.list_files.freeze()
+            self.list_files.clear()
+        self.list_directory.freeze()
+        self.list_directory.clear()
+        
+        drive, path = os.path.splitdrive( self.cwd )
+        if path != '\\':
+            self.list_directory.append([os.pardir])
+
+        for file in files:
+            file_path = os.path.join(self.cwd, file)
+            if os.path.isfile(file_path) and self.list_files <> None:
+                if self.filter <> None:
+                    match = self.filter.match(file_path)
+                    if match <> None:
+                    #f, ext = os.path.splitext(file)
+                    #if ext == self.filter:
+                        self.list_files.append([file])
+                else:
+                    self.list_files.append([file])
+            elif os.path.isdir(file_path):
+                self.list_directory.append([file])
+        # add drive letters
+        drives = self.get_drives()
+        for drive in drives:
+            self.list_directory.append([drive])
+
+        if self.list_files <> None:
+            self.list_files.thaw()
+        self.list_directory.thaw()
+
+    def file_clicked_cb(self, widget, event, *args):
+        """called when the user selects a file in the file list"""
+        if event.type == gtk.GDK._2BUTTON_PRESS:
+            self.ok_button.clicked()
+                        
+    def file_selected_cb(self, widget, row, col, event, *args):
+        """handle the user selecting a row"""
+        try:
+            idx = self.file_selection.index( row )
+        except:
+            self.file_selection.append( row )     
+            self.update_filename_box()
+
+    def file_unselected_cb( self, widget, row, col, event, *args):
+        """handle the user unselecting a row"""
+        try:
+            self.file_selection.remove( row )
+            self.update_filename_box()
+        except:
+            pass            
+            
+    def update_filename_box( self ):
+        """update the contents of the filename box"""
+        filenames = ""
+        spc = ''
+        if len(self.file_selection) > 1:
+            quote='"'
+        else:
+            quote = ''
+        for row in self.file_selection:
+            filenames = filenames + spc + quote + "%s" % self.list_files.get_text( row, 0 ) + quote    
+            spc = " "
+        self.txt_filename.set_text(filenames)
+
+    def directory_selected_cb(self, widget, event, *args):
+        """called when the user selects a directory in the directory list"""
+        if event.type <> gtk.GDK._2BUTTON_PRESS:
+            return
+        try:
+            row, col = widget.get_selection_info(int(event.x), int(event.y))
+        except:
+            return
+
+        new_dir = widget.get_text(row, col)
+        self.cwd = os.path.normpath(os.path.join(self.cwd, new_dir))
+        self.refresh_directory()
+
+    def filter_cb(self, widget, *args):
+        """called when the filter changes"""
+        import string
+        filter = string.strip(widget.get_text())
+        if filter == '':
+            self.filter = FilterSpec( nls.get("filedlg-default-filter", "All Files (*.*)|*.*"))
+        else:
+            self.filter = self.filters[filter]
+        self.refresh_directory()
+
+    def set_filter(self, filter):
+        """programmatically set the filename filter"""
+        
+        self.filters = {}
+        self.filter_keys = []
+        self.filter = None
+        #build the filters
+        if filter != None and len(filter) > 0:
+            filterSpecs = filter.split( "|" )
+            if len(filterSpecs) % 2 != 0:
+                gdal.Debug( "OpenEV", "invalid filter spec: %s" % filter )
+                pass
+            else:
+                for i in range(0, len(filterSpecs), 2):
+                    spec = FilterSpec(filterSpecs[i] + "|" + filterSpecs[i+1])
+                    self.filters[spec.name] = spec
+                    self.filter_keys.append( spec.name )
+        else:
+            self.filters[nls.get("filedlg-default-filter", "All Files (*.*)|*.*")] = FilterSpec( nls.get("filedlg-default-filter", "All Files (*.*)|*.*") )
+
+        self.cmb_filter.set_popdown_strings( self.filter_keys )
+        self.filter = self.filters[self.cmb_filter.entry.get_text()]
+    
+    def set_filename(self, filename):
+        """set the filename"""
+        self.txt_filename.set_text(filename)
+
+    def get_filename(self, *args):
+        """return the first filename"""
+        if self.dialog_type == FILE_OPEN:
+            if len(self.file_selection) > 0:
+                filename = self.list_files.get_text( self.file_selection[0], 0 )
+            elif len(self.txt_filename.get_text()) > 0:
+                filename = self.txt_filename.get_text()
+            else:
+                filename = ""
+        elif self.dialog_type == FILE_SAVE:
+            filename = self.txt_filename.get_text()
+        return os.path.join(self.get_directory(), filename)
+        
+    def get_filenames(self, *args):
+        """return all the filenames as a list"""
+        filenames = []
+        for row in self.file_selection:
+            filename = self.list_files.get_text( row, 0 )
+            filenames.append( os.path.join(self.get_directory(), filename) )
+            
+        return filenames
+
+    def get_directory(self, *args):
+        """return the directory"""
+        return self.cwd
+
+    def get_drives(self, *args):
+        if self.drives is None:
+            self.drives = []
+            for d in range(ord('c'), ord('z') + 1):
+                drive = chr(d) + ':' + os.sep
+                if os.path.exists(drive):
+                    self.drives.append(drive)
+        return self.drives
+
+    def cmp_dir(self, a, b):
+        if string.lower(a) < string.lower(b):
+            return -1
+        elif string.lower(a) == string.lower(b):
+            return 0
+        else:
+            return 1
+
+    def quit(self, *args):
+        """close the dialog"""
+        self.remove_grab(None)
+        self.hide()
+        self.notify('quit')
+        return gtk.FALSE
+
+if __name__ == '__main__':
+    dlg = FileDialog(title='Testing', dialog_type=FILE_OPEN)
+    dlg.show()
+    dlg.subscribe('quit', gtk.mainquit)
+    gtk.mainloop()

Added: packages/openev/branches/upstream/current/pymod/generate.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/generate.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/generate.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,510 @@
+import os
+import string
+import scmexpr
+try:
+        from cStringIO import StringIO
+except ImportError:
+        from StringIO import StringIO
+# for fnmatchcase -- filename globbing
+import fnmatch
+
+TRUE  = 1
+FALSE = 0
+
+funcDefTmpl = '    { "%s", _wrap_%s, 1 },\n'
+funcLeadTmpl = 'static PyObject *_wrap_%s(PyObject *self, PyObject *args) {\n'
+enumCodeTmpl = '''    if (PyGtkEnum_get_value(%s, %s, (gint *)&%s))
+        return NULL;\n'''
+flagCodeTmpl = '''    if (PyGtkFlag_get_value(%s, %s, (gint *)&%s))
+        return NULL;\n'''
+setVarTmpl = '''  if (py_%s)
+        %s = %s;\n'''
+getTypeTmpl = '''static PyObject *_wrap_%s(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":%s"))
+        return NULL;
+    return PyInt_FromLong(%s());
+}\n\n'''
+
+nullokTmpl = '''    if (PyGtk_Check(py_%s))
+        %s = %s(PyGtk_Get(py_%s));
+    else if (py_%s != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "%s argument must be a %s or None");
+        return NULL;
+    }\n'''
+
+nullokBoxedTmpl = '''    if (Py%s_Check(py_%s))
+        %s = %s(py_%s);
+    else if (py_%s != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "%s argument must be a %s or None");
+        return NULL;
+    }\n'''
+
+convSpecialCases = {
+        'GtkCList': '_GTK_CLIST',
+        'GtkCTree': '_GTK_CTREE',
+        'GtkCTreePos': '_GTK_CTREE_POS',
+        'GtkCTreeLineStyle': '_GTK_CTREE_LINE_STYLE',
+        'GtkCTreeExpanderStyle': '_GTK_CTREE_EXPANDER_STYLE',
+        'GtkCTreeExpansionStyle': '_GTK_CTREE_EXPANSION_STYLE',
+        'GnomeRootWin': '_GNOME_ROOTWIN',
+        'GnomeAppBar': '_GNOME_APPBAR',
+        'GnomeDEntryEdit': '_GNOME_DENTRY_EDIT',
+}
+
+def toUpperStr(str):
+        """Converts a typename to the equivalent upercase and underscores
+        name.  This is used to form the type conversion macros and enum/flag
+        name variables"""
+        if convSpecialCases.has_key(str):
+                return convSpecialCases[str]
+        ret = []
+        while str:
+                if len(str) > 1 and str[0] in string.uppercase and \
+                   str[1] in string.lowercase:
+                        ret.append("_" + str[:2])
+                        str = str[2:]
+                elif len(str) > 3 and \
+                     str[0] in string.lowercase and \
+                     str[1] in 'HV' and \
+                     str[2] in string.uppercase and \
+                     str[3] in string.lowercase:
+                        ret.append(str[0] + "_" + str[1:3])
+                        str = str[3:]
+                elif len(str) > 2 and \
+                     str[0] in string.lowercase and \
+                     str[1] in string.uppercase and \
+                     str[2] in string.uppercase:
+                        ret.append(str[0] + "_" + str[1])
+                        str = str[2:]
+                else:
+                        ret.append(str[0])
+                        str = str[1:]
+        return string.upper(string.join(ret, ''))
+
+def enumName(typename):
+        """create a GTK_TYPE_* name from the given type"""
+        part = toUpperStr(typename)
+        if len(part) > 4 and part[:4] == '_GTK':
+                return 'GTK_TYPE' + part[4:]
+        else:
+                return 'GTK_TYPE' + part
+
+def conversionMacro(typename):
+        """get the conversion macro for the given type"""
+        return toUpperStr(typename)[1:]
+
+# a dictionary of known enum types ...
+# the value is a cached copy of the GTK_TYPE name
+enums = {}
+# and flags ...
+flags = {}
+
+# a dictionary of objects ...
+# the values are the conversion macros
+objects = {}
+
+# the known boxed types
+boxed = {
+        'GtkAccelGroup': ('PyGtkAccelGroup_Type', 'PyGtkAccelGroup_Get',
+                          'PyGtkAccelGroup_New'),
+        'GtkStyle': ('PyGtkStyle_Type', 'PyGtkStyle_Get', 'PyGtkStyle_New'),
+        'GdkFont': ('PyGdkFont_Type', 'PyGdkFont_Get', 'PyGdkFont_New'),
+        'GdkColor': ('PyGdkColor_Type', 'PyGdkColor_Get', 'PyGdkColor_New'),
+        'GdkEvent': ('PyGdkEvent_Type', 'PyGdkEvent_Get', 'PyGdkEvent_New'),
+        'GdkWindow': ('PyGdkWindow_Type', 'PyGdkWindow_Get','PyGdkWindow_New'),
+        'GdkPixmap': ('PyGdkWindow_Type', 'PyGdkWindow_Get','PyGdkWindow_New'),
+        'GdkBitmap': ('PyGdkWindow_Type', 'PyGdkWindow_Get','PyGdkWindow_New'),
+        'GdkDrawable':('PyGdkWindow_Type','PyGdkWindow_Get','PyGdkWindow_New'),
+        'GdkGC': ('PyGdkGC_Type', 'PyGdkGC_Get', 'PyGdkGC_New'),
+        'GdkColormap': ('PyGdkColormap_Type', 'PyGdkColormap_Get',
+                        'PyGdkColormap_New'),
+        'GdkDragContext': ('PyGdkDragContext_Type', 'PyGdkDragContext_Get',
+                           'PyGdkDragContext_New'),
+        'GtkSelectionData': ('PyGtkSelectionData_Type',
+                             'PyGtkSelectionData_Get',
+                             'PyGtkSelectionData_New'),
+        'GdkAtom': ('PyGdkAtom_Type', 'PyGdkAtom_Get', 'PyGdkAtom_New'),
+        'GdkCursor': ('PyGdkCursor_Type', 'PyGdkCursor_Get',
+                      'PyGdkCursor_New'),
+        'GtkCTreeNode': ('PyGtkCTreeNode_Type', 'PyGtkCTreeNode_Get',
+                         'PyGtkCTreeNode_New'),
+}
+
+class VarDefs:
+        def __init__(self):
+                self.vars = {}
+        def add(self, type, name):
+                if self.vars.has_key(type):
+                        self.vars[type] = self.vars[type] + (name,)
+                else:
+                        self.vars[type] = (name,)
+        def __str__(self):
+                ret = []
+                for type in self.vars.keys():
+                        ret.append('    ')
+                        ret.append(type)
+                        ret.append(' ')
+                        ret.append(string.join(self.vars[type], ', '))
+                        ret.append(';\n')
+                if ret: ret.append('\n')
+                return string.join(ret, '')
+
+class TypesParser(scmexpr.Parser):
+        """A parser that only parses definitions -- no output"""
+        def define_enum(self, name, *values):
+                if not enums.has_key(name):
+                        enums[name] = enumName(name)
+        def define_flags(self, name, *values):
+                if not flags.has_key(name):
+                        flags[name] = enumName(name)
+        def define_object(self, name, *args):
+                if not objects.has_key(name):
+                        objects[name] = conversionMacro(name)
+        def define_boxed(self, name, reffunc=None, unreffunc=None, size=None):
+                if not boxed.has_key(name):
+                        print "Warning --", name, "not supported"
+        def include(self, filename):
+                if filename[0] != '/':
+                        # filename relative to file being parsed
+                        afile = os.path.join(os.path.dirname(self.filename),
+                                             filename)
+                else:
+                        afile = filename
+                if not os.path.exists(afile):
+                        # fallback on PWD relative filename
+                        afile = filename
+                #print "including file", afile
+                fp = open(afile)
+                self.startParsing(scmexpr.parse(fp))
+
+class FunctionDefsParser(TypesParser):
+        def __init__(self, input, prefix='gtkmodule', typeprefix=''):
+                # typeprefix is set to & if type structs are not pointers
+                TypesParser.__init__(self, input)
+                self.impl = open(prefix + '_impl.c', "w")
+                self.defs = open(prefix + '_defs.c', "w")
+                self.tp = typeprefix
+
+        def unknown(self, tup):
+                print "Unknown function:", (tup and tup[0] or "")
+
+        def declare_object(self, name, *args):
+                # Just parse - no output
+                TypesParser.define_object(self, name)
+
+        def define_object(self, name, parent=(), fields=()):
+                TypesParser.define_object(self, name)
+                get_type = string.lower(objects[name]) + '_get_type'
+                self.defs.write(funcDefTmpl % (get_type,get_type))
+                self.impl.write(getTypeTmpl % (get_type,get_type,get_type))
+
+                if fields and fields[0] == 'fields':
+                        for retType, attrname in fields[1:]:
+                                self.get_attr_func(name, retType, attrname)
+
+        def get_attr_func(self, name, retType, attrname):
+                impl = StringIO()
+                funcName = string.lower(objects[name]) + '_get_' + attrname
+                impl.write(funcLeadTmpl % (funcName,))
+                impl.write('    PyObject *obj;\n\n')
+                impl.write('    if (!PyArg_ParseTuple(args, "O!:')
+                impl.write(funcName)
+                impl.write('", ' + self.tp + 'PyGtk_Type, &obj))\n')
+                impl.write('        return NULL;\n')
+                funcCall = '%s(PyGtk_Get(obj))->%s' % (objects[name], attrname)
+                if self.decodeRet(impl, funcCall, retType):
+                        return
+                impl.write('}\n\n')
+                # actually write the info to the output files
+                self.defs.write(funcDefTmpl % (funcName, funcName))
+                self.impl.write(impl.getvalue())
+                
+        def define_func(self, name, retType, args):
+                # we write to a temp file, in case any errors occur ...
+                impl = StringIO()
+                impl.write(funcLeadTmpl % (name,))
+                varDefs = VarDefs()
+                parseStr = ''   # PyArg_ParseTuple string
+                parseList = []  # args to PyArg_ParseTuple
+                argList = []    # args to actual function
+                extraCode = []  # any extra code (for enums, flags)
+                nullret = FALSE # return value could be null
+                if type(retType) == type(()) and len(retType) == 2 \
+                   and retType[0] in objects.keys() \
+                   and retType[1] == 'null-ok':
+                        nullret = TRUE
+                        retType = retType[0]
+                        varDefs.add('GtkObject', '*retval')
+                if retType == 'string':
+                        # this is needed so we can free result string
+                        varDefs.add('char', '*ret')
+                        varDefs.add('PyObject', '*py_ret')
+                if retType == 'const_string':
+                        varDefs.add('const char', '*ret')
+                for arg in args:
+                        argType = arg[0]
+                        argName = arg[1]
+                        # munge special names ...
+                        if argName in ('default', 'if', 'then', 'else',
+                                       'while', 'for', 'do', 'case', 'select'):
+                                argName = '_' + argName
+                        default = ''
+                        nullok = FALSE
+                        if len(arg) > 2:
+                                for a in arg[2:]:
+                                        if type(a) == type(()) and a[0] == '=':
+                                                default = ' = ' + a[1]
+                                                if '|' not in parseStr:
+                                                        parseStr = parseStr+'|'
+                                        elif type(a) == type(()) and \
+                                             a[0] == 'null-ok':
+                                                nullok = TRUE
+                        if argType == 'string':
+                                varDefs.add('char', '*' + argName + default)
+                                if nullok:
+                                        parseStr = parseStr + 'z'
+                                else:
+                                        parseStr = parseStr + 's'
+                                parseList.append('&' + argName)
+                                argList.append(argName)
+                        elif argType == 'const_string':
+                                varDefs.add('const char', '*' + argName + default)
+                                if nullok:
+                                        parseStr = parseStr + 'z'
+                                else:
+                                        parseStr = parseStr + 's'
+                                parseList.append('&' + argName)
+                                argList.append(argName)
+                        elif argType in ('char', 'uchar'):
+                                varDefs.add('char', argName + default)
+                                parseStr = parseStr + 'c'
+                                parseList.append('&' + argName)
+                                argList.append(argName)
+                        elif argType in ('bool', 'int', 'uint', 'long',
+                                         'ulong', 'guint', 'GdkAtom'):
+                                # pretend atoms are integers for input ...
+                                varDefs.add('int', argName + default)
+                                parseStr = parseStr + 'i'
+                                parseList.append('&' + argName)
+                                argList.append(argName)
+                        elif argType in ('float', 'double'):
+                                varDefs.add('double', argName + default)
+                                parseStr = parseStr + 'd'
+                                parseList.append('&' + argName)
+                                argList.append(argName)
+                        elif argType == 'progress_cb':
+                                varDefs.add('PyProgressData', 'sProgressInfo = {NULL,NULL,-1}')
+                                parseStr = parseStr + 'O'
+                                parseList.append('&(sProgressInfo.psPyCallback)')
+                                argList.append('PyProgressProxy')
+                        elif argType == 'progress_data':
+                                parseStr = parseStr + 'O'
+                                parseList.append('&(sProgressInfo.psPyCallbackData)')
+                                argList.append('&sProgressInfo')
+                        elif argType == 'FILE':
+                                varDefs.add('PyObject', '*' + argName+default)
+                                parseStr = parseStr + 'O!'
+                                parseList.append('&PyFile_Type')
+                                parseList.append('&' + argName)
+                                argList.append('PyFile_AsFile(' +
+                                               argName + ')')
+                        elif argType in enums.keys():
+                                varDefs.add(argType, argName + default)
+                                if default:
+                                        varDefs.add('PyObject', '*py_' + \
+                                                    argName + ' = NULL')
+                                else:
+                                        varDefs.add('PyObject', '*py_'+argName)
+                                parseStr = parseStr + 'O'
+                                parseList.append('&py_' + argName)
+                                argList.append(argName)
+                                extraCode.append(enumCodeTmpl %
+                                                 (enums[argType],
+                                                  'py_' + argName,
+                                                  argName))
+                        elif argType in flags.keys():
+                                varDefs.add(argType, argName + default)
+                                if default:
+                                        varDefs.add('PyObject', '*py_' + \
+                                                    argName + ' = NULL')
+                                else:
+                                        varDefs.add('PyObject', '*py_'+argName)
+                                parseStr = parseStr + 'O'
+                                parseList.append('&py_' + argName)
+                                argList.append(argName)
+                                extraCode.append(flagCodeTmpl %
+                                                 (flags[argType],
+                                                  'py_' + argName,
+                                                  argName))
+                        elif argType in objects.keys():
+                                if nullok:
+                                        parseStr = parseStr + 'O'
+                                        parseList.append('&py_' + argName)
+                                        varDefs.add('PyObject', '*py_' +
+                                                    argName + ' = Py_None')
+                                        varDefs.add(argType, '*' + argName +
+                                                    ' = NULL')
+                                        extraCode.append(nullokTmpl %
+                                                         (argName, argName,
+                                                          objects[argType],
+                                                          argName, argName,
+                                                          argName, argType))
+                                        argList.append(argName)
+                                elif default:
+                                        parseStr = parseStr + 'O!'
+                                        parseList.append(self.tp +'PyGtk_Type')
+                                        varDefs.add('PyObject', '*py_' +
+                                                    argName + ' = NULL')
+                                        varDefs.add(argType, '*' + argName +
+                                                    default)
+                                        parseList.append('&py_' + argName)
+                                        extraCode.append(setVarTmpl %
+                                                         (argName, argName,
+                                                          objects[argType] +
+                                                          '(PyGtk_Get(py_' +
+                                                          argName + '))'))
+                                        argList.append(argName)
+                                else:
+                                        parseStr = parseStr + 'O!'
+                                        parseList.append(self.tp +'PyGtk_Type')
+                                        varDefs.add('PyObject', '*' + argName)
+                                        parseList.append('&' + argName)
+                                        argList.append(objects[argType] +
+                                                       '(PyGtk_Get(' +
+                                                       argName + '))')
+                        elif argType in boxed.keys():
+                                tp, get, new = boxed[argType]
+                                if nullok:
+                                        varDefs.add('PyObject', '*py_' +
+                                                    argName + ' = Py_None')
+                                        varDefs.add(argType, '*' + argName +
+                                                    ' = NULL')
+                                        parseStr = parseStr + 'O'
+                                        parseList.append('&py_' + argName)
+                                        extraCode.append(nullokBoxedTmpl %
+                                                         (argType, argName,
+                                                          argName, get,
+                                                          argName, argName,
+                                                          argName, argType))
+                                        argList.append(argName)
+                                else:
+                                        varDefs.add('PyObject', '*' + argName)
+                                        parseStr = parseStr + 'O!'
+                                        parseList.append(self.tp + tp)
+                                        parseList.append('&' + argName)
+                                        argList.append(get + '('+argName+')')
+                        else:
+                                print "%s: unknown arg type '%s'" % (name,
+                                                                     argType)
+                                return
+                                
+                impl.write(str(varDefs))
+                impl.write('    if (!PyArg_ParseTuple(args, "')
+                impl.write(parseStr)
+                impl.write(':')
+                impl.write(name)
+                impl.write('"')
+                if parseList:
+                        impl.write(', ')
+                        impl.write(string.join(parseList, ', '))
+                impl.write('))\n        return NULL;\n')
+                impl.write(string.join(extraCode, ''))
+
+                funcCall = name + '(' + string.join(argList, ', ') + ')'
+                if self.decodeRet(impl, funcCall, retType, nullret):
+                        return
+                impl.write('}\n\n')
+                # actually write the info to the output files
+                self.defs.write(funcDefTmpl % (name,name))
+                self.impl.write(impl.getvalue())
+
+        def decodeRet(self, impl, funcCall, retType, nullret):
+                if retType == 'none':
+                        impl.write('    ')
+                        impl.write(funcCall)
+                        impl.write(';\n    Py_INCREF(Py_None);\n    return Py_None;\n')
+                elif retType == 'static_string':
+                        impl.write('    return PyString_FromString(')
+                        impl.write(funcCall)
+                        impl.write(');\n')
+                elif retType == 'string':
+                        impl.write('    ret = ')
+                        impl.write(funcCall)
+                        impl.write(';\n    py_ret = PyString_FromString(ret);\n  g_free(ret);\n  return py_ret;\n')
+                elif retType == 'const_string':
+                        impl.write('    ret = ')
+                        impl.write(funcCall)
+                        impl.write(';\n')
+                        impl.write('    if( ret != NULL)\n')
+                        impl.write('        return PyString_FromString(ret);\n')
+                        impl.write('    else\n')
+                        impl.write('    {\n')
+                        impl.write('        Py_INCREF(Py_None);\n')
+                        impl.write('        return Py_None;\n')
+                        impl.write('    }\n')
+                elif retType in ('char', 'uchar'):
+                        impl.write('    return PyString_fromStringAndSize(*(')
+                        impl.write(funcCall)
+                        impl.write('));\n')
+                elif retType in ('bool','int','uint','long','ulong','guint') \
+                     or retType in enums.keys() or retType in flags.keys():
+                        impl.write('    return PyInt_FromLong(')
+                        impl.write(funcCall)
+                        impl.write(');\n')
+                elif retType in ('float','double'):
+                        impl.write('    return PyFloat_FromDouble(')
+                        impl.write(funcCall)
+                        impl.write(');\n')
+                elif retType in boxed.keys():
+                        impl.write('    return ')
+                        impl.write(boxed[retType][2])
+                        impl.write('(')
+                        impl.write(funcCall)
+                        impl.write(');\n')
+                elif retType in objects.keys():
+                    if nullret:
+                        impl.write('    retval = (GtkObject *)')
+                        impl.write(funcCall)
+                        impl.write(';\n    if (retval) return PyGtk_New(retval);\n    Py_INCREF(Py_None);\n    return Py_None;\n')
+                    else:
+                        impl.write('    return PyGtk_New((GtkObject *)')
+                        impl.write(funcCall)
+                        impl.write(');\n')
+                else:
+                        print "unknown return type '%s'" % retType
+                        return 1
+                return 0
+
+class FilteringParser(FunctionDefsParser):
+        """A refinement of FunctionDefsParser with some common filter types
+        built in"""
+        def __init__(self, input, prefix='gtkmodule', typeprefix=''):
+                FunctionDefsParser.__init__(self, input, prefix, typeprefix)
+                # hash lookups are pretty fast ...
+                self.excludeList = {}
+                self.excludeGlob = []
+        def filter(self, name):
+                if self.excludeList.has_key(name):
+                        return 0
+                for glob in self.excludeGlob:
+                        if fnmatch.fnmatchcase(name, glob):
+                                return 0
+                return 1
+        def define_func(self, name, retType, args):
+                if self.filter(name):
+                        FunctionDefsParser.define_func(self,name,retType,args)
+                else:
+                        print "%s: filtered out" % (name)
+        def addExcludeFile(self, fname):
+                """Adds the function names from file fname to excludeList"""
+                lines = open(fname).readlines()
+                # remove \n from each line ...
+                lines = map(lambda x: x[:-1], lines)
+                # remove comments and blank lines ...
+                lines = filter(lambda x: x and x[0] != '#', lines)
+                for name in lines:
+                        self.excludeList[name] = None
+        def addExcludeGlob(self, glob):
+                if glob not in self.excludeGlob:
+                        self.excludeGlob.append(glob)
+                

Added: packages/openev/branches/upstream/current/pymod/gtkmissing.c
===================================================================
--- packages/openev/branches/upstream/current/pymod/gtkmissing.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gtkmissing.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,268 @@
+/******************************************************************************
+ * $Id: gtkmissing.c,v 1.8 2004/09/30 21:12:07 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Hand generated python bindings for some Gtk functionality
+ *           not addressed by pygtk.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gtkmissing.c,v $
+ * Revision 1.8  2004/09/30 21:12:07  warmerda
+ * added explicit ref() and unref() though we shouldn't generally need them
+ *
+ * Revision 1.7  2002/07/09 03:34:10  warmerda
+ * added window get/set position calls
+ *
+ * Revision 1.6  2001/07/03 23:25:51  warmerda
+ * added gtk_object_sink
+ *
+ * Revision 1.5  2000/06/23 13:00:41  warmerda
+ * added a few ref count related debugging functions
+ *
+ * Revision 1.4  2000/06/20 13:39:06  warmerda
+ * added standard headers
+ *
+ */
+
+#if defined(WIN32) || defined(_WIN32)
+#  include <pygtk.h>
+#else
+#  include <pygtk/pygtk.h>
+#endif
+
+/*
+ * Functions missing in the python gtk bindings (as of pygtk-0.6.3)
+ */
+
+static PyObject *
+_wrap_gtk_toolbar_append_element(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    GtkWidget *widget = NULL, *icon = NULL;
+    char *text, *tooltip, *tip_private;
+    GtkWidget *ret;
+    PyObject *callback, *py_widget, *py_icon, *extra = NULL, *data;
+    int type;
+    
+    if (!PyArg_ParseTuple(args, "O!iOzzzOO|O!:gtk_toolbar_append_item",
+			  &PyGtk_Type, &t, &type, &py_widget,
+			  &text, &tooltip, &tip_private,
+			  &py_icon, &callback, &PyTuple_Type, &extra))
+	return NULL;
+    if (!PyCallable_Check(callback) && callback != Py_None) {
+	PyErr_SetString(PyExc_TypeError,"eighth argument not callable");
+	return NULL;
+    }
+    Py_INCREF(callback);
+    if (PyGtk_Check(py_widget))
+	widget = GTK_WIDGET(PyGtk_Get(py_widget));
+    else if (py_widget != Py_None)
+    {
+	PyErr_SetString(PyExc_TypeError, "arg 3 must be a widget or None");
+	return NULL;
+    }
+    if (PyGtk_Check(py_icon))
+	widget = GTK_WIDGET(PyGtk_Get(py_icon));
+    else if (py_icon != Py_None)
+    {
+	PyErr_SetString(PyExc_TypeError, "arg 7 must be a widget or None");
+	return NULL;
+    }
+    /* if you set sigfunc to NULL, no signal is connected, rather than
+       the default signal handler being used */
+    ret = gtk_toolbar_append_element(GTK_TOOLBAR(PyGtk_Get(t)), type,
+				     widget, text, tooltip, tip_private,
+				     icon, NULL, NULL);
+    if (callback != Py_None) {
+	if (extra)
+	    Py_INCREF(extra);
+	else
+	    extra = PyTuple_New(0);
+	data = Py_BuildValue("(OO)", callback, extra);
+	gtk_signal_connect_full(GTK_OBJECT(ret), "clicked", NULL,
+				PyGtk_CallbackMarshal, data,
+				PyGtk_DestroyNotify, FALSE, FALSE);
+    }
+    return PyGtk_New((GtkObject *)ret);
+}
+
+static PyObject *
+_wrap_gtk_object_deref_and_destroy(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    GtkObject    *object;
+    
+    if (!PyArg_ParseTuple(args, "O!:gtk_object_deref_and_destroy",
+			  &PyGtk_Type, &t))
+	return NULL;
+
+    object = GTK_OBJECT(PyGtk_Get(t));
+    while( object->ref_count > 1 )
+        gtk_object_unref(object);
+
+    gtk_object_unref(object);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gtk_object_unref(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    GtkObject    *object;
+    
+    if (!PyArg_ParseTuple(args, "O!:gtk_object_unref",
+			  &PyGtk_Type, &t))
+	return NULL;
+
+    object = GTK_OBJECT(PyGtk_Get(t));
+
+    gtk_object_unref(object);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gtk_object_ref(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    GtkObject    *object;
+    
+    if (!PyArg_ParseTuple(args, "O!:gtk_object_ref",
+			  &PyGtk_Type, &t))
+	return NULL;
+
+    object = GTK_OBJECT(PyGtk_Get(t));
+
+    gtk_object_ref(object);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gtk_object_get_ref_count(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    GtkObject    *object;
+    
+    if (!PyArg_ParseTuple(args, "O!:gtk_object_get_ref_count",
+			  &PyGtk_Type, &t))
+	return NULL;
+
+    object = GTK_OBJECT(PyGtk_Get(t));
+
+    return Py_BuildValue("i", object->ref_count );
+}
+
+static PyObject *
+_wrap_gtk_object_sink(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    GtkObject    *object;
+    
+    if (!PyArg_ParseTuple(args, "O!:gtk_object_get_ref_count",
+			  &PyGtk_Type, &t))
+	return NULL;
+
+    object = GTK_OBJECT(PyGtk_Get(t));
+    gtk_object_sink( object );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_py_object_get_ref_count(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    
+    if (!PyArg_ParseTuple(args, "O:py_object_get_ref_count",
+			  &t))
+	return NULL;
+
+    return Py_BuildValue("i", t->ob_refcnt );
+}
+
+static PyObject *
+_wrap_gtk_window_move(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    int x, y;
+    GdkWindow *window;
+    
+    if (!PyArg_ParseTuple(args, "O!ii:gtk_window_move",
+			  &PyGtk_Type, &t, &x, &y))
+	return NULL;
+
+    window = GTK_WIDGET(PyGtk_Get(t))->window;
+
+    gdk_window_move( window, x, y );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gtk_window_get_position(PyObject *self, PyObject *args)
+{
+    PyGtk_Object *t;
+    int x, y;
+    GdkWindow *window;
+    
+    if (!PyArg_ParseTuple(args, "O!:gtk_window_get_position",
+			  &PyGtk_Type, &t))
+	return NULL;
+
+    window = GTK_WIDGET(PyGtk_Get(t))->window;
+
+    gdk_window_get_position( window, &x, &y );
+
+    return Py_BuildValue("(ii)", x, y );
+}
+
+static PyMethodDef gtkmissing_methods[] =
+{
+    {"gtk_toolbar_append_element", _wrap_gtk_toolbar_append_element, 1},
+    {"gtk_object_deref_and_destroy", _wrap_gtk_object_deref_and_destroy, 1},
+    {"gtk_object_unref", _wrap_gtk_object_unref, 1},
+    {"gtk_object_ref", _wrap_gtk_object_ref, 1},
+    {"gtk_object_get_ref_count", _wrap_gtk_object_get_ref_count, 1},
+    {"gtk_object_sink", _wrap_gtk_object_sink, 1},
+    {"py_object_get_ref_count", _wrap_py_object_get_ref_count, 1},
+    {"gtk_window_move", _wrap_gtk_window_move, 1},
+    {"gtk_window_get_position", _wrap_gtk_window_get_position, 1},
+    {NULL, NULL, 0}
+};
+
+void
+init_gtkmissing(void)
+{
+    init_pygtk();
+    
+    Py_InitModule("_gtkmissing", gtkmissing_methods);
+    
+    if (PyErr_Occurred())
+	Py_FatalError("can't initialize module _gtkmissing");
+}

Added: packages/openev/branches/upstream/current/pymod/gtkmissing.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gtkmissing.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gtkmissing.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,70 @@
+import gtk; _gtk = gtk; del gtk
+import _gtkmissing
+import _gv
+import pgu
+
+###############################################################################
+class GtkColorWell(_gtk.GtkButton):
+    get_type = _gv.gtk_color_well_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gtk_color_well_new('')
+        if self._o is None:
+            raise ValueError, "Failed to create GtkColorWell."
+
+    def set_d( self, r, g, b, a ):
+        _gv.gtk_color_well_set_d( self._o, r, g, b, a )
+
+    def set_i8( self, r, g, b, a ):
+        _gv.gtk_color_well_set_i8( self._o, r, g, b, a )
+
+    def set_i16( self, r, g, b, a ):
+        _gv.gtk_color_well_set_i16( self._o, r, g, b, a )
+
+    def set_use_alpha( self, use_alpha ):
+        _gv.gtk_color_well_set_use_alpha( self._o, use_alpha )
+
+    def set_continuous( self, update_continuous ):
+        _gv.gtk_color_well_set_continuous( self._o, update_continuous )
+
+    def set_title( self, title ):
+        _gv.gtk_color_well_set_title( self._o, title )
+
+    def get_d(self):
+        return _gv.gtk_color_well_get_d( self._o );
+
+    def get_color( self ):
+        return self.get_d()
+
+    def set_color( self, color ):
+        self.set_d( color[0], color[1], color[2], color[3] )
+
+
+
+pgu.gtk_register( "GtkColorWell", GtkColorWell )
+
+
+###############################################################################
+
+def toolbar_append_element(self, type, widget, text, tooltip, tp,
+                           icon, callback, *extra):
+    if widget: widget = widget._o
+    if icon: icon = icon._o
+    return _gtk._obj2inst(_gtkmissing.gtk_toolbar_append_element(
+        self._o, type, widget, text, tooltip, tp, icon, callback, extra))
+
+_gtk.GtkToolbar.append_element = toolbar_append_element
+
+###############################################################################
+
+def gtk_window_get_position( self ):
+    return _gtkmissing.gtk_window_get_position( self._o )
+
+def gtk_window_move( self, x, y ):
+    return _gtkmissing.gtk_window_move( self._o, x, y )
+
+_gtk.GtkWindow.get_position = gtk_window_get_position
+_gtk.GtkWindow.window_move = gtk_window_move
+
+del toolbar_append_element
+

Added: packages/openev/branches/upstream/current/pymod/gv.defs
===================================================================
--- packages/openev/branches/upstream/current/pymod/gv.defs	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gv.defs	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1160 @@
+;; -*- Scheme -*-
+
+;; Declaration of some gtk types
+
+(declare-object GtkObject)
+(declare-object GtkWidget)
+(declare-object GtkData)
+(declare-object GtkGLArea)
+
+;; View Area
+
+(define-object GvViewArea (GtkGLArea))
+
+(define-func gv_view_area_new
+  (GtkWidget null-ok)
+  ())
+
+(define-func gv_view_area_get_width
+  int
+  ((GvViewArea view)))
+
+(define-func gv_view_area_get_height
+  int
+  ((GvViewArea view)))
+
+(define-func gv_view_area_add_layer
+  none
+  ((GvViewArea view)
+   (GtkObject layer)))
+
+(define-func gv_view_area_remove_layer
+  none
+  ((GvViewArea view)
+   (GtkObject layer)))
+
+(define-func gv_view_area_get_named_layer
+  (GtkObject null-ok)
+  ((GvViewArea view)
+   (const_string layer_name)))
+
+(define-func gv_view_area_active_layer
+  (GtkObject null-ok)
+  ((GvViewArea view)))
+
+(define-func gv_view_area_set_active_layer
+  none
+  ((GvViewArea area)
+   (GtkObject layer)))
+
+(define-func gv_view_area_swap_layers
+  none
+  ((GvViewArea area)
+   (int layer_a)
+   (int layer_b)))
+
+(define-func gv_view_area_zoom
+  none
+  ((GvViewArea area)
+   (double zoom)))
+
+(define-func gv_view_area_get_zoom
+  double
+  ((GvViewArea view)))
+
+(define-func gv_view_area_get_flip_x
+  int
+  ((GvViewArea area)))
+
+(define-func gv_view_area_get_flip_y
+  int
+  ((GvViewArea area)))
+
+(define-func gv_view_area_set_flip_xy
+  none
+  ((GvViewArea area)
+   (int flip_x)
+   (int flip_y)))
+
+(define-func gv_view_area_rotate
+  none
+  ((GvViewArea area)
+   (double angle)))
+
+(define-func gv_view_area_translate
+  none
+  ((GvViewArea area)
+   (double dx)
+   (double dy)))
+
+(define-func gv_view_area_set_translation
+  none
+  ((GvViewArea area)
+   (double x)
+   (double y)))
+
+(define-func gv_view_area_fit_extents
+  none
+  ((GvViewArea area)
+   (double llx)
+   (double llyy)
+   (double width)
+   (double height)))
+
+(define-func gv_view_area_fit_all_layers
+  none
+  ((GvViewArea area)))
+
+(define-func gv_view_area_set_projection
+  none
+  ((GvViewArea area)
+   (const_string proj_name)))
+
+(define-func gv_view_area_get_projection
+  const_string
+  ((GvViewArea area)))
+
+(define-func gv_view_area_copy_state
+  none
+  ((GvViewArea area)
+   (GvViewArea source)))
+
+(define-func gv_view_area_print_to_file
+  int
+  ((GvViewArea area)
+   (int width)
+   (int height)
+   (string filename)
+   (string format)
+   (int is_rgb)))
+
+(define-func gv_view_area_print_postscript_to_file
+  int
+  ((GvViewArea area)
+   (int width)
+   (int height)
+   (double ulx)
+   (double uly)
+   (double lrx)
+   (double lry)
+   (int is_rgb)
+   (string filename)))
+
+(define-func gv_view_area_page_setup
+  none
+  ())
+
+(define-func gv_view_area_print_to_windriver
+  int
+  ((GvViewArea area)
+   (int width)
+   (int height)
+   (double ulx)
+   (double uly)
+   (double lrx)
+   (double lry)
+   (int is_rgb)))
+
+(define-func gv_view_area_set_mode
+  none
+  ((GvViewArea area)
+   (int flag_3d)))
+
+(define-func gv_view_area_get_mode
+  int
+  ((GvViewArea area)))
+
+(define-func gv_view_area_height_scale
+  none
+  ((GvViewArea area)
+   (double scale)))
+
+(define-func gv_view_area_get_height_scale
+  double
+  ((GvViewArea area)))
+
+(define-func gv_view_area_queue_draw
+  none
+  ((GvViewArea area)))
+
+(define-func gv_view_area_get_raw
+  int
+  ((GvViewArea area)
+   (GtkObject ref_layer)))
+
+(define-func gv_view_area_set_raw
+  int
+  ((GvViewArea area)
+   (GtkObject ref_layer)
+   (int raw_enable)))
+
+(define-func gv_view_area_get_property
+  const_string
+  ((GvViewArea area)
+   (string name)))
+
+(define-func gv_view_area_set_property
+  none
+  ((GvViewArea area)
+   (const_string name)
+   (const_string value)))
+
+;gv_view_area_list_layers
+;gv_view_area_create_thumbnail
+
+;; GvData
+
+(define-object GvData (GtkData))
+
+(define-func gv_data_is_read_only
+  int
+  ((GvData data)))
+
+(define-func gv_data_set_read_only
+  none
+  ((GvData data)
+   (int read_only)))
+
+(define-func gv_data_get_name
+  const_string
+  ((GvData data)))
+
+(define-func gv_data_set_name
+  none
+  ((GvData data)
+   (string name)))
+
+(define-func gv_data_get_projection
+  const_string
+  ((GvData data)))
+
+(define-func gv_data_set_projection
+  none
+  ((GvData data)
+   (string projection)))
+
+(define-func gv_data_get_property
+  const_string
+  ((GvData data)
+   (string name)))
+
+(define-func gv_data_set_property
+  none
+  ((GvData data)
+   (const_string name)
+   (const_string value)))
+
+(define-func gv_data_freeze
+  none
+  ((GvData data)))
+
+(define-func gv_data_thaw
+  none
+  ((GvData data)))
+
+(define-func gv_data_get_parent
+  (GtkObject null-ok)
+  ((GvData data)))
+
+(define-func gv_data_registry_dump
+  none
+  ())
+
+;gv_data_changed
+
+;; GvRecords
+
+(define-object GvRecords (GvData))
+
+(define-func gv_records_new
+  GvData
+  ())
+
+(define-func gv_records_from_dbf
+  (GvData null-ok)
+  ((string filename)
+   (progress_cb cb)
+   (progress_data cb_data)))
+
+(define-func gv_records_from_rec
+  (GvData null-ok)
+  ((string filename)
+   (progress_cb cb)
+   (progress_data cb_data)))
+
+(define-func gv_records_create_records
+  int
+  ((GvRecords records)
+   (int new_records)))
+
+(define-func gv_records_num_records
+  int
+  ((GvRecords records)))
+
+(define-func gv_records_add_field
+  int
+  ((GvRecords records)
+   (const_string name)
+   (int rft)
+   (int width)
+   (int precision)))
+
+(define-func gv_records_set_raw_field_data
+  none
+  ((GvRecords records)
+   (int record_index)
+   (int field_index)
+   (const_string value (null-ok) (= "NULL"))))
+
+(define-func gv_records_get_raw_field_data
+  const_string
+  ((GvRecords records)
+   (int record_index)
+   (int field_index)))
+
+;; GvShapes
+
+(define-object GvShapes (GvData))
+
+(define-func gv_shapes_new
+  GvData
+  ())
+
+(define-func gv_shapes_from_shapefile
+  (GvData null-ok)
+  ((string filename)))
+
+(define-func gv_shapes_to_shapefile
+  int
+  ((string filename)
+   (GvData data)
+   (int shp_type)))
+
+(define-func gv_shapes_from_ogr
+  (GvData null-ok)
+  ((string filename)
+   (int    layer)))
+
+(define-func gv_have_ogr_support
+  int
+  ())
+
+(define-func gv_shapes_num_shapes
+  int
+  ((GvShapes shapes)))
+
+(define-func gv_shapes_add_height
+  none
+  ((GvShapes shapes)
+   (GvData raster)
+   (double offset)
+   (double default_height)))
+
+(define-func gv_shape_get_count
+  int
+  ())
+
+;; GvPoints
+
+(define-object GvPoints (GvData))
+
+(define-func gv_points_new
+  GvData
+  ())
+
+(define-func gv_points_num_points
+  int
+  ((GvPoints points)))
+
+;; GvPolylines
+
+(define-object GvPolylines (GvData))
+
+(define-func gv_polylines_new
+  GvData
+  ())
+
+(define-func gv_polylines_num_lines
+  int
+  ((GvPolylines lines)))
+
+;; GvAreas
+
+(define-object GvAreas (GvData))
+
+(define-func gv_areas_new
+  GvData
+  ())
+
+(define-func gv_areas_num_areas
+  int
+  ((GvAreas areas)))
+
+;; GvLayer
+
+(define-object GvLayer (GvData))
+
+(define-func gv_layer_is_visible
+  int
+  ((GvLayer layer)))
+
+(define-func gv_layer_set_visible
+  none
+  ((GvLayer layer)
+   (int visible)))
+
+(define-func gv_layer_reproject
+  int
+  ((GvLayer layer)
+   (string projection)))
+
+(define-func gv_layer_get_view
+  GvViewArea
+  ((GvLayer layer)))
+
+;gv_layer_extents
+
+;; GvShapeLayer
+
+(define-object GvShapeLayer (GvLayer))
+
+(define-func gv_shape_layer_clear_selection
+  none
+  ((GvShapeLayer layer)))
+
+(define-func gv_shape_layer_select_all
+  none
+  ((GvShapeLayer layer)))
+
+(define-func gv_shape_layer_select_shape
+  none
+  ((GvShapeLayer layer)
+   (int shape_id)))
+
+(define-func gv_shape_layer_deselect_shape
+  none
+  ((GvShapeLayer layer)
+   (int shape_id)))
+
+(define-func gv_shape_layer_subselect_shape
+  none
+  ((GvShapeLayer layer)
+   (int shape_id)))
+
+(define-func gv_shape_layer_get_subselection
+  int
+  ((GvShapeLayer layer)))
+
+;; GvShapesLayer
+
+(define-object GvShapesLayer (GvShapeLayer))
+
+(define-func gv_shapes_layer_new
+  GtkObject
+  ((GvShapes shapes (null-ok) (= "NULL"))))
+
+(define-func gv_shapes_layer_get_symbol_manager
+  (GtkObject null-ok)
+  ((GvShapesLayer layer)
+   (int ok_to_create)))
+
+;; GvPointLayer
+
+(define-object GvPointLayer (GvShapeLayer))
+
+(define-func gv_point_layer_new
+  GtkObject
+  ((GvPoints points (null-ok) (= "NULL"))))
+
+;; GvLineLayer
+
+(define-object GvLineLayer (GvShapeLayer))
+
+(define-func gv_line_layer_new
+  GtkObject
+  ((GvPolylines lines (null-ok) (= "NULL"))))
+
+;; GvAreaLayer
+
+(define-object GvAreaLayer (GvShapeLayer))
+
+(define-func gv_area_layer_new
+  GtkObject
+  ((GvAreas areas (null-ok) (= "NULL"))))
+
+;; GvPqueryLayer
+
+(define-object GvPqueryLayer (GvShapesLayer))
+
+(define-func gv_pquery_layer_new
+  GtkObject
+  ((GvShapes shapes (null-ok) (= "NULL"))))
+
+;; IpGcpLayer
+
+(define-object IpGcpLayer (GvShapesLayer))
+
+(define-func ip_gcp_layer_new
+  GtkObject
+  ())
+
+;; AppCurLayer
+
+(define-object AppCurLayer (GvShapesLayer))
+
+(define-func app_cur_layer_new
+  GtkObject
+  ((GvShapes shapes (null-ok) (="NULL"))))
+
+;; GvSymbolManager
+
+(define-object GvSymbolManager (GtkObject))
+
+(define-func gv_get_symbol_manager
+  GvSymbolManager
+  ())
+
+(define-func gv_symbol_manager_eject_symbol
+  none
+  ((GvSymbolManager manager)
+   (const_string name)))
+
+(define-func gv_symbol_manager_has_symbol
+  int
+  ((GvSymbolManager manager)
+   (const_string name)))
+
+;; GvManager
+
+(define-object GvManager (GtkObject))
+
+(define-func gv_get_manager
+  GvManager
+  ())
+
+(define-func gv_manager_get_preference
+  const_string
+  ((GvManager manager)
+   (const_string name)))
+
+(define-func gv_manager_set_preference
+  none
+  ((GvManager manager)
+   (const_string name)
+   (const_string value)))
+
+(define-func gv_manager_get_busy
+  int
+  ((GvManager manager)))
+
+(define-func gv_manager_set_busy
+  none
+  ((GvManager manager)
+   (int busy)))
+
+(define-func gv_manager_dump
+  none
+  ((GvManager manager)))
+
+;; GvTool
+
+(define-object GvTool (GtkObject))
+
+(define-func gv_tool_activate
+  none
+  ((GvTool tool)
+   (GvViewArea view)))
+
+(define-func gv_tool_deactivate
+  none
+  ((GvTool tool)
+   (GvViewArea view)))
+
+(define-func gv_tool_get_view
+  (GvViewArea null-ok)
+  ((GvTool tool)))
+
+(define-func gv_tool_set_cursor
+  none
+  ((GvTool tool)
+   (int cursor_type)))
+
+;; GvSelectionTool
+
+(define-object GvSelectionTool (GvTool))
+
+(define-func gv_selection_tool_new
+  GvTool
+  ())
+
+(define-func gv_selection_tool_set_layer
+  none
+  ((GvSelectionTool tool)
+   (GvShapeLayer layer)))
+
+;; GvZoompanTool
+
+(define-object GvZoompanTool (GvTool))
+
+(define-func gv_zoompan_tool_new
+  GvTool
+  ())
+
+;; GvPointTool
+
+(define-object GvPointTool (GvTool))
+
+(define-func gv_point_tool_new
+  GvTool
+  ())
+
+(define-func gv_point_tool_set_layer
+  none
+  ((GvPointTool tool)
+   (GvShapeLayer layer)))
+
+(define-func gv_point_tool_set_named_layer
+  none
+  ((GvPointTool tool)
+   (string name)))
+
+;; GvLineTool
+
+(define-object GvLineTool (GvTool))
+
+(define-func gv_line_tool_new
+  GvTool
+  ())
+
+(define-func gv_line_tool_set_layer
+  none
+  ((GvLineTool tool)
+   (GvShapeLayer layer)))
+
+(define-func gv_line_tool_set_named_layer
+  none
+  ((GvLineTool tool)
+   (string name)))
+
+;; GvRectTool
+
+(define-object GvRectTool (GvTool))
+
+(define-func gv_rect_tool_new
+  GvTool
+  ())
+
+(define-func gv_rect_tool_set_layer
+  none
+  ((GvRectTool tool)
+   (GvShapeLayer layer)))
+
+(define-func gv_rect_tool_set_named_layer
+  none
+  ((GvRectTool tool)
+   (string name)))
+
+;; GvRotateTool
+
+(define-object GvRotateTool (GvTool))
+
+(define-func gv_rotate_tool_new
+  GvTool
+  ())
+
+(define-func gv_rotate_tool_set_layer
+  none
+  ((GvRotateTool tool)
+   (GvShapeLayer layer)))
+
+(define-func gv_rotate_tool_set_named_layer
+  none
+  ((GvRotateTool tool)
+   (string name)))
+
+;; GvAreaTool
+
+(define-object GvAreaTool (GvTool))
+
+(define-func gv_area_tool_new
+  GvTool
+  ())
+
+(define-func gv_area_tool_set_layer
+  none
+  ((GvAreaTool tool)
+   (GvShapeLayer layer)))
+
+(define-func gv_area_tool_set_named_layer
+  none
+  ((GvAreaTool tool)
+   (string name)))
+
+;; GvNodeTool
+
+(define-object GvNodeTool (GvTool))
+
+(define-func gv_node_tool_new
+  GvTool
+  ())
+
+(define-func gv_node_tool_set_layer
+  none
+  ((GvNodeTool tool)
+   (GvShapeLayer layer)))
+
+;; GvRoiTool
+
+(define-object GvRoiTool (GvTool))
+
+(define-func gv_roi_tool_new
+  GvTool
+  ())
+
+;gv_roi_tool_get_rect
+
+;gv_roi_tool_new_rect
+
+;; GvPoiTool
+
+(define-object GvPoiTool (GvTool))
+
+(define-func gv_poi_tool_new
+  GvTool
+  ())
+
+;; GvTrackTool
+
+(define-object GvTrackTool (GvTool))
+
+(define-func gv_track_tool_new
+  GvTool
+  ((GtkObject label)))
+
+;; GvToolbox
+
+(define-object GvToolbox (GvTool))
+
+(define-func gv_toolbox_new
+  GvTool
+  ())
+
+(define-func gv_toolbox_add_tool
+  none
+  ((GvToolbox toolbox)
+   (string name)
+   (GvTool tool)))
+
+(define-func gv_toolbox_activate_tool
+  none
+  ((GvToolbox toolbox)
+   (string name)))
+
+;; GvUndo
+
+(define-func gv_undo_register_data
+  none
+  ((GvData data)))
+
+(define-func gv_undo_pop
+  none
+  ())
+
+(define-func gv_undo_clear
+  none
+  ())
+
+(define-func gv_undo_can_undo
+  int
+  ())
+
+(define-func gv_undo_close
+  none
+  ())
+
+(define-func gv_undo_open
+  none
+  ())
+
+(define-func gv_undo_start_group
+  int
+  ())
+
+(define-func gv_undo_end_group
+  none
+  ((int group)))
+
+;; GvViewLink
+
+(define-object GvViewLink (GtkObject))
+
+(define-func gv_view_link_new
+  GtkObject
+  ())
+
+(define-func gv_view_link_register_view
+  none
+  ((GvViewLink link)
+   (GvViewArea view)))
+
+(define-func gv_view_link_remove_view
+  none
+  ((GvViewLink link)
+   (GvViewArea view)))
+
+(define-func gv_view_link_enable
+  none
+  ((GvViewLink link)))
+
+(define-func gv_view_link_disable
+  none
+  ((GvViewLink link)))
+
+(define-func gv_view_link_set_cursor_mode
+  none
+  ((GvViewLink link)
+   (int cursor_mode)))
+
+;; GvRaster
+
+(define-object GvRaster (GvData))
+
+(define-func gv_raster_flush_cache
+  none
+  ((GvRaster raster)
+   (int x_off)
+   (int y_off)
+   (int width)
+   (int height)))
+
+(define-func gv_raster_get_min
+  float
+  ((GvRaster raster)))
+
+(define-func gv_raster_get_max
+  float
+  ((GvRaster raster)))
+
+(define-func gv_raster_cache_get_max
+  int
+  ())
+
+(define-func gv_raster_cache_get_used
+  int
+  ())
+
+(define-func gv_raster_cache_set_max
+  none
+  ((int new_max)))
+
+(define-func gv_raster_set_poly_order_preference
+  none
+  ((GvRaster raster)
+   (int poly_order)))
+
+;gv_raster_new
+
+;; GvRasterLayer
+
+(define-object GvRasterLayer (GvLayer))
+
+(define-func gv_raster_layer_lut_color_wheel_new
+  int
+  ((GvRasterLayer layer)
+   (int h_type)
+   (float h_param)
+   (int s_type)
+   (float s_param)
+   (int v_type)
+   (float v_param)))
+
+(define-func gv_raster_layer_lut_color_wheel_new_ev
+  int
+  ((GvRasterLayer layer)
+   (int set_phase)
+   (int set_magnitude)))
+
+(define-func gv_raster_layer_lut_color_wheel_1d_new
+  int
+  ((GvRasterLayer layer)
+   (float s)
+   (float v)
+   (float offset)))
+
+(define-func gv_raster_layer_alpha_set
+  int
+  ((GvRasterLayer layer)
+   (int alpha_mode)
+   (float alpha_check_val)))
+
+(define-func gv_raster_layer_min_set
+  int
+  ((GvRasterLayer layer)
+   (int   isource)
+   (float min)))
+
+(define-func gv_raster_layer_min_get
+  float
+  ((GvRasterLayer layer)
+   (int isource)))
+
+(define-func gv_raster_layer_max_set
+  int
+  ((GvRasterLayer layer)
+   (int isource)
+   (float max)))
+
+(define-func gv_raster_layer_max_get
+  float
+  ((GvRasterLayer layer)
+   (int isource)))
+
+(define-func gv_raster_layer_nodata_set
+  int
+  ((GvRasterLayer layer)
+   (int isource)
+   (float nodata_real)
+   (float nodata_imaginary)))
+
+(define-func gv_raster_layer_type_get
+  float
+  ((GvRasterLayer layer)
+   (int isource)))
+
+(define-func gv_raster_layer_get_const_value
+  int
+  ((GvRasterLayer layer)
+   (int isource)))
+
+(define-func gv_raster_layer_get_data
+  (GvRaster null-ok)
+  ((GvRasterLayer layer)
+   (int isource)))
+
+(define-func gv_raster_layer_get_mode
+  int
+  ((GvRasterLayer layer)))
+
+(define-func gv_raster_layer_texture_clamp_set
+  int
+  ((GvRasterLayer layer)
+   (int s_clamp)
+   (int t_clamp)))
+
+(define-func gv_raster_layer_zoom_set
+  int
+  ((GvRasterLayer layer)
+   (int max_mode)
+   (int min_mode)))
+
+(define-func gv_raster_layer_blend_mode_set
+  int
+  ((GvRasterLayer layer)
+   (int mode)
+   (int sfactor)
+   (int dfactor)))
+
+(define-func gv_raster_layer_lut_type_get
+  int
+  ((GvRasterLayer layer)))
+
+(define-func gv_raster_layer_add_height
+  none
+  ((GvRasterLayer layer)
+   (GvRaster height_raster)
+   (double default_height)))
+
+(define-func gv_raster_layer_clamp_height
+  none
+  ((GvRasterLayer layer)
+   (int bclamp_min)
+   (int bclamp_max)
+   (double min_height)
+   (double max_height)))
+
+(define-func gv_texture_cache_dump
+  none
+  ())
+
+(define-func gv_texture_cache_get_max
+  int
+  ())
+
+(define-func gv_texture_cache_get_used
+  int
+  ())
+
+(define-func gv_texture_cache_set_max
+  none
+  ((int new_max)))
+
+(define-func gv_build_skirt
+  GvLayer
+  ((GvRasterLayer raster)
+   (double base_z)))
+
+;; GtkColorWell
+
+(define-object GtkColorWell (GtkButton))
+
+(define-func gtk_color_well_new
+  GtkColorWell
+  ((string title)))
+
+(define-func gtk_color_well_set_d
+  none
+  ((GtkColorWell cwell)
+   (double r)
+   (double g)
+   (double b)
+   (double a)))
+
+(define-func gtk_color_well_set_i8
+  none
+  ((GtkColorWell cwell)
+   (int r)
+   (int g)
+   (int b)
+   (int a)))
+
+(define-func gtk_color_well_set_i16
+  none
+  ((GtkColorWell cwell)
+   (int r)
+   (int g)
+   (int b)
+   (int a)))
+
+(define-func gtk_color_well_set_use_alpha
+  none
+  ((GtkColorWell cwell)
+   (int use_alpha)))
+
+(define-func gtk_color_well_set_continuous
+  none
+  ((GtkColorWell cwell)
+   (int update_continuous)))
+
+(define-func gtk_color_well_set_title
+  none
+  ((GtkColorWell cwell)
+   (string title)))
+
+;; GvAutopanTool
+
+(define-object GvAutopanTool (GvTool))
+
+(define-func gv_autopan_tool_new
+  GvAutopanTool
+  ())
+
+(define-func gv_autopan_tool_play
+  int
+  ((GvAutopanTool tool)))
+
+(define-func gv_autopan_tool_pause
+  int
+  ((GvAutopanTool tool)))
+
+(define-func gv_autopan_tool_stop
+  int
+  ((GvAutopanTool tool)))
+
+(define-func gv_autopan_tool_set_speed
+  double
+  ((GvAutopanTool tool)
+   (double speed)))
+
+(define-func gv_autopan_tool_get_speed
+  double
+  ((GvAutopanTool tool)))
+
+(define-func gv_autopan_tool_set_location
+  int
+  ((GvAutopanTool tool)
+   (double x)
+   (double y)
+   (double z)))
+
+(define-func gv_autopan_tool_set_overlap
+  int
+  ((GvAutopanTool tool)
+   (double overlap)))
+
+(define-func gv_autopan_tool_get_overlap
+  double
+  ((GvAutopanTool tool)))
+
+(define-func gv_autopan_tool_set_block_x_size
+  int
+  ((GvAutopanTool tool)
+   (double block_x_size)
+   (int mode)))
+
+(define-func gv_autopan_tool_set_x_resolution
+  int
+  ((GvAutopanTool tool)
+   (double resolution)))
+
+(define-func gv_autopan_tool_set_standard_path
+  int
+  ((GvAutopanTool tool)
+   (int path_type)))
+
+(define-func gv_autopan_tool_set_lines_path
+  int
+  ((GvAutopanTool tool)
+   (GvShapes lines )))
+
+(define-func gv_autopan_tool_clear_trail
+  none
+  ((GvAutopanTool tool)))
+
+(define-func gv_autopan_tool_set_trail_color
+  int
+  ((GvAutopanTool tool)
+   (GvViewArea view)
+   (float red)
+   (float green)
+   (float blue)
+   (float alpha)))
+
+(define-func gv_autopan_tool_set_trail_mode
+  int
+  ((GvAutopanTool tool)
+   (GvViewArea view)
+   (int trail_mode)))
+
+(define-func gv_autopan_tool_set_trail_parameters
+  none
+  ((GvAutopanTool tool)
+   (GvRect overview_extents)
+   (int overview_width_pixels)))
+
+(define-func gv_autopan_tool_save_trail_tiles
+  int
+  ((GvAutopanTool tool)
+   (const_string basename)))
+
+(define-func gv_autopan_tool_load_trail_tiles
+  int
+  ((GvAutopanTool tool)
+   (const_string basename)
+   (int num_trail_tiles)))
+
+(define-func gv_autopan_tool_register_view
+  int
+  ((GvAutopanTool tool)
+   (GvViewArea view)
+   (int can_resize)
+   (int can_reposition)
+   (int trail_mode)))
+
+(define-func gv_autopan_tool_remove_view
+  int
+  ((GvAutopanTool tool)
+   (GvViewArea view)))
+
+
+
+

Added: packages/openev/branches/upstream/current/pymod/gv_ciet.c
===================================================================
--- packages/openev/branches/upstream/current/pymod/gv_ciet.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gv_ciet.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,985 @@
+/******************************************************************************
+ * $Id: gv_ciet.c,v 1.12 2005/07/27 18:31:50 warmerda Exp $
+ *
+ * Project:  CIETmap
+ * Purpose:  High performance function for collecting stratified
+ *           sample data to accelerate the CIETmap TABLES command.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, CIETcanada
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gv_ciet.c,v $
+ * Revision 1.12  2005/07/27 18:31:50  warmerda
+ * fixed allocation handling for paszFieldNames
+ *
+ * Revision 1.11  2004/11/19 15:38:18  zjamesatdm
+ * added support for optional else clause to recode.
+ *
+ * Revision 1.10  2004/09/28 19:43:26  warmerda
+ * Avoid extra reference increment on PyDict objects in StratTreeToDict().
+ * Was causing a substantial leak as noted in:
+ *   https://www.lan.dmsolutions.ca/bugzilla/show_bug.cgi?id=3033
+ *
+ * Revision 1.9  2004/08/23 21:29:36  pgs
+ * fixed memory allocation problem in gv_records_asdict
+ *
+ * Revision 1.8  2004/08/20 13:32:07  warmerda
+ * Removed some unused variables.
+ *
+ * Revision 1.7  2004/08/18 20:55:21  pgs
+ * added ability to return columns of data in a python dictionary
+ *
+ * Revision 1.6  2004/01/13 19:26:46  pgs
+ * fixed SortRecodings algorithim
+ *
+ * Revision 1.5  2003/08/23 04:02:39  warmerda
+ * added gv_records_recode
+ *
+ * Revision 1.4  2003/08/14 18:31:47  warmerda
+ * handle case of no data collected, support just 1 or 2 vars
+ *
+ * Revision 1.3  2003/08/11 20:06:33  warmerda
+ * added progress reporting
+ *
+ * Revision 1.2  2003/08/11 18:36:49  warmerda
+ * added integer type support
+ *
+ * Revision 1.1  2003/08/08 18:10:16  warmerda
+ * New
+ *
+ */
+
+typedef struct _StratNode {
+    int nNodeCount;
+    struct _StratNode *pasItemSubNode;
+    int  *panItemCount;
+} StratNode;
+
+typedef struct {
+    int nFieldIndex;
+    int nValueCount;
+    double *padfValueList;
+    PyObject **papyValueList;
+    int  *panValueIndex;
+    int  *panValueCount;
+
+    int  bLastVar;
+    int  bIsInteger;
+} StratVarInfo;
+
+/************************************************************************/
+/*                         StratVarFindValue()                          */
+/*                                                                      */
+/*      Try to find the passed in value in the list of values           */
+/*      currently associated with the variable.  We keep the values     */
+/*      in sorted order so we can do a binary search.  If we find       */
+/*      it, just return the index (unsorted that is).                   */
+/************************************************************************/
+
+static int StratVarFindValue( StratVarInfo *psVar, double dfValue )
+
+{
+    int  iStart, iEnd, iMiddle;
+
+/* -------------------------------------------------------------------- */
+/*      Special case for the very common case of one or two values      */
+/*      in the list.                                                    */
+/* -------------------------------------------------------------------- */
+    if( psVar->nValueCount > 0 && dfValue == psVar->padfValueList[0] )
+        return psVar->panValueIndex[0];
+
+    else if( psVar->nValueCount > 1 && dfValue == psVar->padfValueList[1] )
+        return psVar->panValueIndex[1];
+
+/* -------------------------------------------------------------------- */
+/*      Do a binary search on the list.                                 */
+/* -------------------------------------------------------------------- */
+    iStart = 0;
+    iEnd = psVar->nValueCount - 1;
+    while( iEnd >= iStart )
+    {
+        iMiddle = (iEnd + iStart) / 2;
+        if( dfValue < psVar->padfValueList[iMiddle] )
+            iEnd = iMiddle - 1;
+        else if( dfValue > psVar->padfValueList[iMiddle] )
+            iStart = iMiddle + 1;
+        else
+            return psVar->panValueIndex[iMiddle];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We didn't find the value.  Add it to the end of the list.       */
+/* -------------------------------------------------------------------- */
+    psVar->nValueCount++;
+
+    psVar->padfValueList = (double *) 
+        VSIRealloc(psVar->padfValueList, sizeof(double) * psVar->nValueCount );
+    psVar->padfValueList[psVar->nValueCount-1] = dfValue;
+    
+    psVar->panValueIndex = (int *) 
+        VSIRealloc(psVar->panValueIndex, sizeof(int) * psVar->nValueCount );
+    psVar->panValueIndex[psVar->nValueCount-1] = psVar->nValueCount-1;
+
+    psVar->panValueCount = (int *) 
+        VSIRealloc(psVar->panValueCount, sizeof(int) * psVar->nValueCount );
+    psVar->panValueCount[psVar->nValueCount-1] = 0;
+
+/* -------------------------------------------------------------------- */
+/*      Now sort it into the correct location in order.                 */
+/* -------------------------------------------------------------------- */
+    for( iStart = psVar->nValueCount - 2; iStart >= 0; iStart-- )
+    {
+        if( psVar->padfValueList[iStart] > psVar->padfValueList[iStart+1] )
+        {
+            /* swap values */
+
+            dfValue = psVar->padfValueList[iStart];
+            psVar->padfValueList[iStart] = psVar->padfValueList[iStart+1];
+            psVar->padfValueList[iStart+1] = dfValue;
+
+            iMiddle = psVar->panValueIndex[iStart];
+            psVar->panValueIndex[iStart] = psVar->panValueIndex[iStart+1];
+            psVar->panValueIndex[iStart+1] = iMiddle;
+
+            /* note: the counts are in unsorted order */
+        }
+        else
+            break;
+    }
+
+    return psVar->nValueCount-1;
+}
+
+/************************************************************************/
+/*                            StratNodeAdd()                            */
+/*                                                                      */
+/*      Add the passed value to the counts in this node, and return     */
+/*      the subnode associated with the value.  Extent the item list    */
+/*      if needed.                                                      */
+/************************************************************************/
+
+static StratNode *StratNodeAdd( StratNode *psNode, StratVarInfo *psVar, 
+                                double dfValue )
+
+{
+    int nItem = StratVarFindValue( psVar, dfValue );
+
+    psVar->panValueCount[nItem]++;
+
+/* -------------------------------------------------------------------- */
+/*      Reallocate the list of items larger if we find that the new     */
+/*      item is higher than the available item list.                    */
+/* -------------------------------------------------------------------- */
+    if( nItem >= psNode->nNodeCount )
+    {
+        if( psVar->bLastVar )
+        {
+            psNode->panItemCount = (int *) 
+                VSIRealloc(psNode->panItemCount,
+                           sizeof(int) * psVar->nValueCount);
+            memset( psNode->panItemCount + psNode->nNodeCount, 
+                    0, sizeof(int)*(psVar->nValueCount-psNode->nNodeCount) );
+        }
+        else
+        {
+            psNode->pasItemSubNode = (StratNode *) 
+                VSIRealloc(psNode->pasItemSubNode,
+                           sizeof(StratNode) * psVar->nValueCount);
+            memset( psNode->pasItemSubNode + psNode->nNodeCount, 
+                    0, 
+                    sizeof(StratNode)*(psVar->nValueCount-psNode->nNodeCount));
+        }
+        psNode->nNodeCount = psVar->nValueCount;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Increment count (for last variable), or return appropriate      */
+/*      subnode.                                                        */
+/* -------------------------------------------------------------------- */
+    if( psVar->bLastVar )
+    {
+        psNode->panItemCount[nItem]++;
+        return NULL;
+    }
+    else
+        return psNode->pasItemSubNode + nItem;
+}
+
+/************************************************************************/
+/*                          StratTreeToDict()                           */
+/************************************************************************/
+
+PyObject *StratTreeToDict( StratNode *psNode, StratVarInfo *pasVarInfos )
+
+{
+    PyObject *psDict = NULL;
+    int      i, bHaveItems = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have any children?  If not, we don't need to do           */
+/*      anything.                                                       */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < psNode->nNodeCount && !bHaveItems; i++ )
+    {
+        if( pasVarInfos[0].bLastVar  )
+            bHaveItems |= (psNode->panItemCount[i] != 0);
+        else 
+            bHaveItems |= (psNode->pasItemSubNode[i].nNodeCount != 0);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a dictionary into which to stick subdictionaries, or     */
+/*      counts.                                                         */
+/* -------------------------------------------------------------------- */
+    if( bHaveItems )
+    {
+        psDict = PyDict_New();
+    
+        for( i = 0; i < psNode->nNodeCount; i++ )
+        {
+            PyObject *pyValue = NULL; 
+
+            if( pasVarInfos[0].bLastVar  )
+            {
+                if( psNode->panItemCount[i] != 0 )
+                    pyValue = Py_BuildValue( "i", psNode->panItemCount[i] );
+            }
+            else 
+            {
+                if( psNode->pasItemSubNode[i].nNodeCount != 0 )
+                {
+                    pyValue = StratTreeToDict( psNode->pasItemSubNode + i, 
+                                               pasVarInfos + 1 );
+                }
+            }
+
+            if( pyValue )
+            {
+                PyDict_SetItem( psDict, pasVarInfos[0].papyValueList[i], 
+                                pyValue );
+                Py_DECREF( pyValue );
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup resources associated witht his node.                    */
+/* -------------------------------------------------------------------- */
+    CPLFree( psNode->panItemCount );
+    CPLFree( psNode->pasItemSubNode );
+
+    return psDict;
+}
+
+/************************************************************************/
+/*                  GvRecords.MultiStratifiedCollect()                  */
+/************************************************************************/
+
+static PyObject *
+_wrap_gv_records_MultiStratifiedCollect(PyObject *self, PyObject *args)
+{
+    PyObject *py_records, *pyResultTree;
+    PyObject *py_data_selection, *py_strat_vars;
+    PyObject *v1_keys=Py_None, *v2_keys=Py_None, *result;
+    char     *var1 = NULL, *var2 = NULL;
+    GvRecords *records;
+    int       nSelectionCount, *panSelectionList = NULL, i;
+    int       nVarCount, iVar, bCancelled = FALSE;
+    StratVarInfo *pasVarInfo;
+    StratNode *psRoot = NULL;
+    double    *padfRecValues;
+    PyProgressData sProgressInfo;
+
+    sProgressInfo.psPyCallback = NULL;
+    sProgressInfo.psPyCallbackData = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!O!ssO!OO:gv_records_MultiStratifiedCollect",
+                          &PyGtk_Type, &py_records,
+                          &PyList_Type, &py_data_selection,
+                          &var1, &var2, 
+                          &PyList_Type, &py_strat_vars,
+                          &(sProgressInfo.psPyCallback),
+                          &(sProgressInfo.psPyCallbackData) ) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+
+    /* 
+    ** Initialize progress.
+    */
+    if( !PyProgressProxy( 0.0, "", &sProgressInfo ) )
+        return FALSE;
+
+    /*
+    ** Build the variable list.  Var2 is manditory, var1 is optional. 
+    */
+    nVarCount = PyList_Size( py_strat_vars ) + 1;
+    if( var1 != NULL && strlen(var1) > 0 )
+        nVarCount++;
+
+    pasVarInfo = (StratVarInfo *) VSICalloc(sizeof(StratVarInfo),nVarCount);
+    padfRecValues = (double *) VSICalloc(sizeof(double),nVarCount);
+
+    for( iVar = 0; iVar < nVarCount; iVar++ )
+    {
+        const char *pszVarName = NULL;
+        int iField;
+
+        if( iVar < nVarCount-2 )
+        {
+            if( !PyArg_Parse( PyList_GET_ITEM(py_strat_vars,iVar), "s",
+                              &pszVarName ) )
+            {
+                PyErr_SetString(PyExc_ValueError,
+                                "expecting variable name in strat_vars.");
+                return NULL;
+            }
+        }
+        else if( iVar == nVarCount - 2 )
+        {
+            pszVarName = var1;
+        }
+        else if( iVar == nVarCount - 1 )
+        {
+            pszVarName = var2;
+            pasVarInfo[iVar].bLastVar = TRUE;
+        }
+
+        for( iField = 0; iField < records->nFieldCount; iField++ )
+        {
+            if( EQUAL(pszVarName,records->papszFieldName[iField]) )
+            {
+                pasVarInfo[iVar].nFieldIndex = iField;
+                pasVarInfo[iVar].bIsInteger = 
+                    records->panFieldType[iField] == GV_RFT_INTEGER;
+                break;
+            }
+        }
+
+        if( iField == records->nFieldCount )
+        {
+            PyErr_SetString(PyExc_ValueError,
+                            "unrecognised field name.");
+            return NULL;
+        }
+    }
+
+    /*
+    ** Build up the "selection" list.
+    */
+    nSelectionCount = PyList_Size( py_data_selection );
+    panSelectionList = (int *) CPLMalloc(sizeof(int) * nSelectionCount );
+
+    for( i = 0; i < nSelectionCount; i++ )
+    {
+        if( !PyArg_Parse( PyList_GET_ITEM(py_data_selection,i), "i",
+                          panSelectionList + i ) )
+        {
+            PyErr_SetString(PyExc_ValueError,
+                            "problem extracting selection item.");
+            return NULL;
+        }
+    }
+
+    /*
+    ** Allocate a root node for the data collection "tree".
+    */
+
+    psRoot = (StratNode *) CPLCalloc(sizeof(StratNode),1);
+
+    /*
+    ** Process all records.
+    */
+    
+    for( i = 0; i < nSelectionCount && !bCancelled; i++ )
+    {
+        int rec_index = panSelectionList[i];
+        StratNode *psNode = psRoot;
+
+        for( iVar = 0; iVar < nVarCount; iVar++ )
+        {
+            const char *pszRawFieldValue 
+                = gv_records_get_raw_field_data( records, rec_index, 
+                                                 pasVarInfo[iVar].nFieldIndex);
+            if( pszRawFieldValue == NULL )
+                break;
+            
+            if( pasVarInfo[iVar].bIsInteger )
+                padfRecValues[iVar] = atoi(pszRawFieldValue);
+            else
+                padfRecValues[iVar] = atof(pszRawFieldValue);
+        }
+
+        if( i % 100 == 0 )
+        {
+            if( !PyProgressProxy( i / (double) nSelectionCount, 
+                                  "", &sProgressInfo ) )
+                bCancelled = TRUE;
+        }
+
+        /* skip records with nulls in a used variable. */
+        if( iVar < nVarCount )
+            continue;
+
+        for( iVar = 0; iVar < nVarCount; iVar++ )
+        {
+            psNode = StratNodeAdd( psNode, pasVarInfo + iVar, 
+                                   padfRecValues[iVar] );
+        }
+    }
+
+    CPLFree( padfRecValues );
+    CPLFree( panSelectionList );
+
+    if( !bCancelled )
+        PyProgressProxy( 1.0, "", &sProgressInfo );
+
+    /*
+    ** Unsort the values list in the StratVarInfo's so that our indexes can
+    ** be used easily to get back to the variable values. 
+    */
+    for( iVar = 0; iVar < nVarCount; iVar++ )
+    {
+        StratVarInfo  *psVar = pasVarInfo + iVar;
+        int           iValue;
+        double        *padfNewValueList;
+
+        padfNewValueList = (double *) 
+            CPLMalloc(sizeof(double) * psVar->nValueCount);
+        
+        for( iValue = 0; iValue < psVar->nValueCount; iValue++ )
+        {
+            padfNewValueList[psVar->panValueIndex[iValue]] = 
+                psVar->padfValueList[iValue];
+        }
+        CPLFree( psVar->padfValueList );
+        psVar->padfValueList = padfNewValueList;
+
+        CPLFree( psVar->panValueIndex );
+        psVar->panValueIndex = NULL;
+    }
+
+    /*
+    ** Create Python string reresentations of the values for use in the
+    ** final results dictionary.
+    */
+    for( iVar = 0; iVar < nVarCount; iVar++ )
+    {
+        StratVarInfo *psVar = pasVarInfo + iVar;
+        int           iValue;
+
+        psVar->papyValueList = (PyObject **) 
+            CPLMalloc(sizeof(PyObject *) * psVar->nValueCount);
+
+        for( iValue = 0; iValue < psVar->nValueCount; iValue++ )
+        {
+            if( psVar->bIsInteger )
+            {
+                psVar->papyValueList[iValue] = 
+                    Py_BuildValue( "i", (int) psVar->padfValueList[iValue] );
+            }
+            else
+            {
+                psVar->papyValueList[iValue] = 
+                    Py_BuildValue( "d", psVar->padfValueList[iValue] );
+            }
+        }
+    }
+
+    /*
+    ** Marshall the results into a tree of dictionaries.  Wipe tree as
+    ** part of the process.
+    */
+    
+    pyResultTree = StratTreeToDict( psRoot, pasVarInfo );
+    if( pyResultTree == NULL )
+    {
+        pyResultTree = Py_None;
+        Py_INCREF( Py_None );
+    }
+
+    /*
+    ** Create v1/v2_keys ... a dictionary with one entry for each value of 
+    ** var1/var2 that occurs in the data.
+    */
+
+    for( iVar = nVarCount-1; iVar >= nVarCount-2 && iVar >= 0; iVar-- )
+    {
+        int iValue;
+        PyObject *key_dict = PyDict_New();
+        
+        for( iValue = 0; iValue < pasVarInfo[iVar].nValueCount; iValue++ )
+        {
+            PyDict_SetItem( key_dict, pasVarInfo[iVar].papyValueList[iValue], 
+                            Py_None );
+        }
+
+        if( iVar == nVarCount-1 )
+            v2_keys = key_dict;
+        else
+            v1_keys = key_dict;
+    }
+    
+    /*
+    ** Cleanup working structures. 
+    */
+
+    /*
+    ** Prepare final result tuple. 
+    */
+
+    result = Py_BuildValue( "(OOO)", v1_keys, v2_keys, pyResultTree );
+
+    if( v1_keys != Py_None )
+    {
+        Py_DECREF( v1_keys );
+    }
+    Py_DECREF( v2_keys );
+    Py_DECREF( pyResultTree );
+
+    if( bCancelled )
+    {
+        PyErr_SetString(PyExc_ValueError,
+                        "User called processing.");
+        Py_DECREF( result );
+        return NULL;
+    }
+    else
+    {
+        return result;
+    }
+}
+
+typedef struct {
+    double dfMin;
+    double dfMax;
+    double dfReplacement;
+    int    bReplaceNULL;
+} GvRecodeEntry;
+
+/************************************************************************/
+/*                           SortRecodings()                            */
+/*                                                                      */
+/*      Simple bubble sort of the recode table entries.                 */
+/************************************************************************/
+
+void SortRecodings( int nCount, GvRecodeEntry *pasRecodings )
+
+{
+    int  i, j;
+
+    for( i = 0; i < nCount-1; i++ )
+    {
+        for( j = 0; j < nCount-1; j++ )
+        {
+            if( pasRecodings[j].dfMin > pasRecodings[j+1].dfMin )
+            {
+                GvRecodeEntry sTempEntry;
+
+                memcpy( &sTempEntry, pasRecodings+j, sizeof(sTempEntry) );
+                memcpy( pasRecodings+j, pasRecodings+j+1, sizeof(sTempEntry) );
+                memcpy( pasRecodings+j+1, &sTempEntry, sizeof(sTempEntry) );
+            }
+        }
+    }
+}
+
+/************************************************************************/
+/*                            FindRecoding()                            */
+/************************************************************************/
+
+GvRecodeEntry *FindRecoding( double dfValue, 
+                             int nCount, GvRecodeEntry *pasRecodings )
+
+{
+    int  iStart, iEnd, iMiddle;
+
+    iStart = 0;
+    iEnd = nCount - 1;
+    
+    //TODO: dump out here???
+
+    while( iEnd >= iStart )
+    {
+        iMiddle = (iStart+iEnd)/2;
+
+        if( pasRecodings[iMiddle].dfMin > dfValue )
+            iEnd = iMiddle - 1;
+        else if( pasRecodings[iMiddle].dfMax < dfValue )
+            iStart = iMiddle+1;
+        else if( pasRecodings[iMiddle].dfMin <= dfValue
+                 && pasRecodings[iMiddle].dfMax >= dfValue )
+            return pasRecodings + iMiddle;
+        else
+            return NULL;
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                         gv_records_recode()                          */
+/************************************************************************/
+
+static PyObject *
+_wrap_gv_records_recode(PyObject *self, PyObject *args)
+{
+    PyObject *py_records, *py_single_mapping, *py_range_mapping;
+    GvRecords *records;
+    char      *pszSrcVarName = NULL, *pszDstVarName = NULL;
+    int       bCancelled = FALSE, i, nRecordCount;
+    int       nRangeRecodeCount = 0, nSingleRecodeCount = 0;
+    int       iSrcField, iDstField, nIgnored=0, nAltered=0, nUnchanged=0;
+    PyProgressData sProgressInfo;
+    GvRecodeEntry *pasSingleRecodings;
+    GvRecodeEntry *pasRangeRecodings;
+    PyObject    *pyKey = NULL, *pyValue = NULL;
+    int       bHaveNULLReplacement = FALSE;
+    GvRecodeEntry sNULLEntry;
+    int       bElseClause = FALSE;
+    GvRecodeEntry sElseEntry;
+    
+    sProgressInfo.psPyCallback = NULL;
+    sProgressInfo.psPyCallbackData = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!O!O!ssOO:gv_records_recode",
+                          &PyGtk_Type, &py_records,
+                          &PyDict_Type, &py_single_mapping,
+                          &PyList_Type, &py_range_mapping,
+                          &pszSrcVarName, &pszDstVarName, 
+                          &(sProgressInfo.psPyCallback),
+                          &(sProgressInfo.psPyCallbackData) ) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+
+    /*
+    ** Identify the source and destination field indexes.
+    */
+    iSrcField = -1;
+    iDstField = -1;
+    for( i = 0; i < records->nFieldCount; i++ )
+    {
+        if( EQUAL(pszSrcVarName,records->papszFieldName[i]) )
+            iSrcField = i;
+        if( EQUAL(pszDstVarName,records->papszFieldName[i]) )
+            iDstField = i;
+    }
+
+    if( iSrcField == -1 || iDstField == -1 )
+    {
+        PyErr_SetString( PyExc_ValueError, "RECODE variable not recognised." );
+        return NULL;
+    }
+
+    cos( 0.0 );
+
+    /*
+    ** Collect the single value mappings.
+    */
+    pasSingleRecodings = 
+        g_new( GvRecodeEntry, PyDict_Size(py_single_mapping) );
+
+    nSingleRecodeCount = 0;
+    i = 0;
+    while( PyDict_Next( py_single_mapping, &i, &pyKey, &pyValue ) )
+    {
+        char *pszKey = NULL,  *pszValue = NULL;
+
+        if( pyValue == Py_None )
+        {
+            pasSingleRecodings[nSingleRecodeCount].dfReplacement = 0.0;
+            pasSingleRecodings[nSingleRecodeCount].bReplaceNULL = TRUE;
+        }
+        else if( !PyArg_Parse( pyValue, "s", &pszValue ) )
+        {
+            PyErr_SetString(PyExc_TypeError, 
+                            "Single value is not numeric." );
+            return NULL;
+        }
+        else
+        {
+            pasSingleRecodings[nSingleRecodeCount].dfReplacement =
+                atof( pszValue );
+            pasSingleRecodings[nSingleRecodeCount].bReplaceNULL = FALSE;
+        }
+
+        if( pyKey == Py_None )
+        {
+            bHaveNULLReplacement = TRUE;
+            memcpy( &sNULLEntry, 
+                    pasSingleRecodings + nSingleRecodeCount, 
+                    sizeof(sNULLEntry) );
+        }
+        else if( !PyArg_Parse( pyKey, "s", &pszKey ) )
+        {
+            PyErr_SetString(PyExc_TypeError, 
+                            "Single value is not numeric." );
+            return NULL;
+        }
+        else if( EQUAL(pszKey,"None") || EQUAL(pszKey,"NULL") )
+        {
+            bHaveNULLReplacement = TRUE;
+            memcpy( &sNULLEntry, 
+                    pasSingleRecodings + nSingleRecodeCount, 
+                    sizeof(sNULLEntry) );
+        }
+        else if( EQUAL(pszKey,"ELSE")  )
+        {
+            /*NULL is a special case*/
+            if( pyValue != Py_None )
+            {
+                sElseEntry.dfReplacement = atof( pszValue );
+            }
+            else
+            {
+                bHaveNULLReplacement = TRUE;
+            }
+            
+            /*any value should be caught by else*/
+            sElseEntry.dfMin = 0 - HUGE_VAL;
+            sElseEntry.dfMax = HUGE_VAL;
+            memcpy( &sElseEntry, 
+                    pasSingleRecodings + nSingleRecodeCount, 
+                    sizeof(sElseEntry) );
+            bElseClause = TRUE;
+        }
+        else
+        {
+            pasSingleRecodings[nSingleRecodeCount].dfMin = atof(pszKey);
+            pasSingleRecodings[nSingleRecodeCount].dfMax = atof(pszKey);
+            nSingleRecodeCount++;
+        }
+    }
+
+    SortRecodings( nSingleRecodeCount, pasSingleRecodings );
+    
+    /*
+    ** Collect range mappings. 
+    */
+
+    nRangeRecodeCount = PyList_Size(py_range_mapping);
+    pasRangeRecodings = g_new( GvRecodeEntry, nRangeRecodeCount );
+
+    for( i = 0; i < nRangeRecodeCount; i++ )
+    {
+        char *pszReplacement=NULL;
+
+        if( !PyArg_Parse( PyList_GetItem( py_range_mapping, i ), "(ddz)", 
+                          &(pasRangeRecodings[i].dfMin),
+                          &(pasRangeRecodings[i].dfMax),
+                          &pszReplacement ) )
+        {
+            PyErr_SetString(PyExc_TypeError, 
+                            "Range value is not (min,max,result) tuple." );
+            return NULL;
+        }
+
+        if( pszReplacement == NULL )
+        {
+            pasRangeRecodings[i].dfReplacement = 0.0;
+            pasRangeRecodings[i].bReplaceNULL = TRUE;
+        }
+        else
+        {
+            pasRangeRecodings[i].dfReplacement = atof(pszReplacement);
+            pasRangeRecodings[i].bReplaceNULL = FALSE;
+        }
+    }
+
+    SortRecodings( nRangeRecodeCount, pasRangeRecodings );
+
+    /* 
+    ** Initialize progress.
+    */
+    if( !PyProgressProxy( 0.0, "", &sProgressInfo ) )
+        return FALSE;
+
+    /*
+    ** Process all records (not just selected ones!). 
+    */
+    nRecordCount = gv_records_num_records( records );
+    for( i = 0; i < nRecordCount && !bCancelled; i++ )
+    {
+        double dfValue;
+        GvRecodeEntry *psEntry;
+        const char *pszRawFieldValue = 
+            gv_records_get_raw_field_data( records, i, iSrcField );
+
+        if( pszRawFieldValue == NULL )
+        {
+            if( bHaveNULLReplacement )
+                psEntry = &sNULLEntry;
+            else
+                psEntry = NULL;
+        }
+        else
+        {
+            dfValue = atof(pszRawFieldValue);
+        
+            psEntry = FindRecoding( dfValue, 
+                                    nSingleRecodeCount, pasSingleRecodings );
+            if( psEntry == NULL)
+                psEntry = FindRecoding( dfValue, 
+                                        nRangeRecodeCount, pasRangeRecodings );
+            
+            /*
+             if nothing else was applied, use the ELSE value after adding
+             the correct mapping
+            */
+            if (psEntry == NULL && bElseClause)
+            {
+                psEntry = &sElseEntry;
+            }
+        }
+
+        
+        if( psEntry == NULL )
+            nUnchanged++;
+        else if( psEntry->bReplaceNULL )
+        {
+            gv_records_set_raw_field_data( records, i, iDstField, NULL );
+            nIgnored++;
+        }
+        else
+        {
+            char szReplaceText[200];
+
+            sprintf( szReplaceText, "%g", psEntry->dfReplacement );
+            gv_records_set_raw_field_data( records, i, iDstField, 
+                                           szReplaceText);
+            nAltered++;
+        }
+        
+        if( i % 100 == 0 )
+        {
+            if( !PyProgressProxy( i / (double) nRecordCount,
+                                  "", &sProgressInfo ) )
+                bCancelled = TRUE;
+        }
+    }
+
+    if( !bCancelled )
+        PyProgressProxy( 1.0, "", &sProgressInfo );
+
+    g_free( pasSingleRecodings );
+    g_free( pasRangeRecodings );
+
+    return Py_BuildValue( "(iii)", nUnchanged, nAltered, nIgnored );
+}
+
+
+
+/************************************************************************/
+/*                         gv_records_aslist()                          */
+/************************************************************************/
+
+static PyObject *
+_wrap_gv_records_asdict(PyObject *self, PyObject *args)
+{
+    PyObject *py_records, *py_fields;
+    GvRecords *records;
+    int       i, j, nRecordCount, nFields, *panFields;
+    PyObject *py_dict = NULL, *py_list = NULL;
+    char ** paszFieldNames;
+    
+    if (!PyArg_ParseTuple(args, "O!O!:gv_records_asdict",
+                          &PyGtk_Type, &py_records,
+                          &PyList_Type, &py_fields ) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+
+
+    nFields = PyList_Size(py_fields);
+    paszFieldNames = (char **) calloc( nFields, sizeof( char * ) );
+    panFields = (int *)malloc( nFields * sizeof( int ) );
+    for( i = 0; i < nFields; i++ )
+    {
+        PyArg_Parse( PyList_GET_ITEM(py_fields,i), "s", &paszFieldNames[i] );
+        panFields[i] = -1;
+        for( j = 0; j < records->nFieldCount; j++ )
+        {
+            if( EQUAL(paszFieldNames[i],records->papszFieldName[j]) )
+                panFields[i] = j;
+        }
+        if( panFields[i] == -1)
+        {
+            PyErr_SetString( PyExc_ValueError, "ASDICT variable not recognised." );
+            return NULL;
+        }
+    }
+    
+    nRecordCount = gv_records_num_records( records );
+    py_dict = PyDict_New();
+    for( i = 0; i < nFields; i++ )
+    {
+        PyDict_SetItem( py_dict, PyString_FromString(paszFieldNames[i]), PyList_New( nRecordCount ) );
+    }
+    
+    /*
+     ** Process all records (not just selected ones!). 
+     */
+    
+    for( i = 0; i < nRecordCount; i++ )
+    {
+        for( j = 0; j < nFields; j++ )
+        {
+            const char *pszRawFieldValue = 
+                gv_records_get_raw_field_data( records, i, panFields[j] );
+
+            py_list = PyDict_GetItem( py_dict, PyString_FromString(paszFieldNames[j]) );
+            
+            if( pszRawFieldValue == NULL )
+            {
+                PyList_SetItem( py_list, i, Py_None );
+            }
+            else
+            {
+                switch( records->panFieldType[panFields[j]] )
+                {
+                    case GV_RFT_INTEGER:
+                        PyList_SetItem( py_list, i, Py_BuildValue( "i", atoi(pszRawFieldValue) ));
+                        break;
+                    case GV_RFT_FLOAT:
+                        PyList_SetItem( py_list, i, Py_BuildValue( "d", atof(pszRawFieldValue) ));
+                        break;
+                    default:
+                        PyList_SetItem( py_list, i, Py_BuildValue( "s", pszRawFieldValue));
+                }
+            }
+            //PyDict_SetItem( py_dict, PyString_FromString(paszFieldNames[j]), py_list );
+        }
+    }
+    
+    free( panFields );
+    free( paszFieldNames );
+    
+    return py_dict;
+}

Added: packages/openev/branches/upstream/current/pymod/gvalg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvalg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvalg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,142 @@
+###############################################################################
+# $Id: gvalg.py,v 1.7 2004/11/19 23:59:37 gmwalter Exp $
+#
+# Project:  CIETMap/OpenEV
+# Purpose:  Assorted algorithm python entry points.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Frank Warmerdam, warmerda at home.com
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvalg.py,v $
+#  Revision 1.7  2004/11/19 23:59:37  gmwalter
+#  Check in Aude's rasterization updates.
+#
+#  Revision 1.6  2002/12/13 21:21:04  warmerda
+#  Fixed docs.
+#
+#  Revision 1.5  2002/12/13 21:13:33  warmerda
+#  Added some user documentation.
+#
+#  Revision 1.4  2001/04/22 17:34:21  pgs
+#  changed wid_interpolate to take variable exponent for d
+#
+#  Revision 1.3  2001/03/29 14:59:56  warmerda
+#  added fill_short flag to control handling of slivers
+#
+#  Revision 1.2  2000/09/15 15:13:14  warmerda
+#  flush raster
+#
+#  Revision 1.1  2000/09/15 01:44:12  warmerda
+#  New
+#
+#
+
+import _gv
+
+"""
+Python wrappers for various algorithms.
+"""
+
+
+###############################################################################
+
+def rasterize_shapes( raster, shapes, burn_value, fill_short = 1 ):
+    """Rasterize GvShape polygons into a GvRaster.
+
+    A python list of GvShapes are burned into a GvRaster with a user supplied
+    "burn value".  Only polygons are supported at this time.   The polygon
+    coordinates must be in the same coordinate system as the GvRaster.  The
+    polygons may have inner rings, or even multiple outer rings.
+
+    Example:
+     import gvalg, gview
+     
+     polygons = gview.GvShapes( shapefilename = poly_filename )
+
+     rfile = gdal.GetDriverByName('GTiff').Create(filename,xsize,ysize,1)
+     rfile.SetGeoTransform( geotransform )
+     raster = gview.GvRaster( dataset = rfile )
+
+     gvalg.rasterize_shapes( raster, polygons, 255 )
+
+
+    raster -- a GvRaster with update access.
+    shapes -- A GvShapes or list object containing the list of shapes to
+              burn into the raster.
+    burn_value -- The value to be assigned to pixels under the polygons.
+    fill_short -- 1 to fill partial scanlines, or 0 to not do so.
+
+    NEW: fill_short = 2: same algorithm as above but with less bugs
+         fill_short = 3: a pixel is considered inside the shape if and only if its
+         centre is inside the shape. 
+               
+
+    Returns 0 on failure, or a non-zero value on success.
+    """
+    
+    shapes_o = []
+    for shape in shapes:
+        shapes_o.append( shape._o )
+
+    ret = _gv.gv_raster_rasterize_shapes( raster._o, shapes_o, burn_value,
+                                          fill_short )
+    raster.get_band().FlushCache()
+    return ret
+
+###############################################################################
+
+def wid_interpolate( raster, points, exponent = 2.0, progress_cb=None, cb_data=None ):
+
+    """Weighted Inverse Distance Interpolation
+
+    This algorithm interpolates the pixel values of a GvRaster using a simple
+    inverse distance interpolator with an additional per point weighting
+    factor.  The algorithm supports GDALProcessFunc style
+    progress reporting, and user termination support.
+
+    Note that the (x,y) values for the points must be in pixel/line coordinates
+    on the raster, not georeferenced coordinates.  They may have subpixel
+    accuracy. 
+ 
+    points -- a list of (x,y,value,weight) tuples where each tuple is
+    a point to interpolate.  
+ 
+    raster -- the GvRaster representating the band to apply changes to.
+    The GvRaster must be in update mode.
+ 
+    exponent -- the exponent to apply to the distance calculate in the
+    weighting formula
+ 
+    progress_cb -- progress function, leave defaulted if no progress called
+    is needed.
+
+    cb_data -- a value to be passed into the progress_cb. 
+ 
+    Returns a CPL error number on failure, or 0 on success.  Note that
+    a user interrupt will result in CPLE_UserInterrupt being returned.
+    """
+    
+    ret = _gv.WIDInterpolate( points, raster.get_band()._o,
+                              exponent, progress_cb, cb_data )
+    raster.get_band().FlushCache()
+    return ret
+
+
+

Added: packages/openev/branches/upstream/current/pymod/gvbitlayerlut.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvbitlayerlut.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvbitlayerlut.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,73 @@
+
+class GvBitLayerLUT:
+    """Class for managing a LUT for display bitplane rasters.
+
+    This class keeps state for managing 8 colourized bitplanes, and
+    can produce an appropriate lut in string format, suitable for using
+    with GvRasterLayer.put_lut() when needed."""
+
+    def __init__(self):
+        """Initialize with eight distinct colours for bit planes."""
+        self.plane_color = [(0,0,0,0), (255,0,0,255), (0,255,0,255), 
+                            (0,0,255,255), (255,255,0,255), (255,0,255,255), 
+                            (0,255,255,255), (255,255,255,255)]
+        self.priority = [0, 1, 2, 3, 4, 5, 6, 7]
+
+    def set_color(self,plane,color):
+        """Set the color of one of the bit planes.
+
+        plane -- a bit plane number from 0 to 7.
+
+        color -- an RGBA tuple such as (255,0,0,255)"""
+        self.plane_color[plane] = color
+
+    def set_priority(self,priority,plane):
+        """Set what plane is drawn at a given priority level.
+
+        By default (after initialization) bit plane zero is
+        priority zero, through to bit plane seven being at
+        priority seven.  To move bit plane 0 to top priority, and
+        move everything else down one it would be necessary to do
+        something like this example:
+
+        bit_lut.set_priority(7,0)
+        bit_lut.set_priority(6,7)
+        bit_lut.set_priority(5,6)
+        bit_lut.set_priority(4,5)
+        bit_lut.set_priority(3,4)
+        bit_lut.set_priority(2,3)
+        bit_lut.set_priority(1,2)
+        bit_lut.set_priority(0,1)
+
+        priority -- a priority level from 0 to 7
+
+        plane -- a plane from 0 to 7"""
+
+        self.priority[priority] = plane
+
+    def get_lut_as_string(self):
+        """Fetch a lut suitable for use with GvRasterLayer.lut_put()
+        representing the current bit plane configuration."""
+        lut = []
+        for i in range(256):
+            lut.append((0,0,0,0))
+
+        for ip in range(8):
+            plane = self.priority[ip]
+            bitvalue = 2 ** plane
+            for i in range(256):
+                if (i % (bitvalue*2)) >= bitvalue:
+                    lut[i] = self.plane_color[plane]
+
+        lut_string = ''
+        for i in range(256):
+            rgba_tuple = lut[i]
+            lut_string = lut_string + \
+                (chr(rgba_tuple[0])+chr(rgba_tuple[1])+ \
+                 chr(rgba_tuple[2])+chr(rgba_tuple[3]))
+        return lut_string
+
+if __name__ == '__main__':
+    x = GvBitLayerLUT()
+
+    print x.get_lut_as_string()

Added: packages/openev/branches/upstream/current/pymod/gvclassification.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvclassification.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvclassification.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1367 @@
+###############################################################################
+# $Id: gvclassification.py,v 1.30 2004/06/29 16:06:15 dem Exp $
+#
+# Project:  CIETMap / OpenEV
+# Purpose:  GvClassification class responsible for managing classification
+#           related properties on a GvRasterLayer.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Frank Warmerdam <warmerda at home.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvclassification.py,v $
+#  Revision 1.30  2004/06/29 16:06:15  dem
+#  fixed a little bug in ranges serialization
+#  (big numbers was truncated by '%s' (str) ; now use '%r' (repr) instead)
+#
+#  Revision 1.29  2004/04/01 13:50:58  dem
+#  CVS correction
+#
+#  Revision 1.28  2004/04/01 13:24:25  dem
+#  CVS problem (sorry)
+#
+#  Revision 1.27  2004/04/01 10:30:48  dem
+#  fixed bug in compute_rep when raw_value is out of
+#  min/max range (< range[0][0] and > range[self.count - 1][1])
+#  fixed a conversion warning
+#  transmit rescale argument from update_layer to update_raster
+#
+#  Revision 1.26  2003/12/28 15:46:19  warmerda
+#  fixed up support for negatives
+#
+#  Revision 1.25  2003/08/13 14:03:08  pgs
+#  added code provided by Dan Puter for Std Dev and Quantile classification.
+#
+#  Revision 1.24  2002/11/05 15:04:43  warmerda
+#  added support for getting discrete values for rasters
+#
+#  Revision 1.23  2002/11/05 03:40:49  warmerda
+#  fixed syntax error in try statement
+#
+#  Revision 1.22  2002/10/30 18:21:16  pgs
+#  modiified computation of raw_value for vectors to skip shapes that don't have
+#  the classification property set.
+#
+#  Revision 1.21  2002/10/29 17:12:59  warmerda
+#  use min_v and max_v for local vars to avoid optimized conflicts
+#
+#  Revision 1.20  2002/10/28 15:01:28  warmerda
+#  Improved handling for discrete integer classifications (int comparisons were
+#  failing).  Also report if more than 80 discrete classes are discovered as
+#  only 80 will be proceessed.
+#
+#  Revision 1.19  2002/09/10 21:17:05  warmerda
+#  removed debugging statement
+#
+#  Revision 1.18  2002/09/10 17:33:46  warmerda
+#  default raw_value to 0.0 if get_property() fails on GvShape
+#
+#  Revision 1.17  2002/09/10 15:44:18  pgs
+#  fixed bug in remove_layer()
+#
+#  Revision 1.16  2002/09/04 14:38:51  pgs
+#  separate ranges with : to prevent problems with discrete classification on
+#  strings with spaces.
+#
+#  Revision 1.15  2002/08/27 19:13:35  pgs
+#  extended default classification limits to 80 classes.
+#
+#  Revision 1.14  2002/08/19 17:13:49  pgs
+#  bug fixes related to discrete classifications
+#
+#  Revision 1.13  2002/08/16 17:50:24  pgs
+#  outline classified areas in a black pen
+#
+#  Revision 1.12  2002/08/13 17:43:35  pgs
+#  fixed raster classification and fixed legend for point layers
+#
+#  Revision 1.11  2002/08/09 21:05:20  pgs
+#  finished vector support
+#
+#  Revision 1.10  2002/08/01 20:13:01  warmerda
+#  partial implementation of vector layer support
+#
+#  Revision 1.9  2001/05/07 14:01:33  warmerda
+#  significant fixes to rescaling logic
+#
+#  Revision 1.8  2001/04/09 19:42:43  pgs
+#  added flag to add_raster to turn off auto-init on first raster
+#
+#  Revision 1.7  2001/02/14 02:10:30  pgs
+#  *** empty log message ***
+#
+#  Revision 1.6  2000/10/09 14:01:21  pgs
+#  added a default legend title in prepare_default()
+#
+#  Revision 1.5  2000/10/02 20:55:39  pgs
+#  fixed bug in remove_class.
+#
+#  Revision 1.4  2000/09/29 04:29:47  warmerda
+#  made default classification extend slightly outside min/max
+#
+#  Revision 1.3  2000/09/27 14:35:27  warmerda
+#  added auto update of legend, and classification serial numbers
+#
+#  Revision 1.2  2000/09/27 13:38:18  warmerda
+#  (pgs) added lots of access methods, prepare_default()
+#
+#  Revision 1.1  2000/09/21 03:01:43  warmerda
+#  New
+#
+#
+
+#
+# TODO: Add Sybmols
+#
+
+import gview
+import string
+import gdal
+import gvutils
+import Numeric
+
+###############################################################################
+#
+# Classification Type Handling
+#
+# A classification type dictates how the default classification for a given set
+# of layers is created.  To support different types of classifications, you
+# add a new CLASSIFY_XXXXXX declaration using the next available number, update
+# CLASSIFY_LAST to the new maximum number, and then add an entry to the
+# classification_types dictionary that maps a name to the  number and add code
+# to GvClassification.prepare_default() to handle the new type.
+#
+###############################################################################
+
+#classification types
+CLASSIFY_BASE = 0 #used for validation
+CLASSIFY_DISCRETE = 0
+CLASSIFY_EQUAL_INTERVAL = 1
+CLASSIFY_QUANTILE = 2
+CLASSIFY_NORM_SD = 3
+CLASSIFY_LAST = CLASSIFY_NORM_SD #used for validation
+
+#classification type dictionary
+classification_types = { 'Discrete Values' : CLASSIFY_DISCRETE, 
+                         'Equal Interval' : CLASSIFY_EQUAL_INTERVAL,
+                         'Quantiles' : CLASSIFY_QUANTILE,
+                         'Normal Std Dev' : CLASSIFY_NORM_SD }
+
+class GvClassification:
+
+    """Manage layer classification scheme.
+
+    Holds information on a layer classification scheme, and
+    can be instantiated from, and serialized to, the properties of the
+    GvLayer.  Currently supports GvRasterLayer and GvShapesLayer.
+    """
+
+    ###########################################################################
+    def __init__(self, layer=None, type=CLASSIFY_EQUAL_INTERVAL):
+        self.count = 0
+        self.name = []
+        self.desc = []
+        self.range = []
+        self.color = []
+        self.title = ''
+        self.layers = []
+        self.point_symbols = []
+        self.symbol_scales = []
+        self.set_type( type )
+
+        self.legend_dialog = None
+
+        if layer is not None:
+            self.add_layer( layer )
+            
+    ###########################################################################
+    def dump( self, *args ):
+        print str(self)
+
+    ###########################################################################
+    def __str__(self):
+        s = ''
+        s = s + 'Classification Object:\n'
+        s = s + 'type: ' + str(self.type) + '\n'
+        s = s + 'Title: ' + self.title + '\n'
+        s = s + 'count: ' + str(self.count) + '\n'
+        for i in range(len(self.name)):
+            s = s + 'name: ' + self.name[i] + '\n'
+            s = s + 'desc: ' + self.desc[i] + '\n'
+            s = s + 'range: ' + str(self.range[i]) + '\n'
+            s = s + 'color: ' + str(self.color[i]) + '\n'
+            s = s + 'symbol: ' + str(self.point_symbols[i]) + '\n'
+            s = s + 'symbol scale: ' + str(self.symbol_scales[i]) + '\n'
+        return s
+
+    ###########################################################################
+    def add_raster(self, raster, init=1):
+        """Add a ranster layer to the list of layers managed by this 
+        classification
+        
+        DEPRECATED - use add_layer instead
+        """
+        print "GvClassification.add_raster() is deprecated, " + \
+              "use add_layer instead"
+        self.add_layer( raster, init )
+
+    ###########################################################################
+    def add_layer(self, layer, init = 1, property = None ):
+        """Add a layer to the list of layers managed by this
+        classification.
+
+        layer -- GvRasterLayer or GvShapesLayer to operate on.
+
+        init -- true (1) to initialize classification from this layer
+             -- false(0) to not initialize ...
+
+        property -- the property (attribute) from GvShapesLayers that should
+                    be used as the value.
+
+        Initializes classification scheme from indicated layer, remembering
+        tied layer.  If available the classification properties are read from
+        the GvLayer, otherwise a default classification scheme is prepared."""
+        if init:
+            self.deserialize( layer.get_properties() )
+
+        if property is not None:
+            self.set_classify_property( layer, property )
+
+        if self.get_classify_property( layer ) is None \
+           and issubclass(layer.__class__,gview.GvShapesLayer):
+            schema = layer.get_parent().get_schema()
+            if len(schema) > 0:
+                self.set_classify_property( layer, schema[0][0] )
+            
+        self.layers.append( layer )
+
+    ###########################################################################
+    def remove_raster(self, raster):
+        """Remove a raster from the classification
+        
+        DEPRECATED - use remove_layer instead
+        """
+        print "GvClassification.remove_raster() is deprecated, " + \
+              "use remove_layer instead"
+        self.remove_layer( raster )
+        
+    ###########################################################################
+    def remove_layer( self, layer):
+        """Remove a layer from the classification
+
+        layer -- the GvLayer to remove.
+        """
+        for n in range(len(self.layers)):
+            if layer is self.layers[n]:
+                del self.layers[n]
+                break
+
+    ###########################################################################
+    def remove_all_layers(self):
+        """remove all layers currently being managed by this classification"""
+        self.layers = []
+
+    ###########################################################################
+    def set_classify_property( self, layer, property ):
+        """Set property (attribute field) to use for classification."""
+
+        if self.title == 'Legend: ' + str(self.get_classify_property( layer )):
+            self.title = 'Legend: ' + property
+        layer.set_property( 'Classification_Property', property )
+
+    ###########################################################################
+    def get_classify_property( self, layer ):
+        """Get property (attribute field) to use for classification."""
+
+        return layer.get_property( 'Classification_Property' )
+
+    ###########################################################################
+    def set_class(self, color, range_min, range_max=None,
+                    name=None, desc=None, class_id=None, 
+                    symbol=None, symbol_scale=None ):
+          """Set class info
+
+          Create a new class, or reset the information for an existing class.
+          Note that set_class() calls do not cause
+
+          color -- the color value to use as an RGBA tuple with values between
+                   0.0 and 1.0.
+
+          range_min -- the minimum value in the pixel value range to be
+                       considered part of this class.
+
+          range_max -- the maximum value in the pixel value range to be
+                       considered part of this class.  If None, the
+                       range_min is used.
+
+          name -- the class name.  If none is provided the class name will
+                  be made up from the range.
+
+          desc -- the class description.  If None is provided, an empty
+                  string will be used.
+
+          class_id -- the class number to assign, if defaulted a new class will
+          be created after the largest existing class number.  Zero
+          based.
+          
+          symbol -- the symbol to assign to this class (for point layers)
+          
+          symbol_scale -- the scale to draw the symbol at
+
+          Returns the class number assigned."""
+          
+          
+          if class_id is None:
+              class_id = self.count
+
+          if class_id+1 > self.count:
+              self.count = class_id+1
+              self.name.append('')
+              self.desc.append('')
+              self.color.append('')
+              self.range.append('')
+              self.point_symbols.append('')
+              self.symbol_scales.append('')
+              
+          if desc is None:
+              desc = ''
+
+          if range_max is None:
+              range_max = range_min
+          
+          try:
+            range_min = string.strip( range_min )
+            range_max = string.strip( range_max )
+          except:
+            pass
+
+          if name is None:
+              #name = 'class_%d' % class_id
+              name = string.strip(str(range_min))
+              if range_max != range_min and range_max != '':
+                name = name + ' - ' + string.strip(str(range_max))
+
+          if len(color) == 3:
+              (r,g,b) = color
+              color = (r,g,b,1.0)
+              
+          if symbol_scale is None:
+            symbol_scale = 2.0
+
+          self.name[class_id] = name
+          self.desc[class_id] = desc
+          self.color[class_id] = color
+          self.range[class_id] = (range_min, range_max)
+          self.point_symbols[class_id] = symbol
+          self.symbol_scales[class_id] = symbol_scale
+
+          return class_id
+
+    ###########################################################################
+    def remove_class(self, class_id):
+          """Removes a class
+
+          class_id -- the index of the class to remove."""
+
+          if class_id >= 0 and class_id < self.count:
+              del self.name[class_id]
+              del self.desc[class_id]
+              del self.color[class_id]
+              del self.range[class_id]
+              del self.point_symbols[class_id]
+              del self.symbol_scales[class_id]
+              self.count = self.count - 1
+
+    ###########################################################################
+    def remove_all_classes(self):
+          """Removes all classification data"""
+
+          self.count = 0
+          self.name = []
+          self.desc = []
+          self.range = []
+          self.color = []
+          self.point_symbols = []
+          self.symbol_scales = []
+
+    ###########################################################################
+    def update_all_layers(self, rescale=1):
+        """Updates all GvLayers managed by this classification"""
+        self.order_classes()
+        cd = self.serialize()
+        for layer in self.layers:
+            self.update_layer(layer, cd, rescale )
+
+    ###########################################################################
+    def update_layer(self, layer, cd, rescale=1 ):
+        ###################################################################
+        # Update properties on the GvLayer with the new classification
+        # information.
+        
+        self.update_layer_properties( layer, cd )
+
+        ###################################################################
+        # Do layer specific actions.
+        
+        if issubclass(layer.__class__,gview.GvShapesLayer):
+            self.update_vector( layer )
+        elif issubclass(layer.__class__,gview.GvRasterLayer):
+            self.update_raster( layer, rescale )
+        else:
+            raise ValueError, 'Unsupported layer class in GvClassification'
+
+    ###########################################################################
+    def update_raster(self, raster, rescale=1):
+          """Updates GvRasterLayer color table.
+
+          Updates the color table on the GvRasterLayer based on the current
+          classification scheme.  If rescale is non-zero the GvRaster's
+          scaling min and max may also be reset to better handle the given
+          data range.
+
+          Note that the GvRasterLayer will rescale data to the range 0-255, and
+          the color table is applied after this rescaling.  The range values
+          in the GvClassification are in raw data pixel values, but the final
+          color table is not.  If the total data ranges of the classes exceeds
+          256 then there may be discretization in the generated color table.
+
+          The update_raster() call also causes the classification scheme to
+          be updated on the GvRasterLayer's property list.
+
+          raster -- the GvRasterLayer to update
+
+          rescale -- Set to 0 to disable possible changes to GvRaster scaling
+          min/max.
+          """
+
+          ###################################################################
+          # Compute scaling range
+
+          if self.count == 0:
+              overall_min = 0
+              overall_max = 255
+          else:
+              overall_min = self.range[0][0]
+              overall_max = self.range[0][1]
+
+          for class_id in range(self.count):
+              overall_min = min(self.range[class_id][0],overall_min)
+              overall_max = max(self.range[class_id][1],overall_max)
+
+          if overall_min < 0 or overall_max > 255 \
+             or raster.get_parent().get_band().DataType != gdal.GDT_Byte:
+              scale_min = overall_min
+              scale_max = overall_max
+          else:
+              scale_min = 0
+              scale_max = 255
+
+          if rescale == 0:
+              scale_min = raster.min_get(0)
+              scale_max = raster.max_get(0)
+          else:
+              raster.min_set(0,float(scale_min))
+              raster.max_set(0,float(scale_max))
+
+          ###################################################################
+          # Build color table
+
+          pct = ''
+          for iColor in range(256):
+              raw_value = (iColor/255.0) * (scale_max-scale_min) + scale_min
+
+              rep = self.compute_rep( raw_value )
+              color = rep[1]
+
+              pct = pct + (chr(int(255*color[0])) + chr(int(255*color[1]))
+                           + chr(int(255*color[2])) + chr(int(255*color[3])))
+
+          raster.lut_put( pct )
+
+    ###########################################################################
+    def update_vector(self, vector, class_prop = None ):
+        """
+        Updates GvShapesLayer representation information.
+
+        vector -- the GvShapesLayer to operate on.
+        class_prop -- the property based on which to classify.  If not
+        provided it will be fetched off the layer metadata.
+        """
+        if class_prop is None:
+            class_prop = self.get_classify_property( vector )
+
+
+        ###################################################################
+        # Set representations for all shapes.
+
+        shapes = vector.get_parent()
+        for shape in shapes:
+
+            try:
+                raw_value = shape.get_property( class_prop )
+            except:
+                continue
+            
+            try:
+                raw_value = float(raw_value)
+            except:
+                pass
+                
+            rep = self.compute_rep( raw_value )
+
+            color = rep[1]
+            ogrfs_color = '#%02x%02x%02x%02x' % (int(color[0] * 255.999),
+                                                 int(color[1] * 255.999),
+                                                 int(color[2] * 255.999),
+                                                 int(color[3] * 255.999))
+
+            stype = shape.get_type()
+            if stype == gview.GVSHAPE_POINT:
+                ogrfs = 'SYMBOL(id:%s,c:%s,s:%s)' % (rep[0], ogrfs_color, 
+                                              rep[2])
+            elif stype == gview.GVSHAPE_LINE:
+                ogrfs = 'PEN(c:%s)' % ogrfs_color
+            elif stype == gview.GVSHAPE_AREA:
+                ogrfs = 'BRUSH(fc:%s);PEN(c:#010101ff)' % ogrfs_color
+            shape.set_property( '_gv_ogrfs', ogrfs )
+            
+        shapes.changed()
+
+    ###########################################################################
+    def compute_rep( self, raw_value ):
+
+        """
+        Compute representation corresponding to a input value.
+
+        raw_value -- the value to run through the classification.
+
+        Returns a list of property information with the following values:
+        
+        [0] - symbol name
+        [1] - symbol color
+        [2] - symbol scale
+
+        """
+        rep = []
+        symbol = 'ogr-sym-0'
+        color = (0.0, 0.0, 0.0, 1.0)
+        scale = 2.0
+
+        # Try to produce a string corresponding to the raw value.
+        # Ensure that integer values are represented without any decimals
+        raw_string = string.strip(str(raw_value))
+        try:
+            num_value = float(raw_string)
+            if num_value == int(num_value):
+                raw_string = str(int(float(raw_string)))
+        except:
+            pass
+
+        if self.get_type() == CLASSIFY_DISCRETE:
+            for i in range(self.count):
+                if raw_string == string.strip(str(self.range[i][0])):
+                    symbol = self.point_symbols[i]
+                    color = self.color[i]
+                    scale = self.symbol_scales[i]
+                    break
+        else:
+            if raw_value < self.range[0][0]:
+                symbol = self.point_symbols[0]
+                color = self.color[0]
+                scale = self.symbol_scales[0]
+            elif raw_value > self.range[self.count - 1][1]:
+                symbol = self.point_symbols[self.count - 1]
+                color = self.color[self.count - 1]
+                scale = self.symbol_scales[self.count - 1]
+            else:
+                for i in range(self.count):
+                    #if a value lies directly in the range, then use the value directly
+                    if raw_value >= self.range[i][0] \
+                       and raw_value <= self.range[i][1] \
+                       or raw_value == self.range[i][0]:
+                        symbol = self.point_symbols[i]
+                        color = self.color[i]
+                        scale = self.symbol_scales[i]
+                        break
+
+                    #this scales colors and scales for values that are between classes,
+                    #but assigns the symbol name from the closest class (favouring the
+                    #lower class
+                    if i < self.count-1 \
+                       and raw_value > self.range[i][1] \
+                       and raw_value < self.range[i+1][0]:
+                        try:
+                            ratio = (raw_value-self.range[i][1]) \
+                                    / (self.range[i+1][0] - self.range[i][1])
+                        except:
+                            ratio = 0.5
+                        c1 = self.color[i]
+                        c2 = self.color[i+1]
+                        s1 = self.symbol_scales[i]
+                        s2 = self.symbol_scales[i+1]
+                        if ratio > 0.5:
+                            symbol = self.point_symbols[i+1]
+                        else:
+                            symbol = self.point_symbols[i]
+
+                        color = (c1[0] * (1.0 - ratio) + c2[0] * ratio,
+                                 c1[1] * (1.0 - ratio) + c2[1] * ratio,
+                                 c1[2] * (1.0 - ratio) + c2[2] * ratio,
+                                 c1[3] * (1.0 - ratio) + c2[3] * ratio)
+
+                        scale = s1 * (1.0 - ratio) + s2 * ratio
+
+                        break
+
+        rep.append( symbol )
+        rep.append( color )
+        rep.append( scale )
+
+        return rep
+        
+    ###########################################################################
+    # Update properties on the GvShapesLayer with the new classification
+    # information.
+    def update_layer_properties( self, layer, cd ):
+
+        od = layer.get_properties()
+        nd = {}
+
+        for key in od.keys():
+          if key[:6] != 'Class_':
+              nd[key] = od[key]
+
+        for key in cd.keys():
+          nd[key] = cd[key]
+
+        # manage a serial number for the class metadata version so the
+        # legend can more easily determine if it needs to react.
+        if od.has_key('Class_sn'):
+          nd['Class_sn'] = str(int(od['Class_sn'])+1)
+        else:
+          nd['Class_sn'] = '1'
+
+        layer.set_properties( nd )
+        layer.changed()
+
+    ###########################################################################
+    def swap_classes(self, class_id_1, class_id_2):
+          temp = self.name[class_id_1]
+          self.name[class_id_1] = self.name[class_id_2]
+          self.name[class_id_2] = temp
+
+          temp = self.desc[class_id_1]
+          self.desc[class_id_1] = self.desc[class_id_2]
+          self.desc[class_id_2] = temp
+
+          temp = self.color[class_id_1]
+          self.color[class_id_1] = self.color[class_id_2]
+          self.color[class_id_2] = temp
+
+          temp = self.range[class_id_1]
+          self.range[class_id_1] = self.range[class_id_2]
+          self.range[class_id_2] = temp
+
+    ###########################################################################
+    def order_classes(self):
+          """Reorder classes in order.
+
+          Reorder classes in ascending over of range_min.  Modify range_max
+          values if necessary to avoid overlapping classes."""
+
+          for i in range(self.count):
+              for j in range(self.count-i-1):
+                  if self.range[j][0] > self.range[j+1][0]:
+                      self.swap_classes(j,j+1)
+
+          for i in range(self.count-1):
+              if self.range[i][1] > self.range[i+1][0]:
+                  self.range[i] = (self.range[i][0], self.range[i+1][0])
+
+
+    ###########################################################################
+    def serialize(self):
+          cdict = {}
+          for class_id in range(self.count):
+
+              key = 'Class_%d_Name' % class_id
+              cdict[key] = self.name[class_id]
+
+              key = 'Class_%d_Desc' % class_id
+              cdict[key] = self.desc[class_id]
+
+              key = 'Class_%d_Color' % class_id
+              (r, g, b, a) = self.color[class_id]
+              r = int(max(0,min(255,r*255.0)))
+              g = int(max(0,min(255,g*255.0)))
+              b = int(max(0,min(255,b*255.0)))
+              a = int(max(0,min(255,a*255.0)))
+              cdict[key] = '#%02x%02x%02x%02x' % (r, g, b, a)
+
+              key = 'Class_%d_Range' % class_id
+              cdict[key] = '%r:%r' % self.range[class_id]
+              
+              if self.point_symbols[class_id] is not None:
+                  key = 'Class_%d_Symbol' % class_id
+                  cdict[key] = self.point_symbols[class_id]
+
+                  key = 'Class_%d_Scale' % class_id
+                  cdict[key] = str(self.symbol_scales[class_id])
+
+          cdict['Classification_Title'] = self.title
+          cdict['Classification_Type'] = str(self.get_type())
+
+          return cdict
+
+    ###########################################################################
+    def deserialize(self, dict):
+    
+        self.remove_all_classes()
+        
+        self.title = dict.get('Classification_Title', '')
+        type = int(dict.get('Classification_Type', '1'))
+        self.set_type( type )
+
+        class_id = 0
+        while dict.has_key('Class_%d_Color' % class_id):
+
+            key = 'Class_%d_Name' % class_id
+            name = dict.get(key, '')
+
+            key = 'Class_%d_Desc' % class_id
+            desc = dict.get(key, '')
+
+            key = 'Class_%d_Color' % class_id
+            color_spec = dict.get(key, '#000000FF')
+
+            try:
+                color = ( string.atoi(color_spec[1:3],16) / 255.0,
+                        string.atoi(color_spec[3:5],16) / 255.0,
+                        string.atoi(color_spec[5:7],16) / 255.0,
+                        string.atoi(color_spec[7:9],16) / 255.0 )
+            except:
+                color = ( 0.0, 0.0, 0.0, 1.0 )
+
+            key = 'Class_%d_Range' % class_id
+            range_spec = dict.get(key, '0 0')
+            try:
+                (range_min, range_max) = range_spec.split(':')
+                range_min = string.strip(range_min)
+                range_max = string.strip(range_max)
+            except:
+                range_min = range_spec
+                range_min = string.strip(range_min)
+                range_max = None
+            #allow ranges to be discrete string values
+            try:
+                range_min = float(range_min)
+                range_max = float(range_max)
+            except:
+                pass
+            
+            key = 'Class_%d_Symbol' % class_id
+            try:
+                symbol = dict[key]
+            except:
+                symbol = None
+
+            key = 'Class_%d_Scale' % class_id
+            try:
+                scale = float(dict[key])
+            except:
+                scale = None
+            
+            self.set_class( color, range_min, range_max, name, desc,
+                          class_id, symbol, scale )
+
+            class_id = class_id + 1
+
+        
+    ###########################################################################
+    def set_title(self, title):
+          """Set new title (for Legend)
+
+      title -- the new title for this classification"""
+
+          self.title = title
+
+    ###########################################################################
+    def get_title(self):
+        return self.title
+
+    ###########################################################################
+    def get_name(self, idx):
+        """return the name at the given index or None if the index is invalid"""
+        return self.get_value(self.name, idx)
+
+    ###########################################################################
+    def set_name(self, idx, name):
+        """set the name for the given index"""
+        self.set_value(self.name, idx, name)
+
+    ###########################################################################
+    def get_color(self, idx):
+        """return the color at the given index or None if the index is invalid"""
+        return self.get_value(self.color, idx)
+
+    ###########################################################################
+    def set_color(self, idx, color):
+        """set the color at the given index"""
+        if len(color) == 3:
+            (r,g,b) = color
+            color = (r,g,b,1.0)
+
+        self.set_value(self.color, idx, color)
+
+    ###########################################################################
+    def get_desc(self, idx):
+        """return the description at the given index or None if the index is invalid"""
+        return self.get_value(self.desc, idx)
+
+    ###########################################################################
+    def get_range(self, idx):
+        """return the range at the given index or None if the index is invalid"""
+        return self.get_value(self.range, idx)
+
+    ###########################################################################
+    def set_range(self, idx, range_min, range_max):
+        self.set_value(self.range, idx, (range_min, range_max))
+
+    ###########################################################################
+    def get_value(self, lst, idx):
+        """return the item at the given idx from the given list or None if
+        something is wrong"""
+        result = None
+        try:
+            result = lst[idx]
+        except:
+            pass
+        return result
+
+    ###########################################################################
+    def set_value(self, lst, idx, val):
+        """set the item at the given idx in the given list"""
+        try:
+            lst[idx] = val
+        except:
+            pass
+
+    ###########################################################################
+    def get_symbol(self, idx):
+        """return the range at the given index or None if the index is invalid
+        """
+        return self.get_value(self.point_symbols, idx)
+
+    ###########################################################################
+    def set_symbol(self, idx, symbol):
+        if symbol[0] == '"':
+            symbol = symbol[1:]
+        if symbol[-1] == '"':
+            symbol = symbol[:-1]
+        self.set_value(self.point_symbols, idx, symbol)
+
+    ###########################################################################
+    def get_scale(self, idx):
+        """return the range at the given index or None if the index is invalid
+        """
+        return self.get_value(self.symbol_scales, idx)
+
+    ###########################################################################
+    def set_scale(self, idx, scale):
+        self.set_value(self.symbol_scales, idx, scale)
+        
+    ###########################################################################
+    def set_type(self, type):
+        """Set the classification type - affects prepare_default only
+        """
+        if type >= CLASSIFY_BASE and type <= CLASSIFY_LAST:
+            self.type = type
+        else:
+            print 'invalid classification type: ', type
+
+    ###########################################################################
+    def get_type(self):
+        """return the classification type
+        """
+        return self.type
+        
+    ###########################################################################
+    def collect_range(self, layer, property = None ):
+        if issubclass(layer.__class__,gview.GvShapesLayer):
+            shapes = layer.get_parent()
+            min_v = None
+            max_v = None
+            
+            for shape in shapes:
+                try:
+                    value = float(shape.get_property( property ))
+                    if min_v is None:
+                        min_v = value
+                        max_v = value
+                    else:
+                        if value < min_v:
+                            min_v = value
+                        if value > max_v:
+                            max_v = value
+                except:
+                    pass
+
+            return (min_v,max_v)
+            
+        elif issubclass(layer.__class__,gview.GvRasterLayer):
+            #is there a better way to do this, or is it even necessary?
+            if layer.get_mode() == gview.RLM_RGBA:
+                return (min(layer.min_get(0),
+                            layer.min_get(1),
+                            layer.min_get(2)),
+                        max(layer.max_get(0),
+                            layer.max_get(1),
+                            layer.max_get(2)))
+            else:
+                return (layer.min_get(0), layer.max_get(0))
+
+        else:
+            raise ValueError, 'unsupported layer type in collect_range'
+        
+    ###########################################################################
+    def collect_unique(self, layer, property = None ):
+        """
+        Collect list of unique values occuring in a GvLayer.
+
+        The returned list is a dictionary with the keys being the unique
+        values found, and the values associated with the keys being the
+        occurance count for each value.
+
+        layer -- the GvLayer (GvRasterLayer or GvShapesLayer) to be queried
+        property -- for GvShapesLayer this is the property name to be scanned.
+        """
+        
+        if issubclass(layer.__class__,gview.GvShapesLayer):
+            shapes = layer.get_parent()
+
+            val_count = {}
+            for shape in shapes:
+                try:
+                    #value = float(shape.get_property( property ))
+                    value = shape.get_property( property )
+                    if value is not None:
+                        if val_count.has_key( value ):
+                            val_count[value] = val_count[value] + 1
+                        else:
+                            val_count[value] = 1
+                except:
+                    pass
+
+            return val_count
+            
+        elif issubclass(layer.__class__,gview.GvRasterLayer):
+
+            raster = layer.get_data()
+            datatype = raster.get_band().DataType
+
+            count = 65536
+            h_min = raster.get_min()
+            h_max = raster.get_max()
+            delta = h_max - h_min
+            if delta < 0.1:
+                delta = 0.1
+            h_min = h_min - delta*0.25
+            h_max = h_max + delta*0.25
+
+            is_int = 0
+            
+            if datatype == gdal.GDT_Byte:
+                h_min = 0
+                h_max = 256
+                count = 256
+                is_int = 1
+            elif datatype == gdal.GDT_Int16:
+                h_min = -32768
+                h_max = 32767
+                count = 65536
+                is_int = 1
+            elif datatype == gdal.GDT_UInt16:
+                h_min = 0
+                h_max = 65536
+                count = 65536
+                is_int = 1
+
+            histogram = \
+                      raster.get_band().GetHistogram( h_min, h_max,
+                                                      buckets = count,
+                                                      include_out_of_range = 0,
+                                                      approx_ok = 0 )
+            delta = (h_max - h_min)/count
+            val_count = {}
+            for i in range(count):
+                if histogram[i] > 0:
+                    if is_int:
+                        value = h_min + i
+                    else:
+                        value = h_min + delta * i
+                    
+                    val_count[value] = histogram[i]
+
+            return val_count
+
+        else:
+            raise ValueError, 'unsupported layer type in collect_unique'
+
+    ###########################################################################
+    def collect_values(self, layer, property = None ):
+        """
+        Collect the data values from a property field occuring in a GvLayer.
+
+        The returned list contains all values of the property field for shapes
+        that have a non-missing value for that field.
+
+        layer -- the GvLayer (GvRasterLayer or GvShapesLayer) to be queried
+        property -- for GvShapesLayer this is the property name to be scanned.
+        """
+        # Obtain the data values for a shapefile vector layer
+        if issubclass(layer.__class__,gview.GvShapesLayer):
+            shapes = layer.get_parent()
+
+            shpvals = []
+            for shape in shapes:
+                try:
+                    value = float(shape.get_property( property ))
+                    if value is not None:
+                        shpvals.append(value)
+                except:
+                    pass
+
+            return shpvals
+
+        # Obtain the data values for a raster layer
+        elif issubclass(layer.__class__,gview.GvRasterLayer):
+
+            raster = layer.get_data()
+            datatype = raster.get_band().DataType
+
+            count = 65536
+            h_min = raster.get_min()
+            h_max = raster.get_max()
+            delta = h_max - h_min
+            if delta < 0.1:
+                delta = 0.1
+            h_min = h_min - delta*0.25
+            h_max = h_max + delta*0.25
+
+            is_int = 0
+            
+            if datatype == gdal.GDT_Byte:
+                h_min = 0
+                h_max = 256
+                count = 256
+                is_int = 1
+            elif datatype == gdal.GDT_Int16:
+                h_min = -32768
+                h_max = 32767
+                count = 65536
+                is_int = 1
+            elif datatype == gdal.GDT_UInt16:
+                h_min = 0
+                h_max = 65536
+                count = 65536
+                is_int = 1
+
+            histogram = \
+                      raster.get_band().GetHistogram( h_min, h_max,
+                                                      buckets = count,
+                                                      include_out_of_range = 0,
+                                                      approx_ok = 0 )
+            delta = (h_max - h_min)/count
+            rastvals = []
+            for i in range(count):
+                if histogram[i] > 0:
+                    if is_int:
+                        for j in range(histogram[i]):
+                            rastvals.append((h_min + i))
+                    else:
+                        for j in range(histogram[i]):
+                            rastvals.append((h_min + delta * i))
+            return rastvals
+
+        else:
+            raise ValueError, 'unsupported layer type in collect_unique'
+
+
+    ###########################################################################
+    def quantile(self,values,cats):
+        """
+        Determine the minimum and maximum values of the break points in a 
+        quantile (equal proportions) classification.
+
+        The returned list contains two sublists:
+        [0] a list of the minimum values for each category
+        [1] a list of the maximum values for each category.
+
+        layer -- the GvLayer (GvRasterLayer or GvShapesLayer) to be queried
+        property -- for GvShapesLayer this is the property name to be scanned.
+        """
+
+        segl = len(values)/cats # Min number of obs in a category
+        lftout = len(values) - (segl*cats) # The "residual" obs needed to be assigned
+        catobs = cats*[segl] # initialize the number of observations per category var
+        # If there are unassigned obs, assign the needed number of "middle" categories
+        # one observation each until the number of assigned obs is equal to actual obs
+        if lftout > 0:
+            midcat = len(catobs)/2 # the "middle" category
+            catobs[midcat] = segl + 1 # up this category by one observation
+            # The following loop assigns the remaining obs around the middle category
+            for i in range(1,cats):
+                if Numeric.sum(catobs) < len(values):
+                    catobs[midcat-i] = segl + 1
+                else:
+                    break
+                if Numeric.sum(catobs) < len(values):
+                    catobs[midcat+i] = segl + 1
+                else:
+                    break
+        # Use the catobs variable as an index to figure out the location of the break
+        # points for the categories
+        valsort=Numeric.sort(Numeric.array(values)) # Sort the original data
+        maxind = [0]*cats  # the maximum value of each break point
+        maxind = list(Numeric.cumsum(catobs) - 1)
+        # Based on the index, get the maximum value for each category
+        maxval = range(cats) # intialize the maxval variable
+        # The following for-loop assigns the top values using maxind
+        for i in range(cats):
+            maxval[i]=valsort[maxind[i]]
+        minval = range(cats) # initialize the bottom break points
+        minval[0] = min(values)
+        # The following for-loop assigns the other minimum value break points
+        for i in range(1,cats):
+            minval[i] = maxval[i-1]
+
+        outquant = [minval,maxval]
+        return outquant
+    ###########################################################################
+    def nstddev(self,values):
+        """
+        Determine the minimum and maximum values of the break points in a 
+        classifier based on standard deviations from the mean.  This implicitly
+        assumes a symmetric distribution
+
+        The returned list contains two sublists:
+        [0] a list of the minimum values for each category
+        [1] a list of the maximum values for each category
+        [2] the total number of categories as an integer.
+        [3] the labels to use in the legend
+
+        layer -- the GvLayer (GvRasterLayer or GvShapesLayer) to be queried
+        property -- for GvShapesLayer this is the property name to be scanned.
+        """
+
+        mnval = Numeric.sum(values)/len(values) # The mean
+        sdval = Numeric.sqrt(Numeric.sum(pow((Numeric.array(values)-mnval),2))/(len(values)-1))
+        # The SD
+        possds = int((max(values)-mnval)/sdval) # SDs above the mean
+        if (max(values)-mnval)/sdval > possds:
+            possds = possds + 1
+        negsds = int((mnval-min(values))/sdval) # SDs below the mean
+        if (mnval-min(values))/sdval > negsds:
+            negsds = negsds + 1
+        
+        # Handling the maximum category values below the mean
+        maxblw = range(negsds)
+        blwlbl = range(negsds)
+        for i in range(negsds):
+            maxblw[((negsds-1)-i)] = mnval - (i*sdval)
+            blwlbl[((negsds-1)-i)] = "%i to %i standard deviations" % (-1*i, -1*(i+1))
+        # maxblw[(negsds-1)] = mnval - 0.0001
+        
+        # Handling the maximum category values above the mean
+        maxabv = range(possds)
+        abvlbl = range(possds)
+        # maxabv[(possds-1)] = max(values)
+        # for i in range((possds-1)):
+        for i in range(possds):
+            maxabv[i] = mnval + ((i+1)*sdval)
+            abvlbl[i] = "%i to %i standard deviations" % (i, i+1)
+        # maxabv[0] = mnval + 0.0001
+        
+        maxval = maxblw + [mnval] + maxabv # concatinate the below and above max values
+        meanlb = "Mean: %f" % mnval
+        labels = blwlbl + [meanlb] + abvlbl
+        minval = range((possds+negsds+1)) # initialize the category min values
+        minval[0] = min(values) - 0.0001
+        # The following for-loop assigns the other minimum value break points
+        for i in range(1,(possds+negsds+1)):
+            minval[i] = maxval[i-1]
+
+        # outnsd = [minval,maxval,2*numsds]
+        outnsd = [minval,maxval,(possds+negsds+1),labels]
+        return outnsd
+    ###########################################################################
+    def prepare_default(self, count=5):
+        """Prepare a default classification scheme.
+        
+        count -- the number of classes to create by default (the actual number
+                 may differ if creating a discrete classification)
+        
+        If the layer is a GvShapesLayer and the classify property is not 
+        numeric then the type will be changed to a discrete classication with
+        a maximum of 32 discrete values
+        """
+        
+        if len(self.layers) == 0:
+            return
+
+        overall_min = None
+        overall_max = None
+        bUnique = 0
+        unique_vals = []
+        symbol = None
+        name = None
+        
+        for layer in self.layers:
+            property = self.get_classify_property( layer )
+            
+            if property is not None and \
+                issubclass( layer.__class__, gview.GvShapesLayer):
+                #determine the type of the 
+                props = layer.get_parent().get_properties()
+                k = props.keys()[ props.values().index( property ) ]
+                try:
+                    property_type = props[ "_field_type_%s" % k[12:] ]
+                except:
+                    property_type = None
+                if layer.get_parent()[0].get_type() == gview.GVSHAPE_POINT:
+                    symbol = 'ogr-sym-0'
+                if property_type == "string":
+                    self.set_type( CLASSIFY_DISCRETE )
+            else:
+                property_type = None
+                
+            if self.get_type() == CLASSIFY_DISCRETE:
+                vals = self.collect_unique( layer, property )
+                keys = vals.keys()
+                keys.sort()
+                unique_vals.extend( keys )
+                count = min( len(unique_vals), 80 )
+                if len(unique_vals) > 80:
+                    gvutils.warning( '%d discrete values identified, but only the first 80 are being used' % len(unique_vals) )
+            else:
+                this_min, this_max = self.collect_range( layer, property )
+                if overall_min is None:
+                    overall_min = this_min
+                    overall_max = this_max
+                elif this_min is not None:
+                    if this_min < overall_min:
+                        overall_min = this_min
+                    if this_max > overall_max:
+                        overall_max = this_max
+
+        if count == 0:
+            print 'no values to classify on'
+            return
+
+        if overall_min is None and not self.get_type() == CLASSIFY_DISCRETE:
+            print 'overall_min still None in prepare_default()!'
+            return
+        elif self.get_type() == CLASSIFY_DISCRETE:
+            overall_min = 0
+            overall_max = count
+            
+        epsilon = (overall_max-overall_min) * 0.002
+        overall_min = overall_min - epsilon
+        overall_max = overall_max + epsilon
+        
+        # Below are calls to the classifiers that require the entire property field
+
+        if self.get_type() == CLASSIFY_QUANTILE:
+            svalues = self.collect_values( layer, property )
+            qminmax = self.quantile(svalues,count)
+
+        elif self.get_type() == CLASSIFY_NORM_SD:
+            svalues = self.collect_values( layer, property )
+            nsdminmax = self.nstddev(svalues)
+            count = nsdminmax[2]
+            
+        input_incr = (overall_max - overall_min) / count
+        color_incr = float(1.0 / (count + 1))
+
+        for n in range(count):
+            if self.get_type() == CLASSIFY_EQUAL_INTERVAL:
+                range_min = round(overall_min + (input_incr * n), 4)
+                range_max = round(overall_min + (input_incr * (n + 1)), 4)
+                name = None
+            elif self.get_type() == CLASSIFY_QUANTILE:
+                range_min = round(qminmax[0][n], 4)
+                range_max = round(qminmax[1][n], 4)
+                name = None
+            elif self.get_type() == CLASSIFY_NORM_SD:
+                range_min = round(nsdminmax[0][n], 4)
+                range_max = round(nsdminmax[1][n], 4)
+                name = nsdminmax[3][n]
+            elif self.get_type() == CLASSIFY_DISCRETE:
+                try:
+                    range_min = string.strip(unique_vals[n])
+                    range_max = ''
+                except:
+                    range_min = unique_vals[n]
+                    range_max = unique_vals[n]
+                name = str(unique_vals[n])
+            c = float(color_incr * (n + 1))
+            self.set_class((c,c,c,1.0), range_min, range_max, name=name, symbol = symbol)          
+
+        if property is not None:
+            self.set_title( 'Legend: ' + property )
+        else:
+            self.set_title('Legend')
+
+if __name__ == '__main__':
+
+    cs = GvClassification()
+
+    print cs.count
+
+    cs.set_class( (1.0, 0.0, 0.0, 1.0), 0 )
+    cs.set_class( (0.0, 1.0, 0.0, 1.0), 2 )
+    cs.set_class( (0.0, 0.0, 1.0, 1.0), 1, 3 )
+
+    cs.order_classes()
+
+    print cs.count
+
+    d = cs.serialize()
+
+    cs2 = GvClassification()
+    cs2.deserialize( d )
+
+    d = cs2.serialize()
+
+    for key in d.keys():
+        print key + " = " + d[key]
+
+
+    print '-------'
+
+    cs.remove_class(1)
+    print cs.count
+    cs.remove_class(1)
+    print cs.count
+    d = cs.serialize()
+    for key in d.keys():
+        print key + ' = ' + d[key]
+
+
+    #cs2.update_raster()

Added: packages/openev/branches/upstream/current/pymod/gvclassifydlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvclassifydlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvclassifydlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,962 @@
+###############################################################################
+# $Id: gvclassifydlg.py,v 1.31 2004/08/27 17:11:06 pgs Exp $
+#
+# Project:  OpenEV
+# Purpose:  Raster classification dialogs
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvclassifydlg.py,v $
+#  Revision 1.31  2004/08/27 17:11:06  pgs
+#  updated logic for building the reclassify dialogs list of entries so it would be ordered correctly
+#
+#  Revision 1.30  2004/08/27 16:46:41  pgs
+#  change repr to str to avoid extra decimal places in values
+#
+#  Revision 1.29  2004/07/23 16:57:23  pgs
+#  restructure notifications when OK/Apply clicked
+#
+#  Revision 1.28  2004/07/02 16:40:41  dem
+#  - Implement project files portability
+#  - last_strech restored in projects reloading
+#  - add a "File/Save Project as..." menu
+#
+#  Revision 1.27  2003/12/28 15:46:19  warmerda
+#  fixed up support for negatives
+#
+#  Revision 1.26  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.25  2002/11/05 14:17:04  warmerda
+#  fixed set_class call in add_class_cb
+#
+#  Revision 1.24  2002/10/30 17:35:14  pgs
+#  added overwrite protection message when saving classifications.
+#
+#  Revision 1.23  2002/09/04 14:40:29  pgs
+#  space out range names
+#
+#  Revision 1.22  2002/08/27 19:13:34  pgs
+#  extended default classification limits to 80 classes.
+#
+#  Revision 1.21  2002/08/19 17:13:49  pgs
+#  bug fixes related to discrete classifications
+#
+#  Revision 1.20  2002/08/13 17:43:32  pgs
+#  fixed raster classification and fixed legend for point layers
+#
+#  Revision 1.19  2002/08/09 21:05:20  pgs
+#  finished vector support
+#
+#  Revision 1.18  2002/08/01 20:12:44  warmerda
+#  added vector layers, and the property selector - partly working
+#
+#  Revision 1.17  2002/08/01 14:51:19  pgs
+#  fixed default button, added support for file dialog filters and fixed bug in
+#  adding new class
+#
+#  Revision 1.16  2001/09/21 20:23:06  pgs
+#  modified for new colorbutton.
+#
+#  Revision 1.15  2001/07/01 03:01:52  pgs
+#  fixed issue with ramp menu
+#
+#  Revision 1.14  2001/06/11 23:17:19  pgs
+#  modified to use pguEntry
+#
+#  Revision 1.13  2001/05/25 16:26:27  pgs
+#  close reclass dialog if required when closing classification dlg
+#
+#  Revision 1.12  2001/05/24 14:02:58  pgs
+#  removed ramps from reclass dialog
+#
+#  Revision 1.11  2001/05/07 14:01:51  warmerda
+#  allow rescaling
+#
+#  Revision 1.10  2001/03/30 01:37:30  pgs
+#  added ramp config file option
+#
+#  Revision 1.9  2001/03/23 00:12:12  pgs
+#  added exception handling to ramp code
+#
+#  Revision 1.8  2001/03/21 05:26:06  warmerda
+#  fix reset_cb() to avoid exception
+#
+#  Revision 1.7  2001/02/14 02:11:25  pgs
+#  modified for multiple raster layer support
+#
+#  Revision 1.6  2000/10/17 18:59:36  pgs
+#  modified load/save to use .leg extensions.
+#
+#  Revision 1.5  2000/10/09 14:04:13  pgs
+#  added support for loading and saving classification schemes.
+#
+#  Revision 1.4  2000/10/03 00:28:42  pgs
+#  fixed bug in delete_class callback.
+#
+#  Revision 1.3  2000/10/02 20:56:58  pgs
+#  enabled removal of classes, moved some stuff around in the UI.
+#
+#  Revision 1.2  2000/09/28 22:43:19  pgs
+#  modified to work with the new ColorRamp
+#
+#  Revision 1.1  2000/09/27 14:34:41  warmerda
+#  New
+#
+#
+
+import gview
+import gvutils
+import gtk
+import GDK
+from gvsignaler import Signaler
+from pgucolor import ColorSwatch, ColorButton, ColorDialog, ColorRamp
+from gvclassification import *
+from pguentry import pguEntry
+import gvogrfsgui
+from pgumenu import *
+import gdal #for CPLDebug
+
+from string import *
+
+"""gvclassifydlg.py module contains two classes related to raster classification.
+
+GvClassificationDlg is the main gui for modifying raster classifications.
+GvReclassifyDlg is a supplementary dialog for changing the number of classes in
+a classification scheme.
+
+This module also contains a number of supplementary utilties for working with ramps.
+
+
+TODO:
+
+ x find out how to modify the background color of a widget and use it in the column
+   headers
+
+ x determine when range values are valid/invalid.  When invalid, set bg color of cell
+   to red or something.
+
+ x provide more buttons for doin' stuff.
+"""
+
+MIN_COLOR=0
+LOW_COLOR = 21845
+HI_COLOR = 65535
+MAX_COLOR = 65535
+
+def set_widget_background(widget, rgba_color):
+    style = widget.get_style().copy()
+    for i in range(1):
+        style.base[i] = gdk_from_rgba(widget, rgba_color)
+        style.bg[i] = style.base[i]
+    widget.set_style(style)
+
+def gdkcolor(widget, red=0, green=0, blue=0):
+    return widget.get_colormap().alloc(int(red), int(green), int(blue))
+    #return gtk.GdkColor(red, green, blue, 1)
+
+def gdk_from_rgba(widget, rgba_color):
+    r = rgba_color[0] * MAX_COLOR
+    g = rgba_color[1] * MAX_COLOR
+    b = rgba_color[2] * MAX_COLOR
+    return gdkcolor(widget, r, g, b)
+
+def rgba_from_gdk(gdk_color):
+    r = float(gdk_color.red) / MAX_COLOR
+    g = float(gdk_color.green) / MAX_COLOR
+    b = float(gdk_color.blue) / MAX_COLOR
+    return (r, g, b, 1.0)
+
+def rgba_tuple(red=0.0, green=0.0, blue=0.0, alpha=1.0):
+    return (red, green, blue, alpha)
+
+def load_ramps():
+    """reads in all the ramp files in the ramps directory and creates ramps for them"""
+    import os
+    import os.path
+    ramps = []
+    ramp_dir = gview.get_preference('ramp_directory')
+    if ramp_dir is None:
+        ramp_dir = os.path.join(gview.home_dir,'ramps')
+    if os.path.isdir(ramp_dir):
+        files = os.listdir(ramp_dir)
+        for file in files:
+            ramp_file = os.path.join(ramp_dir, file)
+            if os.path.isfile(ramp_file):
+                ramp = ColorRamp()
+                try:
+                    ramp.deserialize(ramp_file)
+                    ramps.append(ramp)
+                except:
+                    print 'invalid ramp file %s' % ramp_file
+    else:
+        print 'no default ramp files in ', ramp_dir
+    return ramps
+
+def load_ramp_config_file():
+    """
+    Reads in ramp files specified in the ramp config file
+    in the ramps directory and creates ramps for them
+
+    This allows for ordering of the ramps in the config
+    file and for specifying separators
+    """
+    import os
+    import os.path
+    import string
+    ramps = []
+    ramp_dir = gview.get_preference('ramp_directory')
+    if ramp_dir is None:
+        ramp_dir = os.path.join(gview.home_dir,'ramps')
+    if os.path.isdir(ramp_dir):
+        config_path = os.path.join(ramp_dir, 'ramps.cfg')
+        if os.path.isfile(config_path):
+            #load config file and parse ramps ...
+            config = open(config_path)
+            lines = config.readlines()
+            for line in lines:
+                ramp_file = string.strip(line)
+                if ramp_file == '<separator>':
+                    ramps.append(gtk.GtkHSeparator())
+                else:
+                    ramp_file = os.path.join(ramp_dir, ramp_file)
+                    if os.path.isfile(ramp_file):
+                        ramp = ColorRamp()
+                        try:
+                            ramp.deserialize(ramp_file)
+                            ramps.append(ramp)
+                            ramp.show()
+                        except:
+                            print 'invalid ramp file %s' % ramp_file
+                    else:
+                        print 'not a file (%s)' % ramp_file
+        else:
+            print 'no ramps.cfg file, loading ramps directly'
+            return load_ramps()
+    else:
+        print 'no default ramp files in ', ramp_dir
+    return ramps
+
+class GvClassificationDlg(gtk.GtkWindow, Signaler):
+    """A dialog for modifying the classification scheme of a GvLayer."""
+
+    def __init__(self, classification):
+        """Initialize a GvClassificationDlg on a particular GvLayer"""
+        gtk.GtkWindow.__init__(self)
+        self.set_title('Layer Classification')
+        self.set_usize(-1, 400)
+        self.connect('delete-event', self.close)
+        self.set_border_width(5)
+        self.color_buttons = []
+        self.sym_menus = []
+        self.scale_spinners = []
+        self.view_mgr = None
+        self.ranges = []
+        self.labels = []
+        self.reclassdlg = None
+        self.updating = FALSE
+        items = load_ramp_config_file()
+        self.ramp = None
+        if classification is None:
+            self.classification = GvClassification()
+        elif issubclass(classification.__class__, GvClassification):
+            self.classification = classification
+        else:
+            raise TypeError, 'GvClassificationDlg now requires a \
+                              GvClassification instance'
+        if self.classification.count <= 0:
+            self.ramp = items[0]
+            self.classification.prepare_default()
+        #d = self.classification.serialize()
+        #main vertical box
+        vbox = gtk.GtkVBox(spacing=3)
+
+        save_box = gtk.GtkHButtonBox()
+        btn_save = gtk.GtkButton('Save ...')
+        btn_save.connect('clicked', self.save_cb)
+        btn_load = gtk.GtkButton('Load ...')
+        btn_load.connect('clicked', self.load_cb)
+        save_box.pack_start(btn_load)
+        save_box.pack_start(btn_save)
+
+        try:
+            import pgucombo
+            self.property_list = pgucombo.pguCombo()
+        except:
+            self.property_list = gtk.GtkCombo()
+            
+        self.property_list.entry.connect('changed',self.property_select_cb)
+        self.update_property_list()
+        
+        save_box.pack_start(self.property_list)
+        vbox.pack_start(save_box, expand=gtk.FALSE)
+
+        #classification frame
+        class_frame = gtk.GtkFrame()
+        frame_box = gtk.GtkVBox(spacing=3)
+
+        title_box = gtk.GtkHBox()
+        title_lbl = gtk.GtkLabel('Legend Title: ')
+        self.title_txt = gtk.GtkEntry()
+        self.title_txt.set_text(self.classification.get_title())
+        self.title_txt.connect('changed', self.title_changed_cb)
+
+        title_box.pack_start(title_lbl, expand=gtk.FALSE)
+        title_box.pack_start(self.title_txt)
+
+        frame_box.pack_start(title_box, expand=gtk.FALSE)
+        frame_box.set_border_width(5)
+
+        #classification list
+        class_box = gtk.GtkScrolledWindow()
+        self.class_list = gtk.GtkList()
+        self.class_list.connect( 'select-child', self.list_selected )
+        class_box.add_with_viewport(self.class_list)
+        frame_box.pack_start(class_box)
+        self.reset_classification_list()
+
+        class_frame.add(frame_box)
+        vbox.pack_start(class_frame)
+
+        ar_box = gtk.GtkHButtonBox()
+        add_btn = gtk.GtkButton('Add class')
+        add_btn.connect('clicked', self.add_class_cb)
+        classify_btn = gtk.GtkButton('reclassify ...')
+        classify_btn.connect('clicked', self.reclassify_cb)
+        reset_btn = gtk.GtkButton('Revert')
+        reset_btn.connect('clicked', self.reset_cb)
+        ar_box.pack_start(add_btn)
+        ar_box.pack_start(classify_btn)
+        ar_box.pack_start(reset_btn)
+        vbox.pack_start(ar_box, expand=gtk.FALSE)
+
+        #Color Ramp choices
+        ramp_table=gtk.GtkTable(rows=2, cols=2)
+        ramp_table.show()
+        ramp_lbl = gtk.GtkLabel('Color Ramps: ')
+        ramp_lbl.show()
+        ramp_table.attach(ramp_lbl, 0, 1, 0, 1)
+        ramp_opt = gtk.GtkOptionMenu()
+        ramp_opt.show()
+        self.ramp_menu = gtk.GtkMenu()
+        self.ramp_menu.show()
+        ramp_item=gtk.GtkMenuItem()
+        ramp_item.add(gtk.GtkHSeparator())
+        ramp_item.set_sensitive(gtk.FALSE)
+        ramp_item.show_all
+        self.ramp_menu.append(ramp_item)
+        for n in items:
+            ramp_item = gtk.GtkMenuItem()
+            ramp_item.add(n)
+            ramp_item.show_all()
+            if issubclass(n.__class__, ColorRamp):
+                ramp_item.connect('activate', self.ramp_cb, n)
+            else:
+                ramp_item.set_sensitive(gtk.FALSE)
+            self.ramp_menu.append(ramp_item)
+        ramp_opt.set_menu(self.ramp_menu)
+        ramp_opt.show()
+        ramp_opt.set_history(0)
+        ramp_table.attach(ramp_opt, 1, 2, 0, 1)
+        ramp_table.show_all()
+        vbox.pack_start(ramp_table, expand=gtk.FALSE)
+        #buttons
+        button_box = gtk.GtkHButtonBox()
+        #button_box.set_layout_default(gtk.BUTTONBOX_START)
+        self.ok_button = gtk.GtkButton('OK')
+        self.ok_button.connect('clicked', self.ok_cb)
+        self.apply_button = gtk.GtkButton('Apply')
+        self.apply_button.connect('clicked', self.apply_cb)
+        self.cancel_button = gtk.GtkButton('Cancel')
+        self.cancel_button.connect('clicked', self.cancel_cb)
+        button_box.pack_start(self.ok_button, expand=gtk.FALSE)
+        button_box.pack_start(self.apply_button, expand=gtk.FALSE)
+        button_box.pack_start(self.cancel_button, expand=gtk.FALSE)
+        vbox.pack_start(button_box, expand=gtk.FALSE)
+        vbox.show_all()
+        self.add(vbox)
+
+        #make ok_button a default button ? why isn't it working ?
+        self.ok_button.set_flags(gtk.CAN_DEFAULT)
+        self.ok_button.grab_default()
+        self.publish('classification-changed')
+
+        self.update_property_list()
+        
+    def close(self, *args):
+        """close and destroy this dialog"""
+        self.hide()
+        self.destroy()
+        if self.reclassdlg is not None:
+            self.reclassdlg.destroy()
+        return gtk.TRUE
+
+    def ok_cb(self, *args):
+        """Close the dialog and notify listeners and the raster that the
+        classification has changed"""
+        #allowing the raster to be rescale screws things up!
+        self.classification.update_all_layers(rescale=1)
+        self.notify('classification-changed')
+        return self.close()
+
+    def apply_cb(self, *args):
+        """apply the current classification"""
+        self.notify('classification-changed')
+        self.classification.update_all_layers(rescale=1)
+
+    def cancel_cb(self, *args):
+        """close the classification dialog without doing anything
+        about the classification"""
+        return self.close()
+        
+    def list_selected( self, *args ):
+        self.class_list.unselect_all()
+
+    def add_class_cb(self, *args):
+        """add a single class to the classification.  Add it at the end
+        with the same value and color as the upper range value of the
+        last class.  If there are no classes, use the entire range.
+        """
+        #first create the new class
+        cls = self.classification
+        if len(self.color_buttons) > 0:
+            color = self.color_buttons[len(self.color_buttons)-1].get_d()
+            print color
+            rng = cls.get_range(cls.count-1)
+            rng = (rng[1], rng[1])
+            symbol = cls.get_symbol( cls.count - 1 )
+            scale = cls.get_scale( cls.count - 1 )
+        else:
+            color = ( 0.0, 0.0, 0.0, 1.0 )
+            rng = cls.collect_range()
+            #for point layers only
+            if cls.layers[0].get_parent()[0].get_type() == gview.GVSHAPE_POINT:
+                symbol = '"ogr-sym-0"'
+            else:
+                symbol = None
+            scale = 1.0
+        n = cls.set_class(color = color, 
+                          range_min = rng[0], 
+                          range_max = rng[1],
+                          symbol = symbol,
+                          symbol_scale = scale)
+        self.insert_class( n )                          
+        
+    def insert_class( self, class_id ):
+        """Create gui elements for the class_id and insert them
+        into the gui
+        """
+        cls = self.classification
+        self.color_buttons.insert(class_id, ColorButton(cls.get_color(class_id)))
+        self.color_buttons[class_id].connect('color-set', 
+                                             self.color_button_cb, class_id)
+        symbol = cls.get_symbol( class_id )
+        if symbol is not None:
+            sym_menu = pguMenuFactory(MENU_FACTORY_OPTION_MENU)
+            entries = []
+            for i in range(len(gvogrfsgui.ogrfs_symbol_names)):
+                sym_name = gvogrfsgui.ogrfs_symbol_names[i]
+                sym_img = gvogrfsgui.ogrfs_symbols[sym_name][1]
+                a = '<image:' + sym_img + '>'
+                entries.append((a, None, self.symbol_change, class_id, 
+                                gvogrfsgui.ogrfs_symbols[sym_name][0]))
+            sym_menu.add_entries(entries)
+            sym_menu.set_usize(70, 30)
+            sym_menu.set_history(int(symbol[8:]))
+            self.sym_menus.insert( class_id, sym_menu )
+
+            scale = cls.get_scale( class_id )
+            adj = gtk.GtkAdjustment( value=scale, lower=0.0, upper=100.0, 
+                                     step_incr=0.11, page_incr=1.0, page_size=1.0 )
+            scale_spin = gtk.GtkSpinButton(adj)
+            scale_spin.set_editable( TRUE )
+            adj.connect( 'value-changed', self.scale_change, class_id )
+            self.scale_spinners.insert( class_id, scale_spin )
+        else:
+            self.sym_menus.insert( class_id, None )
+            self.scale_spinners.insert( class_id, None )
+        
+        self.ranges.insert(class_id, pguEntry())
+        rng = cls.get_range(class_id)
+        rng_txt = str( rng[0] )
+        if rng[1] != '' and cls.get_type() != CLASSIFY_DISCRETE:
+            rng_txt = rng_txt + "-" + str( rng[1] )
+        self.ranges[class_id].set_text(rng_txt)
+        self.ranges[class_id].connect('changed', self.range_changed_cb, class_id)
+        self.labels.insert(class_id, pguEntry())
+        self.labels[class_id].set_text(cls.get_name(class_id))
+        self.labels[class_id].connect('changed', self.label_changed_cb, class_id)
+        self.add_classification_item(self.color_buttons[class_id], 
+                                     self.sym_menus[class_id],
+                                     self.scale_spinners[class_id],
+                                     self.ranges[class_id], 
+                                     self.labels[class_id])
+        self.class_list.show_all()
+
+    def add_classification_item(self, clr, sym, scl, rng, lbl, 
+                                delete_button=gtk.TRUE):
+        """add a single row to the classification list.  Optionally add a delete 
+        button that will delete that row from the classification.
+        """
+        class_item = gtk.GtkListItem()
+        class_box = gtk.GtkHBox()
+        #explicitly size the first 5, let the last one fill the rest of the 
+        #space.
+        clr.set_usize(70, -1)
+        if sym is not None:
+            sym.set_usize(70, -1)
+        if scl is not None:
+            scl.set_usize(70, -1)
+        rng.set_usize(130, -1)
+        lbl.set_usize(130, -1)
+        class_box.pack_start(clr, expand=gtk.FALSE, fill=gtk.FALSE)
+        if sym is not None:
+            class_box.pack_start(sym, expand=gtk.FALSE, fill=gtk.FALSE)
+        if scl is not None:
+            class_box.pack_start(scl, expand=gtk.FALSE, fill=gtk.FALSE)
+        class_box.pack_start(rng, expand=gtk.FALSE, fill=gtk.FALSE)
+        class_box.pack_start(lbl, expand=gtk.FALSE, fill=gtk.FALSE)
+        if delete_button:
+            del_btn = gtk.GtkButton('x')
+            del_btn.set_usize( 45, -1 )
+            del_btn.connect('clicked', self.delete_item, class_item)
+            class_box.pack_start(del_btn, expand=gtk.FALSE, fill=gtk.FALSE)
+        class_box.add( gtk.GtkLabel( '' ) )
+        class_item.add(class_box)
+        class_item.show()
+        self.class_list.add(class_item)
+
+    def reset_classification_list(self, *args):
+        """Set the contents of class_list to the classification
+        scheme in the classification object."""
+
+        #clear existing UI side items.
+        self.class_list.clear_items(0, -1)
+        del self.color_buttons, self.ranges, self.labels
+        self.color_buttons = []
+        self.ranges = []
+        self.labels = []
+
+        cls = self.classification
+        #prepare a default classification if one doesn't exist
+        if cls.count == 0:
+            cls.prepare_default(5)
+
+        symbol = cls.get_symbol( 0 )
+        #setup the column headers
+        class_item = gtk.GtkListItem()
+        set_widget_background(class_item, (1.0, 0.0, 0.0))
+        class_box = gtk.GtkHBox()
+        clr_frm = gtk.GtkFrame()
+        clr_frm.add(gtk.GtkLabel('Color'))
+        clr_frm.set_shadow_type(gtk.SHADOW_OUT)
+        if symbol is not None:
+            sym_frm = gtk.GtkFrame()
+            sym_frm.add( gtk.GtkLabel( 'Symbol' ))
+            sym_frm.set_shadow_type( gtk.SHADOW_OUT )
+        
+            scale_frm = gtk.GtkFrame()
+            scale_frm.add( gtk.GtkLabel( 'Scale' ))
+            scale_frm.set_shadow_type( gtk.SHADOW_OUT )
+        else:
+            sym_frm = None
+            scale_frm = None
+        rng_frm = gtk.GtkFrame()
+        rng_frm.add(gtk.GtkLabel('Range'))
+        rng_frm.set_shadow_type(gtk.SHADOW_OUT)
+        lbl_frm = gtk.GtkFrame()
+        lbl_frm.add(gtk.GtkLabel('Label'))
+        lbl_frm.set_shadow_type(gtk.SHADOW_OUT)
+        self.add_classification_item(clr_frm, sym_frm, scale_frm, rng_frm, 
+                                     lbl_frm, gtk.FALSE)
+
+        #for each class, create an entry in the list
+        for n in range(cls.count):
+            self.insert_class( n )
+            
+        self.class_list.show_all()
+
+        if self.ramp is not None:
+            self.apply_ramp(self.ramp)
+            
+    def reclassify_cb(self, *args):
+        """show the reclassify dlg"""
+        dlg = GvReclassifyDlg(ok_cb = self.reset_dlg_cb, 
+                              classify_type = self.classification.get_type())
+        self.reclassdlg = dlg
+        dlg.show()
+
+    def reset_dlg_cb(self, dlg, *args):
+        """reset the classification to the default"""
+        self.classification.set_type( dlg.classify_type )
+        self.classification.remove_all_classes()
+        self.classification.prepare_default(dlg.classes)
+        self.reset_classification_list()
+        self.reclassdlg = None
+
+    def reset_cb(self,*args):
+        """reset the classification to the default"""
+        self.classification.remove_all_classes()
+        self.classification.prepare_default(5)
+        self.reset_classification_list()
+
+    def delete_item(self, btn, item):
+        """Remove a class from the classification"""
+        n = self.class_list.child_position(item) - 1
+        self.classification.remove_class(n)
+        self.class_list.remove_items([item])
+        del self.color_buttons[n]
+        del self.ranges[n]
+        del self.labels[n]
+
+    def color_button_cb(self, widget, color, num, *args):
+        """Handle the user changing a color value"""
+        self.classification.set_color(num, widget.get_color())
+        
+    def symbol_change( self, widget, index, symbol ):
+        self.classification.set_symbol( index, symbol )
+            
+    def scale_change( self, widget, index ):
+        self.classification.set_scale( index, widget.value )
+        
+    def range_changed_cb(self, widget, num):
+        """Handle the user changing a range value.  This requires validation"""
+        print 'range_changed_cb'
+        #if self.updating: return
+        self.updating = TRUE
+        range_txt = strip(widget.get_text()) #remove whitespace
+        vals = split(range_txt, '-')
+        if range_txt == '':
+            #nothing entered
+            return
+        # lots of hackery here recognise various cases with negatives.
+        # Negatives come out of the split as an empty token.
+        if len(vals) == 4 and vals[0] == '' and vals[2] == '':
+            try:
+                low = -float(vals[1])
+                hi = -float(vals[3])
+            except:
+                low = '-' + vals[1]
+                hi = '-' + vals[3]
+        elif len(vals) == 3 and vals[0] == '':
+            try:
+                low = -float(vals[1])
+                hi = float(vals[2])
+            except:
+                low = '-' + vals[1]
+                hi = vals[2]
+            
+        elif len(vals) == 3 and vals[1] == '':
+            try:
+                low = float(vals[0])
+                hi = -float(vals[2])
+            except:
+                low = vals[0]
+                hi = '-' + vals[2]
+            
+        elif len(vals) == 2:
+            #two vals
+            try:
+                low = float(vals[0])
+                hi = float(vals[1])
+            except:
+                low = vals[0]
+                hi = vals[1]
+
+        elif len(vals) == 1:
+            #one val
+            try:
+                low = float(vals[0])
+                hi = float(vals[0])
+            except:
+                low = vals[0]
+                hi = vals[0]
+        else:
+            #too many values
+            return
+        
+        try:
+            if int(low) == low:
+                low_txt = "%.0f" % low
+            else:
+                low_txt = "%s" % low
+        except:
+            low_txt = low
+            
+        try:
+            if int(hi) == hi:
+                hi_txt = "%.0f" % hi
+            else:
+                hi_txt = "%s" % hi
+        except:
+            hi_txt = hi
+            
+        r_low, r_hi = self.classification.get_range( num )
+        old_name = self.classification.get_name( num )
+        
+        try:
+            if int(r_low) == r_low:
+                r_low_txt = "%.0f" % r_low
+            else:
+                r_low_txt = "%s" % r_low
+        except:
+            r_low_txt = r_low
+        
+        try:
+            if int(r_hi) == r_hi:
+                r_hi_txt = "%.0f" % r_hi
+            else:
+                r_hi_txt = "%s" % r_hi
+        except:
+            r_hi_txt = r_hi
+            
+        if r_hi_txt == "":
+            calc_name = r_low_txt
+        else:
+            calc_name = "%s - %s" % (r_low_txt, r_hi_txt)
+        
+        print 'old rng is ', r_low, r_hi
+        print 'new rng is ', low, hi
+        print 'name is ', old_name
+        print 'calc is ', calc_name
+        if calc_name == old_name:
+            if hi_txt == "":
+                calc_name = low_txt
+            else:
+                calc_name = "%s - %s" % ( low_txt, hi_txt )
+        else:
+            calc_name = old_name
+        print 'new is ', calc_name            
+        self.classification.set_range(num, low, hi)
+        self.labels[num].set_text( calc_name )
+        self.updating = FALSE
+
+    def label_changed_cb(self, widget, num):
+        """Handle the user changing the label."""
+        self.classification.set_name(num, widget.get_text())
+
+    def title_changed_cb(self, widget):
+        """Handle the user changing the title"""
+        self.classification.set_title(widget.get_text())
+
+    def ramp_cb(self, widget, ramp, *args):
+        if ramp is not None:
+            self.ramp = ramp
+            self.apply_ramp(ramp)
+        else:
+            #TODO: custom ramp creator here.
+            pass
+
+    def apply_ramp_cb(self, n, color):
+        self.classification.set_color(n, color)
+        self.color_buttons[n].set_color(color)
+
+    def apply_ramp(self, ramp, *args):
+        ramp.apply_ramp(self.apply_ramp_cb, self.classification.count)
+
+    def save_cb(self, *args):
+        import filedlg
+        dlg = filedlg.FileDialog(dialog_type=filedlg.FILE_SAVE, filter='Legend Files|*.leg')
+        dlg.ok_button.connect('clicked', self.save, dlg)
+        dlg.cancel_button.connect('clicked', dlg.hide)
+        dlg.show()
+
+    def save(self, widget, dlg, *args):
+        import pickle
+        import os
+        filename = dlg.get_filename()
+        path, ext = os.path.splitext(filename)
+        if not (ext == ".leg"):
+            gvutils.warning("filename extension changed to .leg")
+        ext = ".leg"
+        filename = path + ext
+        dlg.hide()
+        if os.path.exists( filename ):
+            warning_pix = os.path.join(gview.home_dir, 'pics', 'warning.xpm' )
+            win = gvutils._MessageBox( "Do you wish to overwrite the existing file?",
+                                       ( 'Yes', 'No', ), warning_pix, modal=TRUE)
+            win.set_title( 'File Exists' )
+            win.show()
+            gtk.mainloop()
+            if win.ret == 'No':
+                print 'not saving'
+                return
+        print 'saving'        
+        file = open(filename, "w")
+        d = self.classification.serialize()
+        try:
+            pickle.dump(d, file)
+        except PicklingError:
+            gvutils.error('An error occurred saving the classification:\n' + filename)
+
+    def load_cb(self, *args):
+        import filedlg
+        dlg = filedlg.FileDialog(dialog_type=filedlg.FILE_OPEN, filter='Legend Files|*.leg')
+        dlg.ok_button.connect('clicked', self.load, dlg)
+        dlg.cancel_button.connect('clicked', dlg.hide)
+        dlg.show()
+
+    def load(self, widget, dlg, *args):
+        import pickle
+        filename = dlg.get_filename()
+        dlg.hide()
+        try:
+            file = open(filename, "r")
+            d = pickle.load(file)
+            self.classification.deserialize(d)
+            self.ramp = None
+            self.reset_classification_list()
+            self.title_txt.set_text(self.classification.get_title())
+        except:
+            gvutils.error('Error opening classification file:\n' + filename)
+
+    def property_select_cb(self, *args):
+        if len(self.classification.layers) == 0:
+            return
+
+        if self.property_updating:
+            return
+        
+        layer = self.classification.layers[0]
+        
+        if not issubclass(layer.__class__,gview.GvShapesLayer):
+            self.property_list.hide()
+            return
+        
+        new_property = self.property_list.get_text()
+
+        self.classification.set_classify_property( layer, new_property )
+
+        self.classification.remove_all_classes()
+        self.classification.prepare_default(5)
+        self.reset_classification_list()
+        self.title_txt.set_text( self.classification.get_title() )
+    def update_property_list( self, *args ):
+        if len(self.classification.layers) == 0:
+            return
+        
+        layer = self.classification.layers[0]
+
+        if not issubclass(layer.__class__,gview.GvShapesLayer):
+            self.property_list.hide()
+            return
+        
+        self.property_list.show()
+        
+        property = self.classification.get_classify_property( layer )
+        schema = layer.get_parent().get_schema()
+        fields = []
+        for field_def in schema:
+            fields.append( field_def[0] )
+
+        self.property_updating = 1
+        self.property_list.set_popdown_strings( tuple(fields) )
+        if property is not None:
+            self.property_list.select_string( property )
+        self.property_updating = 0
+
+class GvReclassifyDlg(gtk.GtkWindow):
+    """This dialog displays a re-classification dialog that allows
+    the user to specify the number of classes, and the type of classification
+    """
+    def __init__(self, ok_cb=None, cancel_cb=None, cb_data=None, 
+                 classify_type=CLASSIFY_EQUAL_INTERVAL):
+        gtk.GtkWindow.__init__(self)
+        self.set_title('Classification')
+        self.user_ok_cb = ok_cb
+        self.user_cancel_cb = cancel_cb
+        self.user_cb_data = cb_data
+        self.classify_type = classify_type
+        self.set_border_width(6)
+        #main vertical box
+        vbox = gtk.GtkVBox(spacing=6)
+        type_box = gtk.GtkHBox(spacing=6)
+        type_box.pack_start(gtk.GtkLabel('Type:'), expand=gtk.FALSE)
+        opt_menu = gtk.GtkOptionMenu()
+        type_menu = gtk.GtkMenu()
+        
+        #using classification_types dictionary from gvclassification
+        for i in range(len(classification_types)):
+            for type in classification_types.iteritems():
+                if type[1] == i:
+                    item = gtk.GtkMenuItem( type[0] )
+                    item.connect( 'activate', self.type_menu_cb, classification_types[type[0]] )
+                    type_menu.append( item )
+            
+        opt_menu.set_menu(type_menu)
+        opt_menu.set_history( classify_type )
+        opt_menu.resize_children()
+        type_box.pack_start(opt_menu)
+        vbox.pack_start(type_box, expand=gtk.FALSE)
+        #Number of classes
+        classes_box = gtk.GtkHBox(spacing=6)
+        classes_box.pack_start(gtk.GtkLabel('Number of classes:'))
+        adj = gtk.GtkAdjustment(5, 2, 80, 1, 5, 5, 0)
+        self.spinner = gtk.GtkSpinButton(adj)
+        self.spinner.set_snap_to_ticks(gtk.TRUE)
+        self.spinner.set_digits(0)
+        classes_box.pack_start(self.spinner)
+        vbox.pack_start(classes_box, expand=gtk.FALSE)
+        #add the ok and cancel buttons
+        button_box = gtk.GtkHButtonBox()
+        ok_button = gtk.GtkButton("OK")
+        ok_button.connect('clicked', self.ok_cb, cb_data)
+        cancel_button = gtk.GtkButton("Cancel")
+        cancel_button.connect('clicked', self.cancel_cb, cb_data)
+        button_box.pack_start(ok_button)
+        button_box.pack_start(cancel_button)
+        vbox.pack_start(button_box, expand=gtk.FALSE)
+        vbox.show_all()
+        self.add(vbox)
+        ok_button.set_flags(gtk.CAN_DEFAULT)
+        ok_button.grab_default()
+
+    def type_menu_cb(self, menu_item, classify_type):
+        self.classify_type = classify_type
+
+    def ok_cb(self, *args):
+        self.classes = self.spinner.get_value_as_int()
+        if self.user_ok_cb is not None:
+            self.user_ok_cb(self, self.user_cb_data)
+        self.hide()
+        self.destroy()
+
+    def cancel_cb(self, *args):
+        if self.user_cancel_cb is not None:
+            self.user_cancel_cb(self.user_cb_data, self)
+        self.hide()
+        self.destroy()
+
+if __name__ == '__main__':
+    gview.set_preference('ramp_directory', 'c:\\CIETmap\\ramps')
+    shapes = gview.GvShapes( shapefilename="c:/projects/dmsolutions/ciet/ciet_data/wcsite.shp" )
+    layer = gview.GvShapesLayer( shapes=shapes )
+    cls = GvClassification( layer )
+    dlg = GvClassificationDlg( cls )
+    dlg.apply_button.connect('clicked', cls.dump)
+    dlg.apply_button.connect('clicked', gtk.mainquit)
+    
+    dlg.connect('delete-event', gtk.mainquit)
+    dlg.show()
+    gtk.mainloop()

Added: packages/openev/branches/upstream/current/pymod/gvcommand.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvcommand.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvcommand.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,717 @@
+###############################################################################
+# $Id: gvcommand.py,v 1.10 2004/10/15 13:25:26 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Base classes for Command Parsing.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2002, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvcommand.py,v $
+#  Revision 1.10  2004/10/15 13:25:26  gmwalter
+#  Fix bug that causes cietmap problems.
+#
+#  Revision 1.9  2004/09/20 22:37:24  gmwalter
+#  Fix bug in argument validation.
+#
+#  Revision 1.8  2003/07/28 19:42:33  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.7  2002/08/06 16:07:18  warmerda
+#  ensure failed validation stops execution
+#
+#  Revision 1.6  2002/07/29 21:00:26  warmerda
+#  substantially restructured introducing CommandContext
+#
+#  Revision 1.5  2002/07/26 17:56:07  warmerda
+#  added default value for arguments
+#
+#  Revision 1.4  2002/07/24 14:50:15  warmerda
+#  temporary hack for token_string
+#
+#  Revision 1.3  2002/07/18 19:57:37  warmerda
+#  fixed typo
+#
+#  Revision 1.2  2002/07/18 19:44:17  warmerda
+#  added commanding parsing function
+#
+#  Revision 1.1  2002/07/18 17:38:03  warmerda
+#  New
+#
+#
+
+import string
+
+###############################################################################
+class CommandBase:
+    """Base class for command objects.
+
+    Commands should have the following data members:
+
+    Name -- command name... the first keyword on any line that is this command.
+    Usage -- optional short usage message (eg. 'DEFINE {column_name} [FLOAT/STRING/INTEGER]')
+    HelpURL -- web url to help for this command.  Suitable for use with gvhtml.
+    Args -- List of CommandArgDef objets defining the arguments to this command.
+    Group -- Textual token indicating the command group this command belongs
+    to.
+
+    A command must also implement the execute() method.  The execute() method
+    can depend on the arg_tokens argument being a list of argument values,
+    with one entry for each entry in self.Args.  Switch arguments will have
+    a logical TRUE/FALSE (nonzero/zero) value.  Other arguments will be a
+    string (the token value) or None if the argument did not occur.  Execute()
+    isn't called unless all automated validation passed.
+
+    """
+    def __init__(self):
+        pass
+
+    def execute( arg_tokens, line, interpreter ):
+        pass
+
+
+###############################################################################
+class ArgDef:
+
+    """
+Argument Attributes
+-------------------
+
+type:
+ - The type of the argument. Legal values are "string_word", "string_token",
+ "switch", "numeric" and "string_chunk".  Details are later in docs.
+ 
+name:
+ - The name of the argument.  Used as the key for argument values in the
+   dictionary of arguments passed into the Evaluate method. 
+
+valid_list:
+ - A list of valid values for this argument.  The list will be treated as
+   literal text to be case insensitively compared against the value when
+   checking if the argument is present/valid.
+
+required:
+ - 1 or 0 depending on whether the argument is required or not.  Arguments
+   that are not required, and are not present will be passed with a None
+   value into the Execute method of the command.
+
+prompt:
+ - Text to be shown to the user when they are prompted for the value of
+   this argument.  If not supplied the name of the argument will be shown
+   to the user as the prompt. 
+
+prompt_func:
+ - A function to be called when prompt text is needed.  The returned string
+   will be the prompt.  Useful when the prompt will vary depending on the
+   current environment. 
+
+parse_func:
+ - A function that should be called to parse text output of the input command
+   that is intended to be part of the argument.  Normally None indicating
+   that default parsing logic should be used. 
+
+validate_method: 
+ - A function that should be called on the argument value to determine if it
+   is valid or not.  The function should return None if valid, or a string
+   indicating the problem if it is not.  It also receives the current
+   interpreter as a context, but shouldn't use it for input/output.
+
+read_token_method:
+ - A function that should be called on the input text to get the next token.
+   If not provided a built-in will be used based on the 'type'.  The read
+   token method will receive as input an input text string, and a      
+   CommandContext object.  It should return a (token,remaining_text) tuple.
+
+process_token_method:
+- A function that should be called on the token to convert it to the
+  appropriate argument type (eg. evaluating a variable, converting
+  a string to a number).  This gets called in parsing.
+
+default:
+ - The value that should be assigned to this argument if it isn't found on
+   the commandline.  If not provided, None is assumed.  Not applicable to
+   switch arguments.
+   
+
+Argument Types
+--------------
+
+string_word: 
+  - Normally a white space terminated chunk of text. 
+  - Any mix of non-white space characters. 
+  - If quoted it may contain white space, and the quotes will be dropped
+    from the processed value.
+
+string_token:
+  - a well formed token.  Alpha-numeric, starting with a letter.  May
+    include underscores but no other special characters.  Used for variable
+    names, and so forth. 
+
+switch:
+  - May appear at any point in the command (perhaps limit to all switches 
+    preceeding non-switch arguments, but allow reordering of switches?)
+  - Must start with a '-' or '/'.
+  - Must have a valid_list with the set of possible values. eg ['-n','/n'].
+  - Currently can't take any arguments. 
+
+multi_switch:
+  - Like switch, except that instead of returning 1 or zero, the argument
+    returned is the name used for the switch or None (useful for switches
+    which can take on one of a few values, but different behaviours are
+    needed for the different options- eg. /off or /on)
+
+list_type:
+  - Argument is one of a list of possible string values
+  - Must supply a list of valid options (valid_opts)
+
+numeric:
+  - value is numeric.
+  - min/max range values can be tested in validate. 
+
+string_chunk:
+  - Similar to string_word, but an arbitrary terminator string can be
+    supplied.  The terminator string may be None indicating all the remainder
+    of the command should be consumed.
+
+variable:
+  - A user variable, such as a numpy array.
+  - internally, both the variable's value and its name will be passed to
+    the command (as a list) so that in place modifications can be made
+    if need be.
+
+    """
+   
+
+    def __init__(self, name = None, type = 'string_word', prompt = None,
+                 prompt_func = None, required = 1, valid_list = None,
+                 validate_method = None, default = None,
+                 read_token_method = None, process_token_method = None, valid_opts=None ):
+        self.name = name
+        self.type = type
+        self.prompt = prompt
+        self.required = required
+        self.valid_list = valid_list
+        self.valid_opts = valid_opts
+        self.validate_method = validate_method
+        self.default = default
+
+        # Generate valid sequence for switches if not explicitly provided.
+        if self.type == 'switch' and valid_list is None:
+            self.valid_list = [ '-' + name, '/' + name ]
+
+        if self.type == 'list_type' and valid_opts is None:
+            raise AttributeError,'list type requires a list of valid string arguments (valid_opts)'
+        
+        # Ensure "valid_list" entries are all forced into lower case for
+        # comparison purposes.
+
+        self.valid_list_lower = None
+        if self.valid_list is not None:
+            self.valid_list_lower = []
+            for item in self.valid_list:
+                self.valid_list_lower.append( string.lower(item) )
+            
+        # Validate some requirements
+        if self.type not in [ 'string_word', 'string_token', 'switch','list_type',
+                              'multi_switch','numeric', 'string_chunk', 'variable']:
+            raise ValueError, 'Unsupported argument type: ' + self.type
+        
+        # Provide token reader if not specified in args.
+        if read_token_method is not None:
+            self.read_token_method = read_token_method
+        
+        elif type == 'string_word':
+            self.read_token_method = self.read_quotable_token
+        
+        elif type == 'variable':
+            self.read_token_method = self.read_quotable_token
+                
+        elif type == 'string_chunk':
+            self.read_token_method = self.read_remainder_token
+
+        else:
+            self.read_token_method = self.read_simple_token
+
+        # Processing method: in variable case, this evaluates the
+        # variable's value in the pyshell.  In the simple case
+        # it does nothing (just returns the input)
+        if process_token_method is not None:
+            self.process_token_method = process_token_method
+
+        elif type == 'variable':
+            self.process_token_method = self.process_variable_token
+
+        elif type == 'numeric':
+            self.process_token_method = self.process_numeric_token
+
+        else:
+            self.process_token_method = self.process_simple_token
+
+
+    ###########################################################################
+    def validate(self, arg_text, command_context):
+        if self.validate_method is not None:
+            return self.validate_method( arg_text, command_context )
+
+        if self.valid_list_lower is not None:
+            lower_arg_text = string.lower(arg_text)
+            if lower_arg_text not in self.valid_list_lower:
+                return '%s: %s is not in the set of valid options.' % \
+                       ( self.name, arg_text )
+            else:
+                return None
+
+        if self.type == 'numeric':        
+            try:
+                string.atof( arg_text )
+            except ValueError:
+                return '%s: %s is not numeric.' % \
+                       ( self.name, arg_text )
+
+        elif self.type == 'string_token':
+            # Must start with a letter
+            first_char='abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+            later_char = first_char + '0123456789'
+            if len(arg_text) > 0:
+                if arg_text[0] not in first_char:
+                    return '%s: %s is not a valid variable name.' % \
+                           (self.name, arg_text)
+                for cchar in arg_text[1:]:
+                    if cchar not in later_char:
+                        return '%s: %s is not a valid variable name.' % \
+                               (self.name, arg_text)
+                    
+
+        return None
+            
+    ###########################################################################
+    def read_simple_token(self, line, cc ):
+        tokens = string.split(line,None,1)
+        if len(tokens) == 1:
+            return (tokens[0], '')
+        elif len(tokens) == 2:
+            return (tokens[0], tokens[1])
+        else:
+            return (None,'')
+        
+    ###########################################################################
+    def read_quotable_token(self, line, cc ):
+        # Check for non-keyword quoted string
+        if len(line) < 1:
+            return (None,'')
+        
+        if (line[0] == '"'):
+            tokens1 = string.split(line,'"',1)
+            tokens2 = string.split(tokens1[1],'"',1)
+            if len(tokens2) < 2:
+                interpreter.showText( 'Unmatched "  in argument line ','error' )
+                return (None,'')
+            return (tokens2[0],tokens2[1])
+        elif (line[0] == "'"):
+            tokens1 = string.split(line,"'",1)
+            tokens2 = string.split(tokens1[1],"'",1)
+            if len(tokens2) < 2:
+                interpreter.showText( "Unmatched '  in argument line ",'error' )
+                return (None,'')
+            return (tokens2[0],tokens2[1])
+        
+        # Check for keyword
+        tokens1=string.split(line,None,1)
+        
+        if string.find(tokens1[0],'=') == -1:
+            # No keyword found- simple token
+            return self.read_simple_token( line, cc )
+        else:
+            # keyword found- check for quoted token
+            tokens2=string.split(tokens1[0],'=')
+            if tokens2[1][0] == '"':
+                # Keyword found.  return quoted token with keyword
+                # to identify the argument in later parsing, but separated
+                # from remainder of line
+                tokens1 = string.split(line,'"',1)
+                tokens2 = string.split(tokens1[1],'"',1)
+                if len(tokens2) < 2:
+                    interpreter.showText( 'Unmatched "  in argument line ','error' )
+                    return (None,'')
+                token = tokens1[0]+tokens2[0]
+                return (token,tokens2[1])
+            elif tokens2[1][0] == "'":
+                # Keyword found.  return quoted token with keyword
+                # to identify the argument in later parsing, but separated
+                # from remainder of line
+                tokens1 = string.split(line,"'",1)
+                tokens2 = string.split(tokens1[1],"'",1)            
+                if len(tokens2) < 2:
+                    interpreter.showText( "Unmatched '  in argument line ",'error' )
+                    return (None,'')
+                token = tokens1[0]+tokens2[0]
+                return (token,tokens2[1])
+            else:
+                return self.read_simple_token( line, cc )    
+
+###############################################################################
+    def read_remainder_token(self, line, cc ):
+        return (line, '')
+
+###############################################################################
+    def process_variable_token(self, token, cc ):
+        # Convention for variables: argument consists of a
+        # list: (argument value, argument name).  argument value
+        # is None if the token is not already present in the shell
+        # environment
+        # CURRENTLY ALL VARIABLE ARGUMENTS VALUES ARE PASSED AS STRINGS
+        # IF THEY ARE NOT SHELL VARIABLES (ie. '0.5' will be passed
+        # as '0.5', not atof('0.5')).  
+        if cc.interpreter.locals.has_key(token):
+            return (cc.interpreter.locals[token],token)
+        else:
+            return(None,token)
+
+############################################################################### 
+    def process_numeric_token(self, token, cc ):
+        return string.atof( token )
+        
+###############################################################################    
+    def process_simple_token(self, token, cc):
+        return token
+    
+###############################################################################
+# The following is intended to document the methods a command interpreter
+# must implement.  It isn't critical that custom command interpreters
+# derive from this class as long as they provide reasonable behaviour.
+#
+class CommandInterpreter:
+
+    def isInteractive():
+        """Is command interactive?
+
+        Returns non-zero if the command should be treated as interactive,
+        meaning that arguments can be prompted for, and usage help can be
+        provided.
+        """
+        pass
+    
+    def showText( text, text_class ):
+        """
+        Show text to the user.
+
+        The text may be one or more lines.  The class argument indicates
+        the purpose of the text, and may be used to differentiate fonts,
+        or even to direct output in different ways.  The available values
+        for class are "result", "error", or "usage".
+
+        text -- the single, or multi line text to display.
+
+        text_class - the indicator of the purpose of the text.
+
+        No value is returned.
+        """
+        pass
+
+    def showPrompt( *args ):
+        """To be determined."""
+        pass
+
+###############################################################################
+# CommandContext
+#
+# Objects of this class capture the state of a command as it is parsed.  It
+# allows customized parsing actions to be done taking into account the
+# arguments that come before, and it is responsible for command parsing.
+
+class CommandContext:
+
+    ###########################################################################
+    def __init__(self, line, command_dict = None, interpreter = None ):
+        self.interpreter = interpreter
+        self.command_dict = command_dict
+        self.line = line
+
+        self.text_remaining = line
+        
+        # The following are set after a successful parse.
+        self.args = None
+        self.cmd_obj = None
+        self.parsed = 0
+        self.error = 0
+        self.args_parsed = 0
+        
+
+    ###########################################################################
+    def parse_command_text(self):
+        tokens = string.split( self.line, None, 1 )
+        
+        # Null input line is considered valid.
+        if len(tokens) == 0:
+            return ('', '')
+
+        if len(tokens) == 1:
+            return (string.lower(tokens[0]), '')
+        else:
+            return (string.lower(tokens[0]), tokens[1])
+            
+    ###########################################################################
+    def parse_args(self):
+
+        self.parsed = 0
+        self.args_parsed = 0
+        self.args = []
+
+        # Next argument to try to parse (added to allow
+        # out-of-order keywords)
+        self.current_arg = 0
+        self.arg_is_parsed = []
+
+        for i in range(len(self.cmd_obj.Args)):
+            self.args.append( None )
+            self.arg_is_parsed.append(0)
+
+        while self.args_parsed < len(self.cmd_obj.Args):
+            if not self.parse_one_arg():
+                return 0                
+
+        self.parsed = 1
+        return 1
+
+    ###########################################################################
+    def parse_one_arg(self):
+        
+        #######################################################################
+        # Argument parsing logic:
+        # Maintain an argument "current_arg" to indicate which argument is
+        # currently being searched for.  "current_arg" starts at 0 and goes
+        # up to len(self.cmd_obj.Args)-1 (number of args-1).  Each time
+        # parse_one_arg is called, check if current_arg has been parsed yet.
+        # If it has, increment current_arg and continue.  If not, read the
+        # next token as a quotable token and determine if the next argument
+        # is a switch or multi_switch (which may occur anywhere in the line)
+        # or an argument specified by keyword (which may also appear out-of-
+        # order).  If the argument is found to be a switch/multi_switch or
+        # a later argument specified by keyword, reset arg to reflect the
+        # appropriate command argument.  Set next_arg_index to current_arg,
+        # since current_arg still hasn't been found and we are parsing a
+        # later argument.  Next, reread the token using the proper argument
+        # method (discard the quotable token results) and finish parsing the
+        # argument.
+        
+        if self.arg_is_parsed[self.current_arg] == 1:
+            # argument has already been found and parsed by keyword
+            self.current_arg = self.current_arg + 1
+            return 1
+
+        # Index of next argument to search for
+        next_arg_index = self.current_arg + 1
+        
+        arg = self.cmd_obj.Args[self.current_arg]
+        interpreter = self.interpreter
+
+        #######################################################################
+        # Before parsing using the current argument's method, check that
+        # the argument isn't a switch/multi-switch (which can occur anywhere)
+        # or designated by a keyword.
+        # 
+        test_token, test_remainder = ArgDef.read_quotable_token(arg,self.text_remaining,self)
+
+        #######################################################################
+        # Has the token been assigned a keyword?
+
+        arg_keyword = None
+        if test_token is not None:
+            checknamed = string.split(test_token,'=',1)
+            if len(checknamed) == 2:
+                if arg.name != string.strip(checknamed[0]):
+                    checknamed[0] = string.strip(checknamed[0])
+                    # keyword doesn't match current argument name:
+                    arg = None
+
+                    # Next argument to search for should be the
+                    # original again, but fill in the one that
+                    # does match so its token isn't lost
+                    next_arg_index = self.current_arg 
+                    for idx in range(len(self.cmd_obj.Args)):
+                        new_arg=self.cmd_obj.Args[idx]
+                        if new_arg.name == checknamed[0]:
+                            arg = new_arg
+                            self.current_arg = idx
+
+                if arg is None:
+                    # If keyword not found, = may be part of current
+                    # argument's value.  Reset to original argument
+                    # and try to parse
+                    arg = self.cmd_obj.Args[self.current_arg]
+                    next_arg_index = self.current_arg + 1
+                else:
+                    arg_keyword=string.strip(checknamed[0])
+                
+
+        
+            elif (len(checknamed) == 1) and (len(test_token) > 1) and (test_token[0] in ['/','-']):
+                #######################################################################
+                # Check to make sure this isn't a switch or multi_switch argument
+                # rather than the token for the current argument.  Check the command
+                # line argument list in order, stopping at the first switch or
+                # multi_switch variable that is compatible with the switch value
+                # and has not been parsed yet.
+            
+                for idx in range(len(self.cmd_obj.Args)):
+                    new_arg=self.cmd_obj.Args[idx]
+                    if ((new_arg.type in ['switch','multi_switch']) and
+                        (self.arg_is_parsed[idx] == 0)):
+                        if test_token in new_arg.valid_list:
+                            next_arg_index = self.current_arg
+                            arg = new_arg
+                            self.current_arg = idx
+
+        
+        #######################################################################
+        # At this point, the correct argument should have been selected.                
+        # Parse out a candidate token using the appropriate method for this 
+        # argument.
+        
+        if arg_keyword is None:
+            token, next_remainder = arg.read_token_method( self.text_remaining,
+                                                       self )
+        else:
+            # Read token, leaving off keyword.
+            token, next_remainder = arg.read_token_method( self.text_remaining[len(arg_keyword)+1:],
+                                                           self )
+
+        #######################################################################
+        # If this is not really a switch, reset the token
+        if ((arg.type == 'switch') and (token is not None) and
+            (token[0] not in ['/','-'])):
+            token = None
+        
+        #######################################################################
+        # If we are out of tokens, ensure that the arg is optional.
+        if token is None:
+            if arg.required:
+                interpreter.showText( 'missing argument ' + arg.name,
+                                      'error' )
+                return 0
+            elif arg.type == 'switch':
+                self.args[self.current_arg] = 0
+                self.arg_is_parsed[self.current_arg] = 1
+                self.current_arg = next_arg_index
+                self.args_parsed = self.args_parsed + 1
+                return 1
+            elif  arg.type == 'multi_switch':
+                self.args[self.current_arg] = arg.default
+                self.arg_is_parsed[self.current_arg] = 1
+                self.current_arg = next_arg_index
+                self.args_parsed = self.args_parsed + 1
+                return 1
+            else:
+                self.args[self.current_arg] = arg.default
+                self.arg_is_parsed[self.current_arg]=1
+                self.current_arg = next_arg_index               
+                self.args_parsed = self.args_parsed + 1
+                return 1
+
+
+        #######################################################################
+        # Is this a legitimate token for this argment or was it skipped.
+
+
+        val_msg = arg.validate( token, self )
+
+        # Did we actually consume the token?
+        if val_msg is None:
+            self.text_remaining = next_remainder
+
+        else:
+            interpreter.showText( val_msg, 'error' )
+            return 0
+        
+        # If argument type was variable, do required processing
+        token = arg.process_token_method(token, self)
+
+        # Report validation errors for required arguments.
+        if arg.type == 'switch':
+            self.args[self.current_arg] = val_msg is None
+            self.arg_is_parsed[self.current_arg] = 1
+            self.current_arg = next_arg_index
+            self.args_parsed = self.args_parsed + 1
+        elif arg.type == 'multi_switch':
+            self.args[self.current_arg] = token
+            self.arg_is_parsed[self.current_arg] = 1
+            self.current_arg = next_arg_index
+            self.args_parsed = self.args_parsed + 1
+            
+        elif arg.type == 'list_type':
+            if token not in self.valid_opts:
+                txt=token+' is not a valid option. Valid options are:\n'
+                for copt in self.valid_opts:
+                    txt=txt + str(copt) + ' '
+                interpreter.showText(txt,'error')
+                return 0
+            else:
+                self.args[self.current_arg] = token
+                self.arg_is_parsed[self.current_arg] = 1            
+                self.current_arg = next_arg_index
+                self.args_parsed = self.args_parsed + 1
+                
+        else:
+            self.args[self.current_arg] = token
+            self.arg_is_parsed[self.current_arg] = 1            
+            self.current_arg = next_arg_index
+            self.args_parsed = self.args_parsed + 1
+
+        return 1
+
+    ###########################################################################
+    # Parse complete command.  Returns 0 on failure, or non-zero on success.
+    def parse(self):
+        if self.interpreter is None:
+            raise ValueError, 'No interpreter set in CommandContext.parse()'
+
+        if self.command_dict is None:
+            raise ValueError, 'No command dictionary set in CommandContext.parse()'
+
+        #######################################################################
+        # Parse out the command and check for it in the command dictionary.
+        
+        cmd, self.text_remaining = self.parse_command_text()
+
+        if not self.command_dict.has_key(cmd):
+            self.interpreter.showText( "Command '%s' not recognised." % cmd,
+                                       'error' )
+            return 0
+
+        self.cmd_obj = self.command_dict[cmd]
+
+        #######################################################################
+        # Parse the arguments out.
+
+        self.error = 0
+        result = self.parse_args()
+
+        if self.error or result == 0:
+            return 0
+
+        return 1
+
+    ###########################################################################
+    def execute(self):
+        if not self.parsed and not self.parse():
+            return 0
+
+        return self.cmd_obj.execute( self.args, self, self.interpreter )

Added: packages/openev/branches/upstream/current/pymod/gvconst.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvconst.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvconst.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,206 @@
+###############################################################################
+# $Id: gvconst.py,v 1.12 2003/08/06 17:09:14 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Declaration of OpenEV constants
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvconst.py,v $
+#  Revision 1.12  2003/08/06 17:09:14  warmerda
+#  Added GLRA values
+#
+#  Revision 1.11  2002/07/24 14:50:41  warmerda
+#  added GVSHAPE_COLLECTION
+#
+#  Revision 1.10  2001/08/14 17:03:24  warmerda
+#  added standard deviation autoscaling support
+#
+#  Revision 1.9  2000/07/31 21:17:23  srawlin
+#  added change_info type constants for GvShapes and GvRaster
+#
+#  Revision 1.8  2000/07/07 14:56:06  srawlin
+#  added default LUT
+#
+#  Revision 1.7  2000/06/23 12:58:36  warmerda
+#  added GvRasterLayer modes
+#
+#  Revision 1.6  2000/06/14 22:11:35  warmerda
+#  Added real/imaginary support to color wheel
+#
+#  Revision 1.5  2000/06/14 13:17:51  warmerda
+#  added RL_LUT_ codes
+#
+#  Revision 1.4  2000/06/13 22:19:49  srawlin
+#  added 3D python bindings
+#
+#  Revision 1.3  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+"""
+Constants for use in the GvRasterLayer functions
+
+Constants for texture_mode_set
+"""
+
+RL_TEXTURE_REPLACE  = 0
+RL_TEXTURE_MODULATE  = 1
+
+"""
+Constants for zoom_set
+
+Magnification / Minification
+"""
+
+RL_FILTER_BILINEAR  = 0
+RL_FILTER_NEAREST   = 1
+
+"""
+Minification only -- if we ever use mipmaps this is useful
+"""
+
+RL_FILTER_TRILINEAR = 2
+
+
+"""
+Preset modes for blend_mode_set
+"""
+
+RL_BLEND_OFF      = 0
+RL_BLEND_FILTER   = 1
+RL_BLEND_MULTIPLY = 2
+RL_BLEND_ADD      = 3
+RL_BLEND_CUSTOM   = 4
+
+"""
+Constants for custom blend modes
+These can go in both the source and destination modes
+"""
+
+RL_BLEND_FACT_ZERO          = 0
+RL_BLEND_FACT_ONE           = 1
+RL_BLEND_FACT_DST_COLOR     = 2
+RL_BLEND_FACT_MIN_DST_COLOR = 3
+RL_BLEND_FACT_MIN_SRC_COLOR = 4
+RL_BLEND_FACT_SRC_ALPHA     = 5
+RL_BLEND_FACT_MIN_SRC_ALPHA = 6
+
+"""
+Constants for alpha_mode_set
+"""
+
+RL_ALPHA_OFF     = 0
+RL_ALPHA_NEVER   = 1
+RL_ALPHA_ALWAYS  = 2
+RL_ALPHA_LESSER  = 3
+RL_ALPHA_LEQUAL  = 4
+RL_ALPHA_EQUAL   = 5
+RL_ALPHA_GEQUAL  = 6
+RL_ALPHA_GREATER = 7
+RL_ALPHA_NEQUAL  = 8
+
+"""
+Constants to set texture wrapping/clamping
+"""
+
+RL_TEXCOORD_CLAMP  = 0
+RL_TEXCOORD_REPEAT = 1
+
+"""
+Constants to describe type of LUT attached to the RasterLayer
+"""
+
+RL_LUT_NONE = 0
+RL_LUT_1D   = 1
+RL_LUT_2D   = 2
+
+"""
+Constants for GvRasterLayer.lut_color_wheel_new() mode arguments.
+"""
+
+RL_LUT_MAGNITUDE = 0
+RL_LUT_PHASE_ANGLE = 1
+RL_LUT_SCALAR = 2
+RL_LUT_REAL = 3
+RL_LUT_IMAGINARY = 4
+
+"""
+GvRasterLayer modes
+"""
+
+RLM_AUTO = 0
+RLM_SINGLE = 1
+RLM_RGBA = 2
+RLM_COMPLEX = 3
+
+
+"""
+GvShape types.
+"""
+
+GVSHAPE_POINT = 1
+GVSHAPE_LINE  = 2
+GVSHAPE_AREA  = 3
+GVSHAPE_COLLECTION = 4
+
+
+"""
+GvRaster and GvShapes change_info types, from gvtypes.h
+"""
+GV_CHANGE_ADD      = 0x001
+GV_CHANGE_REPLACE  = 0x002
+GV_CHANGE_DELETE   = 0x003
+
+"""
+GvView   2D = Orthonormal Projection
+         3D = Perspective
+"""
+MODE_2D = 0
+MODE_3D = 1
+
+
+"""
+Default Rainbow Look Up Table, colour order (red, orange, yellow, green, cyan, blue, purple, red)
+"""
+STD_LUT = '\377\000\000\377\377\006\000\377\377\014\000\377\377\022\000\377\377\030\000\377\377\036\000\377\377$\000\377\377*\000\377\3770\000\377\3776\000\377\377<\000\377\377B\000\377\377H\000\377\377N\000\377\377T\000\377\377Z\000\377\377`\000\377\377f\000\377\377l\000\377\377r\000\377\377x\000\377\377~\000\377\377\204\000\377\377\212\000\377\377\220\000\377\377\226\000\377\377\234\000\377\377\242\000\377\377\250\000\377\377\256\000\377\377\264\000\377\377\272\000\377\377\300\000\377\377\306\000\377\377\314\000\377\377\322\000\377\377\330\000\377\377\336\000\377\377\344\000\377\377\352\000\377\377\360\000\377\377\366\000\377\377\374\000\377\373\377\000\377\365\377\000\377\357\377\000\377\351\377\000\377\343\377\000\377\335\377\000\377\327\377\000\377\321\377\000\377\313\377\000\377\305\377\000\377\277\377\000\377\271\377\000\377\263\377\000\377\255\377\000\377\247\377\000\377\241\377\000\377\233\377\000\377\225\377\000\377\217\377\000\377\211\377\000\377\203\377\000\377}\377\000\377w\377\000\377q\377\000\377k\377\000\377e\377\000\377_\377\000\377Y\377\000\377S\377\000\377M\377\000\377G\377\000\377A\377\000\377;\377\000\3775\377\000\377/\377\000\377)\377\000\377#\377\000\377\035\377\000\377\027\377\000\377\021\377\000\377\013\377\000\377\005\377\000\377\000\377\000\377\000\377\006\377\000\377\014\377\000\377\022\377\000\377\030\377\000\377\036\377\000\377$\377\000\377*\377\000\3770\377\000\3776\377\000\377<\377\000\377B\377\000\377H\377\000\377N\377\000\377T\377\000\377Z\377\000\377`\377\000\377f\377\000\377l\377\000\377r\377\000\377x\377\000\377~\377\000\377\204\377\000\377\212\377\000\377\220\377\000\377\226\377\000\377\234\377\000\377\242\377\000\377\250\377\000\377\256\377\000\377\264\377\000\377\272\377\000\377\300\377\000\377\306\377\000\377\314\377\000\377\322\377\000\377\330\377\000\377\336\377\000\377\344\377\000\377\352\377\000\377\360\377\000\377\366\377\000\377\374\377\000\373\377\377\000\365\377\377\000\357\377\377\000\351\377\377\000\343\377\377\000\335\377\377\000\327\377\377\000\321\377\377\000\313\377\377\000\305\377\377\000\277\377\377\000\271\377\377\000\263\377\377\000\255\377\377\000\247\377\377\000\241\377\377\000\233\377\377\000\225\377\377\000\217\377\377\000\211\377\377\000\203\377\377\000}\377\377\000w\377\377\000q\377\377\000k\377\377\000e\377\377\000_\377\377\000Y\377\377\000S\377\377\000M\377\377\000G\377\377\000A\377\377\000;\377\377\0005\377\377\000/\377\377\000)\377\377\000#\377\377\000\035\377\377\000\027\377\377\000\021\377\377\000\013\377\377\000\005\377\377\000\000\377\377\006\000\377\377\014\000\377\377\022\000\377\377\030\000\377\377\036\000\377\377$\000\377\377*\000\377\3770\000\377\3776\000\377\377<\000\377\377B\000\377\377H\000\377\377N\000\377\377T\000\377\377Z\000\377\377`\000\377\377f\000\377\377l\000\377\377r\000\377\377x\000\377\377~\000\377\377\204\000\377\377\212\000\377\377\220\000\377\377\226\000\377\377\234\000\377\377\242\000\377\377\250\000\377\377\256\000\377\377\264\000\377\377\272\000\377\377\300\000\377\377\306\000\377\377\314\000\377\377\322\000\377\377\330\000\377\377\336\000\377\377\344\000\377\377\352\000\377\377\360\000\377\377\366\000\377\377\374\000\377\377\377\000\373\377\377\000\365\377\377\000\357\377\377\000\351\377\377\000\343\377\377\000\335\377\377\000\327\377\377\000\321\377\377\000\313\377\377\000\305\377\377\000\277\377\377\000\271\377\377\000\263\377\377\000\255\377\377\000\247\377\377\000\241\377\377\000\233\377\377\000\225\377\377\000\217\377\377\000\211\377\377\000\203\377\377\000}\377\377\000w\377\377\000q\377\377\000k\377\377\000e\377\377\000_\377\377\000Y\377\377\000S\377\377\000M\377\377\000G\377\377\000A\377\377\000;\377\377\0005\377\377\000/\377\377\000)\377\377\000#\377\377\000\035\377\377\000\027\377\377\000\021\377\377\000\013\377\377\000\005\377\377\000\000\377'
+
+"""
+GvRaster autoscaling algorithms.
+"""
+
+ASAAutomatic = 0
+ASAPercentTailTrim = 1
+ASAStdDeviation = 2
+
+"""
+OGR Feature Style Anchor points for LABELs.
+"""
+
+GLRA_LOWER_LEFT               = 1
+GLRA_LOWER_CENTER             = 2
+GLRA_LOWER_RIGHT              = 3
+GLRA_CENTER_LEFT              = 4
+GLRA_CENTER_CENTER            = 5
+GLRA_CENTER_RIGHT             = 6
+GLRA_UPPER_LEFT               = 7
+GLRA_UPPER_CENTER             = 8
+GLRA_UPPER_RIGHT              = 9

Added: packages/openev/branches/upstream/current/pymod/gvcorecmds.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvcorecmds.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvcorecmds.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1290 @@
+###############################################################################
+# $Id: gvcorecmds.py,v 1.6 2004/06/03 20:26:07 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Implementation of some sample OpenEV core commands.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2002, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvcorecmds.py,v $
+#  Revision 1.6  2004/06/03 20:26:07  warmerda
+#  added shell command
+#
+#  Revision 1.5  2003/09/17 20:45:21  gmwalter
+#  Get rid of python 2.3 deprecation warning.
+#
+#  Revision 1.4  2003/07/28 19:42:33  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.3  2003/02/20 19:27:20  gmwalter
+#  Updated link tool to include Diana's ghost cursor code, and added functions
+#  to allow the cursor and link mechanism to use different gcps
+#  than the display for georeferencing.  Updated raster properties
+#  dialog for multi-band case.  Added some signals to layerdlg.py and
+#  oeattedit.py to make it easier for tools to interact with them.
+#  A few random bug fixes.
+#
+#  Revision 1.2  2003/02/03 18:38:07  warmerda
+#  Added the ClearView command.
+#
+#  Revision 1.1  2002/12/12 07:42:49  warmerda
+#  New
+#
+#
+
+import gview
+import gvcommand
+
+
+###############################################################################
+# The Commands command.
+class GvCommandsCommand(gvcommand.CommandBase):
+
+    """
+    List all currently loaded commands and display their usage strings.  
+
+    Parameters: 
+        group- used to limit the commands listed to
+               those from a single group (eg. commands core
+               will display the usage strings of the commands
+               in group "core" only).
+
+        /v- verbose mode.  Display full help for commands.
+        
+    """
+    
+    def __init__(self):
+        self.Name = 'commands'
+        self.Usage = 'commands [group] [-v]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'group', type = 'string_word',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'v', type = 'switch', required=0 )
+            ]
+        
+    def execute( self, args, line, interp ):
+        group=args[0]
+        verbose=args[1]
+        
+        commands = gview.app.shell.get_commands()
+        interp.showText( '%-24s %s' % ('Command', 'Usage' ), 'result' )
+        interp.showText( '%-24s %s' % ('-------', '-----' ), 'result' )
+        for cmd in commands:
+            if group is None:
+                if verbose == 0:
+                    interp.showText( '%-24s %s' % (cmd.Name, cmd.Usage),
+                                     'result')
+                else:
+                    txt=interp.get_command_help(cmd.Name,quiet=1)
+                    if txt is None:
+                        interp.showText(cmd.Name+':\nNo help available.\n',
+                                        'result')
+                    else:
+                        interp.showText(cmd.Name+':\n'+txt+'\n','result')
+            else:
+                if (hasattr(cmd,'Group') and (cmd.Group == group)):
+                    if verbose == 0:
+                        interp.showText( '%-24s %s' % (cmd.Name, cmd.Usage),
+                                         'result')                    
+                    else:
+                        txt=interp.get_command_help(cmd.Name,quiet=1)
+                        if txt is None:
+                            interp.showText(cmd.Name+
+                                            ':\nNo help available.\n','result')
+                        else:
+                            interp.showText(cmd.Name+':\n'+txt+'\n','result')
+
+        return 1
+        
+###############################################################################
+# Functions command.
+
+class GvFunctionsCommand(gvcommand.CommandBase):
+    """
+    The functions command is used to list all currently
+    loaded functions, or to scan a python module for
+    functions (module need not be loaded in python
+    shell, but must be accessible within the python
+    path).  
+
+    Parameters:
+
+        module- if a module name is specified, the module
+                will be scanned for functions, and a 
+                list of the results will be displayed.
+                If no module name is specified, the
+                functions currently loaded in the
+                python shell will be displayed.
+
+        /v- verbose mode.  Display full help for each function.
+        
+    """
+    
+    def __init__(self):
+        self.Name = 'functions'
+        self.Usage = 'functions [module] [-v]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'module', type = 'string_word',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'v', type = 'switch', required=0 )
+            ]
+        
+    def execute( self, args, line, interp ):
+        import string
+        import os
+        import Numeric
+
+        modname=args[0]
+        verbose = args[1]
+        
+        local_dict = interp.locals
+        next_txt=''
+        if modname is not None:
+            # User has specified a module name
+            # to search for functions.
+
+            # to do: use the help in the
+            # helpfiles, but only if the module
+            # names match
+            
+            if modname in local_dict.keys():
+                modinst=local_dict[modname]
+                modkeys=dir(local_dict[modname])                
+            else:
+                try:
+                    exec 'import '+modname
+                    exec 'modinst='+modname
+                    exec 'modkeys=dir('+modname+')'
+                except:
+                    interp.showText('Unable to locate module '+modname+'.',
+                                    'error')
+                    return 0
+                
+            # If particular module is specified,
+ 
+            keys_to_show = []
+            for ckey in modkeys:
+                ckey_type=None
+                exec 'ckey_type=type(modinst.'+ckey+')'
+                if (ckey_type == type(Register) and
+                     ckey[0] != "_"):
+                     keys_to_show.append(ckey)
+                elif (ckey_type == type(Numeric.logical_and) and
+                    ckey[0] != "_"):
+                    keys_to_show.append(ckey)
+                elif (ckey_type == type(hasattr) and
+                    ckey[0] != "_"):
+                    keys_to_show.append(ckey)
+    
+            interp.showText( 'Functions: '+modname , 'result' )
+            interp.showText(
+                '------------------------------------------------' , 'result' )
+            keys_to_show.sort()
+            if verbose == 0:
+                count=1
+                for ckey in keys_to_show:
+                    # In non verbose mode, show 2 columns
+                    if (count % 2) == 0:
+                        next_txt=next_txt+'\t'+ckey
+                        interp.showText(next_txt,'result')
+                        next_txt=''
+                    else:
+                        next_txt=next_txt+'%-24s' % ckey
+                    count=count+1
+                return 1
+
+            # Verbose mode
+            for ckey in keys_to_show:
+                # First, check to see if there is help for
+                # the function.
+                
+                txt=interp.get_function_help(ckey,modname,quiet=1)
+                if txt is None:
+                    txt=interp.get_builtin_help(ckey,modname,quiet=1)
+
+                interp.showText( '%s:\n\n%s' % (ckey, txt),'result')
+                
+            return 1
+            
+
+        interp.showText( 'Functions:' , 'result' )
+        interp.showText( '------------------------------------------------' ,
+                         'result' )
+        keys_to_show=[]
+        for ckey in local_dict.keys():
+            if (type(local_dict[ckey]) == type(Register) and
+               ckey[0] != "_"):
+                keys_to_show.append(ckey)
+            elif (type(local_dict[ckey]) == type(Numeric.logical_and) and
+               ckey[0] != "_"):
+                keys_to_show.append(ckey)
+            elif (type(local_dict[ckey]) == type(hasattr) and
+               ckey[0] != "_"):
+                keys_to_show.append(ckey)
+               
+        keys_to_show.sort()
+        count=1
+        for ckey in keys_to_show:
+            if verbose == 1:
+                txt=interp.get_function_help(ckey,quiet=1)
+                if txt is None:
+                    txt=interp.get_builtin_help(ckey,quiet=1)
+                if txt is None:
+                    txt='No help available.'
+                interp.showText( '%s:\n%s' % (ckey, txt),'result')
+            else:
+                # In non verbose mode, show 2 columns
+                if (count % 2) == 0:
+                    next_txt=next_txt+'\t'+ckey
+                    interp.showText(next_txt,'result')
+                    next_txt=''
+                else:
+                    next_txt=next_txt+'%-24s' % ckey
+                
+            count=count+1
+
+        return 1
+
+    
+###############################################################################
+# NewView command.
+class GvNewViewCommand(gvcommand.CommandBase):
+    """
+    Create a new OpenEV view window.
+
+    Parameters:
+
+        title- title for the new view.
+        
+    """
+    
+    def __init__(self):
+        self.Name = 'newview'
+        self.Usage = 'newview [title]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'title', type = 'string_chunk',
+                              required=0 )
+            ]
+        
+    def execute( self, args, line, interp ):
+
+        title = args[0]
+
+        if title is None or title == '':
+            title = None
+
+        if gview.app.shell.standalone == 1:
+            gview.app.new_view(title,menufile='PyshellNewViewMenuFile.xml')
+        else:
+            gview.app.new_view( title )
+        return 1
+        
+###############################################################################
+# ClearView command.
+class GvClearViewCommand(gvcommand.CommandBase):
+    """
+    Clear the active view.
+    
+    The clearview command is used to clear the currently
+    active view.  The currently active view is the one
+    selected in the layer dialog (Edit->Layers); usually,
+    it is the last view modified or clicked on.
+    
+    """
+    
+    def __init__(self):
+        self.Name = 'clearview'
+        self.Usage = 'clearview'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = []
+        
+    def execute( self, args, line, interp ):
+
+        view = gview.app.sel_manager.get_active_view()
+
+        layer_list = view.list_layers()
+        while len(layer_list) > 0:
+            view.remove_layer( layer_list[0] )
+            layer_list = view.list_layers()
+
+        return 1
+        
+###############################################################################
+# View3D Command
+class GvView3DCommand(gvcommand.CommandBase):
+    """
+    Display a 3D raster in the current OpenEV view.
+
+    Parameters:
+
+        demfile- file to use as for elevation values.
+
+        drapefile- file to use as drape.  Defaults to demfile
+                   if drapefile is not specified.
+
+        mesh_lod- mesh level of detail to use in sampling
+                  demfile (numeric).
+
+        hscale- amount to scale demfile by to generate
+                elevation values (numeric).
+
+    """
+    
+    def __init__(self):
+        self.Name = 'view3d'
+        self.Usage = 'view3d <demfile> [drapefile] [mesh_lod] [hscale]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'demfile', type = 'string_word',
+                              required=1 ),
+            gvcommand.ArgDef( name = 'drapefile', type = 'string_word',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'mesh_lod', type = 'numeric',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'hscale', type = 'numeric', required=0 )
+            ]
+        
+    def execute( self, args, line, interp ):
+        
+        dem_filename = args[0]
+        drape_filename = args[1]
+        if drape_filename is None:
+            drape_filename = dem_filename
+        if args[2] is None:
+            mesh_lod = None
+        else:
+            mesh_lod = int(args[2])
+        if args[3] is None:
+            hscale = None
+        else:
+            hscale = float(args[3])
+
+            
+        view_win = gview.app.sel_manager.get_active_view_window()
+
+        view_win.view3d_action( dem_filename, drape_filename, mesh_lod, hscale)
+
+        return 1
+
+###############################################################################
+# Help Command
+
+class GvHelpCommand(gvcommand.CommandBase):
+    """
+    Print help for a function or command.
+
+    The help command is used to print the help for a function
+    or command and/or to launch the help graphical user interface 
+    (GUI).  The help GUI displays help for currently loaded commands 
+    and functions, and may display help on other functions
+    and commands if additional help files have been registered.
+       
+    The procedure followed by the help GUI to locate information
+    is the following:
+
+    1) Look for a function or command of the requested name in 
+       the registered help file text, searching first commands,
+       then functions.  If the function or command is loaded 
+       locally,  try to determine the function or command's 
+       parent module by searching the function/command's attributes.
+       If text is found and the module name specified in 
+       the file matches the function or command's parent
+       module, this text will be displayed.  If the command or
+       function's parent module cannot be found from a search
+       of the function/command's attributes, the text will be 
+       displayed anyway.
+
+    2) If no suitable registered help is found but cf_name is
+       recognized as a loaded command, print the command's usage
+       string and group.
+
+    3) If no registered help is found but cf_name is recognized
+       as a function, print the function's __doc__ string.
+
+    Note that the help GUI takes a "snapshot" of the shell at
+    the time it is launched.  If you load new functions or
+    commands and want to view help on them, relaunch the GUI
+    or use the command line help.
+
+    Parameters:
+
+        cf_name- command or function name.
+
+        /g- launch the help GUI.
+        
+    """
+
+    def __init__(self):
+        self.Name = 'help'
+        self.Usage = 'help [cf_name] [-g]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'cf_name', type = 'string_word',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'g', type = 'switch', required=0 )
+            ]
+
+    def execute(self, args, line, interp):
+        cf_name = args[0]
+        gui_switch=args[1]
+        
+        import Numeric
+
+        if ((cf_name is None) and (gui_switch == 0)):
+            print interp.get_command_help('help')
+            return 1
+            
+
+        if gui_switch == 1:
+            # user requested GUI help
+            import pyshell
+            helpwin=pyshell.PyshellHelpDialog()
+            if cf_name is not None:
+                if cf_name in helpwin.total_cmd_keys:
+                    txt=interp.get_command_help(cf_name)
+                    if cf_name in helpwin.loaded_cmd_keys:
+                        if txt is not None:
+                            txt='\t\t\t'+cf_name+' (command)\n\n'+txt
+                        else:
+                            txt='\t\t\t'+cf_name+\
+                            ' (command)\n\nNo help available.'
+                    
+                    else:
+                        if txt is not None:
+                            txt='\t\t\t'+cf_name+\
+                            ' (command- not loaded)\n'+txt
+                        else:
+                            txt='\t\t\t'+cf_name+\
+                            ' (command- not loaded)\n\nNo help available.'
+                elif cf_name in helpwin.total_func_keys:
+                    txt=interp.get_function_help(cf_name)
+                    if cf_name in helpwin.loaded_func_keys:
+                        if txt is not None:
+                            txt='\t\t\t'+cf_name+\
+                            ' (function)\n\n'+txt
+                        else:
+                            txt='\t\t\t'+cf_name+\
+                            ' (function)\n\nNo help available.'
+                    
+                    else:
+                        if txt is not None:
+                            txt='\t\t\t'+cf_name+\
+                            ' (function- not loaded)\n'+txt
+                        else:
+                            txt='\t\t\t'+cf_name+\
+                            ' (function- not loaded)\n\nNo help available.'
+                elif cf_name in helpwin.total_builtin_keys:
+                    txt=interp.get_builtin_help(cf_name)
+                    if cf_name in helpwin.loaded_builtin_keys:
+                        if txt is not None:
+                            txt='\t\t\t'+cf_name+\
+                            ' (built-in function)\n\n'+txt
+                        else:
+                            txt='\t\t\t'+cf_name+\
+                            ' (built-in function)\n\nNo help available.'
+                    
+                    else:
+                        if txt is not None:
+                            txt='\t\t\t'+cf_name+\
+                            ' (built-in function- not loaded)\n'+txt
+                        else:
+                            txt='\t\t\t'+cf_name+\
+                  ' (built-in function- not loaded)\n\nNo help available.'
+
+                else:
+                    txt=cf_name+\
+                 ' not recognized as a command or function.\nNo help available'
+                    
+                helpwin.update_text(txt)
+            
+            return 1
+
+        # GUI not requested
+        if interp.cmdlist.has_key(cf_name):
+            interp.showText('Command '+cf_name+':','report')
+            txt=interp.get_command_help(cf_name)
+            if txt is None:
+                txt='No help available.\n'
+            interp.showText(txt,'report')
+        elif (interp.locals.has_key(cf_name) and
+              (type(interp.locals[cf_name]) == type(Register))):
+            interp.showText('Function '+cf_name+':','report')
+            txt=interp.get_function_help(cf_name)
+            if txt is None:
+                txt='No help available.\n'
+            interp.showText(txt,'report')
+        elif (interp.locals.has_key(cf_name) and
+              (type(interp.locals[cf_name]) == type(Numeric.cos))):
+            interp.showText('ufunc '+cf_name+':','report')
+            txt=interp.get_function_help(cf_name)
+            if txt is None:
+                txt='No help available.\n'
+            interp.showText(txt,'report')
+        elif (interp.locals.has_key(cf_name) and
+              (type(interp.locals[cf_name]) == type(hasattr))):
+            interp.showText('Built-in function '+cf_name+':','report')
+            txt=interp.get_builtin_help(cf_name)
+            if txt is None:
+                txt='No help available.\n'
+            interp.showText(txt,'report')               
+        elif interp.get_command_help(cf_name) is not None:
+            # command isn't loaded, but is registered
+            interp.showText('Command '+cf_name+': (not loaded)','report')
+            txt=interp.get_command_help(cf_name)
+            interp.showText(txt,'report')
+        elif interp.get_function_help(cf_name) is not None:
+            # command isn't loaded, but is registered
+            interp.showText('Function '+cf_name+': (not loaded)','report')
+            txt=interp.get_function_help(cf_name)
+            interp.showText(txt,'report')
+        elif interp.get_builtin_help(cf_name) is not None:
+            # command isn't loaded, but is registered
+            interp.showText('Built-in function '+cf_name+': (not loaded)',
+                            'report')
+            txt=interp.get_function_help(cf_name)
+            interp.showText(str(txt),'report')
+            
+        else:
+            interp.showText('No help found for '+cf_name+'.','report')
+
+        return 1
+
+###############################################################################
+# GvLocalsCommand(gvcommand.CommandBase):
+class GvLocalsCommand(gvcommand.CommandBase):
+    """
+    List local variables and functions loaded/created by the user.  
+
+    Parameters:
+
+        type- type of object to list.  eg. locals array
+              will list all Numeric python arrays currently
+              in the environment.  type may also be a
+              comma separated list of variable types to 
+              display.  If type is not specified, all
+              variables/functions loaded by the user will
+              be listed.
+
+    """
+    
+    def __init__(self):
+        self.Name='locals'
+        self.Usage = 'locals [type]'
+                     
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'type', type = 'string_chunk')
+            ]
+
+    def execute(self, args, line, interp):
+        searchtypes=args[0]
+        import gvshell
+
+        if ((searchtypes is not None) and (len(searchtypes) > 0)):
+            import string
+            typelist=string.split(searchtypes,',')
+            txtlst=gvshell.local_vars_list(interp.locals,typelist)
+        else:
+            txtlst=gvshell.local_vars_list(interp.locals)
+
+        for txt in txtlst:
+            interp.showText(txt,'result')
+
+        return 1
+
+###############################################################################
+# execute a series of pyshell lines (commands or functions) from a text file
+#
+# NOTE: This is really a placeholder- the interpreter should intercept macro
+#       command lines at the top level and deal with them there because this
+#       particular command could not be implemented using the GvCommand
+#       structure (it would have resulted in nested interpreter "push" calls,
+#       which lead to infinite loops because of buffer clearing issues).  The
+#       SOLE purpose of putting this placeholder here is so that the various
+#       help functions will treat the macro command as a regular command
+#       (macro should look like any other command to the user).
+#       
+class GvMacroCommand(gvcommand.CommandBase):
+    """
+    Run a sequence of python statements and commands from a file.  
+
+    The macro command will look for macro_file in the
+    following locations, using the first one it finds:
+
+    1) macro_file
+    2) OPENEV_MACRO_PATH/macro_file
+    3) OPENEVHOME/macros/macro_file
+    4) OPENEV_HOME/macros/macro_file
+
+    where OPENEV_MACRO_PATH, OPENEVHOME, and OPENEV_HOME
+    are environment variables.  OPENEV_MACRO_PATH may 
+    contain multiple search directories separated by
+    semi-colons (;).
+
+    Parameters:
+
+        macro_file- name of the macro text file to run.
+                    OpenEV macros are text files and must
+                    start with the line:
+                    # openev macro
+                    
+    """
+    
+    def __init__(self):
+        self.Name = 'macro'
+        self.Usage = 'macro <macro_file>'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'macro_file', type = 'string_word',
+                              required=1 )
+            ]
+
+    def execute(self, args, line, interp):
+        interp.showText('In macro placeholder command- should not be here!',
+                        'error')
+        return 1
+
+    
+############################################################
+# Store commands to a file
+
+class GvJournalCommand(gvcommand.CommandBase):
+    """
+    Store statements entered at the command line to a text file.
+
+    Parameters:
+
+        filename- file to store text in (required the first
+                  time the journal command is entered in a
+                  given session). 
+
+        mode- whether to turn journaling on (/on) or off (/off).  
+              Defaults to /on.
+
+        umode- whether to append to filename (/a) or overwrite
+               it (/w).  Defaults to /a.
+               
+    """
+    
+    def __init__(self):
+        self.Name = 'journal'
+        self.Usage = "journal <filename> [mode] [umode]" 
+        
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'filename', type = 'string_word',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'mode', type ='multi_switch', required=0,
+                              valid_list=['-off','-on','/off','/on'] ), 
+            gvcommand.ArgDef( name = 'umode', type = 'multi_switch',
+                              required=0,
+                              valid_list=['/w','-w','/a','-a'])            
+            ]
+
+    def execute(self, args, line, interp):
+        fname = args[0]
+        state=args[1]
+        mode=args[2]
+
+        # If journal file was open before,
+        # close it.
+        
+        if interp.journal_fh is not None:
+            interp.journal_fh.close()
+            interp.journal_fh = None
+
+        if (state is not None) and (state in ['-off','/off']):
+            return 1
+
+        if ((fname is not None) and (len(fname) == 0) and
+            (interp.journal_fname is None)):
+            fname = SelectFile("Journal File") 
+            
+        if (fname is not None) and (len(fname) > 0):
+            interp.journal_fname=fname
+
+        if interp.journal_fname is not None:
+            if ((mode is not None) and ((mode == 'w') or
+               (mode == '/w') or (mode == '-w'))):
+                interp.journal_fh=open(interp.journal_fname,'w')
+                interp.showText('overwriting '+interp.journal_fname,'result')
+            else:
+                interp.journal_fh=open(interp.journal_fname,'a')
+                interp.showText('appending to '+interp.journal_fname, 'result')
+
+            return 1
+        else:
+            interp.showText(
+                'Unable to launch journaling- no filename specified','error')
+            return 0
+
+        
+#############################################################################
+# import a group of commands
+class GvLoadextCommand(gvcommand.CommandBase):
+    """
+    Load a command extension module.
+
+    The loadext command is used to load an extension
+    module of OpenEV commands.  This is similar
+    to the import keyword for pure python code.
+    
+    """
+    
+    def __init__(self):
+        self.Name = 'loadext'
+        self.Usage = 'loadext <extension_name>'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'extension_name', type = 'string_word',
+                              required=1 )
+            ]
+
+    def execute(self, args, line, interp):
+
+        # Get the module name
+        module_name = args[0]
+
+        # Get the current python shell
+        import gview
+        
+        exec "import " + module_name
+        exec "reload(" + module_name + ")"
+        exec module_name + '.Register(gview.app.shell)'
+
+        return 1
+
+############################################################
+# Get the currently active raster or vector file and load up the relevant part
+
+class GvGetCommand(gvcommand.CommandBase):
+    """
+    Get data from the active OpenEV view/layer.
+
+    The get command is used to grab data from the currently 
+    active OpenEV view/layer and place it in variable
+    <varname> in the python shell.  If the /s option
+    is specified, the data grabbed will be screenshot-style; 
+    otherwise, data will be retrieved from the underlying
+    raster or shapes object.  
+
+    If the view's active layer is a raster and a region of 
+    interest (ROI) is drawn and /s is not specified, data will
+    only be extracted from the ROI.  The ROI extracted is 
+    always a rectangle, so if the view is in georeferenced 
+    display mode, the corners will be reprojected to
+    the raster's pixel/line space and a rectangle will
+    be chosen to encompass the full area specified
+    by the ROI plus extra on the edges to make it 
+    rectangular.  ROI's are ignored if /s is specified.
+
+    If the view's active layer is a shapes layer
+    and shapes are selected, only those shapes will be 
+    extracted.
+
+    In all cases, it is a COPY of the data that is
+    extracted; the original will be unchanged by changes
+    to varname.
+
+    Note that the /s option may not always work- some
+    OpenGL drivers give flakey results. 
+
+    Parameters:
+
+        varname- name of python shell variable to extract 
+                 data to (will overwrite any existing
+                 variable by that name).
+      
+        /s- screenshot mode switch (off by default).
+        
+    """
+    
+    def __init__(self):
+        self.Name = 'get'
+        self.Usage = 'get <varname> [/s]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'varname', type = 'string_token',
+                              required=1 ),
+            gvcommand.ArgDef( name = 's', type = 'switch', required=0 )
+            ]
+
+    def execute(self, args, line, interp):
+ 
+        import gvutils
+        import gview
+        import gdalnumeric
+        
+        clayer = gview.app.sel_manager.get_active_layer()
+        if clayer is None:
+            interp.showText('No layer is currently active!','error')
+            return 0
+ 
+
+        try:
+            roi=gview.app.toolbar.get_roi()
+        except:
+            roi=None
+
+        # /s argument is 1 if user requested screenshot, 0
+        # if the underlying data was requested (default).
+        # NOTE: roi is ignored for screenshot option
+        is_ss=args[1]
+        shell_vars={}
+        
+        if is_ss == 1:
+            cview=gview.app.sel_manager.get_active_view()
+            if roi is not None:
+                txt='Warning- ROI is ignored for screenshot-style get.\n'+\
+                     'Grabbing whole view area.\n'
+                interp.showText(txt,'error')
+
+            # Note: for now, assume colour mode to start with (since
+            # even single band images may have luts applied), and
+            # if all three bands are identical in the end (greyscale
+            # equivalent), return 1.
+
+            err=cview.print_to_file(cview.get_width(),cview.get_height(),
+                                    '_temp.tif','GTiff',1)
+
+            import os
+            
+            if err != 0:
+                interp.showText(
+            'Error grabbing screenshot- unable to generate temporary file.\n',
+            'error')
+                os.unlink('_temp.tif')
+                return 0
+
+            try:
+                import Numeric
+                new_arr=gdalnumeric.LoadFile('_temp.tif')
+                if ((max(Numeric.ravel(Numeric.fabs(new_arr[0,:,:] - new_arr[1,:,:])))
+                     == 0) and
+                    (max(Numeric.ravel(Numeric.fabs(new_arr[2,:,:] - new_arr[1,:,:])))
+                     == 0)):
+                    shp=Numeric.shape(new_arr)
+                    new_arr=Numeric.reshape(new_arr[0,:,:],(shp[1],shp[2]))
+            except:
+                interp.showText(
+                'Error grabbing screenshot- unable to load temporary file.\n',
+                'error')
+                os.unlink('_temp.tif')
+                return 0
+
+            shell_vars[args[0]]=new_arr
+            os.unlink('_temp.tif')
+
+            return(1,shell_vars)
+        
+        if gvutils.is_of_class(clayer.__class__,'GvRasterLayer'):
+            ds=clayer.get_parent().get_dataset()
+                
+            if roi is None:
+                shell_vars[args[0]]=gdalnumeric.DatasetReadAsArray(ds)
+                return (1,shell_vars)
+            else:
+                # Here, need to check if georeferencing is on or not and
+                # convert to pixel/line coordinates if it is on.
+                cview=gview.app.sel_manager.get_active_view()                
+                if (cview.get_raw(clayer) == 0):
+                    # view is georeferenced- convert corners
+                    [pixel,line] = clayer.view_to_pixel(roi[0],roi[1])
+                    [pixel2,line2] = clayer.view_to_pixel(roi[0]+roi[2],
+                                                          roi[1]+roi[3])
+                    [pixel3,line3] = clayer.view_to_pixel(roi[0],
+                                                          roi[1]+roi[3])
+                    [pixel4,line4] = clayer.view_to_pixel(roi[0]+roi[2],
+                                                          roi[1])
+
+                    # Get pixel-space rectangle (offsets of 1 ensure that
+                    # only pixels fully enclosed by the roi are included-
+                    # int casting will round floating point pixel/line
+                    # values down)
+                    max_pix = int(max(pixel,pixel2,pixel3,pixel4))
+                    min_pix = int(min(pixel,pixel2,pixel3,pixel4))+1
+                    max_line = int(max(line,line2,line3,line4))
+                    min_line = int(min(line,line2,line3,line4))+1
+
+                    # in pixel/line space, selected region is a parallelogram
+                    # but not necessarily a rectangle.  Choose a rectangle
+                    # that fully encloses the parallelogram
+                    roi = (min_pix,min_line,max_pix-min_pix,max_line-min_line)
+
+                
+                shell_vars={}
+                shell_vars[args[0]]=gdalnumeric.DatasetReadAsArray(ds,roi[0],
+                                                   roi[1],roi[2],roi[3]) 
+                return (1,shell_vars)
+        elif gvutils.is_of_class(clayer.__class__,'GvShapesLayer'):
+            shps=clayer.get_parent()
+            selected = clayer.get_selected()
+
+            if len(selected) == 0:
+                newshps = gview.GvShapes()
+                for item in shps.get_schema():
+                    newshps.add_field(item[0],item[1],item[2],item[3])
+                for shp in shps:
+                    if shp is not None:
+                        newshps.append(shp.copy())
+                    
+                shell_vars={}
+                shell_vars[args[0]]=newshps
+                return (1,shell_vars)
+            else:
+                newshps = gview.GvShapes()
+                for item in shps.get_schema():
+                    newshps.add_field(item[0],item[1],item[2],item[3])
+                for idx in selected:
+                    newshps.append(shps[idx].copy())
+                shell_vars={}
+                shell_vars[args[0]]=newshps
+                return (1,shell_vars)          
+        else:
+            interp.showText(
+                'Active layer is not a raster or recognized vector layer!',
+                'error')
+            return 0
+
+######################################################################
+#
+# Display a variable in a view
+#
+class GvShowCommand(gvcommand.CommandBase):
+    """
+    Display a variable in an OpenEV view.
+
+    The show command displays variable varname in an
+    OpenEV view.  
+
+    Parameters:
+
+        varname- python shell variable to display.  Must
+                 be either a 1-D, 2-D, or 3-D Numeric python
+                 array or a GvShapes variable.
+
+        /nocopy- switch to indicate that the original
+                 data must be displayed rather than a
+                 copy.  Later changes made to varname will
+                 be reflected in the view when the 
+                 refresh button is pressed; and
+                 changes made through the view will
+                 be reflected in varname (eg. shapes
+                 deletion).  Off by default.
+
+        /o- switch to indicate that varname should be
+            displayed in the current view rather than
+            in a new view.
+            
+    """
+    
+    def __init__(self):
+        self.Name = 'show'
+        self.Usage = 'show <varname> [/nocopy] [/o]'
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'varname', type = 'variable',
+                              required=1 ),
+            gvcommand.ArgDef( name = 'nocopy', type = 'switch', required=0 ),
+            gvcommand.ArgDef( name = 'o', type = 'switch', required=0 ) 
+             ]
+
+    def execute(self, args, line, interp):
+        import gdalnumeric
+        import gview
+
+        data=args[0][0]
+        dataname=args[0][1]
+        if data is None:
+            interp.showText('No input variable supplied','error')
+            return 0
+        
+        ncswitch=args[1]
+        vswitch=args[2]
+
+
+        if vswitch == 0:
+            import gview
+            win = gview.app.new_view()
+            if gview.app.shell.standalone == 1:
+                item = win.menuf.find('File/Exit')
+                if item is not None:
+                    item.hide()
+                item = win.menuf.find('File/New View')
+                if item is not None:
+                    item.hide()
+                item = win.menuf.find('File/Save Project')
+                if item is not None:
+                    item.hide()
+
+        import Numeric
+        if type(data) == type(Numeric.zeros([1,1])):
+            if ncswitch == 0:
+                # By default, display a copy of the
+                # data rather than the original
+                import copy
+                if len(Numeric.shape(data)) == 1:
+                    # reshape 1x1 arrays so they can be
+                    # displayed.
+                    newdata=Numeric.reshape(data,(1,Numeric.shape(data)[0]))
+                else:
+                    newdata=copy.deepcopy(data)
+            else:
+                if len(Numeric.shape(data)) == 1:
+                    txt='Vectors for display must have dimensions Nx1 or 1xN,\n'
+                    txt=txt+'not N.  Either reshape the array, or turn off the nocopy\nswitch '
+                    txt=txt+'so that show is permitted to reshape it for you.'
+                    interp.showText(txt,'error')
+                    return 0
+                
+                newdata=data
+            
+            try:
+                # Only array data should get to here
+                array_name = gdalnumeric.GetArrayFilename(newdata)
+                ds = gview.manager.get_dataset( array_name )
+                #if prototype_name is not None:
+                #    prototype_ds = gdal.Open( prototype_name )
+                #    gdalnumeric.CopyDatasetInfo( prototype_ds, ds )
+
+                gview.app.file_open_by_name( array_name )
+                return 1
+            except:
+                interp.showText('Unable to open array.','error')
+                return 0
+            
+        elif type(data) == type(gview.GvShapes()):
+            
+            cview = gview.app.sel_manager.get_active_view_window()
+            cview.make_active()
+
+            # Get an okay layer name
+            layer_list = cview.viewarea.list_layers()
+            layer_map = {}           
+            for clayer in layer_list:          
+                layer_map[clayer.get_name()]=clayer
+            counter = 0
+                
+            name = dataname
+            while layer_map.has_key(name):
+                counter = counter + 1
+                name = dataname + '_'+str(counter)
+
+            if ncswitch == 0:
+                newdata=gview.GvShapes()
+                for item in data.get_schema():
+                    newdata.add_field(item[0],item[1],item[2],item[3])
+                for shp in data:
+                    if shp is not None:
+                        newdata.append(shp.copy())
+            else:
+                newdata=data
+                    
+            newdata.set_name(name)
+            gview.undo_register(newdata)
+            layer = gview.GvShapesLayer(newdata)     
+            cview.viewarea.add_layer(layer)
+            return 1
+        else:
+            interp.showText('Unable to recognize input data type.','error')
+            return 0
+
+#######################################################################
+#
+# Save a variable to file            
+#
+class GvSaveCommand(gvcommand.CommandBase):
+    """
+    Save a python shell variable to a file.
+
+    The save command saves python shell variable varname
+    to file filename.
+
+    Parameters:
+
+        varname- python shell variable to save.  Must be
+                 either a Numeric python array or a 
+                 GvShapes object.
+
+        filename- filename to save varname to.
+
+        format- Only for varnames of Numeric python array
+                type.  Raster file format to use in save.
+                Must be a GDAL write-supported format.
+
+        dataset- Only for varnames of Numeric python array
+                 type.  Dataset to copy metadata from in
+                 saving.
+                 
+    """
+    
+    def __init__(self):
+        self.Name = 'save'
+        self.Usage = 'save <varname> <filename> [format] [dataset]' 
+        
+        self.HelpURL = ''
+        self.Group = 'core'
+        self.Args = [
+            gvcommand.ArgDef( name = 'varname', type = 'variable',
+                              required=1 ),
+            gvcommand.ArgDef( name = 'filename', type = 'string_word',
+                              required=1 ),
+            gvcommand.ArgDef( name = 'format', type='string_word',
+                              required=0 ),
+            gvcommand.ArgDef( name = 'dataset', type = 'variable',
+                              required=0 )            
+            ]                  
+
+    def execute(self, args, line, interp):
+        import gdalnumeric
+        import gview
+
+        data=args[0][0]
+        dataname=args[0][1]
+        fname = args[1]
+        fmt = args[2]
+        dataset=args[3]
+        if dataset is not None:
+            dataset=dataset[0]
+        
+        if data is None:
+            interp.showText('No input variable supplied','error')
+            return 0
+        
+        import Numeric
+        if type(data) == type(Numeric.zeros([1,1])):
+            
+            # Only array data should get to here                
+            if ((fmt is None) or (len(fmt) == 0)):
+                fmt = 'GTiff'
+
+            try:
+                gdalnumeric.SaveArray(data,fname,fmt,dataset)
+                return 1
+            except ValueError:
+                txt = fmt + ' format not available.  Available formats are:\n'
+                import gdal
+                for cDriver in gdal.GetDriverList():
+                    txt = txt + cDriver.ShortName + ' '
+                interp.showText(txt,'error')
+            
+        else:
+            try:
+                if fmt is not None:
+                    txt='Warning: format option is only available for rasters.\n'
+                    txt=txt+'         '+fname+\
+                    ' will be saved in shapefile format.'
+                    interp.showText(txt,'error')
+
+                if data.save_to(fname) == 0:
+                    interp.showText('Unable to save '+\
+                                    dataname+' to file '+fname,'error')
+                    return 0
+
+                return 1
+            except:
+                interp.showText('Unable to save '+\
+                                dataname+' to file '+fname,'error')
+
+         
+###############################################################################
+# The Shell command.
+class GvShellCommand(gvcommand.CommandBase):
+    """
+    Execute a shell command.
+
+    The shell command executes the rest of the line as an external
+    operating system command (ie. via a DOS or unix shell).  Output
+    is redirected to the Python shell window.
+
+    Note that the parent process is effectively blocked till the
+    subcommand terminates.
+
+    """
+
+    def __init__(self):
+        self.Name = 'shell'
+        self.Usage = 'shell <operating system command>'
+        self.HelpURL = ''
+        self.Group = 'core'
+
+        self.Args = [
+            gvcommand.ArgDef( name = 'os_command', type = 'string_chunk' )
+            ]
+
+    def execute( self, args, line, anal_win ):
+
+        import os
+        #os.system( args[0] )
+
+        out_fd = os.popen( args[0], 'r' )
+        result = out_fd.read()
+        print result
+
+        return 1
+        
+
+def RegisterHelp( target ):
+    # If the gvcorecmd_help.txt file
+    # is present, register it.
+    import os
+    bpath,junk=os.path.split(__file__)
+    hfname=os.path.join(bpath,'gvcorecmds_help.txt')
+    if os.path.isfile(hfname):
+        target.add_helpfile(hfname)
+
+def Register( target ):
+
+    target.add_command( GvCommandsCommand() )
+    target.add_command( GvFunctionsCommand() )
+    target.add_command( GvHelpCommand() )
+    target.add_command( GvView3DCommand() )
+    target.add_command( GvNewViewCommand() )
+    target.add_command( GvClearViewCommand() )
+    target.add_command( GvLoadextCommand() )
+    target.add_command( GvMacroCommand() ) 
+    target.add_command( GvJournalCommand() )  
+    target.add_command( GvLocalsCommand() )
+    target.add_command( GvGetCommand() )
+    target.add_command( GvShellCommand() )    
+    target.add_command( GvShowCommand() )    
+    target.add_command( GvSaveCommand() )    
+    
+    RegisterHelp( target )
+    
+    
+        

Added: packages/openev/branches/upstream/current/pymod/gvhtml.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvhtml.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvhtml.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,164 @@
+###############################################################################
+# $Id: gvhtml.py,v 1.14 2004/11/04 14:32:33 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Methods for displaying HTML documentation.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvhtml.py,v $
+#  Revision 1.14  2004/11/04 14:32:33  gmwalter
+#  Avoid some of the problems with anchors.
+#
+#  Revision 1.13  2004/03/12 21:01:00  gmwalter
+#  Add image flip key sequences, avoid help
+#  popping up when horizontal flip sequence
+#  entered.
+#
+#  Revision 1.12  2003/12/06 17:47:45  gmwalter
+#  Try using webbrowser module before giving up on finding a browser.
+#
+#  Revision 1.11  2003/01/24 15:44:32  warmerda
+#  browse command may be None if browser not found
+#
+#  Revision 1.10  2002/04/17 14:43:06  warmerda
+#  use gvutils.FindExecutable()
+#
+#  Revision 1.9  2001/03/19 21:57:14  warmerda
+#  expand tabs
+#
+#  Revision 1.8  2000/10/19 03:22:15  warmerda
+#  Fixed to avoid launching two browsers on NT.  Report
+#  error if no browser found on unix.
+#
+#  Revision 1.7  2000/08/14 18:53:57  warmerda
+#  add space to command just before use
+#
+#  Revision 1.6  2000/08/11 16:00:51  warmerda
+#  added %s substitution and preference support
+#
+#  Revision 1.5  2000/08/10 15:58:46  warmerda
+#  added set_help_topic support
+#
+#  Revision 1.4  2000/06/13 15:16:20  warmerda
+#  Added gnome-help-browser, though it doesn't seem to understand http.
+#
+#  Revision 1.3  2000/06/09 02:41:41  warmerda
+#  use gv_launch_url
+#
+#  Revision 1.2  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+import gview
+import os
+import os.path
+import string
+import _gv
+import gtk
+import GDK
+import gview
+import gvutils
+
+def GetBrowseCommand():
+    if gview.get_preference('html_browser'):
+        return gview.get_preference('html_browser')
+
+    # On NT we don't try to find executables, so that we will default
+    # to using gv_launch_url().
+    if os.name == "nt":
+        return ''
+
+    exe_names = ['netscape', 'mozilla', 'mosaic', 'gnome-help-browser' ]
+    for name in exe_names:
+        full_path = gvutils.FindExecutable(name)
+        if (full_path is not None) and (full_path != ''):
+            return full_path
+
+    return ''
+
+def SetBrowseCommand(command):
+    global html_browse_command
+    html_browse_command = command
+    gview.set_preference('html_browser', command)
+    
+
+def LaunchHTML( page_name ):
+    """Display indicated HTML page.
+
+    If the passed name is not an absolute path name, nor has an http: prefix,
+    it will be massaged to point into the GView html help tree."""
+
+    if not os.path.isabs(page_name) and page_name[:5] != 'http:':
+        page_name = os.path.abspath( \
+            os.path.join(gview.home_dir,'html',page_name) )
+        
+    global html_browse_command
+
+    if page_name[:5] != 'http:':
+        page_name = 'file://'+page_name
+    
+    if html_browse_command == '':
+        html_browse_command = GetBrowseCommand()
+
+    if os.name == "nt" and html_browse_command == '':
+        try:
+            import webbrowser
+            webbrowser.open(page_name)
+        except:    
+            _gv.gv_launch_url( page_name )
+            
+        return
+    
+    if html_browse_command == '' or html_browse_command is None:
+        try:
+            import webbrowser
+            webbrowser.open(page_name)
+        except:
+            gvutils.warning( 'Unable to display HTML online help, browser not configured.' )
+        return
+
+    if string.find(html_browse_command,"%s") > -1:
+        full_command = string.replace(html_browse_command,"%s",page_name)+" &"
+    else:
+        full_command = html_browse_command + ' ' + page_name + ' &'
+
+    os.system( full_command )
+    
+def f1_help_cb( item, event, topic, *args ):
+    if (event.keyval == GDK.F1) and not (event.state & GDK.CONTROL_MASK):
+        LaunchHTML( topic )
+        
+def set_help_topic( object, topic ):
+    """Set a help topic for a widget.
+
+    The help topic (an html file) is launched if the user hits F1 over the
+    widget.
+
+    topic -- topic name, such as 'edittools.html', suitable for use
+    with the LaunchHTML() function."""
+    
+    object.connect( "key_press_event", f1_help_cb, topic )
+
+html_browse_command = ''
+
+
+

Added: packages/openev/branches/upstream/current/pymod/gview.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gview.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gview.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4727 @@
+###############################################################################
+# $Id: gview.py,v 1.212 2005/10/17 19:19:15 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Shadow classes for OpenEV C to Python bindings
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gview.py,v $
+#  Revision 1.212  2005/10/17 19:19:15  gmwalter
+#  Wrap gvshape delete_ring function.
+#
+#  Revision 1.211  2005/09/12 15:33:10  gmwalter
+#  Update autopan tool for line paths.
+#
+#  Revision 1.210  2005/07/06 18:16:47  gmwalter
+#  Set translation directly in opening projects
+#  (using relative translations was causing
+#  problems in some applications when view
+#  translation wasn't 0,0 to start with).
+#
+#  Revision 1.209  2005/04/06 14:08:46  gmwalter
+#  Bring gvdata freeze/thaw functions up to Python level.
+#
+#  Revision 1.208  2005/02/22 13:22:38  gmwalter
+#  Add autopan tool.
+#
+#  Revision 1.207  2005/01/17 18:37:51  gmwalter
+#  Add ability to reset tool cursor type.
+#
+#  Revision 1.206  2005/01/14 16:51:51  gmwalter
+#  Checked in Aude's gv_shapes_add_shape_last function
+#  (allows shapes to be added without repeating
+#  indices if others have been deleted).
+#
+#  Revision 1.205  2005/01/14 15:30:16  warmerda
+#  Added flip flag access, and save/restore in project file
+#
+#  Revision 1.204  2005/01/04 18:50:31  gmwalter
+#  Checked in Aude's new gvshape function changes.
+#
+#  Revision 1.203  2004/09/30 21:10:51  warmerda
+#  added sink() in lots of appropriate places
+#
+#  Revision 1.202  2004/08/20 13:58:30  warmerda
+#  GvPqueryLayer: GvShapes in constructor, serialize/deserialize support
+#
+#  Revision 1.201  2004/08/18 17:27:50  warmerda
+#  Don't blow a gasket if the histogram is empty in
+#  GvRasterLayer.equalize().
+#
+#  Revision 1.200  2004/07/02 16:40:51  dem
+#  - Implement project files portability
+#  - last_strech restored in projects reloading
+#  - add a "File/Save Project as..." menu
+#
+#  Revision 1.199  2004/06/23 14:35:06  gmwalter
+#  Added support for multi-band complex imagery.
+#
+#  Revision 1.198  2004/04/21 14:44:29  andrey_kiselev
+#  Fixed problem with reading NODATA value from the project file.
+#
+#  Revision 1.197  2004/04/13 15:07:13  gmwalter
+#  Fix raster layer set_source so that it
+#  gets min/max values from raster rather
+#  than uninitialized sources in itself.
+#
+#  Revision 1.196  2004/02/27 14:17:05  warmerda
+#  applied fix to preserve no-data value when stretching
+#
+#  Revision 1.195  2004/02/18 16:59:51  andrey_kiselev
+#  Determine real mix/max values in GvRasterLayer.set_source().
+#
+#  Revision 1.194  2004/02/12 22:36:07  gmwalter
+#  Add functions for easily creating a line from three
+#  lists of nodes (x,y,z), avoiding python-level for
+#  loop.
+#
+#  Revision 1.193  2004/02/10 15:49:59  andrey_kiselev
+#  Added GvManager.add_dataset method.
+#
+#  Revision 1.192  2004/01/22 20:54:58  andrey_kiselev
+#  New methods in GvRasterLayer class: nodata_get(), nodata_set(), type_get().
+#  get_nodata() method now deprecated (though, it does the same as nodata_get).
+#
+#  Revision 1.191  2003/10/03 13:58:58  gmwalter
+#  Avoid deprecation warning.
+#
+#  Revision 1.190  2003/09/16 15:43:12  gmwalter
+#  Add single selection mode to select tool, checked in developer tutorials.
+#
+#  Revision 1.189  2003/09/12 18:32:32  pgs
+#  modified get_preference to return default, added set_default_preferences
+#
+#  Revision 1.188  2003/09/11 20:00:32  gmwalter
+#  Add ability to specify a preferred polynomial order for warping a raster,
+#  and add "safe mode" (only used if ATLANTIS_BUILD is defined).
+#
+#  Revision 1.187  2003/09/02 17:27:11  warmerda
+#  Added GvSymbolManager support on the GvShapesLayer.
+#  Added has_symbol() and get_names() methods on symbol manager.
+#  Added serialize/deserialize support for symbol manager.
+#
+#  Revision 1.186  2003/08/29 20:53:51  warmerda
+#  use C GvShape xml serialize/deserialize version
+#
+#  Revision 1.185  2003/08/26 17:36:03  warmerda
+#  added recode()
+#
+#  Revision 1.184  2003/08/20 20:02:32  warmerda
+#  Added collection support to GvShape.
+#  Fixed integer type support in GvRecords.add_field().
+#
+#  Revision 1.183  2003/08/14 19:07:27  warmerda
+#  added MultiStratifiedCollect()method on GvRecords
+#
+#  Revision 1.182  2003/08/11 15:34:34  warmerda
+#  don't save nodata attribute for raster layer sources if not set
+#
+#  Revision 1.181  2003/08/06 22:25:15  warmerda
+#  added progress monitor to gv_records load/save funcs
+#
+#  Revision 1.180  2003/08/06 17:10:20  warmerda
+#  added selection option to GvRecords.save_to_dbf()
+#
+#  Revision 1.179  2003/08/05 16:21:58  warmerda
+#  fleshed out GvRecords implementation
+#
+#  Revision 1.178  2003/06/25 17:08:57  warmerda
+#  added rotate tool
+#
+#  Revision 1.177  2003/05/23 16:18:17  warmerda
+#  added GvRecords for CIETMap
+#
+#  Revision 1.176  2003/04/13 04:24:05  warmerda
+#  dont flake out on none shapes when serializing
+#
+#  Revision 1.175  2003/04/08 11:59:47  andrey_kiselev
+#  Added save_vector_symbol() wrapper for gv_symbol_manager_save_vector_symbol().
+#
+#  Revision 1.174  2003/04/02 15:47:00  pgs
+#  added wrapper for gv_format_point_query
+#
+#  Revision 1.173  2003/03/07 17:03:03  gmwalter
+#  Move last_complex_lut property setting down to c-level, fix magphase indeterminate
+#  phase colour setting.
+#
+#  Revision 1.172  2003/02/28 16:50:28  warmerda
+#  Added GvSymbolManager support
+#
+#  Revision 1.171  2003/02/20 19:27:20  gmwalter
+#  Updated link tool to include Diana's ghost cursor code, and added functions
+#  to allow the cursor and link mechanism to use different gcps
+#  than the display for georeferencing.  Updated raster properties
+#  dialog for multi-band case.  Added some signals to layerdlg.py and
+#  oeattedit.py to make it easier for tools to interact with them.
+#  A few random bug fixes.
+#
+#  Revision 1.170  2002/10/30 21:26:47  warmerda
+#  Paul added pick_shape
+#
+#  Revision 1.169  2002/09/11 20:40:50  warmerda
+#  fixed so that 3d views can be saved
+#
+#  Revision 1.168  2002/09/10 21:17:46  warmerda
+#  change strstr() to str() in 3d view stuff
+#
+#  Revision 1.167  2002/09/10 13:27:28  warmerda
+#  added get_height_scale method
+#
+#  Revision 1.166  2002/08/01 22:01:05  warmerda
+#  removed debug print
+#
+#  Revision 1.165  2002/08/01 20:11:45  warmerda
+#  minor changes for vector capable classifications
+#
+#  Revision 1.164  2002/08/01 14:53:17  pgs
+#  removed superfluous XMLFind in gvviewarea initialize_from_xml
+#
+#  Revision 1.163  2002/07/25 15:49:14  warmerda
+#  fix int overflow problem with equalize()
+#
+#  Revision 1.162  2002/07/24 20:33:22  warmerda
+#  added gv_shape_get_property
+#
+#  Revision 1.161  2002/07/24 14:49:33  warmerda
+#  added one-field option to get_schema
+#
+#  Revision 1.160  2002/07/23 16:39:50  pgs
+#  minor bug fix in gvshapeslayer serialize
+#
+#  Revision 1.159  2002/07/18 19:43:53  warmerda
+#  added gv_shapes_get_typed_properties
+#
+#  Revision 1.158  2002/07/18 19:35:16  pgs
+#  added GvShapes.save_to_dbf()
+#
+#  Revision 1.157  2002/07/18 17:46:42  warmerda
+#  Added get_layout() on GvShapes
+#
+#  Revision 1.156  2002/07/16 14:51:30  warmerda
+#  serialize all shapes if there is no _filename
+#
+#  Revision 1.155  2002/07/16 14:31:23  warmerda
+#  removed serialization of selection, made stuff Python 2.0 compatible
+#
+#  Revision 1.154  2002/07/16 14:17:31  warmerda
+#  added support for getting background color
+#
+#  Revision 1.153  2002/07/15 20:27:12  pgs
+#  added XML serialization support for GvShapesLayer and ancestors\nand added restoration of raster layer classifications
+#
+#  Revision 1.152  2002/07/12 12:46:05  warmerda
+#  expanded tabs
+#
+#  Revision 1.151  2002/07/08 19:46:03  warmerda
+#  added project save/load capability
+#
+#  Revision 1.150  2002/07/07 21:06:14  warmerda
+#  preliminary addition of project saving
+#
+#  Revision 1.149  2002/06/27 15:43:51  warmerda
+#  added preliminary serialize() support
+#
+#  Revision 1.148  2002/04/12 14:40:37  gmwalter
+#  Removed the gvmesh rescale function (not needed because of view area
+#  rescaling).
+#
+#  Revision 1.146  2002/03/07 18:31:56  warmerda
+#  added preliminary gv_shape_clip_to_rect() implementation
+#
+#  Revision 1.145  2002/03/07 02:31:56  warmerda
+#  added default_height to add_height functions
+#
+#  Revision 1.144  2002/03/04 16:00:09  warmerda
+#  fixed autoscaling with equalization
+#
+#  Revision 1.143  2002/02/28 18:52:22  gmwalter
+#  Added a point-of-interest tool similar to the region-of-interest
+#  tool (allows a user to select a temporary point without having to add a
+#  new layer).  Added a mechanism to allow some customization of openev
+#  via a textfile defining external modules.
+#
+#  Revision 1.142  2002/01/18 05:48:13  warmerda
+#  added GvShapes.get_extents() method in python
+#
+#  Revision 1.141  2001/12/11 17:51:13  warmerda
+#  fixed GvRasterLayer.autoscale() to call self.autoscale_view() when appr.
+#
+#  Revision 1.140  2001/12/11 15:12:03  gmwalter
+#  Increased default cache sizes for openev, updated mkdist to
+#  copy files under their original rather than linked names.
+#
+#  Revision 1.139  2001/12/08 04:49:39  warmerda
+#  added point in polygon test
+#
+#  Revision 1.138  2001/11/28 19:21:33  warmerda
+#  Added set_gcps(), and get_gcps() on GvRaster.
+#  Added info on geotransform-changed signal on GvRaster.
+#
+#  Revision 1.137  2001/10/17 16:25:58  warmerda
+#  added support for appling complex luts
+#
+#  Revision 1.136  2001/10/16 18:51:18  warmerda
+#  added raster layer histogram, autoscale, and various enhancements
+#
+#  Revision 1.135  2001/10/02 20:30:06  warmerda
+#  update code for establishing .openev file to work on win98
+#
+#  Revision 1.134  2001/08/14 17:03:24  warmerda
+#  added standard deviation autoscaling support
+#
+#  Revision 1.133  2001/08/08 17:46:52  warmerda
+#  added GvShape reference counting support
+#
+#  Revision 1.132  2001/08/08 02:59:04  warmerda
+#  added gv_data_registry_dump()
+#
+#  Revision 1.131  2001/08/07 18:40:45  warmerda
+#  fixed get_view() for unset views on GvTool
+#
+#  Revision 1.130  2001/07/24 21:21:45  warmerda
+#  added EV style phase colormap
+#
+#  Revision 1.129  2001/07/24 02:59:25  warmerda
+#  added force_load method on GvRaster
+#
+#  Revision 1.128  2001/07/24 02:21:54  warmerda
+#  added 8bit phase averaging
+#
+#  Revision 1.127  2001/07/16 15:04:41  warmerda
+#  added get_height and build_skirt on GvRasterLayer
+#
+
+import gtk; _gtk = gtk; del gtk
+import _gtkmissing
+import _gv
+import gvutils
+import os.path, sys
+import os
+import string
+import pgu
+import gdal
+from gvconst import *
+from gdalconst import *
+import pathutils
+
+"""
+Classes for viewing and interacting with geographic image and vector data.
+"""
+
+###############################################################################
+# A few notes on gview.py
+#
+# obj2inst():
+#
+# The GtkObject derived OpenEV classes are automatically registered with
+# the gtk name2cls mechanism by some startup code at the bottom of this
+# module (search for name2cls).  Basically any class in this module starting
+# with Gv is assumed to:
+#  - Be derived from GtkObject
+#  - Have a get_type attributes which is the _gv get type method.
+#
+# Based on this, the _gtk._obj2inst() method can be used to create a
+# Python shadow class for any of the raw object handles (_o).  It will
+# create the correct type based on the name to class translation and the
+# name returned by the get_type attribute.
+#
+# Note that two obj2inst() calls with one GtkObject will result in two
+# Python shadow objects for the same underlying GtkObject.
+###############################################################################
+
+SMAverage = 0
+SMSample = 1
+SMAverage8bitPhase = 2
+
+###############################################################################
+class GvViewArea(_gtk.GtkDrawingArea):
+    """Gtk geographic view area.
+
+    Signals:
+
+    gldraw -- This signal is emitted after all layers have drawn themselves
+    but before the result is displayed to the user.  It provides a hook
+    whereby application code can do additional drawing.
+
+    active-changed -- This signal is emitted after the active layer has been
+    modified, or when a layer is added or removed from the view.  It can be
+    used to update tools that depend on the active layer.
+
+    view-state-changed -- This signal is emitted after the view state changes.
+    This includes flipping, zooming and roaming.  It does not include mouse
+    position changes.
+
+    Note that the GvViewArea is a GtkWidget, and application code may attach
+    callbacks to the motion-notify-event, button-press-event, key-press-event
+    and other similar events.  The map_pointer() method should be used
+    to translate raw GtkWidget coordinates to georeferenced positions.
+    """
+        
+    get_type = _gv.gv_view_area_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_view_area_new()
+        if self._o is None:
+            raise ValueError, "Failed to create GvViewArea, is OpenGL working?"
+
+    def serialize(self, filename=None ):
+        tree = [gdal.CXT_Element, 'GvViewArea']
+
+        tree.append( [CXT_Attribute, 'Mode',
+                    [CXT_Text, str(self.get_mode())]] )
+        tree.append( [CXT_Attribute, 'Raw',
+                      [CXT_Text, str(self.get_raw())]] )
+
+        translation = self.get_translation()
+        tree.append( [CXT_Element, 'Translation',
+                      [CXT_Attribute, 'x',
+                       [CXT_Text, str(translation[0])]],
+                      [CXT_Attribute, 'y',
+                       [CXT_Text, str(translation[1])]]] )
+
+        tree.append( [CXT_Element, 'Zoom',
+                      [CXT_Text, str(self.get_zoom())]] )
+        tree.append( [CXT_Element, 'FlipX', 
+                      [CXT_Text, str(self.get_flip_x())]] )
+        tree.append( [CXT_Element, 'FlipY', 
+                      [CXT_Text, str(self.get_flip_y())]] )
+
+        projection = self.get_projection()
+        if projection is not None and len(projection) > 0:
+            tree.append( [CXT_Element, 'Projection',
+                          [CXT_Text, str(self.get_projection())]] )
+
+        background = self.get_background_color()
+        tree.append( [CXT_Element, 'Background',
+                    [CXT_Attribute, 'red',
+                     [CXT_Text, str(background[0])]],
+                    [CXT_Attribute, 'green',
+                     [CXT_Text, str(background[1])]],
+                    [CXT_Attribute, 'blue',
+                     [CXT_Text, str(background[2])]],
+                    [CXT_Attribute, 'alpha',
+                     [CXT_Text, str(background[3])]]] )
+
+        if self.get_mode() == MODE_3D:
+            eye_pos = self.get_eye_pos()
+            tree.append( [CXT_Element, 'EyePos',
+                        [CXT_Attribute, 'x',
+                         [CXT_Text, str(eye_pos[0])]],
+                        [CXT_Attribute, 'y',
+                         [CXT_Text, str(eye_pos[1])]],
+                        [CXT_Attribute, 'z',
+                         [CXT_Text, str(eye_pos[2])]]] )
+            eye_dir = self.get_eye_dir()
+            tree.append( [CXT_Element, 'EyeDir',
+                        [CXT_Attribute, 'x',
+                         [CXT_Text, str(eye_dir[0])]],
+                        [CXT_Attribute, 'y',
+                         [CXT_Text, str(eye_dir[1])]],
+                        [CXT_Attribute, 'z',
+                         [CXT_Text, str(eye_dir[2])]]] )
+            height_scale = self.get_height_scale()
+            tree.append( [CXT_Element, 'HeightScale',
+                          [CXT_Text, str(height_scale)]] )
+
+        layer_tree = [CXT_Element, 'Layers']
+        tree.append( layer_tree )
+
+        layers = self.list_layers()
+        for layer in layers:
+            layer_tree.append( layer.serialize( filename=filename ) )
+
+        return tree
+
+    def initialize_from_xml( self, tree, filename=None ):
+        self.set_property( '_supress_realize_auto_fit', 'on' )
+
+        layer_trees = gvutils.XMLFind( tree, 'Layers')
+        if layer_trees is not None:
+            for layer_tree in layer_trees[2:]:
+                layer = gvutils.XMLInstantiate( layer_tree, self, filename=filename )
+                if layer is not None:
+                    self.add_layer( layer )
+                    self.set_active_layer( layer )
+                else:
+                    print 'Failed to instantiate layer:', layer_tree
+
+        tr = gvutils.XMLFind( tree, 'Translation')
+        if tr is not None:
+            x = float(gvutils.XMLFindValue( tr, 'x', '0.0'))
+            y = float(gvutils.XMLFindValue( tr, 'y', '0.0'))
+            self.set_translation(x,y)
+
+        zm = float(gvutils.XMLFindValue( tree, 'Zoom', 
+                                         str(self.get_zoom()) ))
+        self.zoom( zm - self.get_zoom() )
+
+        flip_x = int(gvutils.XMLFindValue( tree, 'FlipX','1'))
+        flip_y = int(gvutils.XMLFindValue( tree, 'FlipY','1'))
+        self.set_flip_xy( flip_x, flip_y )
+
+        bg = gvutils.XMLFind( tree, 'Background')
+        if bg is not None:
+            self.set_background_color(
+                (float(gvutils.XMLFindValue( bg, 'red', '0.0')),
+                 float(gvutils.XMLFindValue( bg, 'green', '0.0')),
+                 float(gvutils.XMLFindValue( bg, 'blue', '0.0')),
+                 float(gvutils.XMLFindValue( bg, 'alpha', '1.0'))) )
+
+        eye_pos_xml = gvutils.XMLFind( tree, 'EyePos' )
+        if eye_pos_xml is not None:
+            eye_pos = (float(gvutils.XMLFindValue( eye_pos_xml, 'x', '0.0')),
+                       float(gvutils.XMLFindValue( eye_pos_xml, 'y', '0.0')),
+                       float(gvutils.XMLFindValue( eye_pos_xml, 'z', '1.0')))
+            eye_dir_xml = gvutils.XMLFind( tree, 'EyeDir' )
+            eye_dir = (float(gvutils.XMLFindValue( eye_dir_xml, 'x', '0.0')),
+                       float(gvutils.XMLFindValue( eye_dir_xml, 'y', '0.0')),
+                       float(gvutils.XMLFindValue( eye_dir_xml, 'z', '-1.0')))
+            self.set_3d_view( eye_pos, eye_dir )
+
+            self.height_scale(
+                float(gvutils.XMLFindValue( tree, 'HeightScale', '1.0' )) )
+            
+            self.set_mode( MODE_3D )
+                
+    def get_width(self):
+        """Return width of area in pixels."""
+        return _gv.gv_view_area_get_width(self._o)
+    
+    def get_height(self):
+        """Return height of area in pixels."""
+        return _gv.gv_view_area_get_height(self._o)
+    
+    def add_layer(self, layer):
+        """Add a new layer to the view window. 
+
+        Arguments
+
+          layer -- abc A GvLayer derived object to add to the view."""
+        _gv.gv_view_area_add_layer(self._o, layer._o)
+
+    def remove_layer(self, layer):
+        """Remove a layer from view window.
+
+        Arguments
+
+          layer -- the layer to remove"""
+        _gv.gv_view_area_remove_layer(self._o, layer._o)
+
+    def get_named_layer(self, name):
+        """Fetch the named layer from this view
+
+        Returns None if no such layer exists on the view."""
+
+        _o = _gv.gv_view_area_get_named_layer(self._o,name)
+        if _o is not None: return _gtk._obj2inst(_o)
+        return _o
+
+    def active_layer(self):
+        """Fetch the active layer for this view"""
+        _o = _gv.gv_view_area_active_layer(self._o)
+        if _o: return _gtk._obj2inst(_o)
+        return _o
+
+    def set_active_layer(self, layer):
+        """Set the active layer for this view.
+
+        It is an error to make a layer active if it has not already
+        been added to the view.
+
+        Arguments
+
+          layer -- GvLayer to make active."""
+
+        _gv.gv_view_area_set_active_layer(self._o, layer._o)
+
+    def list_layers(self):
+        """Fetch list of layers attached to this view."""
+        l = _gv.gv_view_area_list_layers(self._o)
+        return map(_gtk._obj2inst, l)
+
+    def swap_layers(self, layer_a, layer_b):
+        """Swap two layers in the display order stack.
+
+        Arguments
+
+          layer_a -- Integer index (within list_layers result) of first layer.
+          
+          layer_b -- Integer index (within list_layers result) of second layer.
+          """
+        _gv.gv_view_area_swap_layers(self._o, layer_a, layer_b)
+
+    def create_thumbnail(self, layer, w, h):
+        """Return area thumbnail as a GdkWindow.
+
+        Arguments
+
+          layer -- the GvLayer to render into the thumbnail.
+
+          w -- Width in pixels of thumbnail.
+
+          h -- Height in pixel of thumbnail."""
+        return _gv.gv_view_area_create_thumbnail(self._o, layer._o, w, h)
+
+    def zoom(self, zoom):
+        """Zoom in or out.
+
+        Note that the zoom value is log base 2 of the linear zoom factor.
+        So to zoom in by a factor of 2, you would pass 1.  To zoom in by
+        a factor of 8 you would pass 3.  To zoom out by a factor of eight
+        you would pass -8.
+        
+        zoom -- the value to add to the current zoom factor."""
+        _gv.gv_view_area_zoom(self._o,zoom)
+
+    def get_zoom(self):
+        """Get zoom value.
+
+        Note that the zoom value is log base 2 of the linear zoom factor.
+        So a zoom value of 1 means the view is zoomed in by a factor of 2.
+        Similarly, a value of 3 means zoomed in by a factor of 8.  Negative
+        values indicate zoomed out.
+        """
+        
+        return _gv.gv_view_area_get_zoom(self._o)
+
+    def rotate(self, angle):
+        """Rotate view about center.
+
+        angle -- the angle to rotate about the center by in degrees.  Positive
+        is causes view contents to rotate counter-clockwise."""
+        _gv.gv_view_area_rotate(self._o,angle)
+
+    def translate(self, dx, dy):
+        """Translate view by dx and dy.
+
+        dx -- the amount to translate along the x axis in georeferenced units
+        dy -- the amount to translate along the y axis in georeferenced units """
+        _gv.gv_view_area_translate(self._o,dx,dy)
+
+    def set_translation(self, x, y):
+        """Set view center translation.
+
+        The translation values are in georeferenced coordinates, and to
+        put the value (1000,2000) at the view center a translation of
+        (-1000,-2000) is needed.
+        
+        x -- the X (easting) value for view center translation.
+        y -- the Y (northing) value for view center translation."""
+        _gv.gv_view_area_set_translation(self._o,x,y)
+
+    def get_translation(self):
+        """Get view center translation.
+
+        The translation values are in georeferenced coordinates, and to
+        put the value (1000,2000) at the view center a translation of
+        (-1000,-2000) would be returned.  The value is returned as an
+        (x,y) tuple."""
+        
+        return _gv.gv_view_area_get_translation(self._o)
+
+    def get_flip_x( self ):
+        """Fetch X flip flag.
+
+        Normally 1, but will be -1 if an X flip (mirroring) has been applied.
+        """
+        return _gv.gv_view_area_get_flip_x(self._o)
+
+    def get_flip_y( self ):
+        """Fetch Y flip flag.
+
+        Normally 1, but will be -1 if a Y flip (mirroring) has been applied.
+        """
+        return _gv.gv_view_area_get_flip_y(self._o)
+
+    def set_flip_xy( self, flip_x, flip_y ):
+        """Set x/y flip flags.
+
+        flip_x -- x mirroring flag, 1 or -1.
+        flip_y -- x mirroring flag, 1 or -1.
+        
+        A value of 1 is unflipped, and -1 is flipped (mirrored).
+        """
+        _gv.gv_view_area_set_flip_xy( self._o, flip_x, flip_y )
+
+    def copy_state(self, src_view):
+        """Copy the view state of another view.
+
+        This includes translation, rotation, and zoom factor.
+        
+        src_view -- the GvViewArea to copy the state from."""
+        _gv.gv_view_area_copy_state(self._o,src_view)
+
+    def map_location(self, xy):
+        """ Translates a point from the current view projection (line/pixel or lat/long)
+        to georeferenced coordinates.  Useful for a go to function that permits the
+        user to enter a coordinate, then use set_translation() with the mapped location.
+
+        returns a (x,y) tuple in georeferenced coordinates
+
+        xy -- (x,y) tuple in current view coordinates"""
+
+        return _gv.gv_view_area_map_location(self._o, xy)
+
+    def get_pointer(self):
+        """Fetch current pointer position.
+
+        The pointer value is returned as an (X,Y) tuple in georeferenced
+        coordinates."""
+        return _gv.gv_view_area_get_pointer(self._o)
+
+    def map_pointer(self, xy):
+        """Translate position to georef coords.
+
+        Translates a pixel/line position into map (georeferenced) coordinates
+        within the view.  The pixel/line coordinates might come from a raw
+        GdkEvent, for instance.
+
+        Returns an (x,y) tuple in georeferenced coordinates.
+        
+        xy -- (x,y) tuple in GvViewArea pixel coordinates"""
+        return _gv.gv_view_area_map_pointer(self._o, xy)
+
+    def inverse_map_pointer(self, xy):
+        """Translate position from georef coords.
+
+        Translates a map (georeferenced) position into pixel/line coordinates
+        within the GvViewArea on screen.
+
+        Returns an (x,y) tuple in pixel/line coordinates.
+        
+        xy -- (x,y) tuple in georeferenced coordinates"""
+        return _gv.gv_view_area_inverse_map_pointer(self._o, xy)
+
+    def get_volume(self):
+        """Fetch volume of layers in view.
+
+        Returns the world volume of all layers attached to a view (not just
+        what can be currently seen).  The volume is returned as an
+        (xmin, xmax, ymin, ymax, zmin, zmax) tuple.  In case of failure a
+        dummy cube of (0,1000,0,1000,0,1000) is returned.
+
+        NOTE: At this time the zmin/zmax are not computed, and are always
+        the default 0,1000 values.
+        """
+        
+        return _gv.gv_view_area_get_volume(self._o)
+
+    def get_extents(self):
+        """Fetch extents of view window.
+
+        The extents are returned as an (xmin,ymin,xmax,ymax) tuple
+        in georeferenced coordinates."""
+        return _gv.gv_view_area_get_extents(self._o)
+
+    def fit_extents(self,llx, lly, width, height):
+        """Fit view window to region.
+
+        The position and size of the window are in georeferenced coordinates.
+        The translation, zoom and rotation are updated to fit the entire
+        desired box.  If the aspect ratio of the box is different than the
+        window, then the window will display the region centered, with
+        extra area viewed in one dimension.  The rotation is always set to
+        zero by this call. 
+        
+        llx -- X (easting) position of the lower left corner.
+        lly -- Y (northing) position of the lower left corner.
+        width -- Width of view region.
+        height -- Height of view region."""
+        _gv.gv_view_area_fit_extents(self._o, llx, lly, width, height)
+
+    def fit_all_layers(self):
+        """Fit view window to extents of all layers
+
+        This sets the 2D and 3D views to the default.  In 2D the default
+        sets the view to the minimum extents that will display all layers.
+        In 3D a default view is set which approximately shows all the data
+        in a reasonable way.  Note that both the 2D and 3D views are set
+        regardless of the current mode.
+        """
+        _gv.gv_view_area_fit_all_layers(self._o)
+
+    def get_projection(self):
+        """Return view coordinate system.
+
+        The coordinate system is returned in OGC WKT format, and will
+        likely be an empty string if there is no coordinate system."""
+        return _gv.gv_view_area_get_projection( self._o )
+
+    def set_projection(self, proj):
+        """Set the coordinate system of the view.
+
+        Currently the only time the coordinate system can be modified
+        is when there are no layers attached.  When raster layers are added
+        to the view, they will attempt to reproject themselves to match the
+        view if possible.  Vector layers do not currently do this.
+        
+        proj -- new coordinate system in OGC WKT."""
+        _gv.gv_view_area_set_projection( self._o, proj )
+
+    def set_background_color(self, color):
+        """Set the background color of the view.
+
+        color -- an RGBA tuple with the color value (scaled 0.0 to 1.0).
+        For reasonable operation alpha should always be 1.0.
+        """
+        _gv.gv_view_area_set_background_color( self._o, color )
+
+    def get_background_color(self):
+        """Get the background color of the view.
+
+        Returns the background color as an RGBA tuple with the color
+        values scaled 0.0 to 1.0.
+        """
+        return _gv.gv_view_area_get_background_color( self._o )
+
+    def print_to_file(self, width, height, filename, format='GTiff',
+                      is_rgb = 1):
+        """Print view (at indicated resolution) to raster file.
+
+        width -- the desired raster width at which to render the image.
+        height -- the desired raster height at which to render the image.
+        filename -- the name of the file to write to.
+        format -- the GDAL format to use (defaults to 'GTiff').
+        is_rgb -- non-zero for RGB output or zero for greyscale output.
+
+        If the aspect ratio of width:height is not the same as the
+        aspect ratio of the view, the extents will be extended in one
+        direction to ensure the entire view data is visible.
+
+        This method return 0 on success or non-zero on failure."""
+        
+        return _gv.gv_view_area_print_to_file(self._o, width, height,
+                                              filename, format, is_rgb)
+
+    def print_postscript_to_file(self, width, height,
+                                 ulx, uly, lrx, lry,
+                                 is_rgb, filename):
+        """Print view (at indicated resolution) to PostScript file.
+
+        width -- the desired raster width at which to render the image.
+        height -- the desired raster height at which to render the image.
+        ulx -- the upper left corner of the image print area in inches
+        uly -- the upper left corner of the image print area in inches
+        lrx -- the lower right corner of the image print area in inches
+        lry -- the lower right corner of the image print area in inches
+        is_rgb -- non-zero for RGB output or zero for greyscale output.
+        filename -- the name of the file to write to.
+
+        If the aspect ratio of width:height is not the same as the
+        aspect ratio of the view, the extents will be extended in one
+        direction to ensure the entire view data is visible.
+
+        This method return 0 on success or non-zero on failure."""
+        
+        return _gv.gv_view_area_print_postscript_to_file(
+            self._o, width, height, ulx, uly, lrx, lry,
+            is_rgb, filename)
+
+    def page_setup(self):
+        _gv.gv_view_area_page_setup()
+
+    def print_to_windriver(self, width, height, 
+                           ulx, uly, lrx, lry,
+                           is_rgb = 1):
+        """Print view to Windows Print Driver
+
+        width -- the desired raster width at which to render the image.
+        height -- the desired raster height at which to render the image.
+        ulx -- the upper left corner of the image print area in inches
+        uly -- the upper left corner of the image print area in inches
+        lrx -- the lower right corner of the image print area in inches
+        lry -- the lower right corner of the image print area in inches
+        is_rgb -- non-zero for RGB output or zero for greyscale output.
+
+        If the aspect ratio of width:height is not the same as the
+        aspect ratio of the view, the extents will be extended in one
+        direction to ensure the entire view data is visible.
+
+        This method return 0 on success or non-zero on failure."""
+        
+        return _gv.gv_view_area_print_to_windriver(self._o, width, height,
+                                                   ulx, uly, lrx, lry, is_rgb)
+
+    def get_mode(self):
+        """Get 2D/3D Mode
+
+        Returns either gview.MODE_2D or gview.MODE_3D depending on the
+        current mode of the view."""
+        
+        return _gv.gv_view_area_get_mode(self._o)
+    
+    def set_mode(self, flag_3D=MODE_2D):
+        """Set 2D/3D view mode.
+
+        Set the view mode to either 2D orthonormal projection or
+        3D perspective projection.
+
+        flag_3D -- Either gview.MODE_2D or gview.MODE_3D"""
+
+        _gv.gv_view_area_set_mode(self._o, flag_3D)
+
+    def set_3d_view(self, eye_pos, eye_dir ):
+        """Set 3D view.
+
+        Set the 3D view position, and direction. 
+
+        eye_pos -- (x,y,z) tuple indicating the eye position in georeferenced
+        coordinates.
+
+        eye_dir -- (x,y,z) tuple indicating the direction of view.  (0,0,-1)
+        is straight down
+        """
+
+        _gv.gv_view_area_set_3d_view( self._o, eye_pos, eye_dir )
+
+    def set_3d_view_look_at(self, eye_pos, eye_look_at ):
+        """Set 3D view.
+
+        Set the 3D view position, and direction. 
+
+        eye_pos -- (x,y,z) tuple indicating the eye position in georeferenced
+        coordinates.
+
+        eye_look_at -- (x,y) tuple indicating the position in the z-plane to
+        look at.
+        """
+
+        _gv.gv_view_area_set_3d_view_look_at( self._o, eye_pos, eye_look_at)
+
+    def get_eye_pos(self):
+        """Fetch 3D eye position
+
+        The 3D eye position is returned as an (x,y,z) tuple."""
+
+        return _gv.gv_view_area_get_eye_pos(self._o)
+        
+    def get_eye_dir(self):
+        """Fetch 3D eye direction
+
+        The 3D eye direction is returned as an (x,y,z) tuple."""
+
+        return _gv.gv_view_area_get_eye_dir(self._o)
+
+    def get_look_at_pos(self):
+        """Fetch georeference location in z-plane that eye is looking
+
+        Location is returned as an (x,y) tuple, or None if looking above z-plane"""
+        return _gv.gv_view_area_get_look_at_pos(self._o)
+        
+    def height_scale(self, scale=1.0):
+        """Set height scaling factor.
+
+        Has no effect unless in 3D mode.  A scale value of 2.0 will exaggerate
+        all elevations by a factor of 2 relative to horizontal (georeferenced)
+        coordinates.
+
+        scale -- scale factor (originally 1.0)
+        """
+        _gv.gv_view_area_height_scale(self._o, scale)
+
+    def get_height_scale(self):
+        return _gv.gv_view_area_get_height_scale(self._o)
+
+    def queue_draw( self ):
+        """Force queuing of a redraw.
+
+        This method should not normally be needed, as a redraw should be
+        triggered any time something changes that would require a redraw.
+        """
+        _gv.gv_view_area_queue_draw( self._o )
+
+    def get_fontnames( self ):
+        """Get list of available fontnames.
+
+        Returns a Python list of font name strings suitable for using with
+        LABEL() tools on this view area.  Generally the same for all views.
+        """
+        return _gv.gv_view_area_get_fontnames( self._o )
+
+    def get_raw( self, ref_layer = None ):
+        """Check if in Raw Mode
+
+        This returns true if the view is in raw mode relative to the identified
+        raster layer.  Raw mode is active if the coordinate system of the
+        display is the raw pixel/line coordinates of the indicated raster.
+
+        ref_layer -- a GvRasterLayer to check against.  If ref_layer is None,
+                     always returns 0. 
+        """
+
+        if ref_layer is None:
+            return 0
+        else:
+            return _gv.gv_view_area_get_raw( self._o, ref_layer._o )
+
+    def set_raw( self, ref_layer, raw_enable ):
+        """Set Raw Mode Enable
+
+        Force the layer to be in raw mode (raw_enable=TRUE) or in georeferenced
+        mode (raw_enable=FALSE) relative to the indicated raster layer.  If
+        this requires a change, the coordinate system, and view extents of
+        the view will be altered, and the mesh of the underlying raster will
+        also be altered.
+        
+        ref_layer -- a GvRasterLayer to check against.
+        raw_enable -- TRUE to set raw mode or FALSE for georeferenced mode.
+
+        A value of zero is returned if this operation is successful, or a
+        value of non-zero on failure.
+        """
+        if ref_layer is None:
+            return 1
+        
+        return _gv.gv_view_area_set_raw( self._o, ref_layer._o, raw_enable )
+
+    def get_property(self,name):
+        """Get a GvViewArea property.
+
+        name -- the key or name of the property being set.  Should be a
+        well behaved token (no spaces, equal signs, or colons).
+
+        NOTE: Returns None if property does not exist."""
+        
+        return _gv.gv_view_area_get_property(self._o,name)
+
+    def set_property(self,name,value):
+        """Set a GvViewArea property.
+
+        name -- the key or name of the property being set.  Should be a
+        well behaved token (no spaces, equal signs, or colons).
+        
+        value -- the value to be assigned.  Any text is acceptable."""
+        
+        return _gv.gv_view_area_set_property(self._o,name,value)
+        
+    def format_point_query( self, x, y ):
+        """Format a point as text
+         
+           x - the x coordinate to format
+           y - the y coordinate to format
+           
+           TODO: provide information on formatting using preferences
+                 to control output.
+        """
+        
+        return _gv.gv_format_point_query( self._o, x, y )
+
+###############################################################################
+def GvShapeFromXML( tree, parent, filename=None ):
+    """
+    construct a gvshape object from an xml tree
+    """
+    
+    _obj = _gv.gv_shape_from_xml( tree )
+    return GvShape( _obj = _obj )
+
+class GvShape:
+    """Vector feature class for GvShapes/GvShapesLayer
+
+    The following properties have special interpretation for a GvShape.
+    Note that modifying these properties does not automatically trigger a
+    display-change signal ... please call display_change() manually.
+
+    _gv_color -- RGBA value used for point, line, or area edge color.
+    
+    _gv_fill_color -- RGBA value used for area fill color.
+    
+    Note that similar (names differ) color properties can also be set on the
+    GvShapesLayer.  Setting these properties on a particular shape overrides
+    any layer drawing styles for that shape.
+
+    """
+    
+    def __init__(self, _obj=None,type=GVSHAPE_POINT):
+        if _obj is None:
+            _obj = _gv.gv_shape_new(type)
+
+        _gv.gv_shape_ref( _obj )
+        self.__dict__['_o'] = _obj
+
+    def __del__(self):
+        if self.__dict__['_o'] is not None:
+            _gv.gv_shape_unref( self.__dict__['_o'] )
+            self.__dict__['_o'] = None
+
+    def copy(self):
+        """Make a copy of a shape"""
+
+        copy = _gv.gv_shape_copy( self._o )
+        if copy is None:
+            return None
+
+        return GvShape( _obj=copy )
+
+    def __getattr__(self,attr):
+        if self.__dict__.has_key(attr):
+            return self.__dict__[attr]
+
+        value = _gv.gv_shape_get_property( self._o, attr )
+        if value is None:
+            raise AttributeError, attr
+        else:
+            return value
+
+    def __delattr__(self,attr):
+        if self.__dict__.has_key(attr):
+            del self.__dict__[attr]
+
+        properties = self.get_properties()
+        if not properties.has_key(attr):
+            raise AttributeError, attr
+        else:
+            del properties[attr]
+            self.set_properties(properties)
+
+    def __setattr__(self,attr,value):
+        if attr == '_o':
+            self.__dict__['_o'] = value
+        else:
+            self.set_property(attr,value)
+
+    def __str__(self):
+        result = ''
+        properties = self.get_properties()
+        for key in properties.keys():
+            result = result + key + '=' + properties[key] + '\n'
+            
+        result = result + self.geometry_to_wkt() + '\n'
+
+        return result
+
+    def serialize( self, base = None ):
+        """serialize this object in a format suitable for XML representation.
+        """
+        if base is not None:
+            raise ValueError, 'GvShape.serialize() doesnt allow base'
+
+        return _gv.gv_shape_to_xml( self._o )
+
+    def geometry_to_wkt(self):
+        t = self.get_type()
+        fmt = '%f %f %f'
+        geom = ''
+        if t == GVSHAPE_POINT:
+            geom = ('POINT ('+fmt+')') % self.get_node()
+        elif t == GVSHAPE_LINE:
+            geom = 'LINESTRING ('
+            for node in range(self.get_nodes()):
+                if node > 0:
+                    geom = geom + ','
+                term = fmt % self.get_node(node)
+                geom = geom + term
+            geom = geom + ')'
+        elif t == GVSHAPE_AREA:
+            geom = 'POLYGON ('
+            for ring in range(self.get_rings()):
+                if ring > 0:
+                    geom = geom + ','
+                geom = geom + '('
+                for node in range(self.get_nodes()):
+                    if node > 0:
+                        geom = geom + ','
+                    term = fmt % self.get_node(node)
+                    geom = geom + term
+                geom = geom + ')'
+            geom = geom + ')'
+        return geom
+
+    def destroy(self):
+        """Destroy shape.
+
+        The GvShape is not a GtkObject, and doesn't employ the same
+        reference counting mechanisms to ensure destruction when no longer
+        referenced.  It is the applications responsibility to destroy
+        GvShape's with an explicit call to the GvShape.destroy() method
+        when appropriate."""
+        if self.__dict__['_o'] is not None:
+            _gv.gv_shape_unref(self.__dict__['_o'])
+            self.__dict__['_o'] = None
+
+    def get_properties(self):
+        """Get GvShape properties (attributes) as a dictionary.
+
+        The properties are returned as a Python dictionary.  Note that
+        changes to this dictionary are not applied back to the GvShape."""
+        return _gv.gv_shape_get_properties(self.__dict__['_o'])
+
+    def get_typed_properties(self, prop_list):
+        return _gv.gv_shape_get_typed_properties(self.__dict__['_o'],prop_list)
+
+    def get_property(self,property_name, default_value=None):
+        """Get the value of a property.
+
+        Fetches the value of a single property on the shape.  Roughly
+        equivelent to shape_obj.get_properties()[property_name] or
+        shape_obj.property_name but if the property does not exist
+        get_property() returns a default value instead of throwing an
+        exception.
+
+        property_name -- the name of the property (attribute field) to fetch.
+        default_value -- the value to return if the property does not exist,
+                         defaults to None.
+
+        Returns the property value (always a string) or the default value."""
+
+        value = _gv.gv_shape_get_property( self._o, property_name )
+        if value is None:
+            return default_value
+        else:
+            return value
+
+    def set_property(self,name,value):
+        """Set a GvShape property.
+
+        name -- the key or name of the property being set.  Should be a
+        well behaved token (no spaces, equal signs, or colons).
+        
+        value -- the value to be assigned.  Any text is acceptable."""
+        
+        return _gv.gv_shape_set_property(self._o,name,value)
+
+    def set_properties(self,properties):
+        """Set a GvShape properties.
+
+        Clear all existing properties, and assign the new set passed in.
+
+        properties -- a python dictionary with the keys being property names,
+        and the result is the property value"""
+        
+        return _gv.gv_shape_set_properties(self._o,properties)
+
+    def get_node(self,node=0,ring=0):
+        """Fetch a node (point) as a tuple.
+
+        node -- the node within the selected ring to return.  Defaults to zero.
+        ring -- the ring containing the desired node.  Defaults to zero.
+
+        The node is returned as an (x,y,z) tuple.  None is returned if the
+        requested node is out of range."""
+        return _gv.gv_shape_get_node(self._o,node,ring)
+    
+    def set_node(self,x,y,z=0,node=0,ring=0):
+        """Set a node.
+
+        x -- the x value to assign.
+        y -- the y value to assign.
+        z -- the z value to assign.
+        node -- the node to set.
+        ring -- the ring containing the node to set.
+
+        Note that the node and ring will be created if not already in
+        existance."""
+        return _gv.gv_shape_set_node(self._o,x,y,z,node,ring)
+    
+    def add_node(self,x,y,z=0,ring=0):
+        """Add a node.
+
+        x -- the x value to assign.
+        y -- the y value to assign.
+        z -- the z value to assign.
+        ring -- the ring containing the node to set.
+
+        Note that the ring will be created if not already in
+        existance.  The index of the newly created node is returned. """
+        return _gv.gv_shape_add_node(self._o,x,y,z,ring)
+
+    def get_nodes(self,ring=0):
+        """Get number of nodes.
+
+        ring -- the ring to check.  Defaults to zero.
+
+        Note that the returned number of nodes will be zero for non-existent
+        rings."""
+        return _gv.gv_shape_get_nodes(self._o,ring)
+    
+    def get_rings(self):
+        """Get number of rings.
+        """
+        return _gv.gv_shape_get_rings(self._o)
+
+    def get_type(self):
+        """Get shape type.
+
+        The returned integer will match one of gview.GVSHAPE_POINT,
+        gview.GVSHAPE_LINE, gview.GVSHAPE_AREA or gview.GVSHAPE_COLLECTION."""
+        return _gv.gv_shape_get_type(self._o)
+
+    def delete_ring(self, ring):
+        """Delete a ring
+
+        ring -- the ring to delete."""
+        return _gv.gv_shape_delete_ring(self._o,ring)
+    
+    def point_in_polygon(self, x, y ):
+        """Check if point in this area.
+
+        x -- the x component of the location to check.
+        y -- the y component of the location to check.
+        
+        Returns a non-zero value if the point (x,y) is inside this
+        shapes area.  Returns zero if not, or if the current shape is not
+        an area."""
+        return _gv.gv_shape_point_in_polygon( self._o, x, y )
+
+    def distance_from_polygon(self, x, y ):
+        """Compute shortest distance between point and outline of polygon
+
+        x -- the x component of the location to check.
+        y -- the y component of the location to check.
+        
+        Returns the distance as a double."""
+        return _gv.gv_shape_distance_from_polygon( self._o, x, y )
+    
+    def clip_to_rect(self, x, y, width, height ):
+        """Clip shape to a rectangle.
+
+        x -- the minimum x of the clip rectangle.
+        y -- the minimum y of the clip rectangle.
+        width -- the width of the clip rectangle.
+        height -- the height of the clip rectangle.
+
+        Creates a new GvShape which is clipped to the indicated rectangle.
+        If the shape does not intersect the rectangle None is returned.  If
+        it is entirely contained within the rectangle a copy is returned.
+        Otherwise a copy of the shape with clipped geometry is created and
+        returned.  Clipping will generally not work well for complex polygons
+        with holes due to an incomplete implementation of the clipping
+        algorithm.  This will be fixed at some point in the future as needed.
+        """
+
+        result = _gv.gv_shape_clip_to_rect( self._o, x, y, width, height )
+        if result is None:
+            return None
+        else:
+            return GvShape( _obj=result )
+
+    def add_shape( self, shape ):
+        _gv.gv_shape_add_shape( self._o, shape._o )
+
+    def get_shape( self, shape_index ):
+        return _gv.gv_shape_get_shape( self._o, shape_index )
+
+    def collection_get_count( self ):
+        return _gv.gv_shape_collection_get_count( self._o )
+            
+def gv_shape_get_count():
+    return _gv.gv_shape_get_count()
+
+def gv_shape_line_from_nodes(xlist,ylist,zlist):
+    """ Create a new line shape from three lists
+        of nodes.
+        Inputs: xlist- x coordinates
+                ylist- y coordinates
+                zlist- z coordinates
+        xlist, ylist, and zlist must be the same
+        length.
+    """
+    # cast so that tuples and numeric arrays are
+    # accepted
+    if type(xlist) != type([1]):
+        xlist=list(xlist)
+    if type(ylist) != type([1]):
+        ylist=list(ylist)
+    if type(zlist) != type([1]):
+        zlist=list(zlist)
+    obj=_gv.gv_shape_line_from_nodelists(xlist,ylist,zlist)
+    if obj is None:
+        return None
+    else:
+        return GvShape(_obj=obj,type=GVSHAPE_LINE)
+
+def gv_shapes_lines_for_vecplot(xlist,ylist,zlist,oklist):
+    """ Create a new line shape from three lists
+        of nodes.
+        Inputs: xlist- x coordinates
+                ylist- y coordinates
+                zlist- z coordinates
+                oklist- whether or not the coordinate
+                        should be included
+        xlist, ylist, zlist, and oklist must be the same
+        length.
+    """
+    if type(xlist) != type([1]):
+        xlist=list(xlist)
+    if type(ylist) != type([1]):
+        ylist=list(ylist)
+    if type(zlist) != type([1]):
+        zlist=list(zlist)
+    if type(oklist) != type([1]):
+        oklist=list(oklist)
+    obj=_gv.gv_shapes_lines_for_vecplot(xlist,ylist,zlist,oklist)
+    if obj is None:
+        return None
+    else:
+        return GvShapes(_obj=obj)
+
+    
+###############################################################################
+class GvData(_gtk.GtkData):
+    """Base class for various raster and vector data containers.
+
+    All GvDatas have a name string, common undo semantics, and changing/changed
+    event notication semantics.
+
+    Signals:
+
+    changing -- Indicates that the underlying data is going to be changed.
+    This will trigger capture of an undo memento if undo is enabled for
+    this object.
+
+    changed -- Indicates that the underlying data has changed.
+
+    Note that the change_info for changing and changed varies depending on
+    the particular type of object.  In particular, GvRaster can carry the
+    modified region, and shape containing objects can have a list of shapes.
+    """
+
+    get_type = _gv.gv_data_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+
+    def serialize( self, base = None, filename=None ):
+        """serialize this object in a format suitable for XML representation.
+        """
+        
+        if base is None:
+            base = [gdal.CXT_Element, 'GvData']
+
+        base.append( [gdal.CXT_Attribute, 'read_only',
+                      [gdal.CXT_Text, str(self.is_read_only())]] )
+        base.append( [gdal.CXT_Attribute, 'name',
+                      [gdal.CXT_Text, str(self.get_name())]] )
+        projection = self.get_projection()
+        if  projection is not None and len(projection) > 0:
+            base.append( [gdal.CXT_Element, 'Projection',
+                          [gdal.CXT_Text, self.get_projection()]] )
+
+        props = self.get_properties()
+
+        for key in props.keys():
+            if key == '_gv_add_height_portable_path':
+                continue
+            v = props[key]
+            if key == '_gv_add_height_filename' and os.path.exists( v ):
+                base.append( [gdal.CXT_Element, 'Property',
+                              [gdal.CXT_Attribute, 'name',
+                               [gdal.CXT_Text,
+                                '_gv_add_height_portable_path']],
+                              [gdal.CXT_Text, pathutils.PortablePath( v, ref_path = filename ).serialize()]] )
+            base.append( [gdal.CXT_Element, 'Property',
+                          [gdal.CXT_Attribute, 'name', [gdal.CXT_Text, key]],
+                          [gdal.CXT_Text, v]] )
+        return base
+
+    def sink( self ):
+        _gtkmissing.gtk_object_sink( self._o )
+        
+    def initialize_from_xml( self, tree, filename=None ):
+        """Initialize this instance's properties from the XML tree
+        
+        Restores name, read_only, projection and all Property instances
+        """
+        self.set_name( gvutils.XMLFindValue( tree, 'name', self.get_name() ) )
+        self.set_read_only(int(gvutils.XMLFindValue(tree, 'read_only',
+                                                    str(self.is_read_only()))))
+        projection = gvutils.XMLFindValue( tree, 'projection',
+                                                   self.get_projection() )
+        if projection is not None:
+            self.set_projection( projection )
+
+        for subtree in tree[2:]:
+            if subtree[1] == 'Property':
+                name = gvutils.XMLFindValue( subtree, 'name' )
+                if name is not None:
+                    value = gvutils.XMLFindValue( subtree, '', '' )
+                    self.set_property(name, value)
+
+    def get_name(self):
+        """Fetch the name of this GvData."""
+        return _gv.gv_data_get_name(self._o)
+    
+    def set_name(self, name):
+        """Set the name of this GvData."""
+        _gv.gv_data_set_name(self._o, name)
+        
+    def is_read_only(self):
+        """Fetch the read_only flag."""
+        return _gv.gv_data_is_read_only(self._o)
+    
+    def set_read_only(self, read_only):
+        """Set the read_only flag of this GvData."""
+        _gv.gv_data_set_read_only(self._o, read_only)
+        
+    def changed(self):
+        """Emit GvData changed signal.
+
+        Send a notification that this data has changed, with a NULL 
+        change_info value."""
+        _gv.gv_data_changed(self._o)
+
+    def get_parent(self):
+        """Fetch parent GvData object.
+
+        This is typically used to get the underlying GvData on which
+        a GvLayer (which is also a GvData) depends."""
+
+        _o_parent = _gv.gv_data_get_parent(self._o)
+        if _o_parent is None:
+            return None
+        else:
+            return _gtk._obj2inst(_o_parent)
+
+    def get_projection(self):
+        """Fetch projection, if any.
+
+        The projection is normally expressed in OpenGIS Well Known Text
+        format or an empty string if no value is available."""
+        
+        return _gv.gv_data_get_projection(self._o)
+
+    def set_projection(self, projection):
+        """Set the projection.
+
+        This method won't actually modify the data geometry, only the
+        interpretation of the geometry."""
+        _gv.gv_data_set_projection(self._o, projection)
+
+    def get_properties(self):
+        """Get GvData properties (attributes) as a dictionary.
+
+        The properties are returned as a Python dictionary.  Note that
+        changes to this dictionary are not applied back to the GvData."""
+        return _gv.gv_data_get_properties(self._o)
+    
+    def get_property(self,name):
+        """Get a GvData property.
+
+        name -- the key or name of the property being set.  Should be a
+        well behaved token (no spaces, equal signs, or colons).
+
+        NOTE: Returns None if property does not exist."""
+        
+        return _gv.gv_data_get_property(self._o,name)
+
+    def set_properties( self, properties ):
+        """Set GvData properties
+
+        Clear all existing properties, and assign the new set passed in.
+
+        properties -- a python dictionary with the keys being property names,
+        and the result is the property value"""
+
+        return _gv.gv_data_set_properties( self._o, properties )
+    
+    def set_property(self,name,value):
+        """Set a GvData property.
+
+        name -- the key or name of the property being set.  Should be a
+        well behaved token (no spaces, equal signs, or colons).
+        
+        value -- the value to be assigned.  Any text is acceptable."""
+        
+        return _gv.gv_data_set_property(self._o,name,value)
+
+    def freeze(self):
+        """Freeze a GvData object so that changes are not propagated
+           until thawed"""
+        return _gv.gv_data_freeze(self._o)
+
+    def thaw(self):
+        """Thaw a frozen GvData object"""
+        return _gv.gv_data_thaw(self._o)
+
+def gv_data_registry_dump():
+    _gv.gv_data_registry_dump()
+    
+
+###############################################################################
+class GvRecord:
+
+    def __init__(self, records, rec_index ):
+        self.records = records
+        self.rec_index = rec_index
+
+    def get_properties( self ):
+        return self.records.get_rec_properties( self.rec_index )
+
+    def get_typed_properties( self ):
+        return self.records.get_rec_typed_properties( self.rec_index )
+
+    def get_property( self, property_name, default_value = None ):
+        properties = self.records.get_rec_properties( self.rec_index )
+        if properties.has_key( property_name ):
+            return properties[property_name]
+        else:
+            return default_value
+
+    def set_property(self,name,value):
+        field_index = self.records.fieldname_to_index( name )
+        self.records.set_rec_property( self.rec_index, field_index, value )
+    
+    def set_properties(self,properties):
+        pass
+        
+
+###############################################################################
+class GvRecords(GvData):
+    get_type = _gv.gv_records_get_type
+    def __init__(self, name=None, _obj=None, filename=None,
+                 progress_cb = None, cb_data = None ):
+        if _obj:
+            self._o = _obj
+            self.sink()
+            return
+
+        if filename is not None:
+            if gvutils.is_shapefile(filename):
+                self._o = _gv.gv_records_from_dbf( filename, progress_cb, cb_data )
+            else:
+                self._o = _gv.gv_records_from_rec( filename, progress_cb, cb_data )
+                
+            if self._o is None:
+                raise ValueError, 'Reading %s failed.' % filename
+
+            self.sink()
+            if name is not None:
+                self.set_name(name)
+            else:
+                self.set_name(filename)
+
+            # default to all fields.
+            self.set_used_properties(None)
+            return
+            
+        self._o = _gv.gv_records_new()
+        if name: self.set_name(name)
+
+    def __len__(self):
+        """Return number of shapes in container."""
+        return _gv.gv_records_num_records(self._o)
+    
+    def __getitem__(self, rec_index):
+        if rec_index < 0 or rec_index >= _gv.gv_records_num_records(self._o):
+            raise IndexError
+        else:
+            return GvRecord( self, rec_index )
+        
+    def __setitem__(self, index, shape):
+        pass
+        
+    def create_records( self, count = 1 ):
+        return _gv.gv_records_create_records( self._o, count )
+
+    def fieldname_to_index( self, field_name, schema = None ):
+        if schema is None:
+            schema = self.get_schema()
+        for i in range(len(schema)):
+            if schema[i][0] == field_name:
+                return i
+
+        field_name = string.lower(field_name)
+        for i in range(len(schema)):
+            if string.lower(schema[i][0]) == field_name:
+                return i
+
+        return -1
+    
+    def set_used_properties( self, property_list ):
+        schema = self.get_schema()
+        index_list = []
+        if property_list is not None:
+            for i in range(len(property_list)):
+                item = property_list[i]
+                i_fld = self.fieldname_to_index( item )
+                if i_fld == -1:
+                    raise ValueError, 'Unknown field name:' + item
+                index_list.append( i_fld )
+        else:
+            index_list = range(len(schema))
+
+        _gv.gv_records_set_used_properties( self._o, index_list )
+
+
+    def get_rec_typed_properties( self, record_index ):
+        return _gv.gv_records_get_typed_properties( self._o, record_index )
+
+    def get_rec_properties( self, record_index ):
+        return _gv.gv_records_get_properties( self._o, record_index )
+
+    def get_rec_property( self, record_index, field_index ):
+        return _gv.gv_records_get_raw_field_data( self._o, record_index,
+                                                  field_index )
+
+    def set_rec_property( self, record_index, field_index, value ):
+        if value is not None:
+            value = str(value)
+            
+        return _gv.gv_records_set_raw_field_data( self._o, record_index,
+                                                  field_index, value )
+
+    def get_schema( self, fieldname = None ):
+        prop = self.get_properties()
+
+        schema = []
+        cur_field = 1
+        key_name = '_field_name_' + str(cur_field)
+
+        while prop.has_key(key_name):
+            name = prop['_field_name_'+str(cur_field)]
+
+            if fieldname is not None \
+               and string.lower(name) != string.lower(fieldname):
+                cur_field = cur_field + 1
+                key_name = '_field_name_' + str(cur_field)
+                continue
+
+            type = prop['_field_type_'+str(cur_field)]
+            width = int(prop['_field_width_'+str(cur_field)])
+            try:
+                precision = int(prop['_field_precision_'+str(cur_field)])
+            except:
+                precision = 0
+
+            field_schema = (name, type, width, precision)
+
+            if fieldname is not None:
+                return field_schema
+
+            schema.append( field_schema )
+                        
+            cur_field = cur_field + 1
+            key_name = '_field_name_' + str(cur_field)
+
+        if fieldname is not None:
+            return None
+        else:
+            return schema
+
+    def get_layout( self ):
+        pass
+
+    def add_field( self, name, type = 'string', width = 0, precision = 0 ):
+
+        """Add field to schema.
+
+        This function will define a new field in the schema for this
+        container.  The schema is stored in the GvShapes properties, and it
+        used by selected functions such as the save_to() method to define
+        the schema of output GIS files.
+
+        Note that adding a field to the schema does not cause it to be
+        added to all the individual GvShape objects in the container.
+        However, the save_to() method will assume a default value for
+        shapes missing some of the fields from the schema, so this is generally
+        not an issue. 
+
+        name -- the name of the field.  The application is expected to ensure
+                this is unique within the layer.
+
+        type -- the type of the field.  Must be one of 'integer', 'float', or
+                'string'.
+                
+        width -- the field width.  May be zero to indicated variable width.
+        
+        precision -- the number of decimal places to be preserved.  Should be
+                     zero for non-float field types.
+
+        The field number of the newly created field is returned."""
+
+        if precision != 0 and type != 'float':
+            raise ValueError, 'Non-zero precision on '+type+' field '+name+' in GvShapes.add_field()'
+
+        rft = None
+        
+        # GV_RFT_FLOAT
+        if type == 'float':
+            rft = 2
+
+        # GV_RFT_STRING
+        if type == 'string':
+            rft = 3
+
+        # GV_RFT_INTEGER
+        if type == 'integer':
+            rft = 1
+
+        if rft is None:
+            raise ValueError, 'Illegal field type "'+type+'" in GvRecords.add_field(), should be float, integer or string.'
+
+        return _gv.gv_records_add_field(self._o, name, rft, width, precision )
+        
+    def save_to_dbf(self, filename, selection = [],
+                    progress_cb = None, cb_data = None ):
+        """Save records attributes to a DBF file.
+        
+        filename -- name of the file to save to
+
+        selection -- list of shape indexes to write, default for all.
+        """
+        
+        return _gv.gv_records_to_dbf( self._o, filename, selection,
+                                      progress_cb, cb_data )
+
+    def MultiStratifiedCollect( self, selection, var1, var2, strat_vars = [],
+                                progress_cb = None, progress_data = None ):
+        return _gv.gv_records_MultiStratifiedCollect( self._o, selection,
+                                                      var1, var2, strat_vars,
+                                                      progress_cb,
+                                                      progress_data )
+
+    def recode( self, single_mapping, range_mapping, srcvar, dstvar = None,
+                progress_cb = None, progress_data = None ):
+
+        if dstvar is None:
+            dstvar = srcvar
+
+        return _gv.gv_records_recode( self._o, single_mapping, range_mapping,
+                                      srcvar, dstvar,
+                                      progress_cb, progress_data )
+
+###############################################################################
+def GvShapesFromXML( node, parent, filename=None ):
+    """construct a gvshapes object from an xml tree.
+
+    node is the current node to instantiate from
+    parent is the parent object that wants to instantiate
+    this GvShapes object.
+
+    will return the new GvShapes instance or None if something went
+    horribly wrong.
+    """
+    
+    shapes = GvShapes()
+
+    #restore properties
+    shapes.initialize_from_xml( node, filename=filename )
+    
+    #restore shapes
+    shape_tree = gvutils.XMLFind(node, 'Shapes')
+    for subtree in shape_tree[2:]:
+        shape = gvutils.XMLInstantiate( subtree, parent, filename=filename )
+        if shape is None:
+            print 'shape is None'
+        elif shape._o is None:
+            print 'shape._o is None'
+        else:
+            shapes.append(shape)
+
+    return shapes
+
+class GvShapes(GvData):
+    """A GvData of points, lines and areas (GvShapes). 
+
+    This layer can be treated as a list object, where each value is
+    a GvShape.
+
+    Notes on updating Shapes:
+
+    In order to generate proper undo information, and to ensure proper
+    generation of data changing and changed events, it is illegal for
+    applications to change the geometry of attributes of GvShape objects
+    that are members of a GvShapes container.  If you want to change some
+    aspect of a shape, it is necessary to copy it, modify the copy, and then
+    apply the copy back to the GvShapes.
+
+    eg.
+      shape = shapes[45].copy()
+      shape.set_property( "abc", "def" )
+      shapes[45] = shape
+      
+    """
+    
+    get_type = _gv.gv_shapes_get_type
+    def __init__(self, name=None, _obj=None, shapefilename=None):
+        """Create a GvShapes
+
+        name -- name to assign to GvShapes, defaults to None.
+        shapefilename -- name of ESRI shapefile to create from, or None
+                         (default) to create an empty container.
+        """
+        if _obj:
+            self._o = _obj
+            self.sink()
+            return
+        
+        if shapefilename:
+            self._o = _gv.gv_shapes_from_shapefile(shapefilename)
+            self.sink()
+            return
+        
+        self._o = _gv.gv_shapes_new()
+        if name: self.set_name(name)
+        self.sink()
+        
+    def __len__(self):
+        """Return number of shapes in container."""
+        return _gv.gv_shapes_num_shapes(self._o)
+    
+    def __getitem__(self, index):
+        """Return an individual shape by index"""
+        shp_o = _gv.gv_shapes_get_shape(self._o, index)
+        if shp_o is not None:
+            return GvShape(_obj=shp_o)
+        else:
+            if index < 0 or index >= _gv.gv_shapes_num_shapes(self._o):
+                raise IndexError
+            else:
+                return None;
+        
+    def __setitem__(self, index, shape):
+        """Overwrite a shape by index
+
+        Note that this actually deletes the old shape, and the new shape
+        will hereafter be referenced in the layer.  Only freestanding GvShape
+        objects (not assigned to any existing GvShapes) should be assigned in
+        this manner, and they will thereafter be owned by the GvShapes."""
+
+        _gv.gv_shapes_replace_shapes(self._o, [index,], [shape._o,])
+        
+    def __delitem__(self, index):
+        """Delete an individual shape by index"""
+
+        _gv.gv_shapes_delete_shapes(self._o, [index,])
+        
+    def serialize( self, base = None, filename = None ):
+        """serialize this object in a format suitable for XML representation.
+        """
+        if base is None:
+            base = [gdal.CXT_Element, 'GvShapes']
+
+        GvData.serialize( self, base, filename=filename )
+        
+        shapes = [gdal.CXT_Element, 'Shapes' ]
+        for i in range(len(self)):
+            if self[i] is not None:
+                shapes.append(self[i].serialize( ))
+        base.append( shapes )
+        
+        return base
+
+    def append(self, shape):
+        """Add GvShape to GvShapes. If there is an empty space in GvShapes
+        (i.e. a shape has been deleted), place the new shape in it. 
+
+        shape -- GvShape to add.
+
+        Returns the id of the newly added shape."""
+        return _gv.gv_shapes_add_shape(self._o, shape._o)
+
+    def append_last(self, shape):
+        """Add GvShape to GvShapes always at the end of list. 
+
+        shape -- GvShape to add.
+
+        Returns the id of the newly added shape."""
+        return _gv.gv_shapes_add_shape_last(self._o, shape._o)
+
+    def get_extents(self):
+        """Fetch bounds extents of shapes.
+
+        The extents are returned as an (xmin,ymin,xsize,ysize) tuple."""
+        return _gv.gv_shapes_get_extents(self._o)
+
+    def delete_shapes(self, shapes):
+        """Delete a list of shapes
+
+        shapes -- a list of integer shape indexes to delete
+
+        Note the underlying GvShape objects are destroyed in addition to
+        them being removed from the container."""
+        
+        _gv.gv_shapes_delete_shapes(self._o, shapes)
+
+    def save_to(self, filename, type = 0):
+        """Save layer to ESRI Shapefile
+
+        Shapefiles can only hold a single geometric type.  By default this
+        method will select a type based on the geometry of the first feature
+        (GvShape) written, but it can be overridden with the type argument.
+        The possible values are listed as SHPT_ codes in shapefil.h.
+        
+        filename -- name of file to save to (extension will be automatically
+                    supplied)
+        type -- One of the shapefile type codes, or zero to try and auto
+        detect the type of file to create.  Zero is the default."""
+        
+        return _gv.gv_shapes_to_shapefile( filename, self._o, type )
+        
+    def save_to_dbf(self, filename):
+        """Save shape attributes to a DBF file.
+        
+        filename -- name of the file to save to
+        """
+        
+        return _gv.gv_shapes_to_dbf( filename, self._o )
+
+    def get_change_info(self, c_object):
+        """Used to convert a PyCObject as returned from a changed/changing
+        signal to a python tuple.
+        
+        Takes a PyCObject and returns the equivalent python object.
+
+        (change_type, num_shapes, (shape_ids))
+        where change_type is defined in gvconst.py
+              num_shapes is an integer
+              shape_ids is a list of shape IDs"""
+        
+        return _gv.gv_shapes_get_change_info(c_object)
+
+    def add_height( self, raster, offset=0.0, default_height = 0.0 ):
+        """Set vertex heights from raster DEM.
+
+        Sets the Z component of each vertex to the value sampled from
+        the raster, and then adds the offset.  Values off the DEM, or
+        in _nodata_ areas of the DEM are set to 0.0.  Old z coordinates
+        are always lost.
+
+        The offset is normally used to boost the vectors up a bit over any
+        raster layers so they remain visible.  Usually a value on the order
+        of 1/3 the size of a pixel is sufficient.
+
+        raster -- a GvRaster from which to sample elevations, should be in
+        a compatible coordinate system to the shapes.
+        offset -- optional offset to apply to the vertices. """
+
+        # Actually add the height.
+        _gv.gv_shapes_add_height( self._o, raster._o, offset, default_height )
+
+    def get_schema( self, fieldname = None ):
+        """Fetch attribute schema.
+
+        When read from GIS data sources such as shapefiles or OGR supported
+        GIS formats, there will be a schema associated with a GvShapes
+        grouping.  The schema is the definition of the list of attributes
+        shared by all shapes in the container.  This information is stored
+        in the properties of the GvShapes.  This method extracts the schema
+        information in a more easily used form for Python.
+
+        The returned schema will contain a list of tuples, with one tuple
+        per attribute in the schema.  The tuples wille each contain four
+        elements.
+
+         - name: the name of the field.
+         - type: the type of the field.  One of 'integer', 'float' or 'string'.
+         - width: the width normally used for displaying the field, and
+                  limiting storage capacity in some formats (ie. shapefiles)
+         - precision: the number of decimal places preserved.  For non
+                      floating point values this is normally 0.
+
+        Optionally, a single fieldname can be given as an argument and only
+        schema tuple for that field will be returned (or None if it does not
+        exist).
+        
+        """
+        prop = self.get_properties()
+
+        schema = []
+        cur_field = 1
+        key_name = '_field_name_' + str(cur_field)
+
+        while prop.has_key(key_name):
+            name = prop['_field_name_'+str(cur_field)]
+
+            if fieldname is not None \
+               and string.lower(name) != string.lower(fieldname):
+                cur_field = cur_field + 1
+                key_name = '_field_name_' + str(cur_field)
+                continue
+
+            type = prop['_field_type_'+str(cur_field)]
+            width = int(prop['_field_width_'+str(cur_field)])
+            try:
+                precision = int(prop['_field_precision_'+str(cur_field)])
+            except:
+                precision = 0
+
+            field_schema = (name, type, width, precision)
+
+            if fieldname is not None:
+                return field_schema
+
+            schema.append( field_schema )
+                        
+            cur_field = cur_field + 1
+            key_name = '_field_name_' + str(cur_field)
+
+        if fieldname is not None:
+            return None
+        else:
+            return schema
+
+    def get_layout( self ):
+        """Get tabular layout information.
+
+        This returns a list of field names, and their suggested width.  The
+        list of field names is based on the results of get_schema(), so
+        any fields not listed in the schema will be missed.  The width
+        returned for each field is the maximum of the field title, and all
+        the field values occuring in all shapes.
+
+        The return result is a list of (field_name, width) tuples in the
+        same order as in the schema.
+        """
+
+        schema = self.get_schema()
+        name_list = []
+        width_list = []
+        for item in schema:
+            name_list.append( item[0] )
+            width_list.append( len(item[0]) )
+
+        field_count = len(name_list)
+
+        for rec in self:
+
+            props = rec.get_properties()
+
+            for i_field in range(field_count):
+                try:
+                    l = len(props[name_list[i_field]])
+                    if l > width_list[i_field]:
+                        width_list[i_field] = l
+                except:
+                    pass
+
+        result = []
+        for i_field in range(field_count):
+            result.append( (name_list[i_field], width_list[i_field]) )
+
+        return result
+
+    def add_field( self, name, type = 'string', width = 0, precision = 0 ):
+
+        """Add field to schema.
+
+        This function will define a new field in the schema for this
+        container.  The schema is stored in the GvShapes properties, and it
+        used by selected functions such as the save_to() method to define
+        the schema of output GIS files.
+
+        Note that adding a field to the schema does not cause it to be
+        added to all the individual GvShape objects in the container.
+        However, the save_to() method will assume a default value for
+        shapes missing some of the fields from the schema, so this is generally
+        not an issue. 
+
+        name -- the name of the field.  The application is expected to ensure
+                this is unique within the layer.
+
+        type -- the type of the field.  Must be one of 'integer', 'float', or
+                'string'.
+                
+        width -- the field width.  May be zero to indicated variable width.
+        
+        precision -- the number of decimal places to be preserved.  Should be
+                     zero for non-float field types.
+
+        The field number of the newly created field is returned."""
+
+        if type != 'float' and type != 'integer' and type != 'string':
+            raise ValueError, 'Illegal field type "'+type+'" in GvShapes.add_field(), should be float, integer or string.'
+
+        if precision != 0 and type != 'float':
+            raise ValueError, 'Non-zero precision on '+type+' field '+name+' in GvShapes.add_field()'
+
+        cur_schema = self.get_schema()
+        new_entry = len(cur_schema) + 1
+        
+        self.set_property( '_field_name_'+str(new_entry), name )
+        self.set_property( '_field_type_'+str(new_entry), type )
+        self.set_property( '_field_width_'+str(new_entry), str(width) )
+        self.set_property( '_field_precision_'+str(new_entry), str(precision))
+
+        return new_entry
+
+       
+
+###############################################################################
+class GvPoints(GvData):
+    get_type = _gv.gv_points_get_type
+    def __init__(self, name=None, _obj=None):
+        if _obj: self._o = _obj; return        
+        self._o = _gv.gv_points_new()
+        if name: self.set_name(name)
+    def __len__(self):
+        return _gv.gv_points_num_points(self._o)
+    def __getitem__(self, index):
+        return _gv.gv_points_get_point(self._o, index)
+    def append(self, point):
+        return _gv.gv_points_new_point(self._o, point)
+
+###############################################################################
+class GvPolylines(GvData):
+    get_type = _gv.gv_polylines_get_type
+    def __init__(self, name=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_polylines_new()
+        if name: self.set_name(name)
+    def __len__(self):
+        return _gv.gv_polylines_num_lines(self._o)
+    def __getitem__(self, index):
+        return _gv.gv_polylines_get_line(self._o, index)
+    def append(self, line):
+        return _gv.gv_polylines_new_line(self._o, line)
+
+###############################################################################
+class GvAreas(GvData):
+    get_type = _gv.gv_areas_get_type
+    def __init__(self, name=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_areas_new()
+        if name: self.set_name(name)
+    def __len__(self):
+        return _gv.gv_areas_num_areas(self._o)
+    def __getitem__(self, index):
+        return _gv.gv_areas_get_area(self._o, index)
+    def append(self, area):
+        return _gv.gv_areas_new_area(self._o, area)
+
+###############################################################################
+
+def GvRasterFromXML( node, parent, filename=None ):
+    band = int(gvutils.XMLFindValue(node,"band","1"))
+    portable_path = gvutils.XMLFindValue( node, 'portable_path' )
+    if portable_path is not None:
+        f = pathutils.PortablePathFromXML( portable_path ).local_path( ref_path = filename )
+    else:
+        for child in node[2:]:
+            if child[0] == gdal.CXT_Text:
+                f = child[1]
+
+    ds = manager.get_dataset( f )
+    raster = manager.get_dataset_raster( ds, band )
+    return raster
+
+class GvRaster(GvData):
+    """Raster data object
+
+    Signals:
+        
+    geotransform-changed -- generated when the geotransform (whether
+    affine or the polynomials based on gcps) changes.  Primarily used
+    by the GvRasterLayer to ensure updates to mesh and display.
+    """
+    
+    get_type = _gv.gv_raster_get_type
+    def __init__(self, filename=None, dataset=None, _obj=None,
+                 sample=SMSample, real=1 ):
+        """Create new raster.
+
+        All the arguments of this method are optional, and can be passed
+        as keywords.
+        
+        filename -- name of the file to open.  It must be suitable for use
+        with gdal.Open().
+
+        dataset -- a gdal.Dataset object as returned by gdal.Open().  May
+        be used as an alternative to filename.
+
+        sample -- Method to use to compute reduced levels of detail.  Either
+        gview.SMAverage (2x2 average) or gview.SMSample (decimation).
+
+        real -- The band from the raster file to use as the band.
+
+        """
+        if _obj: self._o = _obj; return
+        if (filename is None) and (dataset is None):
+            raise ValueError, "expecting filename or dataset handle"
+
+        if not (dataset is None):
+            dataset_raw = dataset._o
+        else:
+            dataset_raw = None
+
+        self._o = _gv.gv_raster_new(filename = filename, sample = sample,
+                                    real = real, dataset = dataset_raw)
+        self.sink()
+
+    def flush_cache(self,x_off=0,y_off=0,width=0,height=0):
+        """Flush data cache.
+
+        This will cause the data caches of GDAL, and this GvRaster to be
+        cleared out.  If this is being done to trigger reload, and redisplay
+        of modified data on disk, then a changed signal should be emitted on
+        the GvRaster instead.  This will trigger a flush automatically, and
+        also invalidate displays, forcing them to be rerendered.
+
+        x_off -- x origin of area to be flushed.
+        y_off -- y origin of area to be flushed.
+        width -- width of area to be flushed (zero for whole image).
+        height -- height of area to be flushed (zero for whole image).
+        """
+        _gv.gv_raster_flush_cache(self._o,x_off,y_off,width,height)
+
+    def get_sample(self, x, y):
+        """Fetch sample value from raster.
+
+        This function will fetch the real, or complex data value at the
+        given location (in raster pixel/line coordinates).  If the result
+        is real a single number is returned.  It it is complex a (real
+        imaginary) tuple is returned.  If the requested point is outside
+        the raster, or if the call fails for some other reason a None is
+        returned.
+
+        x -- x offset from top left corner of pixel to fetch.
+        y -- y offset from top left corner of pixel to fetch."""
+        
+        return _gv.gv_raster_get_sample(self._o, x, y)
+
+    def georef_to_pixel(self, x, y ):
+        """Translate georeferenced coordinates to pixel/line.
+
+        x -- X (easting or longitude) in raster layer georeferencing system.
+        y -- Y (northing or latitude) in raster layer georeferencing system.
+
+        Returns a (pixel,line) coordinate tuple on the raster."""
+        
+        return _gv.gv_raster_georef_to_pixel(self._o, x, y)
+
+    def cursor_link_georef_to_pixel(self, x, y ):
+        """Translate cursor/link georeferenced coordinates to pixel/line.
+
+        x -- X (easting or longitude) in raster layer georeferencing system.
+        y -- Y (northing or latitude) in raster layer georeferencing system.
+
+        Returns a (pixel,line) coordinate tuple on the raster."""
+        
+        return _gv.gv_raster_georef_to_pixelCL(self._o, x, y)
+
+    def pixel_to_georef(self, x, y ):
+        """Translate pixel/line to georeferenced coordinate.
+
+        x -- pixel on raster layer (0.0 is left side of leftmost pixel)
+        y -- line on raster layer (0.0 is top side of topmost pixel)
+
+        Returns an (x,y) coordinate tuple in the raster georeferencing
+        system."""
+        
+        return _gv.gv_raster_pixel_to_georef(self._o, x, y)
+
+    def cursor_link_pixel_to_georef(self, x, y ):
+        """Translate pixel/line to cursor/link georeferenced coordinate.
+
+        x -- pixel on raster layer (0.0 is left side of leftmost pixel)
+        y -- line on raster layer (0.0 is top side of topmost pixel)
+
+        Returns an (x,y) coordinate tuple in the raster georeferencing
+        system defined for the cursor and link mechanism (defaults
+        to the standard pixel_to_georef if no separate gcps have
+        been defined for the cursor/link)."""
+        
+        return _gv.gv_raster_pixel_to_georefCL(self._o, x, y)
+
+    def data_changing(self, x_off, y_off, width, height):
+        """Send GvData changing signal with a raster rectangle.
+
+        This signal indicates that a region of the raster is about to change,
+        and will trigger capture of an undo memento of the region if
+        undo is enabled.
+
+        x_off -- pixel offset to top left corner of change region.
+        y_off -- line offset to top left corner of change region.
+        width -- width of window that is changing. 
+        height -- width of window that is changing."""
+        _gv.gv_raster_data_changing(self._o, x_off, y_off, width, height )
+        
+    def data_changed(self, x_off, y_off, width, height):
+        """Send GvData changed signal with a raster rectangle.
+
+        This signal indicates that a region of the raster has just changed,
+        and will trigger invalidation of any buffered data from the
+        source file, and rereading for display.
+        
+        x_off -- pixel offset to top left corner of change region.
+        y_off -- line offset to top left corner of change region.
+        width -- width of window that is changed. 
+        height -- width of window that is changed."""
+        _gv.gv_raster_data_changed(self._o, x_off, y_off, width, height )
+
+    def get_change_info(self, c_object):
+        """Used to convert a PyCObject as returned from a changed/changing
+        signal to a python tuple.
+
+        Returns an equivalent python tuple object to a GvRasterChangeInfo
+        structure as returned by a display_changed signal.
+
+        Returns (change_type, x_off, y_off, width, height)
+           where change_type is defined in gvconst.py and the rest are integers.
+        """
+        return _gv.gv_raster_get_change_info(c_object)
+
+    def get_band_number(self):
+        band = self.get_band()
+        dataset = self.get_dataset()
+        for iband in range(dataset.RasterCount):
+            test_band = dataset.GetRasterBand(iband+1)
+            if test_band._o == band._o:
+                return iband+1
+        return -1
+    
+    def get_band(self):
+        """Get GDAL raster band
+
+        Fetch the band associated with the GvRaster as a
+        gdal.RasterBand object.
+        """
+        
+        band_swigptr = _gv.gv_raster_get_gdal_band(self._o)
+        if band_swigptr is None:
+            return None
+        
+        return gdal.Band(_obj=band_swigptr);
+        
+    def get_dataset(self):
+        """Get GDAL raster dataset
+
+        Fetch the dataset associated with the GvRaster as a
+        gdal.Dataset object.
+        """
+        
+        band_swigptr = _gv.gv_raster_get_gdal_dataset(self._o)
+        if band_swigptr is None:
+            return None
+        
+        return gdal.Dataset(_obj=band_swigptr);
+
+    def autoscale(self, alg = ASAAutomatic, alg_param = -1.0, assign = 0):
+        """Force autoscaling to be recomputed.
+
+        alg -- Algorithm to use (see below)
+        alg_param -- the parameter to the algorithm, or -1.0 to get
+                     default parameter value (possibly from preferences).
+        assign -- 1 to assign min/max to GvRaster or 0 to just return it.
+
+        This method will force recomputation of scaling min/max values
+        based on a sample of the data.  The algorithms collect roughly 10000
+        sample points approximately at random.  Any recognised no data values
+        are removed from the sample set.
+
+        The first algorithm option is ASAPercentTailTrim, in which case
+        the parameter is the percentage of the tail to exclude (0.02 for
+        2%).
+
+        The second algorithm option is ASAStdDeviation in which case
+        the min/max values are based on the selected number of standard
+        deviations below and above the mean of the sample.  The parameter
+        value might be 1.25 to use 1.25 standard deviations on each side of
+        the mean as the min/max values.
+
+        The algorithm value ASAAutomatic can also be passed (the default)
+        to determine the preferred algorithm from application preferences.
+
+        If the autoscale() fails (for instance due to an IO error, or if all
+        the sample data is the nodata value) the method will throw an
+        exception otherwise a (min,max) tuple is returned.
+        
+        """
+        return _gv.gv_raster_autoscale(self._o, alg, alg_param, assign)
+    
+    def get_min(self):
+        """Get the minimum for default scaling
+
+        See the autoscale() method for information on this is established."""
+        return _gv.gv_raster_get_min(self._o)
+        
+    def get_max(self):
+        """Get the maximum for default scaling
+        
+        See the autoscale() method for information on this is established."""
+        return _gv.gv_raster_get_max(self._o)
+
+    def force_load(self):
+        """Force loading all full res data.
+
+        All full res data for this GvRaster is forceably loaded, though
+        normal cache expiration rules apply so earlier tiles may already
+        be discarded as later tiles are fetched.  This is a blocking
+        operation and should be use with care.  It's main purpose is to
+        provide speculative "preloading" of smallish files to provide smooth
+        animation effects."""
+
+        _gv.gv_raster_force_load( self._o )
+        
+    def get_gcps(self):
+        gcp_tuple_list = _gv.gv_raster_get_gcps(self._o)
+
+        gcp_list = []
+        for gcp_tuple in gcp_tuple_list:
+            gcp = gdal.GCP()
+            gcp.Id = gcp_tuple[0]
+            gcp.Info = gcp_tuple[1]
+            gcp.GCPPixel = gcp_tuple[2]
+            gcp.GCPLine = gcp_tuple[3]
+            gcp.GCPX = gcp_tuple[4]
+            gcp.GCPY = gcp_tuple[5]
+            gcp.GCPZ = gcp_tuple[6]
+            gcp_list.append(gcp)
+
+        return gcp_list
+
+    def get_cursor_link_gcps(self):
+        gcp_tuple_list = _gv.gv_raster_get_gcpsCL(self._o)
+
+        gcp_list = []
+        for gcp_tuple in gcp_tuple_list:
+            gcp = gdal.GCP()
+            gcp.Id = gcp_tuple[0]
+            gcp.Info = gcp_tuple[1]
+            gcp.GCPPixel = gcp_tuple[2]
+            gcp.GCPLine = gcp_tuple[3]
+            gcp.GCPX = gcp_tuple[4]
+            gcp.GCPY = gcp_tuple[5]
+            gcp.GCPZ = gcp_tuple[6]
+            gcp_list.append(gcp)
+
+        return gcp_list
+
+    def set_gcps(self, gcp_list ):
+        tuple_list = []
+        for gcp in gcp_list:
+            tuple_list.append( (gcp.Id, gcp.Info, gcp.GCPPixel, gcp.GCPLine,
+                                gcp.GCPX, gcp.GCPY, gcp.GCPZ) )
+
+        return _gv.gv_raster_set_gcps( self._o, tuple_list )
+
+    def set_cursor_link_gcps(self, gcp_list, poly_order=1 ):
+        tuple_list = []
+        for gcp in gcp_list:
+            tuple_list.append( (gcp.Id, gcp.Info, gcp.GCPPixel, gcp.GCPLine,
+                                gcp.GCPX, gcp.GCPY, gcp.GCPZ) )
+
+        return _gv.gv_raster_set_gcpsCL( self._o, tuple_list, poly_order )
+
+    def set_poly_order_preference(self, poly_order):
+        # Set preferred polynomial order for this raster (for use in
+        # display).  This is only used if it is appropriate for
+        # the current number of gcp's (ground control points).
+        # If order is <1 or >3 it will be reset to fall within this
+        # range.
+        _gv.gv_raster_set_poly_order_preference(self._o, poly_order)
+
+#def GvLayerFromXML( node, parent, filename=None ):
+#    band = int(gvutils.XMLFindValue(node,"band","1"))
+#    for child in node[2:]:
+#        if child[0] == gdal.CXT_Text:
+#            filename = child[1]
+
+#    ds = manager.get_dataset( filename )
+#    raster = manager.get_dataset_raster( ds, band )
+#    return raster
+
+###############################################################################
+class GvLayer(GvData):
+    """Base class for display layers.
+
+    Signals:
+
+    setup -- called when the layer is being setup.  Internal use. 
+
+    teardown -- called when the layer is being destroyed.  Internal use.
+
+    draw -- called after normal drawing is complete on the layer.  Gives tools
+    an opportunity to draw layer specific overlays.
+
+    get-extents -- called to fetch extents of layer.  Takes a GvRect as an
+    argument (not clear to me why this is a signal).
+
+    display-change -- called to indicate a display property (colour,
+    interpolation method, etc) of this layer has changed.  Triggers redraw.
+    """
+    
+    get_type = _gv.gv_layer_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+
+    def serialize( self, base = None, filename=None ):
+        if base is None:
+            base = [gdal.CXT_Element, 'GvLayer']
+
+        base.append( [gdal.CXT_Attribute, 'visible',
+                      [gdal.CXT_Text, str(self.is_visible())]] )
+
+        GvData.serialize( self, base, filename=filename )
+
+        return base
+
+    def initialize_from_xml( self, tree, filename=None ):
+        """restore object properties from XML tree
+        """
+        GvData.initialize_from_xml( self, tree, filename=filename )
+        self.set_visible( int(gvutils.XMLFindValue( tree, 'visible',
+                                                    str(self.is_visible()))))
+        
+    def is_visible(self):
+        """Check if layer is visible.
+
+        Returns a non-zero value if the layer is currently visible.
+        """
+        return _gv.gv_layer_is_visible(self._o)
+    
+    def set_visible(self, visible):
+        """Set layer visibility.
+
+        visible -- 0 for invisible, and non-zero for visible.
+        """
+        _gv.gv_layer_set_visible(self._o, visible)
+        
+    def extents(self):
+        """Return extents of layer.
+
+        The extents are returned as a tuple (xmin,ymin,width,height)."""
+        return _gv.gv_layer_extents(self._o)
+
+    def reproject(self, projection):
+        """Attempt to change view projection.
+
+        projection -- the projection string in OpenGIS Well Known Text format.
+        
+        Currently this only works for rasters, but eventually it will
+        modify the display projection of any kind of GvLayer.
+
+        Returns 0 on failure, or non-zero on success."""
+        return _gv.gv_layer_reproject(self._o, projection)
+
+    def launch_properties(self):
+        """Launch a properties panel for this layer.
+
+        Returns the dialog object, or None if none can be created."""
+        return None
+
+    def display_change(self):
+        """Send a display property notification."""
+        _gv.gv_layer_display_change(self._o)
+
+    def get_view(self):
+        """Fetch the GvViewArea this layer is on."""
+
+        _o_view = _gv.gv_layer_get_view( self._o )
+        if _o_view is None:
+            return None
+        else:
+            return _gtk._obj2inst(_o_view)
+
+    def classify(self):
+        from gvclassification import GvClassification
+        from gvclassifydlg import GvClassificationDlg
+
+        classification = GvClassification(self)
+        classifyier = GvClassificationDlg(classification)
+        classifyier.show()
+        
+    def show_legend(self):
+        from gvclassification import GvClassification
+        import gvlegenddlg
+        
+        cls = GvClassification(self)
+        if cls.count > 0:
+            gvlegenddlg.show_legend( self )
+
+    def refresh( self ):
+        """Refresh from view of layer as required by layer type."""
+        pass
+
+###############################################################################
+class GvShapeLayer(GvLayer):
+    """Display layer of vector shape objects.
+
+    Signals:
+
+    selection-changed -- This signal is emitted when the selection for the
+    layer has changed (including clearing the selection).  Use get_selected()
+    to get the current selection list.
+
+    subselection-changed -- This signal is emitted when the subselection
+    (the focus shape within the current selection) changes.  Note that a
+    subselection-changed signal is usually generated on selection changes,
+    as well as subselection changes.
+    
+    This class has many other signals, but they are mostly for internal
+    implementation of selection, editing and so forth.
+
+    """
+    get_type = _gv.gv_shape_layer_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+
+    def serialize(self, base, filename=None ):
+        """serialize this object in a format suitable for XML representation.
+
+        Adds properties for the current selection
+        """
+
+        if base is None:
+            base = [gdal.CXT_Element, 'GvShapeLayer']
+
+        GvLayer.serialize( self, base, filename=filename )
+                
+    def initialize_from_xml( self, tree, filename=None ):
+        """initialize this object from the XML tree
+        """
+
+        GvLayer.initialize_from_xml( self, tree, filename=filename )
+        
+    def set_color(self, color):
+        """Set the drawing color.
+
+        This method is deprecated.
+        """
+        _gv.gv_shape_layer_set_color(self._o, color)
+
+    def pick_shape( self, view, x, y ):
+        """
+        """
+        return _gv.gv_shape_layer_pick_shape( self._o, view._o, x, y )
+
+    def get_selected(self):
+        """Get list of currently selected objects.
+
+        The list of shape id's is returned as a list. 
+        """
+        return _gv.gv_shape_layer_get_selected(self._o)
+
+    def get_subselected(self):
+        """Get current subselection.
+
+        Returns the shape id of the current subselection.  The subselection
+        is the one shape out the current set of selected shapes that has
+        special focus of attention from the user.  The return value will be
+        -1 if there is no subselection ... generally because there is no
+        selection."""
+        
+        return _gv.gv_shape_layer_get_subselection(self._o)
+
+    def subselect_shape(self, shape_id):
+        """Change the subselection.
+
+        shape_id -- the new subselection. This may be -1, otherwise it must
+        be in the current selected shape set.
+        """
+        _gv.gv_shape_layer_subselect_shape( self._o, shape_id )
+
+    def clear_selection(self):
+        """Clear selection
+
+        Clear the current shape selection for this layer, sending a
+        selection-changed signal if there was anything selected."""
+
+        _gv.gv_shape_layer_clear_selection(self._o)
+
+    def select_all(self):
+        """Select all shapes in layer
+
+        All shapes in the layer are marked as selected, and if this is a
+        change, a selection-changed signal is sent."""
+
+        _gv.gv_shape_layer_select_all(self._o)
+
+    def select_shape(self,shape_id):
+        """Add a shape to selection
+
+        Adds the indicated shape to the current selection, triggering a
+        selection-changed signal if that shape wasn't previously selected.
+        The existing selection should be cleared explicitly with
+        clear_selection() if you only want the new shape select.
+
+        Nothing will happen if the shape is already selected.
+
+        shape_id -- the id (index) of the shape to be selected."""
+
+        _gv.gv_shape_layer_select_shape(self._o,shape_id)
+
+    def deselect_shape(self,shape_id):
+        """Remove shape from selection
+
+        Removes the indicated shape from the current selection, triggering a
+        selection-changed signal if that shape was previously selected.
+
+        shape_id -- the id (index) of the shape to be deselected."""
+
+        _gv.gv_shape_layer_deselect_shape(self._o, shape_id)
+
+    def set_selection_mode(self,mode=0,clear=1):
+        """Set selection mode
+
+        mode=0:  multiple selection (default)
+        mode=1:  single selection
+
+        clear=0: do not clear existing selections before changing mode
+        clear=1: clear existing selections before changing mode (default)
+        
+        """
+        if clear == 1:
+            self.clear_selection()
+            self.display_change()
+
+        if mode == 1:
+            self.set_property("selection_mode","single")
+        else:
+            self.set_property("selection_mode","multiple")
+
+
+###############################################################################
+def GvShapesLayerFromXML( base, parent, filename=None ):
+    """Create a GvShapesLayer from an XML tree
+
+    Look for a GvShapes tree and use it to build the GvShapes object if
+    possible.  Otherwise, look for the _filename propery and use it.  If
+    neither is found, the layer will not have any shapes.
+    
+    Returns a new GvShapesLayer or None if it failed
+    """
+    shape_tree = gvutils.XMLFind( base, 'GvShapes' )
+    if shape_tree is not None:
+        shapes = gvutils.XMLInstantiate( shape_tree, None, filename=filename )
+    else:
+        portable_path = gvutils.XMLFindValue( base, 'portable_path' )
+        if portable_path is not None:
+            shapefilename = pathutils.PortablePathFromXML( portable_path ).local_path( ref_path = filename )
+        else:
+            shapefilename = gvutils.XMLFindValue( base, "_filename" )
+            if shapefilename is None:
+                shapes = None
+        if shapefilename is not None:
+            shapes = GvShapes( shapefilename = shapefilename )
+
+    layer = GvShapesLayer( shapes = shapes )
+
+    if layer is not None:
+        layer.initialize_from_xml( base, filename=filename )
+
+    return layer
+
+class GvShapesLayer(GvShapeLayer):
+    """Vector Display Layer
+
+    The following properties have special interpretation for a GvShapesLayer.
+    Note that modifying these properties does not automatically trigger a
+    display-change signal ... please call display_change() manually.
+
+    _point_color -- RGBA value used for point color.
+    
+    _point_size -- Size of point cross hairs in screen pixels.
+    
+    _line_color -- RGBA value used for line color.
+    
+    _area_edge_color -- RGBA value used for area edge drawing.  Set alpha to
+    zero to skip drawing edges.
+    
+    _area_fill_color -- RGBA value used for area filling.  Set alpha to zero
+    to skip drawing fill.
+
+    Note that the above property values can also be set on individual
+    shapes to override drawing style on a per-shape basis.
+    """
+    get_type = _gv.gv_shapes_layer_get_type
+
+    def __init__(self, shapes=None, _obj=None):
+        if _obj: self._o = _obj; return
+        if shapes != None: shapes = shapes._o
+        self._o = _gv.gv_shapes_layer_new(shapes)
+        self.sink()
+
+    def serialize( self, base = None, filename=None ):
+        """Create a representation of this object suitable for XMLing.
+        """
+        if base is None:
+            base = [gdal.CXT_Element, "GvShapesLayer"]
+
+        layer_props = self.get_properties()
+        shapes = self.get_parent()
+        if shapes is not None:
+            shape_props = shapes.get_properties()
+            if (layer_props.has_key('_serialize_shapes') and \
+                layer_props['_serialize_shapes'] == '1') \
+                or not shape_props.has_key('_filename'):
+                base.append( shapes.serialize( filename=filename ) )
+            elif shape_props.has_key('_filename'):
+                v = shape_props['_filename']
+                if os.path.exists( v ):
+                    base.append( [gdal.CXT_Attribute, 'portable_path',
+                                  [gdal.CXT_Text, pathutils.PortablePath( v, ref_path = filename ).serialize()]] )
+                base.append( [gdal.CXT_Attribute, '_filename',
+                              [gdal.CXT_Text, v]] )
+
+        # Do we have a symbol manager?
+
+        sm = self.get_symbol_manager(0)
+        if sm is not None:
+            base.append( sm.serialize() )
+            
+        #serialize the base class properties (all the way to GvData)
+        GvShapeLayer.serialize( self, base, filename=filename )
+
+        return base
+        
+    def launch_properties(self):
+        import gvvectorpropdlg
+        return gvvectorpropdlg.LaunchVectorPropDialog( self )
+
+    def get_symbol_manager( self, ok_to_create = 0 ):
+        _sm = _gv.gv_shapes_layer_get_symbol_manager( self._o, ok_to_create )
+        if _sm is None:
+            return None
+        else:
+            return GvSymbolManager( _obj = _sm )
+
+    def initialize_from_xml( self, tree, filename=None ):
+        """initialize this object from the XML tree
+        """
+
+        # Initialize GvSymbolManager.
+        
+        sm_xml = gvutils.XMLFind( tree, 'GvSymbolManager' )
+        if sm_xml is not None:
+            sm = self.get_symbol_manager( 1 )
+            sm.initialize_from_xml( sm_xml, filename=filename )
+
+        GvShapeLayer.initialize_from_xml( self, tree, filename=filename )
+        
+###############################################################################
+class GvPointLayer(GvShapeLayer):
+    get_type = _gv.gv_point_layer_get_type
+    def __init__(self, points=None, _obj=None):
+        if _obj: self._o = _obj; return
+        if points != None: points = points._o
+        self._o = _gv.gv_point_layer_new(points)
+
+###############################################################################
+class GvLineLayer(GvShapeLayer):
+    get_type = _gv.gv_line_layer_get_type
+    def __init__(self, plines=None, _obj=None):
+        if _obj: self._o = _obj; return
+        if plines != None: plines = plines._o
+        self._o = _gv.gv_line_layer_new(plines)
+
+###############################################################################
+class GvAreaLayer(GvShapeLayer):
+    get_type = _gv.gv_area_layer_get_type
+    def __init__(self, areas=None, _obj=None):
+        if _obj: self._o = _obj; return
+        if areas != None: areas = areas._o
+        self._o = _gv.gv_area_layer_new(areas)
+
+###############################################################################
+def GvPqueryLayerFromXML( base, parent, filename=None ):
+    shape_tree = gvutils.XMLFind( base, 'GvShapes' )
+    if shape_tree is not None:
+        shapes = gvutils.XMLInstantiate( shape_tree, None, filename=filename )
+    else:
+        portable_path = gvutils.XMLFindValue( base, 'portable_path' )
+        if portable_path is not None:
+            shapefilename = pathutils.PortablePathFromXML( portable_path ).local_path( ref_path = filename )
+        else:
+            shapefilename = gvutils.XMLFindValue( base, "_filename" )
+            if shapefilename is None:
+                shapes = None
+        if shapefilename is not None:
+            shapes = GvShapes( shapefilename = shapefilename )
+
+    layer = GvPqueryLayer( shapes = shapes )
+
+    if layer is not None:
+        layer.initialize_from_xml( base, filename=filename )
+
+    return layer
+
+class GvPqueryLayer(GvShapesLayer):
+    """Point Query Layer
+
+    This layer is intended to only hold points, but this isn't strictly
+    enforced.  For each point in this layer, coordinate and/or raster
+    values are displayed beside the point. 
+
+    The following properties have special interpretation for a GvPqueryLayer.
+    Note that modifying these properties does not automatically trigger a
+    display-change signal ... please call display_change() manually.
+
+    _point_color -- RGBA value used for point and text color.
+    
+    _point_size -- Size of point cross hairs in screen pixels
+
+    _pixel_mode -- One of "off" or "on" indicating if pixel raster values
+    should be displayed for each query point.
+    
+    _coordinate_mode -- One of "off", "raster", "georef", or "latlong"
+    indicating what coordinate system the coordinates should be displayed in
+    (or skipped for off). 
+    
+    Note that the above property values can also be set on individual
+    shapes to override drawing style on a per-shape basis.
+    """
+    get_type = _gv.gv_pquery_layer_get_type
+    def __init__(self, shapes=None, _obj=None):
+        if _obj: self._o = _obj; return
+        if shapes != None: shapes = shapes._o
+        self._o = _gv.gv_pquery_layer_new( shapes )
+        self.sink()
+
+    def launch_properties(self):
+        import gvpquerypropdlg
+        return gvpquerypropdlg.LaunchPQueryPropDialog( self )
+
+    def serialize( self, base = None, filename=None ):
+        """Create a representation of this object suitable for XMLing.
+        """
+        if base is None:
+            base = [gdal.CXT_Element, "GvPqueryLayer"]
+
+        #serialize the base class properties
+        GvShapesLayer.serialize( self, base, filename=filename )
+
+        return base
+        
+###############################################################################
+class IpGcpLayer(GvShapesLayer):
+    get_type = _gv.ip_gcp_layer_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.ip_gcp_layer_new()
+        self.sink()
+
+###############################################################################
+class AppCurLayer(GvShapesLayer):
+    get_type = _gv.app_cur_layer_get_type
+    def __init__(self, _obj=None, shapes=None):
+        if _obj: self._o = _obj; return
+        if shapes is not None:
+            shapes = shapes._o
+        self._o = _gv.app_cur_layer_new(shapes)
+        self.sink()
+
+###############################################################################
+def GvRasterLayerFromXML( node, parent, filename=None ):
+    prototype_node = gvutils.XMLFind( node, 'Prototype' )
+    if prototype_node is not None:
+        prototype_data = GvRasterFromXML( prototype_node, None,
+                                          filename=filename )
+    else:
+        prototype_data = None
+
+    rl_mode = int(gvutils.XMLFindValue( node, 'mode', str(RLM_AUTO) ))
+    mesh_lod = gvutils.XMLFindValue( node, 'mesh_lod', '0' )
+
+    layer = GvRasterLayer( raster = prototype_data, rl_mode = rl_mode,
+                           creation_properties = [('mesh_lod',mesh_lod)] )
+    layer.initialize_from_xml( node, filename=filename )
+
+    sources_min_max = []
+    for child in node[2:]:
+        if child[0] == gdal.CXT_Element and child[1] == 'Source':
+            raster = GvRasterFromXML( child, None, filename=filename )
+            isource = int(gvutils.XMLFindValue(child,"index","0"))
+            min = float(gvutils.XMLFindValue(child,"min","0"))
+            max = float(gvutils.XMLFindValue(child,"max","0"))
+            sources_min_max.append([min, max])
+            const_value = int(gvutils.XMLFindValue(child,"const_value","0"))
+            nodata = gvutils.XMLFindValue( child, "nodata", None )
+            nodata = eval(gvutils.XMLFindValue(child, "nodata", "None"))
+
+            layer.set_source( isource, raster, min = min, max = max,
+                              const_value = const_value, nodata = nodata )
+        else:
+            raster = None
+
+    stretch = layer.get_property( 'last_stretch' )
+    if stretch is not None:
+	exec 'func = layer.' + stretch + '()'
+
+    # Warning !!! stretch functions generally reset min/max with autoscale for
+    # each source, so we have to reset them to the good values...
+    for i in range(layer.sources):
+        layer.min_set( i, sources_min_max[i][0] )
+        layer.max_set( i, sources_min_max[i][1] )
+
+    # Set classification? 
+    
+    from gvclassification import GvClassification
+    cls = GvClassification( layer )
+    if cls.count > 0:
+        cls.update_all_layers()
+
+    # Set elevations?
+    f = layer.get_property( '_gv_add_height_portable_path' )
+    if f is None:
+        f = layer.get_property( '_gv_add_height_filename' )
+    else:
+        f = pathutils.PortablePathFromXML( f ).local_path( ref_path = filename )
+    if f is not None:
+        ds = manager.get_dataset( f )
+        dem_raster = manager.get_dataset_raster(
+            ds, int(layer.get_property( '_gv_add_height_band' )) )
+        layer.add_height( dem_raster,
+                          float(layer.get_property( '_gv_add_height_default')))
+
+    return layer
+
+class GvRasterLayer(GvLayer):
+    get_type = _gv.gv_raster_layer_get_type
+    def __init__(self, raster=None, creation_properties=None, _obj=None,
+                 rl_mode = RLM_AUTO ):
+        """Create a raster layer.
+
+        raster -- the primary GvRaster to which this layer is tied.  It
+        will be the GvData parent of this layer, and will be used to establish
+        georeferencing, and other information.   Internally this is known
+        as the ``prototype-data''.
+
+        creation-properties -- A list of (name, value) tuples with special
+        creation options.  Currently the only supported one is ('raw','yes')
+        to avoid using affine or gcp based georeferencing information from
+        the GvRaster.
+
+        rl_mode -- One of the RLM modes (documented in get_mode()), or
+        gview.RLM_AUTO meaning the layer type should be deduced from the
+        passed GvRaster (the default).
+        """
+        if _obj:
+            self._o = _obj
+        else:
+            if raster is None:
+                raise ValueError, "expecting GvRaster instance"
+            if creation_properties is None:
+                self._o = _gv.gv_raster_layer_new(raster._o, rl_mode)
+            else:
+                self._o = _gv.gv_raster_layer_new(raster._o, rl_mode,
+                                                  creation_properties)
+
+        self.sink()
+        
+        # Note: we don't want to include alpha in self.sources as it should
+        # not be enhanced.
+        if self.get_mode() == RLM_SINGLE:
+            self.sources = 1
+        elif self.get_mode() == RLM_COMPLEX:
+            self.sources = 1
+        else:
+            self.sources = 3
+
+    def serialize(self, layer = None, filename=None ):
+        if layer is None:
+            layer = [gdal.CXT_Element, 'GvRasterLayer']
+            
+        layer.append( [CXT_Attribute, 'mode',
+                       [CXT_Text, str(self.get_mode())]] )
+
+        layer.append( [CXT_Attribute, 'mesh_lod',
+                       [CXT_Text, str(self.get_mesh_lod())]] )
+
+        GvLayer.serialize( self, layer, filename=filename )
+
+        source_count = self.sources
+        prototype_raster = None
+        for isource in range(source_count):
+            if prototype_raster is None:
+                raster = self.get_data(isource)
+                if raster is not None:
+                    prototype_raster = raster
+
+        if prototype_raster is not None:
+            v = raster.get_dataset().GetDescription()
+            proto = [gdal.CXT_Element, 'Prototype',
+                     [gdal.CXT_Attribute, 'band', 
+                      [gdal.CXT_Text, str(raster.get_band_number())]]]
+            if os.path.exists( v ):
+                proto.append( [gdal.CXT_Attribute, 'portable_path', 
+                               [gdal.CXT_Text, pathutils.PortablePath( v, ref_path=filename ).serialize()]] )
+            proto.append( [gdal.CXT_Text, v] )
+            layer.append( proto )
+
+        # Note that self.sources isn't necessary all the sources.  
+        for isource in range(source_count):
+            src = [gdal.CXT_Element, 'Source',
+                   [gdal.CXT_Attribute, 'index',
+                    [gdal.CXT_Text, str(isource)]],
+                   [gdal.CXT_Attribute, 'min',
+                    [gdal.CXT_Text, str(self.min_get(isource))]],
+                   [gdal.CXT_Attribute, 'max',
+                    [gdal.CXT_Text, str(self.max_get(isource))]]
+                   ]
+
+            if self.nodata_get(isource) != -100000000.0:
+                src.append( [gdal.CXT_Attribute, 'nodata',
+                             [gdal.CXT_Text, str(self.nodata_get(isource))]] )
+                
+            raster = self.get_data(isource)
+            if raster is None:
+                src.append( [gdal.CXT_Attribute, 'constant',
+                             [gdal.CXT_Text, str(self.get_const_value(isource))]] )
+            else:
+                src.append( [gdal.CXT_Attribute, 'band',
+                             [gdal.CXT_Text, str(raster.get_band_number())]] )
+                v = raster.get_dataset().GetDescription()
+                if os.path.exists( v ):
+                    src.append( [gdal.CXT_Attribute, 'portable_path', 
+                                 [gdal.CXT_Text, pathutils.PortablePath( v, ref_path=filename ).serialize()]] )
+                src.append( [gdal.CXT_Text, v] )
+
+            layer.append( src )
+
+        return layer
+
+    def view_to_pixel(self, x, y ):
+        """Translate view coordinates to pixel/line.
+
+        x -- X (easting or longitude) in view georeferencing system.
+        y -- Y (northing or latitude) in view georeferencing system.
+
+        Returns a (pixel,line) coordinate tuple on the raster."""
+        
+        return _gv.gv_raster_layer_view_to_pixel(self._o, x, y)
+
+    def pixel_to_view(self, x, y ):
+        """Translate pixel/line to view coordinate.
+
+        x -- pixel on raster layer (0.0 is left side of leftmost pixel)
+        y -- line on raster layer (0.0 is top side of topmost pixel)
+
+        Returns an (x,y) coordinate tuple in the view georeferencing
+        system."""
+        
+        return _gv.gv_raster_layer_pixel_to_view(self._o, x, y)
+
+    def get_mode(self):
+        """Fetch the GvRasterLayer display mode.
+
+        It will be one of gview.RLM_SINGLE (greyscale or pseudocolored
+        raster band), gview.RLM_RGBA (RGBA composite from individual GvRaster
+        bands), or gview.RLM_COMPLEX (complex GvRaster pseudocolored with 2D
+        lookup table)."""
+        return _gv.gv_raster_layer_get_mode(self._o);
+
+    def get_mesh_lod(self):
+        return _gv.gv_raster_layer_get_mesh_lod(self._o)
+        
+    def get_data(self, isource=0):
+        """Fetch the GvRaster for a source.
+
+        Note that gview.RLM_SINGLE mode has one source (isource=0),
+        gview.RLM_COMPLEX has one source, and gview.RLM_RGBA has four (red,
+        green, blue and alpha).  Any source may be None indicating that
+        that source will use the constant value (see get_const_value()).
+
+        isource -- the source index (from 0 to 3).  
+
+        """
+
+        data = _gv.gv_raster_layer_get_data(self._o, isource)
+        if data is not None:
+            return _gtk._obj2inst(data)
+        else:
+            return None
+        
+    def source_get_lut(self, isource=0):
+        """Fetch the lut for a source
+
+        This fetches the pre-compositing lut applied to a source.  It
+        will be None (if there isn't any in effect), or a String of 256
+        values between 0 and 255.  Source lut's can only be set with
+        GvRasterLayer.set_source().
+        
+        isource -- the source to fetch from."""
+        return _gv.gv_raster_layer_source_get_lut(self._o, isource);
+
+    def get_nodata(self, isource):
+	"""Fetch NODATA value. DEPRECATED: use nodata_get()."""
+        return _gv.gv_raster_layer_get_nodata( self._o, isource )
+        
+    def set_source(self, isource, data, min=None, max=None, const_value=0,
+                   lut=None, nodata=None):
+        """Set a data source
+
+        Sets all the configuration information for one of the data sources
+        of a layer.  This method will trigger a display-change signal if
+        any values are altered.
+
+        isource -- the source index (from 0 to 3)
+
+        data -- a GvRaster, or None if the source should be constant valued.
+
+        min -- the minimum value to use for scaling this raster to the
+        range 0-255.  If min and max are defaulted they will be extracted
+        from the raster. 
+
+        max -- the maximum value to use for scaling this raster to the
+        range 0-255.  If min and max are defaulted they will be extracted
+        from the raster.
+
+        const_value -- Constant value to use in place of data if data is None.
+
+        lut -- Pre-compositing lookup table or None.  If passed it must be
+        a string of exactly 256 characters mapping input values to output
+        values in the range 0-255. 
+        """
+
+        if data is None:
+            data_o = None
+        else:
+            data_o = data._o
+	
+	if (min is None) and (data is not None):
+	    min = data.get_min()
+	
+	if (max is None) and (data is not None):
+	    max = data.get_max()
+
+        if( self.get_property("_scale_lock") is not None and 
+            self.get_property("_scale_lock") == "locked" ) :
+            
+            if( self.get_property("_scale_limits") is not None ) : 
+                min, max = map \
+                ( string.atof, 
+                  string.split(self.get_property("_scale_limits"))
+                )
+
+        return _gv.gv_raster_layer_set_source(self._o, isource, data_o,
+                                              min, max, const_value, lut,
+                                              nodata )
+        
+    def alpha_set(self, alpha_mode, alpha_check_val):
+        return _gv.gv_raster_layer_alpha_set(self._o, alpha_mode, alpha_check_val)
+
+    def alpha_get(self):
+        return _gv.gv_raster_layer_alpha_get( self._o )
+
+    def min_set(self,isource,min):
+        """Set the scaling minimum.
+
+        This will trigger a redraw via the display-change signal if it
+        changes the scaling value.
+
+        isource -- the source index (from 0 to 3).
+        min -- new minimum value for scaling.
+        """
+        return _gv.gv_raster_layer_min_set(self._o, isource, min )
+
+    def min_get(self,isource):
+        """Fetch the scaling minimum."""
+        return _gv.gv_raster_layer_min_get(self._o,isource)
+
+    def max_set(self,isource,max):
+        """Set the scaling maximum.
+
+        This will trigger a redraw via the display-change signal if it
+        changes the scaling value.
+
+        isource -- the source index (from 0 to 3).
+        max -- new maximum value for scaling.
+        """
+        return _gv.gv_raster_layer_max_set(self._o, isource, max)
+
+    def max_get(self,isource):
+        """Fetch the scaling maximum."""
+        return _gv.gv_raster_layer_max_get(self._o,isource)
+
+    def nodata_set(self,isource,real,imaginary):
+        """Set the NODATA value.
+
+        This will trigger a redraw via the display-change signal if it
+        changes the NODATA value.
+
+        isource -- the source index (from 0 to 3).
+        nodata -- new nodata value.
+        """
+        return _gv.gv_raster_layer_nodata_set(self._o,isource,real,imaginary)
+
+    def nodata_get(self,isource):
+        """Fetch the NODATA value."""
+	return _gv.gv_raster_layer_nodata_get(self._o,isource)
+
+    def type_get(self,isource):
+        """Fetch GDAL type of the raster object."""
+	return _gv.gv_raster_layer_type_get(self._o,isource)
+
+    def get_const_value(self,isource):
+        """Fetch source constant value"""
+        return _gv.gv_raster_layer_get_const_value(self._o,isource)
+
+    def zoom_set(self,mag_mode,min_mode):
+        """Set interpolation method
+
+        I believe mag_mode sets the interpolation mode when zooming in past
+        1:1 on a texture, and min_mode is the interpolation mode used for
+        downsampling from the texture, but I am not sure.  Both default to
+        bilinear, and are normally changed together.
+        
+        mag_mode -- One of gview.RL_FILTER_BILINEAR or gview.RL_FILTER_NEAREST.
+        min_mode -- One of gview.RL_FILTER_BILINEAR or gview.RL_FILTER_NEAREST.
+        """
+        return _gv.gv_raster_layer_zoom_set( self._o, mag_mode, min_mode )
+
+    def zoom_get(self):
+        """Fetch zoom mode
+
+        Returns the mag_mode, and min_mode interploation modes as a tuple.
+        See also: zoom_set()"""
+        
+        return _gv.gv_raster_layer_zoom_get(self._o)
+
+    def texture_clamp_set(self,s_mode,t_mode):
+        return _gv.gv_raster_layer_texture_clamp_set(self._o, mag_mode, min_mode )
+
+    def texture_mode_set(self,texture_mode,color):
+        """Set the texture mode.
+
+        The default mode is replace in which case the fragment color is
+        ignored.  In modulate mode the raster is modulated with the
+        provided fragment color.
+        
+        texture_mode -- gview.RL_TEXTURE_REPLACE or gview.GL_TEXTURE_MODULATE
+        color -- fragment color as an RGBA tuple."""
+        return _gv.gv_raster_layer_texture_mode_set(self._o, texture_mode,
+                                                    color )
+
+    def texture_mode_get(self):
+        return _gv.gv_raster_layer_texture_mode_get(self._o)
+
+    def blend_mode_set(self,mode,sfactor=0,dfactor=0):
+        """Set blend mode
+
+        mode -- 0=off, non-0=on
+        sfactor -- ...
+        dfactor -- ..."""
+        
+        return _gv.gv_raster_layer_blend_mode_set(self._o, mode, sfactor, dfactor )
+
+    def blend_mode_get(self):
+        return _gv.gv_raster_layer_blend_mode_get(self._o)
+
+    def lut_put(self,lut=None):
+        """Set the lut.
+
+        This method will reset the compositing lut on a rasterlayer.  The lut
+        should be a string of 1024 bytes for a 1D LUT and 262144 bytes (as a
+        String) for a 2D LUT.  The array should be stored in "RGBARGBA..."
+        format.
+
+        2D LUTs should only be applied to layers in RLM_COMPLEX mode, and
+        1D LUTs should only be applied to layers in RLM_SINGLE mode.  It is
+        an error to apply luts in any other case. 
+
+        lut -- the lut to set, stored as a string.  None may be used to
+               clear the lut.
+        """
+        return _gv.gv_raster_layer_lut_put(self._o, lut)
+
+    def lut_get(self, rgba_complex=0):
+        """Fetch the lut.
+
+        The returned lut will be a string of 1024 bytes if 1D, or 262144 bytes
+        if the lut is 2D.  The data is stored in "RGBARGBA..." format.
+
+        The rgba_complex variable is used to determine whether the
+        real or complex lut should be returned in the rgba case (this
+        mode supports both real and complex data).  Use 0 for real
+        (default for backwards compatibility), 1 for complex.  This
+        variable was added in case complex and real data are mixed within
+        a given RGBA layer, to allow access to either lut.  If mixed
+        real/complex data are never going to be permitted, or if real
+        data within an RGBA layer never has an associated lut, rgba_complex
+        should probably be removed again and the type of lut returned should
+        be based on whether the first source of the layer contains real or
+        complex data.
+        """
+        return _gv.gv_raster_layer_lut_get(self._o, rgba_complex)
+
+    def lut_type_get(self):
+        """Fetch the LUT type.
+
+        Returns one of RL_LUT_NONE, RL_LUT_1D, or RL_LUT_2D (defined in
+        gvconst.py)."""
+        return _gv.gv_raster_layer_lut_type_get( self._o )
+
+    def lut_color_wheel_new(self,h_mode,h_param,s_mode,s_param,v_mode,v_param):
+        """Generate 2D LUT
+
+        Returns a lut suitable for applying to a RLM_COMPLEX layer with the
+        lut_put() method.  See gvrasterpropdlg.py for an example of use of
+        this method. 
+
+        h_mode -- one of the RL_LUT_* values indicating the source of the hue
+        component of the HLS color.
+
+        h_param -- if h_mode is RL_LUT_SCALAR this should be the constant hue
+        value between 0.0 and 1.0. 
+        
+        s_mode -- one of the RL_LUT_* values indicating the source of the 
+        saturation component of the HLS color.
+
+        s_param -- if s_mode is RL_LUT_SCALAR this should be the constant 
+        saturation value between 0.0 and 1.0. 
+        
+        v_mode -- one of the RL_LUT_* values indicating the source of the value
+        component of the HLS color.
+
+        v_param -- if v_mode is RL_LUT_SCALAR this should be the constant value
+        value between 0.0 and 1.0.
+        """
+        return _gv.gv_raster_layer_lut_color_wheel_new(self._o,h_mode,h_param,
+                                                       s_mode,s_param,
+                                                       v_mode,v_param)
+
+    def lut_color_wheel_new_ev(self,set_phase=1, set_magnitude=1):
+        """Generate 2D LUT
+
+        Applies a lut suitable for an RLM_COMPLEX layer with the
+        lut_put() method.  See gvrasterpropdlg.py for an example of use of
+        this method.   This method is similar to lut_color_wheel_new()
+        but is simplified and computes the 2D LUT based on looking up
+        within a standard phase color table (from EV) and using magnitude
+        to modulate the color.
+
+        set_phase -- One if varying phase is to be used to lookup the color
+        in a color table, or zero to use a fixed color of white.
+
+        set_magnitude -- One if magnitude is to be used to modify the
+        intensity of the selected color, or zero to use a constant magnitude
+        factor of 1.0.
+        
+        """
+        return _gv.gv_raster_layer_lut_color_wheel_new_ev(self._o,
+                                                          set_phase,
+                                                          set_magnitude)
+
+    def lut_color_wheel_1d_new( self, s=1.0, v=1.0, offset=0.0 ):
+        return _gv.gv_raster_layer_lut_color_wheel_1d_new( self._o,s,v,offset)
+
+    def autoscale_view(self, alg = ASAAutomatic, alg_param = -1.0, isource=0 ):
+        """Force autoscaling to be recomputed.
+
+        alg -- Algorithm to use (see below)
+        alg_param -- the parameter to the algorithm, or -1.0 to get
+                     default parameter value (possibly from preferences).
+        isource -- the GvRaster to get scaling for
+
+        This method will compute of scaling min/max values based on a sample
+        of the data in the current view for the selected source raster.  The
+        algorithm information is the same as GvRaster.autoscale().
+        
+        If the autoscale_view() method fails (for instance due to an IO error,
+        or if all the sample data is the nodata value) the method will throw an
+        exception otherwise a (min,max) tuple is returned.
+        
+        """
+        return _gv.gv_raster_layer_autoscale_view(self._o, alg, alg_param,
+                                                  isource)
+
+    def autoscale( self, alg = ASAAutomatic, alg_param = -1.0, isource=0,
+                   viewonly = 0):
+        if viewonly == 0:
+            raster = self.get_data(isource)
+            if raster is None:
+                return (self.min_get(isource), self.max_get(isource))
+            else:
+                return raster.autoscale(alg, alg_param)
+
+        else:
+            try:
+                return self.autoscale_view(alg, alg_param, isource)
+            except:
+                return (self.min_get(isource), self.max_get(isource))
+        
+    def histogram_view(self, isource = 0, scale_min = 0.0, scale_max = 255.0,
+                       hist_size = 256 ):
+        """Compute histogram of viewed pixels.
+
+        isource -- the GvRaster to collect histogram from.
+        scale_min -- the min value to use when scaling to histogram buckets.
+        scale_max -- the max value to use when scaling to histogram buckets.
+        hist_size -- the number of histogram buckets to divide the range into.
+
+        This method will attempt to collect all the pixels in the current
+        view into a histogram.  Note that all pixels values are read only from
+        the GvRaster cache (for speed), so missing tiles or tiles only
+        available at reduced levels of detail will be underrepresented.
+
+        In 2D the sampling identification of raster pixels should be exact
+        for unrotated and unwarped images, otherwise it will be based on the
+        bounding rectangle and so will include some extra pixels outside the
+        view.  In 3D all tiles which appear to intersect the view will be
+        sampled, so potentially many pixels outside the view will be included
+        in the histogram.
+
+        The function returns a list object with histogram entry counts of
+        size hist_size, or an exception in the case of failure.
+        
+        """
+        return _gv.gv_raster_layer_histogram_view(self._o, isource,
+                                                  scale_min, scale_max,
+                                                  hist_size )
+    
+    def launch_properties(self):
+        import gvrasterpropdlg
+        return gvrasterpropdlg.LaunchRasterPropDialog( self )
+
+    def add_height(self, height_raster, default_height = 0.0):
+        """ Adds height to raster layer for 3D effect.
+
+        Georeferrencing information will be used to place height_raster
+        with respect to layer.
+
+        height_raster -- a GvRaster containing elevation information in a
+        compatible georeferencing system with this raster layer."""
+
+        # Actually add the height.
+        
+        _gv.gv_raster_layer_add_height(self._o, height_raster._o,
+                                       default_height)
+
+        # In order to be able to reconstitute with elevation data,
+        # we need to store information about where the elevation came from.
+
+        self.set_property( '_gv_add_height_filename',
+                           height_raster.get_dataset().GetDescription() )
+        self.set_property( '_gv_add_height_band',
+                           str(height_raster.get_band_number()) )
+        self.set_property( '_gv_add_height_default',
+                           str(default_height) )
+
+    def clamp_height(self, bclamp_min=0, bclamp_max=0, 
+                     min_height=-30000.0,max_height=30000.0):
+        """ Sets lower mesh height bound to min_height if bclamp_min is 1.
+        Sets upper mesh height bound to max_height if bclamp_max is 1. """
+
+        _gv.gv_raster_layer_clamp_height(self._o, bclamp_min, bclamp_max,
+                                         min_height, max_height)
+            
+    def complex_lut(self, method='magnitude'):
+
+        # This property is set at c-level now.
+        #self.set_property( 'last_complex_lut', method )
+        
+        # Magnitude
+        if method == 'magnitude':
+            self.lut_color_wheel_new_ev( 0, 1 )
+            
+        # Phase
+        elif method == 'phase':
+            self.lut_color_wheel_new_ev( 1, 0 )
+
+        # Magnitude and Phase
+        elif method == 'magphase':
+            self.lut_color_wheel_new_ev( 1, 1 )
+
+        # Real
+        elif method == 'real':
+            self.lut_color_wheel_new( RL_LUT_SCALAR, -1,
+                                      RL_LUT_SCALAR, 0.75,
+                                      RL_LUT_REAL, 1 )
+
+        # Imaginary
+        elif method == 'imaginary':
+            self.lut_color_wheel_new( RL_LUT_SCALAR, -1,
+                                      RL_LUT_SCALAR, 0.75,
+                                      RL_LUT_IMAGINARY, 1 )
+        
+    def equalize(self, viewonly = 0):
+        """Compute a histogram equalized source LUT, and apply.
+
+        This method is not meaningful for RLM_COMPLEX layers, and will
+        be ignored."""
+
+        for isrc in range(self.sources):
+            raster = self.get_data(isrc)
+            if raster is None:
+                continue
+            
+            (smin, smax) = self.autoscale(viewonly=viewonly,isource=isrc)
+            
+            if viewonly == 0:
+                gdal_band = raster.get_band()
+                histogram = gdal_band.GetHistogram(smin, smax, approx_ok = 1)
+            else:
+                try:
+                    histogram = self.histogram_view(isrc, smin, smax, 256 )
+                except:
+                    # normally this means we are "off" the raster.
+                    continue
+
+            cum_hist = []
+            total = 0
+            for bucket in histogram:
+                cum_hist.append(total + bucket/2)
+                total = total + bucket
+
+            if total == 0:
+                total = 1
+                gdal.Debug( 'OpenEV',
+                       'Histogram total is zero in GvRasterLayer.equalize()' )
+                
+            lut = ''
+            for i in range(256):
+                value = (cum_hist[i] * 256L) / total
+                if value < 0 :
+                    value = 0
+                elif value >= 255:
+                    value = 255
+                lut = lut + chr(value)
+                
+            self.set_source(isrc, raster, smin, smax, 
+                            self.get_const_value(isrc), lut,
+                            self.nodata_get(isrc))
+
+        self.set_property( 'last_stretch', 'equalize' )
+
+    def linear( self, viewonly = 0 ):
+
+        for isrc in range(self.sources):
+            (smin, smax) = self.autoscale( isource=isrc, viewonly = viewonly )
+            
+            self.set_source(isrc, self.get_data(isrc), smin, smax,
+                            self.get_const_value(isrc), None,
+                            self.nodata_get(isrc))
+
+        self.set_property( 'last_stretch', 'linear' )
+
+    def none_lut( self, viewonly = 0 ):
+
+        for isrc in range(self.sources):
+            raster = self.get_data(isrc)
+            if raster is None:
+                continue
+            
+            if raster.get_band().DataType == gdal.GDT_Byte:
+                (smin, smax) = (0.0, 255.0)
+            else:
+                (smin, smax) = self.autoscale( isource = isrc,
+                                               viewonly = viewonly )
+
+            self.set_source(isrc, self.get_data(isrc), smin, smax,
+                            self.get_const_value(isrc), None,
+                            self.nodata_get(isrc))
+
+        self.set_property( 'last_stretch', 'none_lut' )
+
+    def log( self, viewonly = 0 ):
+
+        import math
+        
+        lut = ''
+        for i in range(256):
+            value = int((255 * (math.log(1.0+i) / math.log(256.0)))+0.5)
+            if value < 0 :
+                value = 0
+            elif value >= 255:
+                value = 255
+            lut = lut + chr(value)
+                
+        for isrc in range(self.sources):
+            (smin, smax) = self.autoscale( isource=isrc, viewonly = viewonly )
+            self.set_source(isrc, self.get_data(isrc), smin, smax,
+                            self.get_const_value(isrc), lut,
+                            self.nodata_get(isrc))
+
+        self.set_property( 'last_stretch', 'log' )
+
+    def root( self, viewonly = 0 ):
+
+        import math
+        
+        lut = ''
+        for i in range(256):
+            value = 255 * math.sqrt(i/255.0)
+            if value < 0 :
+                value = 0
+            elif value >= 255:
+                value = 255
+            lut = lut + chr(value)
+                
+        for isrc in range(self.sources):
+            (smin, smax) = self.autoscale( isource=isrc, viewonly = viewonly )
+
+            self.set_source(isrc, self.get_data(isrc), smin, smax,
+                            self.get_const_value(isrc), lut,
+                            self.nodata_get(isrc))
+
+        self.set_property( 'last_stretch', 'root' )
+
+    def square( self, viewonly = 0 ):
+
+        import math
+        
+        lut = ''
+        for i in range(256):
+            value = 255 * math.pow(i/255.0,2.0)
+            if value < 0 :
+                value = 0
+            elif value >= 255:
+                value = 255
+            lut = lut + chr(value)
+                
+        for isrc in range(self.sources):
+            (smin, smax) = self.autoscale( isource=isrc, viewonly = viewonly )
+
+            self.set_source(isrc, self.get_data(isrc), smin, smax,
+                            self.get_const_value(isrc), lut,
+                            self.nodata_get(isrc))
+
+        self.set_property( 'last_stretch', 'square' )
+
+    def window_restretch(self):
+
+        # Re-apply the last stretch operation.
+        if self.get_property( 'last_stretch' ) != None:
+            func_name = 'self.'+ self.get_property('last_stretch')
+        else:
+            func_name = 'self.'+ 'linear'
+
+        exec 'func = ' + func_name  in locals()
+
+        func( viewonly = 1 )
+    
+    def get_height( self, x, y ):
+        """Fetch 3D mesh height at location
+
+        Returns the elevation as extracted from the mesh used for 3D
+        display at the indicated georeferenced location on this raster
+        layer.  If the request fails for some reason (such as being off
+        the raster) an exception is generated.
+
+        x -- Georeferenced X location to sample at. 
+        y -- Georeferenced Y location to sample at.
+        """
+        return _gv.gv_raster_layer_get_height( self._o, x, y )
+
+    def build_skirt( self, base_height = 0.0 ):
+        """Build a skirt around the edges of layer.
+
+        The skirt is basically edges dropping down from the edge of the
+        raster to some base height.  The skirt generation knows about nodata
+        regions, and will also build skirts on their edges.  The skirt is
+        coloured according to the location on the raster it is dropped from.
+
+        base_height -- elevation to which the skirt drops, defaults to 0.0.
+
+        The returned skirt is a GvLayer (currently a GvShapesLayer). 
+        """
+        layer_o = _gv.gv_build_skirt( self._o, base_height )
+        if layer_o is not None:
+            return GvLayer( layer_o )
+        else:
+            return None
+
+    def refresh( self ):
+        """Refresh raster data from disk."""
+        for isource in range(4):
+            raster = self.get_data(isource)
+            if raster is not None:
+                raster.changed()
+
+###############################################################################
+class GvTool(_gtk.GtkObject):
+    get_type = _gv.gv_tool_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        
+    def activate(self, view):
+        _gv.gv_tool_activate(self._o, view._o)
+        
+    def deactivate(self, view):
+        _gv.gv_tool_deactivate(self._o, view._o)
+        
+    def get_view(self):
+        v_o = _gv.gv_tool_get_view(self._o)
+        if v_o is None:
+            return None
+        else:
+            return GvViewArea(_obj=v_o)
+        
+    def set_boundary(self, boundary):
+        """Set constraint rectangle.
+
+        boundary -- boundary is a tuple in the form (column,row,width,height)
+        """
+        return _gv.gv_tool_set_boundary(self._o, boundary)
+
+    def set_cursor(self, cursor_type):
+        """ Set the tool's associated cursor.
+
+        cursor_type -- an integer (one of the standard GDK cursor types)
+        """
+        return _gv.gv_tool_set_cursor(self._o, cursor_type)
+
+
+###############################################################################
+class GvSelectionTool(GvTool):
+    get_type = _gv.gv_selection_tool_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_selection_tool_new()
+    def set_layer(self, layer):
+        _gv.gv_selection_tool_set_layer(self._o, layer._o)
+
+###############################################################################
+class GvZoompanTool(GvTool):
+    get_type = _gv.gv_zoompan_tool_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_zoompan_tool_new()
+
+###############################################################################
+class GvPointTool(GvTool):
+    get_type = _gv.gv_point_tool_get_type
+    def __init__(self, layer=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_point_tool_new()
+        if layer: self.set_named_layer(layer)
+    def set_layer(self, layer):
+        _gv.gv_point_tool_set_layer(self._o, layer._o)    
+    def set_named_layer(self, name):
+        _gv.gv_point_tool_set_named_layer(self._o, name)
+
+###############################################################################
+class GvLineTool(GvTool):
+    get_type = _gv.gv_line_tool_get_type
+    def __init__(self, layer=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_line_tool_new()
+        if layer: self.set_named_layer(layer)
+    def set_layer(self, layer):
+        _gv.gv_line_tool_set_layer(self._o, layer._o)
+    def set_named_layer(self, name):
+        _gv.gv_line_tool_set_named_layer(self._o, name)
+
+###############################################################################
+class GvRectTool(GvTool):
+    get_type = _gv.gv_rect_tool_get_type
+    def __init__(self, layer=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_rect_tool_new()
+        if layer: self.set_named_layer(layer)
+    def set_layer(self, layer):
+        _gv.gv_rect_tool_set_layer(self._o, layer._o)
+    def set_named_layer(self, name):
+        _gv.gv_rect_tool_set_named_layer(self._o, name)
+
+###############################################################################
+class GvRotateTool(GvTool):
+    get_type = _gv.gv_rotate_tool_get_type
+    def __init__(self, layer=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_rotate_tool_new()
+        if layer: self.set_named_layer(layer)
+    def set_layer(self, layer):
+        _gv.gv_rotate_tool_set_layer(self._o, layer._o)
+    def set_named_layer(self, name):
+        _gv.gv_rotate_tool_set_named_layer(self._o, name)
+
+###############################################################################
+class GvAreaTool(GvTool):
+    get_type = _gv.gv_area_tool_get_type
+    def __init__(self, layer=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_area_tool_new()
+        if layer: self.set_named_layer(layer)
+    def set_layer(self, layer):
+        _gv.gv_area_tool_set_layer(self._o, layer._o)
+    def set_named_layer(self, name):
+        _gv.gv_area_tool_set_named_layer(self._o, name)
+
+###############################################################################
+class GvNodeTool(GvTool):
+    get_type = _gv.gv_node_tool_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_node_tool_new()
+    def set_layer(self, layer):
+        _gv.gv_node_tool_set_layer(self._o, layer._o)
+
+###############################################################################
+class GvRoiTool(GvTool):
+    """Region of Interest Selection Tool
+
+    Signals:
+
+    roi-changing -- generated when the ROI is being rubber-banded and
+                    coordinates are changing
+        
+    roi-changed -- generated when the ROI has been changed and not currently
+    being modified.
+    """
+    
+    get_type = _gv.gv_roi_tool_get_type
+    def __init__(self, boundary=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_roi_tool_new()
+        if boundary: self.set_boundary(boundary)
+    def get_rect(self):
+        """ Returns the current ROI """
+        return _gv.gv_roi_tool_get_rect(self._o)
+    def append(self, rect):
+        """ Creates an ROI.
+
+        rect -- a tuple (column, row, width, height)
+        """
+        return _gv.gv_roi_tool_new_rect(self._o, rect)
+    
+###############################################################################
+class GvPoiTool(GvTool):
+    """Point of Interest Selection Tool
+
+    Signals:
+
+    poi-changed -- generated when the POI has been changed.
+    """
+    
+    get_type = _gv.gv_poi_tool_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_poi_tool_new()
+
+    def get_point(self):
+        """ Returns the current POI """
+        return _gv.gv_poi_tool_get_point(self._o)
+
+    def set_point(self, point):
+        """ Sets the current POI.
+
+        point -- a tuple (column, row)
+        """
+        return _gv.gv_poi_tool_new_point(self._o, point)
+    
+###############################################################################
+class GvTrackTool(GvTool):
+    get_type = _gv.gv_track_tool_get_type
+    def __init__(self, label=None, _obj=None):
+        if _obj: self._o = _obj; return
+        if label is None:
+            raise ValueError, "expecting GtkLabel instance"
+        self._o = _gv.gv_track_tool_new(label._o)
+
+###############################################################################
+class GvToolbox(GvTool):
+    get_type = _gv.gv_toolbox_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_toolbox_new()
+    def add_tool(self, name, tool):
+        _gv.gv_toolbox_add_tool(self._o, name, tool._o)
+    def activate_tool(self, name):
+        _gv.gv_toolbox_activate_tool(self._o, name)
+
+
+###############################################################################
+class GvAutopanTool(GvTool):
+    """Autopan Tool
+
+    Signals:
+
+    """
+    
+    get_type = _gv.gv_autopan_tool_get_type
+    def __init__(self, boundary=None, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_autopan_tool_new()
+        if boundary: self.set_boundary(boundary)
+
+    def set_extents(self, rect):
+        """ Sets the panning extents.
+
+        rect -- a tuple (column, row, width, height)
+        """
+        return _gv.gv_autopan_tool_new_rect(self._o, rect)
+    
+    def get_extents(self):
+        """ Gets the panning extents.
+            Returns a tuple (column, row, width, height)
+        """
+        return _gv.gv_autopan_tool_get_rect(self._o)
+    
+    def play(self):
+        """ Start panning. """
+        return _gv.gv_autopan_tool_play(self._o)
+    
+    def pause(self):
+        """ Pause panning. Regions remain drawn in secondary views."""
+        return _gv.gv_autopan_tool_pause(self._o)
+    
+    def stop(self):
+        """ Stop panning. Regions do not remain drawn in secondary views."""
+        return _gv.gv_autopan_tool_stop(self._o)
+
+    def set_speed(self, speed):
+        """ Set the relative speed (a value between -1 and 1: -1
+            for maximum speed backwards; 1 for maximum speed
+            forwards).
+        """
+        return _gv.gv_autopan_tool_set_speed(self._o, speed)
+
+    def get_speed(self):
+        """ Returns the current speed setting. """
+        
+        return _gv.gv_autopan_tool_get_speed(self._o)
+
+    def set_overlap(self, overlap):
+        """ Set block overlap perpendicular to panning direction
+            (a value between 0 and 1).
+        """
+        return _gv.gv_autopan_tool_set_overlap(self._o, overlap)
+
+    def get_overlap(self):
+        """ Returns the current overlap setting. """
+        return _gv.gv_autopan_tool_get_overlap(self._o)
+
+    def set_block_x_size(self, xsize, mode):
+        """ Set size of high-resolution block.
+            Inputs:
+                xsize- width of block, as a fraction of the
+                       full panning region width if mode == 0
+                       (in this case 0 < xsize < 1), or in
+                       view coordinates if mode == 1.
+                mode- block size setting mode.
+        """
+        return _gv.gv_autopan_tool_set_block_x_size(self._o, xsize, mode)
+
+    def set_x_resolution(self, resolution):
+        """ Set the resolution of the zoomed extents:
+            Inputs:
+                resolution- view resolution
+        """
+        return _gv.gv_autopan_tool_set_x_resolution(self._o, resolution)
+
+    def set_standard_path(self, path_type):
+        """ Set the standard path to follow in panning:
+            Inputs:
+                path_type- an integer (0-4, currently)
+
+                0- start top left, go right, down, left, down,...
+                1- start bottom left, go right, up, left, up,...
+                2- start top right, go left, down, right, down,...
+                3- start bottom right, go left, up, left, up,...
+                4- start top left, go right, jump down and back
+                   to the left edge, go right,...
+        """
+        return _gv.gv_autopan_tool_set_standard_path(self._o, path_type)
+
+    def set_lines_path(self, lines):
+        """ Sets the path to follow based on a GvShapes object
+            that contains line shapes.
+        """
+        return _gv.gv_autopan_tool_set_lines_path(self._o, lines._o)
+
+    def set_location(self, x, y):
+        """ Set the current location within the panning
+            extents (snaps to nearest computed location).
+        """
+        # z is in case we ever do anything with 3d panning, but
+        # for now only 2d has been considered.
+        return _gv.gv_autopan_tool_set_location(self._o,x,y,0)
+
+    def get_location(self):
+        """ Get the current location within the panning extents.
+            Raises an error if there is no current location.
+        """
+        return _gv.gv_autopan_tool_get_location(self._o)
+
+    def get_state(self):
+        """ Gets the current settings of the following autopan
+            tool parameters (returned as a tuple):
+            
+                play_flag: 0- stopped, 1- playing, 2- paused
+                
+                path_type: 0- starts top left, goes right, down, left...
+                           1- starts bottom left, goes right, up, left...
+                           2- starts top right, goes left, down, right...
+                           3- starts bottom right, goes left, up, right...
+                           
+                
+                block_size_mode, block_x_size, resolution:
+                  block_size_mode = 0:
+                      - user sets block_x_size to a float between 0 and 1,
+                        corresponding to the block size as a fraction of the
+                        total x extents (block_y_size will be determined by
+                        block_x_size and the window's aspect ratio).
+                        resolution is ignored in this mode.
+
+                  block_size_mode = 1:
+                      - same as mode 0, except that block_x_size is in view
+                        coordinates rather than a fraction of total x extents
+                        to be panned.
+
+                  block_size_mode = 2:
+                      - block size is set so that the size of pixels in the 
+                        view will be constant at resolution.
+
+                num_views: number of secondary views associated with
+                           the tool.
+        """
+        return _gv.gv_autopan_tool_get_state(self._o)
+
+    def clear_trail(self):
+        """ Clear the stored trail and refresh secondary views. """
+        return _gv.gv_autopan_tool_clear_trail(self._o)
+
+    def set_trail_color(self, view, red, green, blue, alpha):
+        """ Set the trail color for a secondary view.
+
+            Inputs:
+                view- view to reset the trail color of.
+                red, green, blue, alpha- floating point values in
+                                         the range 0-1.
+        """
+        return _gv.gv_autopan_tool_set_trail_color(self._o, view._o, red,
+                                                   green, blue, alpha)
+
+    def set_trail_mode(self, view, trail_mode):
+        """ Set whether or not to display a trail in a secondary
+            view.
+
+            Inputs:
+                view- view to reset the trail mode on.
+                trail_mode- integer 0 or 1.
+        """
+        return _gv.gv_autopan_tool_set_trail_mode(self._o, view._o, trail_mode)
+
+    def set_trail_parameters(self, overview_extents, overview_width_pixels):
+        """ Set the rough predicted extents that the trail will cover, and
+            the width that this should correspond to in screen pixels. """
+        return _gv.gv_autopan_tool_set_trail_parameters(self._o,
+                                                 overview_extents,
+                                                 overview_width_pixels)
+
+    def get_trail_parameters(self):
+        """ Get the trail parameters and number of trail tiles.
+        
+            Returns a tuple (extents,width,num tiles):
+                extents, width- parameters used to decide trail tile
+                                coverage and resolution (see
+                                set_trail_parameters)
+                num tiles- number of trail tiles created so far.
+        """
+        tup = _gv.gv_autopan_tool_get_trail_parameters(self._o)
+        return ((tup[0],tup[1],tup[2],tup[3]),tup[4],tup[5])
+                
+
+    def save_trail_tiles(self, basename):
+        """ Saves trail tiles to files named basename+'0',
+            basename+'1', etc.  Returns the number of tiles saved
+            (-1 if an error occured).  Tiles are always saved
+            to Geotiff format (requires metadata capability).
+        """
+        return _gv.gv_autopan_tool_save_trail_tiles(self._o,
+                                                    basename)
+    
+    def load_trail_tiles(self, basename, num_trail_tiles):
+        """ Loads trail tiles from files named basename+'0',
+            basename+'1', etc., up to basename+str(num_trail_tiles-1)
+            Returns the number of tiles loaded
+            (-1 if an error occured).
+        """
+        return _gv.gv_autopan_tool_load_trail_tiles(self._o,
+                                                    basename,
+                                                    num_trail_tiles)
+
+    def register_view(self, view, can_resize=0, can_reposition=0,
+                      trail_mode=0):
+        """ Register a secondary view.  Current panning extents will
+            be drawn as a blue-green rectangle in this view.
+            Inputs:
+                view- view to register
+                can_resize- integer 0 or 1: 1 if user can resize the
+                            current panning block size by banding
+                            the rectangle in the secondary view, 0
+                            if they can't. NOT IMPLEMENTED YET: set
+                            to 0.
+                can_reposition- integer 0 or 1: 1 if user can reposition the
+                            current panning block size by dragging
+                            the rectangle in the secondary view, 0
+                            if they can't.
+                trail_mode- integer 0 or 1: 0 if view should not show
+                            trail already traveled; 1 if it should.
+        """
+        return _gv.gv_autopan_tool_register_view(self._o, view._o,
+                                                 can_resize, can_reposition,
+                                                 trail_mode)
+
+    def remove_view(self, view):
+        """ De-register a secondary view. """
+        return _gv.gv_autopan_tool_remove_view(self._o, view)
+
+
+###############################################################################
+class GvViewLink(_gtk.GtkObject):
+    get_type = _gv.gv_view_link_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_view_link_new()
+    def register_view(self, view):
+        _gv.gv_view_link_register_view(self._o, view._o)
+    def remove_view(self, view):
+        _gv.gv_view_link_remove_view(self._o, view._o)
+    def enable(self):
+        _gv.gv_view_link_enable(self._o)
+    def disable(self):
+        _gv.gv_view_link_disable(self._o)
+    def set_cursor_mode(self,mode=0):
+        try:
+            _gv.gv_view_link_set_cursor_mode(self._o,mode)
+        except:
+            pass
+
+###############################################################################
+class GvSymbolManager(_gtk.GtkObject):
+    get_type = _gv.gv_symbol_manager_get_type
+    
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_get_symbol_manager()
+
+    def get_symbol( self, name ):
+        result = _gv.gv_symbol_manager_get_symbol( self._o, name )
+        if result is not None and result[0] == 1:
+            result = (result[0], GvShape( _obj = result[1] ) )
+
+        return result
+
+    def has_symbol( self, name ):
+        return _gv.gv_symbol_manager_has_symbol( self._o, name )
+
+    def get_names( self ):
+        return _gv.gv_symbol_manager_get_names( self._o )
+
+    def inject_raster_symbol( self, name, width, height, rgba_buffer  ):
+        _gv.gv_symbol_manager_inject_raster_symbol( self._o, name,
+                                                    width, height, rgba_buffer)
+
+    def inject_vector_symbol( self, name, shape ):
+        _gv.gv_symbol_manager_inject_vector_symbol( self._o, name, shape._o )
+
+    def eject_symbol( self, name ):
+        return _gv.gv_symbol_manager_eject_symbol( self._o, name )
+
+    def save_vector_symbol( self, name, newname ):
+        _gv.gv_symbol_manager_save_vector_symbol( self._o, name, newname )
+
+    def serialize( self ):
+        tree = [gdal.CXT_Element, 'GvSymbolManager']
+
+        names = self.get_names()
+        
+        for name in names:
+            sym = self.get_symbol( name )
+            if sym[0] == 0:
+                print 'rasters symbol serialization not yet supported.'
+
+            elif sym[0] == 1:
+                vs = [CXT_Element, 'GvVectorSymbol',
+                      [CXT_Attribute, 'name',
+                       [CXT_Text, name]]]
+                vs.append( sym[1].serialize() )
+                tree.append( vs )
+            else:
+                print 'unsupported symbol type, not serialized'
+
+        return tree
+        
+    def initialize_from_xml( self, tree, filename=None ):
+        for item in tree[2:]:
+            if item[0] == CXT_Element and item[1] == 'GvVectorSymbol':
+                name = gvutils.XMLFindValue(item, 'name', None )
+                shape = GvShapeFromXML( gvutils.XMLFind( item, 'GvShape' ), None )
+                self.inject_vector_symbol( name, shape )
+
+###############################################################################
+class GvManager(_gtk.GtkObject):
+    get_type = _gv.gv_manager_get_type
+    def __init__(self, _obj=None):
+        if _obj: self._o = _obj; return
+        self._o = _gv.gv_get_manager()
+
+    def add_dataset(self, dataset):
+        """Adds gdal.Dataset instance to the list of managed datasets.
+
+        This method adds given gdal.Dataset instance to the list of available
+	datasets. Does nothing if this dataset already listed.
+	
+	Returns a gdal.Dataset object (the same as given in parameters)."""
+
+        swig_ds = _gv.gv_manager_add_dataset(self._o, dataset._o)
+        if swig_ds is None:
+            return None
+
+        return gdal.Dataset( _obj=swig_ds )
+
+    def get_dataset(self, filename):
+        """Fetch gdal.Dataset for a filename.
+
+        This method fetches a gdal.Dataset for a given filename, while
+        ensuring that the dataset is only opened once, even if requested
+        more than once.
+	
+	Returns an opened  gdal.Dataset object."""
+
+        swig_ds = _gv.gv_manager_get_dataset(self._o, filename)
+        if swig_ds is None:
+            return None
+
+        return gdal.Dataset( _obj=swig_ds )
+        
+    def get_dataset_raster(self,dataset,band):
+        """Fetch GvRaster for a dataset band.
+
+        This method fetches a GvRaster for a given dataset band, while
+        ensuring that only one GvRaster is instantiated for the band
+        across the whole application. """
+        
+        raster_o = _gv.gv_manager_get_dataset_raster(self._o, dataset._o,band)
+        if raster_o is None:
+            return None
+
+        return GvRaster(_obj=raster_o)
+
+    def set_busy( self, busy_flag ):
+        _gv.gv_manager_set_busy( self._o, busy_flag )
+
+    def get_busy( self ):
+        return _gv.gv_manager_get_busy( self._o )
+
+    def queue_task( self, task_name, priority, cb, cb_data = None ):
+        """Queue an idle task.
+
+        The GvManager has a concept of a unified, prioritized set of idle
+        tasks.  These are only executed when there is no further pending
+        user input, or windows events to process, and are typically used to
+        perform low priority work like loading additional tiles in GvRasters
+        for display purposes.
+
+        This mechanism can also be used by the application level, for
+        instance, to update the gui, or perform chunks of processing.
+
+        Note that there is no mechanism available to remove a task from
+        the queue untill it is actually executed.  Items in the list can be
+        dumped to stderr with the GvManager.dump() call for debugging purposes.
+
+        The OpenEV core currently has three idle tasks which may be queued:
+
+         - zoompan-handler (priority 2): used to implement continuous zooming.
+         - 3d-motion-handler (priority 2): similar to zoompan-handler for 3d.
+         - raster-layer-update (priority 10): used to load, and process
+         additional raster tiles.
+
+        Note that the return value of the user callback is examined to
+        determine if the task should be requeued automatically.  A value
+        of zero indicates that the task should not be requeued, while any
+        other numeric value results in automatic requeuing. 
+
+        task_name -- name for task, useful in debugging.
+        priority -- numeric priority.  Higher values have lower priority.
+        cb -- python callback to invoke
+        cb_data -- single argument to pass to callback (optional).
+        """
+        
+        _gv.gv_manager_queue_task( self._o, task_name, priority, cb, cb_data )
+
+    def dump( self ):
+        """Dump GvManager info to stderr.
+
+        The list of preferences, openev datasets, and idle tasks is written
+        to stderr in human readable format.  Useful as a debugging aid.
+        """
+        _gv.gv_manager_dump( self._o )
+        
+
+###############################################################################
+def undo_register(data):
+    """Register GvData for undo.
+
+    data -- GvData to be registered.
+
+    This call registers the passed GvData with the undo system.  As long
+    as it exists, and the undo system is enabled any changes to it will
+    be recorded for undo.  There is no way to unregister an individual GvData
+    once registered."""
+    _gv.gv_undo_register_data(data._o)
+
+    
+###############################################################################
+def can_undo():
+    """Returns TRUE if undo system is enabled."""
+    return _gv.gv_undo_can_undo()
+
+
+###############################################################################
+def undo_pop():
+    """Undo the most recent undo group."""
+    _gv.gv_undo_pop();
+
+###############################################################################
+def undo_clear():
+    """Destroy all saved undo steps."""
+    _gv.gv_undo_clear();
+
+###############################################################################
+def undo_close():
+    """Temporarily disable capture of undo steps."""
+    _gv.gv_undo_close();
+
+###############################################################################
+def undo_open():
+    """Enable capture of undo steps."""
+    _gv.gv_undo_open();
+
+###############################################################################
+def undo_start_group():
+    """Establish a multi operation undo group.
+
+    All undo operations saved to the undo stack after this call, and
+    before the next undo_end_group() call will be considered to be a single
+    group.  A single call to undo_pop() will cause the entire group of
+    undo steps to be applied.
+
+    This is normally used to group multiple underlying operations that
+    should appear to be a single operation to the user. 
+
+    Returns the undo group integer identifier.  This should be kept and
+    passed to the undo_end_group() method to terminate the grouping."""
+    return _gv.gv_undo_start_group()
+
+###############################################################################
+def undo_end_group( group ):
+    """Close off a multi operation undo group.
+
+    group -- the group id to be terminated.  This should be the value
+    returned by the corresponding undo_start_group()."""
+    return _gv.gv_undo_end_group( group )
+
+
+
+###############################################################################
+# Manage Application Properties:
+
+app_preferences = None
+app_preffile = None
+
+def set_default_preferences( defaults ):
+    """
+    add a set of default preferences to the gview preference manager if they are not already
+    loaded.
+
+    defaults - a dictionary of pref_name = default_value pairs.
+
+    this checks to make sure that the preferences are already loaded.
+    """
+    global app_preferences
+   
+    if app_preferences is None:
+        load_preferences()
+    for key in defaults.keys():
+        if get_preference(str(key)) is None:
+            set_preference( str(key), str(defaults[key]))
+
+
+def get_preference(name, default = None):
+    """Fetch preference value
+
+    This method will return a String value, or None if the preference is
+    unknown.
+    
+    name -- the name of the preference to fetch.
+    """
+    global app_preferences
+    
+    if app_preferences is None:
+        load_preferences()
+        
+    res = _gv.gv_manager_get_preference(manager._o,name)
+    if res is None:
+        return default
+    else:
+        return res
+
+def set_preference(name,value):
+    """Set preference value
+
+    This method will set the preference value in a global application list,
+    which will be saved on application shutdown in the $HOME/.openev file,
+    and restore on subsequent startups.
+
+    name -- preference name (String)
+    value -- preference value (String)
+    """
+    global app_preferences
+    
+    if app_preferences is None:
+        load_preferences()
+
+    return _gv.gv_manager_set_preference(manager._o,name,value)
+
+def load_preferences():
+    global app_preferences
+    
+    app_preferences = {}
+    
+    if not os.path.exists(get_preffile()):
+        return
+    
+    file = open(get_preffile(),'r')
+    contents = file.readlines()
+    file.close()
+    
+    for line in contents:
+        tokens = string.split(line, '=', 1)
+        if len(tokens) == 2:
+            name,value = tokens
+            set_preference( string.strip(name), string.strip(value) )
+    
+def save_preferences():
+    global app_preferences
+
+    if app_preferences is None:
+        return
+
+    prefs = _gv.gv_manager_get_preferences(manager._o)
+    
+    if not os.path.exists(os.path.dirname(get_preffile())):
+        return
+    
+    file = open(get_preffile(), 'w')
+    for item in prefs.items():
+        name, value = item
+        file.write(name + '=' + value + '\n')
+    file.close()
+
+def get_preffile():
+    global app_preffile
+
+    if app_preffile is None:
+        app_preffile = os.path.expanduser('~/.openev')
+        if app_preffile == '\\/.openev' or app_preffile == '~/.openev':
+            app_preffile = 'C:\\.openev'
+
+    return app_preffile
+    
+
+###############################################################################
+def find_gview():
+    try:
+        gv_path = os.environ['OPENEV_HOME']
+        if os.path.isdir(os.path.join(gv_path,'pics')):
+            return gv_path
+    except:
+        pass
+    
+    try:
+        gv_path = os.environ['OPENEVHOME']
+        if os.path.isdir(os.path.join(gv_path,'pics')):
+            return gv_path
+    except:
+        pass
+    
+    for dir in sys.path:
+        gv_path = os.path.normpath(os.path.join(dir,'..'))
+        if os.path.isdir(os.path.join(gv_path,'pics')):
+            return gv_path
+        
+    gv_path = os.path.expanduser('~/devel/gview')
+    if os.path.isdir(os.path.join(gv_path,'pics')):
+        return gv_path
+    
+    gv_path = os.path.dirname(os.path.abspath(sys.argv[0]))
+    if os.path.isdir(os.path.join(gv_path,'pics')):
+        return gv_path
+
+    print 'Unable to find OpenEV tree ... some problems may be encountered.'
+    return ''
+
+###############################################################################
+def raster_cache_get_max():
+    return _gv.gv_raster_cache_get_max()
+
+def raster_cache_set_max(new_max):
+    _gv.gv_raster_cache_set_max(new_max)
+
+def raster_cache_get_used():
+    return _gv.gv_raster_cache_get_used()
+
+def texture_cache_get_max():
+    return _gv.gv_texture_cache_get_max()
+
+def texture_cache_set_max(new_max):
+    _gv.gv_texture_cache_set_max(new_max)
+
+def texture_cache_get_used():
+    return _gv.gv_texture_cache_get_used()
+
+def texture_cache_dump():
+    return _gv.gv_texture_cache_dump()
+
+###############################################################################
+
+def rgba_to_rgb(rgba):
+    #Convert RGBA data to RGB.
+    #
+    #This function is used to accelerate conversion of RGBA LUTs into RGB
+    #so that they can be displayed in a GtkPreview.  Currently only used by
+    #gvrasterpropdlg.py.
+    return _gv.gv_rgba_to_rgb(rgba)
+
+###############################################################################
+
+def gtk_object_deref_and_destroy(object):
+    _gtkmissing.gtk_object_deref_and_destroy(object._o)
+
+def gtk_object_get_ref_count(object):
+    return _gtkmissing.gtk_object_get_ref_count(object._o)
+
+def gtk_object_sink(object):
+    return _gtkmissing.gtk_object_sink(object._o)
+
+def gtk_object_ref(object):
+    return _gtkmissing.gtk_object_ref(object._o)
+
+def gtk_object_unref(object):
+    return _gtkmissing.gtk_object_ref(object._o)
+
+def py_object_get_ref_count(object):
+    return _gtkmissing.py_object_get_ref_count(object)
+
+pgu.gtk_register('IpGcpLayer', IpGcpLayer)
+pgu.gtk_register('AppCurLayer', AppCurLayer)
+for m in map(lambda x: eval(x), filter(lambda x: x[:2] == 'Gv', dir())):
+    if m != 'GvShape':
+        pgu.gtk_register(m.__name__,m)
+        
+del m
+
+manager = GvManager(_obj = _gv.gv_get_manager())
+get_preference('test')
+
+"""Home directory of OpenEV tree for purposes of finding icons, online help,
+etc"""
+home_dir = find_gview()
+
+if get_preference('gdal_cache') != None:
+    import gdal
+    gdal.SetCacheMax( int(get_preference('gdal_cache')) )
+else:
+    gdal.SetCacheMax( 12582912 )
+
+if get_preference('gvraster_cache') != None:
+    raster_cache_set_max( int(get_preference('gvraster_cache')) )
+else:
+    raster_cache_set_max( 39845888 )
+    
+if get_preference('texture_cache') != None:
+    texture_cache_set_max( int(get_preference('texture_cache')) )
+else:
+    texture_cache_set_max( 33554432 )

Added: packages/openev/branches/upstream/current/pymod/gviewapp.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gviewapp.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gviewapp.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2162 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: gviewapp.py,v 1.29 2005/10/17 18:21:13 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  GViewApp and related definitions.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gviewapp.py,v $
+#  Revision 1.29  2005/10/17 18:21:13  gmwalter
+#  Force proper locale.
+#
+#  Revision 1.28  2004/09/26 01:31:42  warmerda
+#  added project to rfl list after open.
+#
+#  Revision 1.27  2004/07/02 16:40:51  dem
+#  - Implement project files portability
+#  - last_strech restored in projects reloading
+#  - add a "File/Save Project as..." menu
+#
+#  Revision 1.26  2004/04/21 14:35:25  andrey_kiselev
+#  New option: save last visited directory.
+#
+#  Revision 1.25  2004/02/18 18:00:10  warmerda
+#  Fixed return in save_project() as per Julien's note.
+#
+#  Revision 1.24  2004/02/12 16:56:41  andrey_kiselev
+#  Comment out gvenhdlg.
+#
+#  Revision 1.23  2004/02/10 15:51:27  andrey_kiselev
+#  Added open_gdal_dataset method.
+#
+#  Revision 1.22  2004/01/22 19:52:10  andrey_kiselev
+#  New switch to control displaying "[NODATA]" marks in the tracker tool. Tweaks
+#  in 'Preferences' dialog.
+#
+#  Revision 1.21  2003/09/12 18:31:28  pgs
+#  updated preference dialog with legend preferences
+#
+#  Revision 1.20  2003/07/28 19:42:33  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.19  2003/06/25 17:52:08  warmerda
+#  added rotate tool
+#
+#  Revision 1.18  2003/03/12 19:50:01  gmwalter
+#  Add cursor type preference.
+#
+#  Revision 1.17  2003/02/20 19:27:20  gmwalter
+#  Updated link tool to include Diana's ghost cursor code, and added functions
+#  to allow the cursor and link mechanism to use different gcps
+#  than the display for georeferencing.  Updated raster properties
+#  dialog for multi-band case.  Added some signals to layerdlg.py and
+#  oeattedit.py to make it easier for tools to interact with them.
+#  A few random bug fixes.
+#
+#  Revision 1.16  2003/01/24 15:44:29  warmerda
+#  browse command may be None if browser not found
+#
+#  Revision 1.15  2002/12/12 07:42:14  warmerda
+#  added gvcorecmds to pyshell
+#
+#  Revision 1.14  2002/11/04 21:08:56  warmerda
+#  added float() locale check when starting up app
+#
+#  Revision 1.13  2002/07/12 12:46:06  warmerda
+#  expanded tabs
+#
+#  Revision 1.12  2002/07/11 01:05:26  warmerda
+#  fix bug in save_project
+#
+#  Revision 1.11  2002/07/08 19:46:03  warmerda
+#  added project save/load capability
+#
+#  Revision 1.10  2002/07/07 21:06:14  warmerda
+#  preliminary addition of project saving
+#
+#  Revision 1.9  2002/05/16 15:04:38  warmerda
+#  ensure there is a graceful recovery if there is no tools directory
+#
+#  Revision 1.8  2002/05/08 17:57:38  gmwalter
+#  Fixed close_all_views to unsubscribe from the main app's 'rfl-change'
+#  signal before destroying themselves.
+#
+#  Revision 1.7  2002/04/25 15:19:58  gmwalter
+#  Updated the menu extension mechanism to get rid of the extend_menu function and to allow the help menu to remain on the right side of the menubar.  Got rid of some extra print statements and the gnuplot 0 y-range warning in the histogram tool initialization.
+#
+#  Revision 1.6  2002/04/18 20:35:15  warmerda
+#  Added ability to auto-load tools
+#
+#  Revision 1.5  2002/04/12 14:20:29  gmwalter
+#  Added functions to clamp and scale gvmesh z values.
+#
+#  Revision 1.4  2002/03/23 20:03:05  gmwalter
+#  Added changes to allow plotting on windows (i.e. specify the temporary
+#  file as a preference rather than relying on os.tempnam(), check that
+#  unix-style filenames are passed to gnuplot).
+#
+#  Revision 1.3  2002/02/28 18:52:22  gmwalter
+#  Added a point-of-interest tool similar to the region-of-interest
+#  tool (allows a user to select a temporary point without having to add a
+#  new layer).  Added a mechanism to allow some customization of openev
+#  via a textfile defining external modules.
+#
+#  Revision 1.2  2001/11/14 22:35:28  warmerda
+#  added import of plot from gvplot for pyshell
+#
+#  Revision 1.1  2001/11/12 18:43:31  warmerda
+#  New
+#
+
+from gvsignaler import Signaler
+import gtk
+from gtk import FALSE, TRUE
+from GDK import *
+import gtkmissing
+import sys
+import GtkExtra
+import gview
+import gvconst
+import layerdlg
+#import gvenhdlg
+import gdal
+import gvutils
+import os
+import os.path
+import pgufilesel
+import pguprogress
+import math
+import gvviewwindow
+import gvhtml
+import gvselbrowser
+import sys
+import traceback
+
+# Force standard c settings for floating point (. rather than ,)
+import locale
+locale.setlocale(locale.LC_NUMERIC,'C')
+
+import pgufont
+default_font = pgufont.XLFDFontSpec()
+default_font.set_font_part('Family', 'Arial')
+default_font.set_font_part('Point Size', '120')
+
+default_preferences = {
+            'legend-background-color': (1.0, 1.0, 1.0, 1.0),
+            'legend-label-font': default_font,
+            'legend-label-font-color': (0.0, 0.0, 0.0, 1.0),
+            'legend-title-font': default_font,
+            'legend-title-font-color': (0.0, 0.0, 0.0, 1.0),
+            'default-font': default_font,
+}
+
+gview.set_default_preferences( default_preferences )
+
+class GViewApp(Signaler):
+    def __init__(self,toolfile=None,menufile=None,iconfile=None,pyshellfile=None,notools=0):
+        self.view_manager = ViewManager()
+        self.sel_manager = gvselbrowser.GvSelectionManager( self.view_manager )
+        self.pref_dialog = None
+        self.filename = None
+
+        # Toolbar
+        self.toolbar = Toolbar()
+        self.view_manager.set_toolbar( self.toolbar )
+
+        # Other dialogs, etc.
+        self.layerdlg = layerdlg.Launch()
+        self.view_manager.set_layerdlg(self.layerdlg)
+
+        self.publish('quit')
+        self.publish('rfl-change')
+
+        # Verify that float() works properly.
+        try:
+            x = float('0.9')
+        except:
+            gvutils.warning( 'It appears that float() doesn\'t work properly on your system.\nThis is likely due to use of a numeric locale with an alternate decimal\nrepresentation.  Please try setting the LC_NUMERIC environment variable\nto C and restarting OpenEV.' )
+
+        # Default configuration files for view and python shell
+        self.menufile=menufile
+        self.iconfile=iconfile
+        self.pyshellfile=pyshellfile
+        
+        # External tools to import and add to view menu
+        self.Tool_List = []
+        if toolfile is not None:
+            self.load_tools_file( toolfile )
+
+        if not notools:
+            self.scan_tools_directories()
+
+        # Tool index: a dictionary with the tool name as a
+        # key and the tool's position in the list as the value
+        self.tool_index = {}
+        for idx in range(len(self.Tool_List)):
+            self.tool_index[self.Tool_List[idx][0]]=idx
+
+        self.shell = None
+
+    def serialize(self,base=None, filename=None):
+        if base is None:
+            base = [gdal.CXT_Element, 'GViewApp']
+
+        for vw in self.view_manager.view_list:
+            base.append( vw.serialize( filename=filename ) )
+
+        return base
+
+    def clear_project( self ):
+        self.view_manager.close_all_views()
+        pass
+
+    def load_project(self, filename):
+        try:
+            raw_xml = open(filename).read()
+        except:
+            gvutils.error( 'Unable to load '+filename )
+            return
+
+        tree = gdal.ParseXMLString( raw_xml )
+        if tree is None:
+            gvutils.error( 'Problem occured parsing project file '+filename )
+            return
+
+        if tree[1] != 'GViewApp':
+            gvutils.error( 'Root of %s is not GViewApp node.' % filename )
+            return
+
+        self.clear_project()
+        self.filename = filename
+        self.add_to_rfl( filename )
+        
+        for subnode in tree[2:]:
+            if subnode[0] == gdal.CXT_Element:
+                gvutils.XMLInstantiate( subnode, self, filename=filename )
+
+    def save_project_with_name_cb( self, filename, *args ):
+        if os.path.splitext(filename)[1] == '':
+            filename = filename + '.opf'
+
+        self.save_project( filename )
+        self.add_to_rfl( filename )
+
+    def save_project_as( self ):
+        if self.filename is None:
+            default_filename = 'default.opf'
+        else:
+            default_filename = self.filename
+        pgufilesel.SimpleFileSelect( self.save_project_with_name_cb,
+                                     title = 'Project Filename',
+                                     default_filename = default_filename )
+    
+    def save_project(self, filename = None):
+        if filename is None and self.filename is not None:
+            filename = self.filename
+
+        if filename is None:
+            self.save_project_as()
+            return
+
+        tree = self.serialize( filename=filename )
+        open( filename, 'w' ).write( gdal.SerializeXMLTree(tree) )
+        
+        self.filename = filename
+
+    def load_tools_file(self, toolfile):
+        tool_count = 0
+        # read in toolfile, initialize tools
+        tool_file = open(toolfile,"r")
+        cur_module = None
+        cur_tool = None
+        for new_line in tool_file.readlines():
+            [key,val] = gvutils.read_keyval( new_line )
+            if (key == "MODULE_NAME"):
+                cur_module = val
+            elif (key == "TOOL_NAME"):
+                cur_tool = val
+                if ((cur_module is not None) and (cur_tool is not None)):
+                    self.load_tool( cur_module, cur_tool )
+                else:
+                    raise AttributeError,"Invalid tool file format"
+
+    def scan_tools_directories(self):
+        self.scan_tool_directory( os.path.join(gview.home_dir,'tools') )
+
+    def scan_tool_directory(self, dir_name):
+        try:
+            files = os.listdir(dir_name)
+        except:
+            return
+
+        old_path = sys.path
+        sys.path.append(dir_name)
+        for file in files:
+            # print file
+            if file[-3:] == '.py':
+                print 'Loading tools from '+os.path.join(dir_name,file)
+                module = file[:-3]
+                
+                try:
+                    exec "import " + module
+                    exec "tool_list = " + module + ".TOOL_LIST"
+                    for item in tool_list:
+                        exec "tool_inst=" + module + "." + item + "(app=self)"
+                        self.Tool_List.append((item,tool_inst))
+                except:
+                    print '... failed to load ... skipping.'
+                    gdal.Debug( "GDA", '-'*60 )
+                    sys_type, sys_value, sys_traceback = sys.exc_info()
+                    exp = traceback.format_exception( sys_type, sys_value, sys_traceback )
+                    exception = ""
+                    for line in exp:
+                        exception = exception + line
+                    gdal.Debug( "GDA", exception )
+                    gdal.Debug( "GDA", '-'*60 )
+
+        # We only add the tool directory to the python path long enough
+        # to load the tool files.
+        sys.path = old_path
+
+    def load_tool(self, module_name, tool_name ):
+        exec "import " + module_name
+        exec "cur_tool_class = " + module_name + "." + tool_name + "(app=self)"
+        self.Tool_List.append([tool_name,cur_tool_class])
+
+    def request_quit(self, *args):
+        response = \
+                 GtkExtra.message_box( 'Confirmation',
+                                       'Are you sure you want to exit OpenEV?',
+                                       ('Yes', 'No') )
+
+        if response == 'Yes':
+            self.quit()
+            return 1
+        else:
+            return 0
+
+    def quit(self, *args):
+        # Save preferences
+        gview.save_preferences()
+        
+        # Notify listeners of quit event
+        self.notify('quit')
+
+    def add_to_rfl(self, filename):
+        # Don't add NUMPY arrays to file list.
+        if filename[:7] == 'NUMPY::':
+            return
+        
+        next_value = filename
+        for i in range(1,6):
+            rbl_name = 'recent_file_'+str(i)
+            rbl_value = gview.get_preference(rbl_name)
+            gview.set_preference(rbl_name, next_value)
+            
+            if rbl_value is None or rbl_value == filename:
+                break;
+
+            next_value = rbl_value
+
+        self.notify('rfl-change')
+
+    def get_rfl(self):
+        list = []
+        for i in range(1,6):
+            rbl_name = 'recent_file_'+str(i)
+            rbl_value = gview.get_preference(rbl_name)
+            if rbl_value is not None:
+                list.append(rbl_value)
+        return list
+
+    def show_layerdlg(self, *args):
+        self.layerdlg.show()
+        self.layerdlg.get_window()._raise()
+
+    def show_toolbardlg(self, *args):
+        self.toolbar.show()
+        self.toolbar.get_window()._raise()
+
+    def show_enhdlg(self, *args):
+	self.enhdlg = gvenhdlg.EnchancementDialog()
+
+    def load_menus_file_from_xml(self,menufile,view_name):
+        # Scan the XML menu file to find which tools to
+        # load, and where to position them.
+
+        import string
+        
+        menufile = os.path.join(gview.home_dir,'xmlconfig',menufile)
+
+        # menu_list contains a mix of regular and tool menu entries,
+        # in order. 
+        menu_list = []
+        try:
+            raw_xml = open(menufile).read()
+        except:
+            raise AttributeError,"Unable to load " + menufile
+            return
+
+        tree = gdal.ParseXMLString( raw_xml )
+        if tree is None:
+            raise AttributeError,"Problem occured parsing menu file " + menufile
+            return
+
+        if tree[1] != 'GViewAppMenu':
+            raise AttributeError,"Root of %s is not GViewAppMenu node " % menufile
+            return
+
+        # loop over entries getting path,accelerator,callback and arguments
+        menu_trees = gvutils.XMLFind( tree, 'Menu')
+        if menu_trees is None:
+            raise AttributeError,"Invalid menu file format"
+
+        # Tools can be specified in a number of ways.  The
+        # <tools> entry can be 'All', 'None', or 'Some'.
+        # In the "All" case, all tools will be loaded up.
+        # If toolentries are specified, the defaults
+        # will be overidden for those tools.  This is the
+        # default.  If "Some" is specified, only the
+        # tools entered in the xml file will be included.
+        # If None is specified, no tools will be loaded
+        # and if toolentries are specified an error will
+        # be raised.
+        tools_to_include = 'All'
+
+        # If tools to include is All, use this list to check for
+        # tools that haven't been added yet, and add them at the
+        # end using their defaults.
+        tools_accounted_for=[]
+
+        
+        menu_list = []
+            
+        for node in menu_trees[2:]:
+            if node[1] == 'entry':
+                node_path  = gvutils.XMLFind( node, 'path')
+                if node_path is None:
+                    raise AttributeError,"Invalid menu file format - missing path"
+                 
+                entry_type = gvutils.XMLFindValue( node_path, 'type', '')
+                entry_path = gvutils.XMLFindValue( node, 'path','')
+                
+                if (string.find(entry_path,"/") == -1):
+                    raise AttributeError,"Invalid menu file format - bad path:%s" % entry_path
+                    
+                if (entry_type != ''):
+                    entry_type = "<" + entry_type + ">"
+                path_split=string.split(entry_path,"/")
+                path_split[-1] = entry_type + path_split[-1]
+                entry_path=string.join(path_split,"/")
+
+                entry_accelerator = gvutils.XMLFindValue( node, 'accelerator', 'None')
+                if (entry_accelerator != 'None'):
+                    (key,mod) = string.split(entry_accelerator,'+')
+                    entry_accelerator = "'<" + key + ">" + mod + "'"
+
+                entry_callback = gvutils.XMLFindValue( node, 'callback', 'None')
+                entry= "("                                             \
+                        + string.join((entry_path,entry_accelerator,   \
+                                       entry_callback),",")
+
+                arguments = gvutils.XMLFind( node, 'arguments')
+                if arguments is not None:
+                    args_list = []
+                    args =  gvutils.XMLFind( arguments, 'arg','')
+                    if args is not None:
+                        for arg in args:
+                            args_list.append(gvutils.XMLFindValue( arg, '',''))
+                        entry = entry + "," + string.join(args_list,",")
+
+                entry = entry + ")"
+
+                menu_list.append(entry)
+                            
+            elif node[1] == 'tools':
+                tools_to_include=node[2][1]
+                
+            elif node[1] == 'simpletoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid menu file format - missing tool name"
+
+                if self.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid menu file format- tool "+toolname+" not loaded."
+
+                ctool=self.Tool_List[self.tool_index[toolname]][1]
+                for cpath in ctool.menu_entries.entries.keys():
+                    caccel=ctool.menu_entries.entries[cpath][2]
+                    if caccel is None:
+                        caccel=str(None)
+                    else:
+                        caccel="'"+caccel+"'"
+                    # main application is stored as self.app in GvViewWindow
+                    # ccb (current callback string) specifies the path to the
+                    # callback from within the gvviewwindow.
+                    ccb="self.app.Tool_List[self.app.tool_index['"+toolname+"']]"+\
+                        "[1].menu_entries.entries['"+cpath+"'][1]"
+
+                    # The name of the view that launched the tool is passed to
+                    # the callback in case the tool needs to locate the view that
+                    # launched it (a view doesn't always become the currently 
+                    # active view until its view area is clicked on, so simply
+                    # getting the active view is not sufficient).  If the tool
+                    # wishes to act on the view that launched it rathern than
+                    # the currently active layer, it must locate the view with
+                    # this name and activate it before proceeding.
+                    viewstr="('"+view_name+"')"
+
+                    entry="("+string.join(("'"+cpath+"'",caccel,ccb,viewstr),",")+")"
+                    menu_list.append(entry)
+                    
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+
+                    
+            elif node[1] == 'complextoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid menu file format - missing tool name"
+                
+                oldpath  = gvutils.XMLFindValue( node, 'oldpath')
+
+                if oldpath is None:
+                    txt="Invalid menu file format - complex tool entry\nrequires oldpath item."
+                    raise AttributeError,txt
+                oldpath = oldpath[1:-1] # Entries in XML file are surrounded by quotes- get rid of them
+                
+                newpath  = gvutils.XMLFindValue( node, 'newpath')
+                if newpath is None:
+                    txt="Invalid menu file format - complex tool entry\nrequires newpath item."
+                    raise AttributeError,txt
+                newpath = newpath[1:-1] # Entries in XML file are surrounded by quotes- get rid of them
+                
+                if self.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid menu file format- tool "+toolname+" not loaded."
+
+                ctool=self.Tool_List[self.tool_index[toolname]][1]
+                if ctool.menu_entries.entries.has_key(oldpath) == 0:
+                    raise AttributeError,'Invalid menu file entry- tool '+toolname+' has no\nmenu entry '+oldpath
+
+                caccel=gvutils.XMLFindValue(node,'accelerator')
+                if caccel is None:
+                    caccel=ctool.menu_entries.entries[oldpath][2]
+                    if caccel is None:
+                        caccel=str(None)
+                    else:
+                        caccel="'"+caccel+"'"
+                else:
+                    # XML file specifies key sequence string without
+                    # the "<" and ">"'s to avoid confusion with tags
+                    # (eg. control+D rather than <control>D).  Add them
+                    # back in here, since parser expects them.
+                    (key,mod)=string.split(caccel,'+')
+                    caccel="'<"+key+">"+mod+"'"
+
+                ccb="self.app.Tool_List[self.app.tool_index['"+toolname+"']]"+\
+                     "[1].menu_entries.entries['"+oldpath+"'][1]"
+
+                viewstr="('"+view_name+"')"
+
+                entry="("+string.join(("'"+newpath+"'",caccel,ccb,viewstr),",")+")"
+                menu_list.append(entry)
+                                   
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+                    
+        if tools_to_include not in ['All','None','Some']:
+            raise AttributeError,"Invalid menu file format- <tool> entry should be All, None, or Some."
+
+        if ((tools_to_include == 'None') and (len(tools_accounted_for) > 0)):
+            txt = "Invalid menu file format- if <tool> entry is None,\nno "
+            txt = txt+"simpletoolentry or complextoolentry items may be specified."
+            raise AttributeError,txt
+
+        if tools_to_include == 'All':
+            for citem in self.Tool_List:
+                if citem[0] not in tools_accounted_for:
+                    ctool=citem[1]
+                    for cpath in ctool.menu_entries.entries.keys():
+                        # default position: find where to insert
+                        # tool.
+                        cpos=ctool.menu_entries.entries[cpath][0]                        
+                        cpos=max(cpos,0)
+
+                        splitpath=string.split(cpath,'/')
+                        rootp=string.join(splitpath[:-1],'/')+'/'
+                        nchars=len(rootp)
+                        matches=0
+                        idx=0
+                        for nextentry in menu_list:
+                            if (len(nextentry) > nchars):
+                                if (nextentry[2:nchars+2] == rootp):
+                                    matches=matches+1
+                            if matches > cpos:
+                                break
+                            idx=idx+1
+                        
+                        caccel=ctool.menu_entries.entries[cpath][2]
+                        if caccel is None:
+                            caccel=str(None)
+                        else:
+                            caccel="'"+caccel+"'"
+                        ccb="self.app.Tool_List[self.app.tool_index['"+citem[0]+"']]"+\
+                             "[1].menu_entries.entries['"+cpath+"'][1]"
+
+                        viewstr="('"+view_name+"')"
+                        
+                        entry="("+string.join(("'"+cpath+"'",caccel,ccb,viewstr),",")+")"
+                        menu_list.insert(idx,entry)
+
+        # Move help entries to end so that Help menu is on the far right
+        help_list=[]
+        idx=0
+        for count in range(len(menu_list)):
+            if len(menu_list[idx]) > 7:
+                if menu_list[idx][:7] == "('Help/":
+                    help_list.append(menu_list.pop(idx))
+                else:
+                    idx=idx+1
+            else:
+                idx=idx+1
+
+        menu_list.extend(help_list)
+        
+        menu_cmd =  "self.menuf.add_entries([" + string.join(menu_list,',') + "])"
+        return menu_cmd
+            
+    
+    def load_icons_file_from_xml(self,iconfile):
+        # Scan the XML icon file to find which tools to
+        # load, and where to position them.
+
+        import string
+        
+        iconfile = os.path.join(gview.home_dir,'xmlconfig',iconfile)
+
+        # icon_count: current position
+        icon_count=0
+        try:
+            raw_xml = open(iconfile).read()
+        except:
+            raise AttributeError,"Unable to load " + iconfile
+            return
+
+        tree = gdal.ParseXMLString( raw_xml )
+        if tree is None:
+            raise AttributeError,"Problem occured parsing icon file " + iconfile
+            return
+
+        if tree[1] != 'GViewAppIconBar':
+            raise AttributeError,"Root of %s is not GViewAppIconBar node " % iconfile
+            return
+
+        # loop over entries getting path,accelerator,callback and arguments
+        icon_trees = gvutils.XMLFind( tree, 'Iconbar')
+        if icon_trees is None:
+            raise AttributeError,"Invalid menu file format"
+
+        # Tools can be specified in a number of ways.  The
+        # <tools> entry can be 'All', 'None', or 'Some'.
+        # In the "All" case, all tools will be loaded up.
+        # If toolentries are specified, the defaults
+        # will be overidden for those tools.  This is the
+        # default.  If "Some" is specified, only the
+        # tools entered in the xml file will be included.
+        # If None is specified, no tools will be loaded
+        # and if toolentries are specified an error will
+        # be raised.
+        tools_to_include = 'All'
+
+        # If tools to include is All, use this list to check for
+        # tools that haven't been added yet, and add them at the
+        # end using their defaults.
+        tools_accounted_for=[]
+        
+        icon_list = []
+            
+        for node in icon_trees[2:]:
+            if node[1] == 'icon':
+                type = None
+                icon_label = gvutils.XMLFindValue( node, 'label','None')
+                icon_hint = gvutils.XMLFindValue( node, 'hint','None')
+                icon_callback = gvutils.XMLFindValue( node, 'callback','None')
+                icon_help = gvutils.XMLFindValue( node, 'help','None')
+                icon_file = gvutils.XMLFindValue( node, 'xpm','None')
+                # xpm files - need to add path and possible help
+                if (icon_file != 'None'):
+                    type = 'xpm'
+                    icon = "self.add_icon_to_bar("                           \
+                            + string.join((icon_file,icon_label,icon_hint,   \
+                                           icon_callback,icon_help),",")     \
+                            + ")" 
+
+                # pixmap files - not adding path or help 
+                icon_file = gvutils.XMLFindValue( node, 'pixmap','None')
+                if (icon_file!= 'None'):
+                    type = 'pixmap'
+                    icon = "self.iconbar.append_item("                        \
+                            + string.join((icon_label,icon_hint,icon_hint,    \
+                                              icon_file,icon_callback),",")   \
+                            + ")" 
+
+                # widget  
+                icon_file = gvutils.XMLFindValue( node, 'widget','None')
+                if (icon_file!= 'None'):
+                    type = 'widget'
+                    icon_file = gvutils.XMLFindValue( node, 'widget','None')
+                    icon = "self.iconbar.append_widget("                       \
+                            + string.join((icon_file,icon_hint,icon_hint),",") \
+                            + ")" 
+                # none of the above
+                if type is None:
+                    raise AttributeError,"Invalid icon file format - unknown type"
+
+                icon_list.append(icon)                
+            elif node[1] == 'tools':
+                tools_to_include=node[2][1]
+            elif node[1] == 'simpletoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid icon file format - missing tool name"
+
+                if self.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid icon file format- tool "+toolname+" not loaded."
+
+                ctool=self.Tool_List[self.tool_index[toolname]][1]
+                
+                idx=0
+                for centry in ctool.icon_entries.entries:
+                    icon_file=centry[0]
+                    
+                    icon_label=centry[1]
+                    if icon_label is not None:
+                        icon_label="'"+icon_label+"'"
+                    else:
+                        icon_label=str(None)
+                    
+                    icon_hint=centry[2]
+                    if icon_hint is not None:
+                        icon_hint="'"+icon_hint+"'"
+                    else:
+                        icon_hint=str(None)
+                    
+                    # Ignore position- it is overridden by this entry's location in the
+                    # xml file
+                    icon_callback=centry[4]
+                    icon_help=centry[5]
+                    if icon_help is not None:
+                        icon_help="'"+icon_help+"'"
+                    else:
+                        icon_help=str(None)
+                     
+                    icon_type=centry[6]
+                    if icon_type == 'xpm':
+                        icon = "self.add_icon_to_bar("                           \
+                                + string.join(("'"+icon_file+"'",icon_label,icon_hint,   \
+                                "self.app.Tool_List[self.app.tool_index['"+\
+                                toolname+"']][1].icon_entries.entries["+\
+                                str(idx)+"][4]",icon_help),",")+")"     
+                                
+                        icon_list.append(icon)
+                    else:
+                        raise AttributeError,"Invalid icon type "+icon_type+" in tool "+toolname+"."
+                    idx=idx+1
+                                                               
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+                    
+            elif node[1] == 'complextoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid icon file format - missing tool name."
+                
+                oindex  = gvutils.XMLFindValue( node, 'index')
+
+                if oindex is None:
+                    txt="Invalid icon file format - complex tool entry\nrequires the index of the icon entry\n"
+                    txt=txt+"to replace (0...number of entries-1).\n"
+                    raise AttributeError,txt
+                try:
+                    oindex=int(oindex)
+                except:
+                    raise AttributeError,"Invalid icon file- icon index to replace must be an integer."
+                
+                if self.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid icon file entry- tool "+toolname+" not loaded."
+
+                ctool=self.Tool_List[self.tool_index[toolname]][1]
+        
+                if len(ctool.icon_entries.entries) < (oindex+1):
+                    txt='Invalid file file entry- for tool '+toolname+'.\n maximum entry index is '
+                    txt=txt+str(len(ctool.icon_entries.entries)-1)+'.' 
+
+                icon_file=gvutils.XMLFindValue( node, 'xpm')
+                icon_hint=gvutils.XMLFindValue( node, 'hint')
+                icon_label=gvutils.XMLFindValue( node, 'label')
+                icon_help=gvutils.XMLFindValue( node, 'help')
+
+                if icon_file is None:
+                    icon_file="'"+ctool.icon_entries.entries[oindex][0]+"'"
+                elif os.path.isfile(icon_file):
+                    if os.name == 'nt':
+                        icon_file="'"+string.replace(icon_file,"\\","\\\\")+"'"
+                    else:
+                        icon_file="'"+icon_file+"'"
+                elif os.path.isfile(os.path.join(gview.home_dir,'tools',icon_file)):
+                    icon_file="'"+os.path.join(gview.home_dir,'tools',icon_file)+"'"
+                    if os.name == 'nt':
+                        icon_file=string.replace(icon_file,"\\","\\\\")
+                elif os.path.isfile(os.path.join(gview.home_dir,'pics',icon_file)):
+                    icon_file="'"+os.path.join(gview.home_dir,'pics',icon_file)+"'"
+                    if os.name == 'nt':
+                        icon_file=string.replace(icon_file,"\\","\\\\") 
+                else:
+                    txt = "Cannot find file "+tempf+'.  Either the full\n'
+                    txt = txt+"path must be specified, or "+tempf+ " must be\n"
+                    txt = txt+"placed in the tools or pics directory."
+                    raise AttributeError,txt
+                
+
+                if icon_label is None:
+                    icon_label=ctool.icon_entries.entries[oindex][1]
+                    
+                if icon_label is not None:
+                    icon_label="'"+icon_label+"'" 
+                else:
+                    icon_label=str(None)
+                    
+                if icon_hint is None:
+                    icon_hint=ctool.icon_entries.entries[oindex][2]
+
+                if icon_hint is not None:
+                    icon_hint="'"+icon_hint+"'"
+                else:
+                    icon_hint=str(None)
+                    
+                if icon_help is None:
+                    icon_help=ctool.icon_entries.entries[oindex][5]
+
+                if icon_help is not None:
+                    icon_help="'"+icon_help+"'"
+                else:
+                    icon_help=str(None)
+
+                icon_callback=ctool.icon_entries.entries[oindex][4]
+                icon_type=ctool.icon_entries.entries[oindex][6]
+                if icon_type == 'xpm':
+                    icon = "self.add_icon_to_bar("                           \
+                            + string.join((icon_file,icon_label,icon_hint,   \
+                         "self.app.Tool_List[self.app.tool_index['"+toolname+\
+                         "']][1].icon_entries.entries["+str(oindex)+"][4]",\
+                                           icon_help),",") + ")"
+                    icon_list.append(icon)
+                else:
+                    raise AttributeError,"Invalid icon type "+icon_type+" in tool "+toolname+"."
+                                                               
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+
+
+        if tools_to_include not in ['All','None','Some']:
+            raise AttributeError,"Invalid icon file format- <tool> entry should be All, None, or Some."
+
+        if ((tools_to_include == 'None') and (len(tools_accounted_for) > 0)):
+            txt = "Invalid icon file format- if <tool> entry is None,\nno "
+            txt = txt+"simpletoolentry or complextoolentry items may be specified."
+            raise AttributeError,txt
+
+        if tools_to_include == 'All':
+            for citem in self.Tool_List:
+                if citem[0] not in tools_accounted_for:
+                    ctool=citem[1]
+                    idx=0
+                    for centry in ctool.icon_entries.entries:
+                        if centry[6] != 'xpm':            
+                            raise AttributeError,"Error loading tool entry for tool "+\
+                                  citem[0]+"- icon type "+centry[6]+" invalid."
+
+                        icon_file="'"+centry[0]+"'"
+                        icon_label=centry[1]
+                        if icon_label is not None:
+                            icon_label="'"+icon_label+"'"
+                        else:
+                            icon_label=str(None)
+
+                        icon_hint=centry[2]    
+                        if icon_hint is not None:
+                            icon_hint="'"+icon_hint+"'"
+                        else:
+                            icon_hint=str(None)
+
+                        icon_help=centry[5]
+                        if icon_help is not None:
+                            icon_help="'"+icon_help+"'"
+                        else:
+                            icon_help=str(None)
+                            
+                        # Default position in icon bar used
+                        pos=centry[3]
+                        icon = "self.add_icon_to_bar(" +\
+                                string.join((icon_file,icon_label,icon_hint,   \
+                                "self.app.Tool_List[self.app.tool_index['"+\
+                                citem[0]+"']][1].icon_entries.entries["+\
+                                str(idx)+"][4]",icon_help),",") + ")"
+                      
+                        pos=max(pos,0)
+                        if pos > len(icon_list):
+                            icon_list.append(icon)
+                        else:
+                            icon_list.insert(pos,icon)
+                        idx=idx+1    
+  
+        return icon_list
+
+    def new_view(self, title=None, menufile=None,iconfile=None, *args):
+        # If menu/icon files aren't specified, use application-wide
+        # defaults
+        if ((menufile is None) and (self.menufile is not None)):
+            menufile=self.menufile
+        if ((iconfile is None) and (self.iconfile is not None)):
+            iconfile=self.iconfile
+
+        view_window = gvviewwindow.GvViewWindow(app=self, title=title, menufile=menufile, iconfile=iconfile)
+        view_name=view_window.title
+        view_menu = view_window.menuf    
+              
+        if ((len(self.Tool_List) > 0) and (menufile is None)):
+            # If no menu configuration file is specified, put
+            # tools in the default positions specified by
+            # the tool menu entry's position parameter.
+            for cur_tool_list in self.Tool_List:
+                cur_tool = cur_tool_list[1]
+                if hasattr(cur_tool.menu_entries.entries,'keys'):
+                    for item in cur_tool.menu_entries.entries.keys():
+                        view_menu.insert_entry(
+                            cur_tool.menu_entries.entries[item][0],
+                            item,
+                            cur_tool.menu_entries.entries[item][2],
+                            cur_tool.menu_entries.entries[item][1],
+                            (view_name))
+
+        # Icons- Note: currently it is assumed that the tool icons are
+        #        xpms.  Support for pixmaps and widgets will be added
+        #        later if necessary (icon type would have to be detected
+        #        from last entry of the relevant tool icon entry, and
+        #        a function would have to be created to deal with them.
+        #        They are slightly more complicated than the xpm case
+        #        and wouldn't use insert_tool_icon.  Would also need
+        #        code in the complextoolentry case to avoid an icon
+        #        of one type (eg. xpm) being replaced by another type
+        #        (eg. widget, pixmap).
+        #
+        #        ALSO: GtkToolbar does not allow callback information
+        #              to be specified, so the viewname cannot be
+        #              passed as an argument to tool icon callbacks
+        #              the way it is for menu callbacks. However,
+        #              if the view is needed, it can be obtained
+        #              using:
+        #              view=args[0].get_toplevel()
+        #              The view title is under:
+        #              view['title'].  Note that this is not quite
+        #              the same as the view's self.title, but is based
+        #              on it (usually 'OpenEV: '+self.title)
+            
+        if ((len(self.Tool_List) > 0) and (iconfile is None)):
+            for cur_tool_list in self.Tool_List:
+                cur_tool=cur_tool_list[1]
+                for item in cur_tool.icon_entries.entries:
+                    view_window.insert_tool_icon(
+                        item[0], # filename
+                        item[1], # label
+                        item[2], # hint text
+                        item[4], # callback
+                        item[5], # help topic
+                        item[3],  # position
+                            )
+        view_window.show()
+        return view_window
+
+    def open_gdal_dataset(self, dataset, lut=None, sds_check=1, *args):
+        view = self.view_manager.get_active_view_window()
+        if view is None:
+            self.new_view()
+            view = self.view_manager.get_active_view_window()
+
+        if view is None:
+            return
+
+        view.open_gdal_dataset( dataset, lut = lut, sds_check = sds_check )
+
+    def file_open_by_name(self, filename, lut=None, sds_check=1, *args):
+        view = self.view_manager.get_active_view_window()
+        if view is None:
+            self.new_view()
+            view = self.view_manager.get_active_view_window()
+
+        if view is None:
+            return
+
+        view.file_open_by_name( filename, lut = lut, sds_check = sds_check )
+        
+    def launch_preferences(self, *args):
+        if self.pref_dialog is None:
+            self.pref_dialog = PrefDialog()
+            self.pref_dialog.connect('destroy', self.destroy_preferences)
+        self.pref_dialog.show()
+        self.pref_dialog.get_window()._raise()
+
+    def destroy_preferences(self,*args):
+        self.pref_dialog = None
+        
+    def pyshell(self, *args):
+        import pyshell
+        pyshell.launch(pyshellfile=self.pyshellfile)
+
+    def do_auto_imports(self):
+        i = 1
+        al = gview.get_preference('auto_load_'+str(i))
+        while al is not None:
+            try:
+                exec 'import '+al
+            except:
+                print 'auto_load_'+str(i)+' error: import '+al
+                print sys.exc_info()[0], sys.exc_info()[1]
+            
+            i = i + 1
+            al = gview.get_preference('auto_load_'+str(i))
+
+    def active_layer(self):
+        return self.view_manager.active_view.viewarea.active_layer()
+        
+class Toolbar(gtk.GtkWindow):
+    def __init__(self):
+        gtk.GtkWindow.__init__(self)
+
+        gvhtml.set_help_topic( self, "edittools.html" );
+
+        toolbox = gview.GvToolbox()        
+        toolbox.add_tool("select", gview.GvSelectionTool())
+        toolbox.add_tool("zoompan", gview.GvZoompanTool())
+        toolbox.add_tool("line", gview.GvLineTool())
+        toolbox.add_tool("rect", gview.GvRectTool())
+        toolbox.add_tool("rotate", gview.GvRotateTool())
+        toolbox.add_tool("area", gview.GvAreaTool())
+        toolbox.add_tool("node", gview.GvNodeTool())
+        toolbox.add_tool("point", gview.GvPointTool())
+        toolbox.add_tool("pquery", gview.GvPointTool())
+        self.roi_tool = gview.GvRoiTool()
+        toolbox.add_tool("roi", self.roi_tool)
+        self.poi_tool = gview.GvPoiTool()
+        toolbox.add_tool("poi", self.poi_tool)
+        
+        toolbar = gtk.GtkToolbar(gtk.ORIENTATION_VERTICAL, gtk.TOOLBAR_TEXT)
+        self.add(toolbar)
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, None,
+                                     'Select', 'Selection tool',
+                                     None, None, self.toggle, "select")
+        self.select_button=but         
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Zoom', 'Zoom/Pan mode',
+                                     None, None, self.toggle, "zoompan")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Point Edit', 'Point editing tool',
+                                     None, None, self.toggle, "point")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Point Query', 'Point query tool',
+                                     None, None, self.toggle, "pquery")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw Line', 'Line drawing tool',
+                                     None, None, self.toggle, "line")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Rotate/Resize','Rotate/resize symbol tool',
+                                     None, None, self.toggle, "rotate")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw Rectangle','Rectangle drawing tool',
+                                     None, None, self.toggle, "rect")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw Area', 'Area drawing tool',
+                                     None, None, self.toggle, "area")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Edit Node', 'Node edit tool',
+                                     None, None, self.toggle, "node")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw Labels', 'Label drawing tool',
+                                     None, None, self.toggle, "label")
+
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw ROI', 'ROI drawing tool',
+                                     None, None, self.toggle, "roi")
+        self.roi_button = but
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Choose POI', 'POI selection tool',
+                                     None, None, self.toggle, "poi")
+        self.poi_button = but
+
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_TOGGLEBUTTON, None,
+                                     'Link Views', 'Link views together',
+                                     None, None, self.link)
+
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_TOGGLEBUTTON, None,
+                                     'Cursor', 'Create cursor in all views',
+                                     None, None, self.cursor)
+        self.cursor_button=but
+        
+        toolbox.activate_tool("select")
+        toolbar.show()
+        self.toolbox = toolbox
+        self.toolbar = toolbar
+        self.link = gview.GvViewLink()
+        self.connect('delete-event', self.close)
+
+    def close(self, *args):
+        self.hide()
+        return gtk.TRUE
+        
+    def toggle(self, but, data):
+        button = gtk.GtkRadioButton(_obj=but)
+        if not button.active:
+            return
+
+        # For Point Query Tool:
+        # Make the special point query layer the current layer and if there
+        # isn't one, create it. 
+        if data == "pquery":
+            view = self.toolbox.get_view()
+            if view is not None:
+                layer_list = view.list_layers()
+                result_layer = None
+                for layer in layer_list:
+                    if layer.get_property('pquery') is not None:
+                        result_layer = layer
+
+                if result_layer is None:
+                    result_layer = gview.GvPqueryLayer()
+                    gview.undo_register( result_layer.get_parent() )
+                    result_layer.set_property('pquery','true')
+                    view.add_layer(result_layer)
+
+                view.set_active_layer( result_layer )
+
+        if data == 'label':
+            import gvlabeledit
+            gvlabeledit.launch()
+            data = 'select'
+
+        self.toolbox.activate_tool(data)
+        
+    def link(self, but):
+        but = gtk.GtkToggleButton(_obj=but)
+        if (but.active):
+            self.link.enable()
+        else:
+            self.link.disable()
+
+    def cursor(self, but):
+        but = gtk.GtkToggleButton(_obj=but)
+        if (but.active):
+            if ((gview.get_preference('cursor_type') is not None) and
+            (gview.get_preference('cursor_type') == 'nonadaptive')):
+                self.link.set_cursor_mode(1)
+            else:
+                self.link.set_cursor_mode(2)     
+        else:
+            self.link.set_cursor_mode(0)
+            
+    def add_view(self, view):
+        self.toolbox.activate(view)
+        self.link.register_view(view)
+
+    def get_roi(self):
+        return self.roi_tool.get_rect()
+
+    def get_poi(self):
+        return self.poi_tool.get_point()
+        
+class ViewManager(Signaler):
+
+    def __init__(self):
+        self.layerdlg = None
+        self.toolbar = None
+        self.active_view = None
+        self.view_list = []
+        self.publish( 'active-view-changed' )
+        self.updating = FALSE
+
+    def set_layerdlg(self,layerdlg):
+        self.layerdlg = layerdlg
+        self.layerdlg.subscribe('active-view-changed',self.layerdlg_cb)
+
+    def layerdlg_cb(self,*args):
+        self.set_active_view( self.layerdlg.get_active_view() )
+        
+    def set_toolbar(self,toolbar):
+        self.toolbar = toolbar
+        self.toolbar.toolbox.connect('activate',self.toolbar_cb)
+
+    def toolbar_cb(self,*args):
+        self.set_active_view( self.toolbar.toolbox.get_view() )
+
+    def add_view(self, new_view ):
+        self.updating = TRUE
+        self.view_list.append( new_view )
+        if self.toolbar is not None:
+            self.toolbar.add_view(new_view.viewarea)
+
+        if self.layerdlg is not None:
+            self.layerdlg.add_view(new_view.title, new_view.viewarea)
+
+        new_view.connect('destroy', self.view_closing_cb)
+        
+        self.updating = FALSE
+        self.set_active_view( new_view )
+
+    def view_closing_cb( self, view_window_in, *args ):
+        # lookup original ViewWindow instance with internal variables.
+        view_window = None
+        for v in self.view_list:
+            if v == view_window_in:
+                view_window = v
+
+        if view_window is None:
+            gdal.Debug( "OpenEV",
+                        "unexpectedly missing view in ViewManager" )
+            return
+   
+        self.view_list.remove( view_window )
+                
+        if view_window_in == self.active_view:
+            if len(self.view_list) > 0:
+                self.set_active_view( self.view_list[0] )
+            else:
+                self.set_active_view( None );
+            
+        if self.layerdlg is not None:
+            self.layerdlg.remove_view( view_window.title )
+            
+        if self.toolbar is not None:
+            self.toolbar.toolbox.deactivate( view_window.viewarea )
+        
+
+        
+    def close_all_views( self, *args ):
+        old_len = len(self.view_list)+1
+        while len(self.view_list) < old_len and old_len > 1:
+            old_len = len(self.view_list)
+            # If views have menus, make sure the main app
+            # doesn't keep trying to update their rfls after 
+            # they're gone...
+            if self.view_list[0].menuf is not None:
+                try:
+                    self.view_list[0].app.unsubscribe('rfl-change',
+                         self.view_list[0].show_rfl)
+                except:
+                    pass
+
+            self.view_list[0].destroy()
+
+        if len(self.view_list) > 0:
+            print 'failed to destroy all views.'
+
+    
+    def get_views(self):
+        return self.view_list
+
+    def get_active_view(self):
+        if self.active_view == None:
+            return None
+        else:
+            return self.active_view.viewarea
+    
+    def get_active_view_window(self):
+        return self.active_view
+    
+    def set_active_view(self, new_view):
+        if self.updating:
+            return
+        
+        if new_view == self.active_view:
+            return
+        
+        if self.active_view is not None \
+           and new_view == self.active_view.viewarea:
+            return
+
+        for v in self.view_list:
+            if v.viewarea == new_view:
+                new_view = v
+
+        self.active_view = new_view
+        if new_view is not None and new_view.get_window() is not None:
+            new_view.get_window()._raise()
+
+        self.notify('active-view-changed')
+
+        if self.layerdlg is not None and new_view is not None:
+            self.layerdlg.view_selected( None, new_view.title )
+
+        if self.toolbar is not None and new_view is not None:
+            self.toolbar.toolbox.activate(new_view.viewarea)
+            
+       
+class PrefDialog(gtk.GtkWindow):
+    def __init__(self):
+        import pgufont
+        
+        gtk.GtkWindow.__init__(self)
+        self.set_title('Preferences')
+
+        gvhtml.set_help_topic( self, "preferences.html" );
+        
+        self.default_color = (0.5, 1.0, 0.5, 1.0)
+        
+        self.default_font = pgufont.XLFDFontSpec()
+        self.default_font.set_font_part('Family', 'Arial')
+        self.default_font.set_font_part('Point Size', '120')
+        
+        self.tips = gtk.GtkTooltips()
+
+
+        self.set_border_width(3)
+        self.notebook = gtk.GtkNotebook()
+        self.add( self.notebook )
+
+        self.create_tracking_tool_prefs()
+        self.create_raster_prefs()
+        self.create_cache_prefs()
+        self.create_paths_and_windows_prefs()
+        #self.create_temporaryfile_prefs()
+        
+        self.notebook.append_page(self.page_legend(), 
+                                  gtk.GtkLabel( 'Legend' ))
+        
+        self.show_all()
+
+    def gvplot_cb(self, *args):
+        fname = self.gvplot_tempfile_entry.get_text()
+        gview.set_preference('gvplot_tempfile',fname)
+
+    def create_cache_prefs(self):
+        self.cachep = gtk.GtkVBox(spacing=10)
+        self.cachep.set_border_width(10)
+        self.notebook.append_page(self.cachep, gtk.GtkLabel('Caching'))
+	table = gtk.GtkTable(rows=2, cols=2)
+	table.set_border_width(5)
+	table.set_row_spacings(5)
+	table.set_col_spacings(5)
+	self.cachep.pack_start(table, expand=FALSE)
+
+        # File Cache
+	gdal_cache_label = gtk.GtkLabel('File Cache (bytes):')
+	gdal_cache_label.set_alignment(0, 0.5)
+	table.attach(gdal_cache_label, 0, 1, 0, 1)
+        
+        self.gdal_cache = gtk.GtkEntry(maxlen=9)
+        self.gdal_cache.connect('activate',self.gdal_cb)
+        self.gdal_cache.connect('leave-notify-event',self.gdal_cb)
+	table.attach(self.gdal_cache, 1, 2, 0, 1)
+
+        self.gdal_cache.set_text(str(gdal.GetCacheMax() \
+                                     +gview.raster_cache_get_max()))
+
+        # Texture Cache
+	texture_cache_label = gtk.GtkLabel('GL Texture (bytes):')
+	texture_cache_label.set_alignment(0, 0.5)
+	table.attach(texture_cache_label, 0, 1, 1, 2)
+        
+        self.texture_cache = gtk.GtkEntry(maxlen=9)
+        self.texture_cache.connect('activate',self.tcache_cb)
+        self.texture_cache.connect('leave-notify-event',self.tcache_cb)
+	table.attach(self.texture_cache, 1, 2, 1, 2)
+
+        self.texture_cache.set_text(str(gview.texture_cache_get_max()))
+
+    def create_raster_prefs(self):
+        
+        self.rpp = gtk.GtkVBox(spacing=10)
+        self.rpp.set_border_width(10)
+        self.notebook.append_page( self.rpp, gtk.GtkLabel('Raster'))
+	table = gtk.GtkTable(rows=6, cols=2)
+	table.set_border_width(5)
+	table.set_row_spacings(5)
+	table.set_col_spacings(5)
+	self.rpp.pack_start(table, expand=FALSE)
+
+        # Warp with GCPs
+	gcp_warp_label = gtk.GtkLabel('Display Georeferenced:')
+	gcp_warp_label.set_alignment(0, 0.5)
+	table.attach(gcp_warp_label, 0, 1, 0, 1)
+        
+        self.gcp_warp_om = \
+               gvutils.GvOptionMenu(('Yes','No'), self.set_gcp_warp_mode)
+	table.attach(self.gcp_warp_om, 1, 2, 0, 1)
+
+        if gview.get_preference('gcp_warp_mode') is not None \
+           and gview.get_preference('gcp_warp_mode') == 'no':
+            self.gcp_warp_om.set_history(1)
+            
+        # Sample Method
+	sm_label = gtk.GtkLabel('Overview Sampling:')
+	sm_label.set_alignment(0, 0.5)
+	table.attach(sm_label, 0, 1, 1, 2)
+        
+        self.sm_om = \
+               gvutils.GvOptionMenu(('Decimate','Average'),
+                                    self.set_sample_method)
+	table.attach(self.sm_om, 1, 2, 1, 2)
+
+        if gview.get_preference('default_raster_sample') is not None \
+           and gview.get_preference('default_raster_sample') == 'average':
+            self.sm_om.set_history(1)
+            
+        # Pixel Interpolation
+	im_label = gtk.GtkLabel('Subpixel Interpolation:')
+	im_label.set_alignment(0, 0.5)
+	table.attach(im_label, 0, 1, 2, 3)
+        
+        self.im_om = \
+               gvutils.GvOptionMenu(('Bilinear','Off (Nearest)'),
+                                    self.set_interp_method)
+	table.attach(self.im_om, 1, 2, 2, 3)
+
+        if gview.get_preference('interp_mode') is not None \
+           and gview.get_preference('interp_mode') == 'nearest':
+            self.im_om.set_history(1)
+            
+        # Default Autoscaling Method
+	scale_label = gtk.GtkLabel('Autoscaling Method:')
+	scale_label.set_alignment(0, 0.5)
+	table.attach(scale_label, 0, 1, 3, 4)
+        
+        self.scale_om = \
+               gvutils.GvOptionMenu(('Percent Tail Trim',
+                                     'Standard Deviations'),
+                                    self.set_scaling_method)
+	table.attach(self.scale_om, 1, 2, 3, 4)
+
+        if gview.get_preference('scale_algorithm') is not None \
+           and gview.get_preference('scale_algorithm') == 'std_deviation':
+            self.scale_om.set_history(1)
+            
+        # Tail Trim Percentage.
+	tt_label = gtk.GtkLabel('Tail Trim Percentage:')
+	tt_label.set_alignment(0, 0.5)
+	table.attach(tt_label, 0, 1, 4, 5)
+        
+        self.tt_entry = gtk.GtkEntry(maxlen=9)
+        self.tt_entry.connect('activate',self.tail_trim_cb)
+        self.tt_entry.connect('leave-notify-event',self.tail_trim_cb)
+	table.attach(self.tt_entry, 1, 2, 4, 5)
+
+        tt_val = gview.get_preference('scale_percent_tail')
+        
+        if tt_val is None:
+            tt_val = '0.02'
+
+        self.tt_entry.set_text(str(float(tt_val)*100.0))
+
+        # Scaling Standard Deviations.
+	sd_label = gtk.GtkLabel('Standard Deviations:')
+	sd_label.set_alignment(0, 0.5)
+	table.attach(sd_label, 0, 1, 5, 6)
+        
+        self.sd_entry = gtk.GtkEntry(maxlen=9)
+        self.sd_entry.connect('activate',self.std_dev_cb)
+        self.sd_entry.connect('leave-notify-event',self.std_dev_cb)
+	table.attach(self.sd_entry, 1, 2, 5, 6)
+
+        sd_val = gview.get_preference('scale_std_deviations')
+
+        try:
+            if sd_val is None or float(sd_val) == 0.0:
+                sd_val = '2.5'
+        except:
+            sd_val = '2.5'
+
+        self.sd_entry.set_text(sd_val)
+
+    def create_paths_and_windows_prefs(self):
+        self.pwp = gtk.GtkVBox(spacing=10)
+        self.pwp.set_border_width(10)
+        self.notebook.append_page(self.pwp, gtk.GtkLabel('Program Paths'))
+	table = gtk.GtkTable(rows=3, cols=2)
+	table.set_border_width(5)
+	table.set_row_spacings(5)
+	table.set_col_spacings(5)
+	self.pwp.pack_start(table, expand=FALSE)
+
+        # HTML Browser
+	html_command_label = gtk.GtkLabel('Browser Command:')
+	html_command_label.set_alignment(0, 0.5)
+	table.attach(html_command_label, 0, 1, 0, 1)
+        
+        self.html_command = gtk.GtkEntry()
+        self.html_command.connect('activate',self.html_cb)
+        self.html_command.connect('leave-notify-event',self.html_cb)
+	table.attach(self.html_command, 1, 2, 0, 1)
+
+        if gvhtml.GetBrowseCommand() is not None:
+            self.html_command.set_text(gvhtml.GetBrowseCommand())
+        else:
+            self.html_command.set_text('')
+
+	# Temporary paths
+	gvplot_tempfile_label = gtk.GtkLabel('Plot file (full path):')
+	gvplot_tempfile_label.set_alignment(0, 0.5)
+	table.attach(gvplot_tempfile_label, 0, 1, 1, 2)
+
+        self.gvplot_tempfile_entry = gtk.GtkEntry(maxlen=100)
+        self.gvplot_tempfile_entry.connect('activate',self.gvplot_cb)
+        self.gvplot_tempfile_entry.connect('leave-notify-event',self.gvplot_cb)
+	table.attach(self.gvplot_tempfile_entry, 1, 2, 1, 2)
+
+        if gview.get_preference('gvplot_tempfile') is not None:
+            gvtext = str(gview.get_preference('gvplot_tempfile'))
+            self.gvplot_tempfile_entry.set_text(gvtext) 
+        else:
+            self.gvplot_tempfile_entry.set_text('')
+
+	# Save last visited directory
+	save_recent_dir_label = gtk.GtkLabel('Save last visited directory:')
+	save_recent_dir_label.set_alignment(0, 0.5)
+	table.attach(save_recent_dir_label, 0, 1, 2, 3)
+
+        self.save_recent_dir_om = \
+               gvutils.GvOptionMenu(('Off','On'), self.set_save_recent_dir)
+	table.attach(self.save_recent_dir_om, 1, 2, 2, 3)
+
+        if gview.get_preference('save_recent_directory') is not None \
+           and gview.get_preference('save_recent_directory') == 'off':
+            self.save_recent_dir_om.set_history(1)
+	else:
+	    self.save_recent_dir_om.set_history(0)
+        
+    def html_cb(self, *args):
+        command = self.html_command.get_text()
+        if len(command) > 0 and command[len(command)-1] != ' ':
+            command = command + ' '
+        gvhtml.SetBrowseCommand( command )
+
+    def gdal_cb(self, *args):
+        value = int(self.gdal_cache.get_text())
+        if value < 2000000:
+            self.gdal_cache.set_text(str(gdal.GetCacheMax()
+                                         +gview.raster_cache_get_max()))
+            return 
+
+        if value == gdal.GetCacheMax() + gview.raster_cache_get_max():
+            return
+
+        gdal_cache = int( 900000 + (value - 900000) * 0.25)
+        gvraster_cache = value - gdal_cache
+        
+        gview.set_preference( 'gdal_cache', str(gdal_cache) )
+        gdal.SetCacheMax( gdal_cache )
+        
+        gview.set_preference( 'gvraster_cache', str(gvraster_cache) )
+        gview.raster_cache_set_max(gvraster_cache)
+
+    def tcache_cb(self, *args):
+        value = int(self.texture_cache.get_text())
+        if value > 4000000:
+            gview.set_preference( 'texture_cache', str(value) )
+            gview.texture_cache_set_max(value)
+        else:
+            self.texture_cache.set_text(str(gview.texture_cache_get_max()))
+
+    def create_tracking_tool_prefs(self):
+        self.ttp = gtk.GtkVBox(spacing=10)
+        self.ttp.set_border_width(10)
+        self.notebook.append_page( self.ttp, gtk.GtkLabel('Tracking Tool'))
+	table = gtk.GtkTable(rows=4, cols=2)
+	table.set_border_width(5)
+	table.set_row_spacings(5)
+	table.set_col_spacings(5)
+	self.ttp.pack_start(table, expand=FALSE)
+
+        # Coordinate
+	coord_label = gtk.GtkLabel('Coordinate:')
+	coord_label.set_alignment(0, 0.5)
+	table.attach(coord_label, 0, 1, 0, 1)
+
+        self.coord_om = gvutils.GvOptionMenu(
+            ('Off','Raster Pixel/Line','Georeferenced','Geodetic (lat/long)'),
+            self.set_coordinate_mode)
+	table.attach(self.coord_om, 1, 2, 0, 1)
+
+        if gview.get_preference('_coordinate_mode') is not None:
+            if gview.get_preference('_coordinate_mode') == 'raster':
+                self.coord_om.set_history(1)
+            elif gview.get_preference('_coordinate_mode') == 'georef':
+                self.coord_om.set_history(2)
+            elif gview.get_preference('_coordinate_mode') == 'latlong':
+                self.coord_om.set_history(3)
+            else:
+                self.coord_om.set_history(0)
+        else:
+                self.coord_om.set_history(2)
+
+        # Lat/Long Display format (dms or decimal)
+	degree_mode_label = gtk.GtkLabel('Lat/Long Format:')
+	degree_mode_label.set_alignment(0, 0.5)
+	table.attach(degree_mode_label, 0, 1, 1, 2)
+
+        self.degree_mode_om = gvutils.GvOptionMenu(('ddd:mm:ss.ss', \
+	    'ddd.ddddddd'), self.set_degree_mode)
+	table.attach(self.degree_mode_om, 1, 2, 1, 2)
+
+        if gview.get_preference('_degree_mode') is not None \
+           and gview.get_preference('_degree_mode') == 'decimal':
+            self.degree_mode_om.set_history(1)
+        else:
+            self.degree_mode_om.set_history(0)
+            
+        # Raster Value
+	pixel_mode_label = gtk.GtkLabel('Pixel Value:')
+	pixel_mode_label.set_alignment(0, 0.5)
+	table.attach(pixel_mode_label, 0, 1, 2, 3)
+
+        self.pixel_mode_om = \
+            gvutils.GvOptionMenu(('On','Off'), self.set_pixel_mode)
+	table.attach(self.pixel_mode_om, 1, 2, 2, 3)
+
+        if gview.get_preference('_pixel_mode') is not None \
+           and gview.get_preference('_pixel_mode') == 'off':
+            self.pixel_mode_om.set_history(1)
+        else:
+            self.pixel_mode_om.set_history(0)
+
+        # NODATA mark
+	nodata_mode_label = gtk.GtkLabel('Show NODATA mark:')
+	nodata_mode_label.set_alignment(0, 0.5)
+	table.attach(nodata_mode_label, 0, 1, 3, 4)
+
+        self.nodata_mode_om = \
+            gvutils.GvOptionMenu(('On','Off'), self.set_nodata_mode)
+	table.attach(self.nodata_mode_om, 1, 2, 3, 4)
+
+        if gview.get_preference('_nodata_mode') is not None \
+           and gview.get_preference('_nodata_mode') == 'off':
+            self.nodata_mode_om.set_history(1)
+        else:
+            self.nodata_mode_om.set_history(0)
+
+    def set_coordinate_mode(self, om):
+        if self.coord_om.get_history() == 0:
+            gview.set_preference( '_coordinate_mode', 'off')
+        elif  self.coord_om.get_history() == 1:
+            gview.set_preference( '_coordinate_mode', 'raster')
+        elif  self.coord_om.get_history() == 2:
+            gview.set_preference( '_coordinate_mode', 'georef')
+        elif  self.coord_om.get_history() == 3:
+            gview.set_preference( '_coordinate_mode', 'latlong')
+
+    def set_pixel_mode(self, om):
+        if om.get_history() == 1:
+            gview.set_preference( '_pixel_mode', 'off')
+        else:
+            gview.set_preference( '_pixel_mode', 'on')
+
+    def set_nodata_mode(self, om):
+        if om.get_history() == 1:
+            gview.set_preference( '_nodata_mode', 'off')
+        else:
+            gview.set_preference( '_nodata_mode', 'on')
+
+    def set_degree_mode(self, om):
+        if om.get_history() == 1:
+            gview.set_preference( '_degree_mode', 'decimal')
+        else:
+            gview.set_preference( '_degree_mode', 'dms')
+
+    def set_gcp_warp_mode(self, om):
+        if om.get_history() == 1:
+            gview.set_preference( 'gcp_warp_mode', 'no' )
+        else:
+            gview.set_preference( 'gcp_warp_mode', 'yes' )
+
+    def set_sample_method(self, om):
+        if om.get_history() == 0:
+            gview.set_preference( 'default_raster_sample', 'sample' )
+        else:
+            gview.set_preference( 'default_raster_sample', 'average' )
+
+    def set_interp_method(self, im):
+        if im.get_history() == 0:
+            gview.set_preference( 'interp_mode', 'linear' )
+        else:
+            gview.set_preference( 'interp_mode', 'nearest' )
+
+    def set_scaling_method(self, om):
+        if om.get_history() == 0:
+            gview.set_preference( 'scale_algorithm', 'percent_tail_trim' )
+        else:
+            gview.set_preference( 'scale_algorithm', 'std_deviation' )
+
+    def tail_trim_cb(self,*args):
+        try:
+            gview.set_preference( 'scale_percent_tail',
+                                  str(float(self.tt_entry.get_text())/100.0) )
+        except:
+            pass
+
+    def std_dev_cb(self,*args):
+        try:
+            if float(self.sd_entry.get_text()) > 0.0:
+                gview.set_preference( 'scale_std_deviations',
+                                      self.sd_entry.get_text())
+        except:
+            pass
+    
+    def set_save_recent_dir(self, om):
+        if om.get_history() == 0:
+            gview.set_preference( 'save_recent_directory', 'off')
+        else:
+            gview.set_preference( 'save_recent_directory', 'on')
+
+    def page_legend(self):
+        """
+        properties for the legend dialog
+        """
+        import pgucolor
+        import pgufont
+        
+        vbox = gtk.GtkVBox()
+        table = gtk.GtkTable(rows=1, cols=3)
+        table.set_border_width(6)
+        table.set_row_spacings(6)
+        table.set_col_spacings(6)
+        vbox.pack_start(table)
+
+        # Background color
+        lbl = gtk.GtkLabel('Legend Background Color:')
+        table.attach(lbl, 0, 1, 0, 1,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+                        
+        color = gview.get_preference('legend-background-color', 
+                                     str(self.default_color))
+        
+        cb = pgucolor.ColorButton( pgucolor.color_string_to_tuple( color ) )
+        cb.connect('color-set', self.set_color_preference,
+                        'legend-background-color')
+        table.attach(cb, 1, 2, 0, 1,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(cb,
+                'Click to change the default color for the legend background')
+
+        # Title Font
+        lbl = gtk.GtkLabel('Title Font:')
+        table.attach(lbl, 0, 1, 1, 2,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+
+        color = gview.get_preference('legend-title-font-color',
+                                     self.default_color)
+        cb = pgucolor.ColorButton( pgucolor.color_string_to_tuple( color ) )
+        cb.connect('color-set', self.set_color_preference, 
+                   'legend-title-font-color')
+        table.attach(cb, 1, 2, 1, 2,
+                xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(cb, 
+                'Click to change the default color for the legend font')
+
+        title_font = pgufont.pguFontControl()
+        title_font.subscribe('font-changed', self.set_any_preference,
+                'legend-title-font', title_font.get_font_string)
+        title_font.set_font(gview.get_preference('legend-title-font', 
+                            self.default_font))
+        table.attach(title_font, 2, 3, 1, 2,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(title_font, 'Select a font for the legend title')
+
+
+        # Label Font
+        lbl = gtk.GtkLabel('Label Font:')
+        table.attach(lbl, 0, 1, 2, 3,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        color = gview.get_preference('legend-label-font-color',
+                                     self.default_color)
+        cb = pgucolor.ColorButton( pgucolor.color_string_to_tuple( color ) )
+        cb.connect('color-set', self.set_color_preference, 
+                   'legend-label-font-color')
+        table.attach(cb, 1, 2, 2, 3,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(cb, 
+                'Click to change the default color for the legend font')
+
+        label_font = pgufont.pguFontControl()
+        label_font.subscribe('font-changed', self.set_any_preference,
+                'legend-label-font', label_font.get_font_string)
+        label_font.set_font(gview.get_preference('legend-label-font', 
+                            self.default_font))
+        table.attach(label_font, 2, 3, 2, 3,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(label_font,  'Select a font for legend labels')
+
+        # Sample Size
+
+        lbl = gtk.GtkLabel( 'Legend Samples:')
+        table.attach(lbl, 0, 1, 3, 4,
+                xoptions=gtk.SHRINK, yoptions=gtk.SHRINK)
+
+        lbl = gtk.GtkLabel( 'X Size:')
+        table.attach(lbl, 1, 2, 3, 4,
+                xoptions=gtk.SHRINK, yoptions=gtk.SHRINK)
+
+
+        x = gview.get_preference('legend-sample-x-size', 30)
+        y = gview.get_preference('legend-sample-y-size', 20)
+
+        spin_adjust = gtk.GtkAdjustment(value=float(x), lower=0.0,
+                        upper=50.0, step_incr=1.0)
+        spin = gtk.GtkSpinButton(spin_adjust)
+        spin.set_digits(0)
+        spin.set_usize(75, 0)
+        spin.connect('changed', self.set_spin_preference, 
+                     'legend-sample-x-size')
+        spin.connect('focus-out-event', self.check_spin_preference, 
+                     'legend-sample-x-size')
+        table.attach(spin, 2, 3, 3, 4,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(spin, 'The X Size of a sample on the legend dialog')
+
+        lbl = gtk.GtkLabel( 'Y Size:')
+        table.attach(lbl, 3, 4, 3, 4,
+                xoptions=gtk.SHRINK, yoptions=gtk.SHRINK)
+
+        spin_adjust = gtk.GtkAdjustment(value=float(y), lower=0.0,
+                        upper=50.0, step_incr=1.0)
+        spin = gtk.GtkSpinButton(spin_adjust)
+        spin.set_digits(0)
+        spin.set_usize(75, 0)
+        spin.connect('changed', self.set_spin_preference, 
+                     'legend-sample-y-size')
+        spin.connect('focus-out-event', self.check_spin_preference, 
+                     'legend-sample-y-size')
+        table.attach(spin, 4, 5, 3, 4,
+                        xoptions = gtk.SHRINK, yoptions=gtk.SHRINK)
+        self.tips.set_tip(spin, 'The Y Size of a sample on the legend dialog')
+        return vbox
+        
+    def set_entry_text(self, widget, dlg, entry, pref):
+        directory = dlg.get_directory()
+        entry.set_text(directory)
+        entry.grab_focus()
+        gview.set_preference( pref, directory )
+        dlg.destroy()
+
+    def set_color_preference(self, widget, color, pref):
+        """
+        """
+        gview.set_preference(pref, str(widget.get_color()))
+
+    def set_toggle_preference( self, widget, pref ):
+        """set a preference from a toggle button
+        """
+        gview.set_preference( pref, str(widget.get_active()) )
+
+    def set_any_preference(self, widget, pref, func):
+        """
+        set a preference from the results of a function call
+        """
+        gview.set_preference(pref, str(func()))
+
+    def set_directory_preference(self, widget, event, pref, func):
+        """
+        set a preference for a directory.  Check the value first.
+        """
+        self.current_widget = widget
+
+        value = func()
+        if not os.path.isdir( value ) and value != "":
+            warning( "Invalid Path:\n%s" % value )
+            widget.set_text( gview.get_preference( pref, gview.home_dir)  )
+        else:
+            self.set_any_preference(widget, pref, func)
+
+    def set_menu_preference(self, widget, pref, value):
+        """
+        """
+        gview.set_preference(pref, str(value))
+
+    def set_spin_preference(self, widget, pref):
+        """
+        """
+
+        if widget.get_text() != str(widget.get_value_as_int()):
+            if widget.get_text() == str(round(widget.get_value_as_float(),2)):
+                gview.set_preference( pref, 
+                                str(round(widget.get_value_as_float(),2)))
+            else:
+                try:
+                    i = float( widget.get_text() )
+                except:
+                    i = 0
+                widget.set_value( i )
+        else:
+            gview.set_preference(pref, str(widget.get_value_as_int()))
+
+    def check_spin_preference(self, widget, event, pref):
+        """
+        added to catch unset preferences in spin buttons on focus-out events.
+        """
+        self.set_spin_preference( widget, pref )
+
+        
+
+class Position_3D_Dialog(gtk.GtkWindow):
+    def __init__(self, view_manager):
+        gtk.GtkWindow.__init__(self)
+        self.set_title('3D Position')
+        self.set_border_width(3)
+        self.create_position_dialog()
+        self.create_lookAt_dialog()
+        self.show_all()
+        self.view_manager = view_manager
+
+    def create_position_dialog(self):
+        self.dialog = gtk.GtkVBox(homogeneous=FALSE, spacing=3)
+        self.add(self.dialog)
+        self.dialog.pack_start(gtk.GtkLabel('Current Position:'))
+        
+        # x
+        x_box = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        self.dialog.pack_start(x_box, expand=FALSE)
+
+        x = gtk.GtkLabel('X: ')
+        x_value = gtk.GtkEntry(maxlen=10)
+        x_value.set_text('')
+        x_box.pack_start(x, expand=FALSE)
+        x_box.pack_start(x_value, expand=FALSE)
+        
+        # y
+        y_box = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        self.dialog.pack_start(y_box, expand=FALSE)
+        
+        y = gtk.GtkLabel('Y: ')
+        y_value = gtk.GtkEntry(maxlen=10)
+        y_value.set_text('')
+        y_box.pack_start(y, expand=FALSE)
+        y_box.pack_start(y_value, expand=FALSE)
+
+        # z
+        z_box = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        self.dialog.pack_start(z_box, expand=FALSE)
+        
+        z = gtk.GtkLabel('Z: ')
+        z_value = gtk.GtkEntry(maxlen=10)
+        z_value.set_text('')
+        z_box.pack_start(z, expand=FALSE)
+        z_box.pack_start(z_value, expand=FALSE)
+
+        self.x_value = x_value
+        self.y_value = y_value
+        self.z_value = z_value
+
+        self.x_value.connect('activate', self.set_position_cb)
+        self.x_value.connect('leave-notify-event',self.set_position_cb)
+        self.y_value.connect('activate', self.set_position_cb)
+        self.y_value.connect('leave-notify-event',self.set_position_cb)
+        self.z_value.connect('activate', self.set_position_cb)
+        self.z_value.connect('leave-notify-event',self.set_position_cb)
+        
+
+    def create_lookAt_dialog(self):
+        # Assume create_position_dialog called
+        
+        # Row or x
+        self.dialog.pack_start(gtk.GtkHSeparator())
+        self.dialog.pack_start(gtk.GtkLabel('Looking At Position:'))
+        row_box = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        self.dialog.pack_start(row_box, expand=FALSE)
+
+        row = gtk.GtkLabel('X: ')
+        row_value = gtk.GtkEntry(maxlen=10)
+        row_value.set_text('')
+        row_box.pack_start(row, expand=FALSE)
+        row_box.pack_start(row_value, expand=FALSE)
+        
+        # Column or y
+        col_box = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        self.dialog.pack_start(col_box, expand=FALSE)
+        
+        col = gtk.GtkLabel('Y: ')
+        col_value = gtk.GtkEntry(maxlen=10)
+        col_value.set_text('')
+        col_box.pack_start(col, expand=FALSE)
+        col_box.pack_start(col_value, expand=FALSE)
+        
+        self.row_value = row_value
+        self.col_value = col_value
+
+        self.row_value.connect('activate', self.set_look_at_cb)
+        self.row_value.connect('leave-notify-event',self.set_look_at_cb)
+        self.col_value.connect('activate', self.set_look_at_cb)
+        self.col_value.connect('leave-notify-event',self.set_look_at_cb)
+        
+
+    def update_cb(self, view, *args):
+        # Reset Dialog values
+
+        eye_pos = view.get_eye_pos()
+        if eye_pos:
+            self.x_value.set_text(str(round(eye_pos[0],2)))
+            self.y_value.set_text(str(round(eye_pos[1],2)))
+            self.z_value.set_text(str(round(eye_pos[2],2)))
+        else:
+            self.x_value.set_text('')
+            self.y_value.set_text('')
+            self.z_value.set_text('')
+
+        lookat_pos = view.get_look_at_pos()
+        if lookat_pos:
+            self.row_value.set_text(str(round(lookat_pos[0],2)))
+            self.col_value.set_text(str(round(lookat_pos[1],2)))
+        else:
+            self.row_value.set_text('')
+            self.col_value.set_text('')
+
+    def update_test(self, view, *args):
+        eye_pos = view.get_eye_pos()
+        lookat_pos = view.get_look_at_pos()
+        
+        view.set_3d_view_look_at((eye_pos[0]+300, eye_pos[1], eye_pos[2]) , lookat_pos)
+
+
+
+    def set_look_at_cb(self, *args):
+        view = self.view_manager.get_active_view()
+
+        # get lookat values, except if at horizon, then None
+        try:
+            lookat_pos = (float(self.row_value.get_text()), float(self.col_value.get_text()))
+        except ValueError:
+            lookat_pos = None
+            
+        eye_pos = view.get_eye_pos()
+
+        if lookat_pos:
+            view.set_3d_view_look_at(eye_pos, lookat_pos )
+        
+    def set_position_cb(self, *args):
+        view = self.view_manager.get_active_view()
+
+        lookat_pos = view.get_look_at_pos()
+        eye_pos = ( float(self.x_value.get_text()),
+                    float(self.y_value.get_text()),
+                    float(self.z_value.get_text()))
+
+        # Could be looking at horizon
+        if lookat_pos:
+            view.set_3d_view_look_at(eye_pos, lookat_pos)
+        else:
+            eye_dir = view.get_eye_dir()
+            view.set_3d_view(eye_pos, eye_dir)
+
+        
+
+class Tool_GViewApp:
+    # Abstract base class to derive tools from
+    def __init__(self,app=None):
+        self.app = app
+        self.menu_entries = Tool_GViewAppMenuEntries()
+        self.icon_entries = Tool_GViewAppIconEntries()
+        self.pymenu_entries = Tool_GViewAppMenuEntries()
+        self.pyicon_entries = Tool_GViewAppIconEntries()
+    # def set_menu(self):
+    #    placeholder function- make self.menu_entries.set_entry
+    #    calls here...
+    #    pass
+
+
+class Tool_GViewAppMenuEntries:
+    # Class to store entries to be added to openev's menu
+    def __init__(self):
+        self.entries = {}
+    
+    def set_entry(self,item,position=0,callback=None,accelerator=None):
+        # item = a string describing menu location
+        # position = default location in the menu (integer): Ignored if an
+        #            xml menu entry is specified for the tool.  Note:
+        #            when used, the position refers to position in the
+        #            lowest level menu.  Eg. if a menu entry is
+        #            'File/menu1/entryN', position refer's to entryN's
+        #            position within menu1, not menu1's position in
+        #            File.  For more flexibility, use the xml form of
+        #            configuration.
+        # callback = callback
+        # accelerator = shortcut key
+
+        if (type(item) == type('')):
+            if (type(position) == type(0)):
+                self.entries[item] = (position,callback, accelerator)
+            else:
+                raise AttributeError,"position should be an integer"
+        else:
+            raise AttributeError,"Menu entry item must be a string"
+
+ 
+class Tool_GViewAppIconEntries:
+    # Class to store entries to be added to openev's menu
+    def __init__(self):
+        self.entries = []
+    
+    def set_entry(self,iconfile,hint_text,position=0,callback=None,help_topic=None,label=None,icontype='xpm'):
+        # iconfile=icon filename (xpm case), or some other string not yet defined
+        #          (pixmap/widget case- not yet supported- may never be)
+        # hint_text=tooltip text to use
+        # position = default location in the icon bar (integer)
+        # callback = callback
+        # help topic = html help file (not yet used by anything)
+        # label = some gtk think- not sure what this does
+        # icontype = 'xpm' (later may allow 'pixmap' or 'widget', but not yet)
+
+        if (type(iconfile) == type('')):
+            import os
+            if os.path.isfile(iconfile):
+                fullfilename=iconfile
+            elif os.path.isfile(os.path.join(gview.home_dir,'tools',iconfile)):
+                fullfilename=os.path.join(gview.home_dir,'tools',iconfile)
+            elif os.path.isfile(os.path.join(gview.home_dir,'pics',iconfile)):
+                fullfilename=os.path.join(gview.home_dir,'pics',iconfile)                
+            else:
+                txt = "Cannot find file "+iconfile+'.  Either the full\n'
+                txt = txt+"path must be specified, or "+iconfile+ " must be\n"
+                txt = txt+"placed in the tools or pics directory."
+                raise AttributeError,txt
+
+            # On nt, path separators need to be trapped and doubled to avoid
+            # being interpreted as an escape before special characters.
+            if os.name == 'nt':
+                import string
+                fullfilename=string.replace(fullfilename,"\\","\\\\")
+                
+            if (type(position) == type(0)):
+                self.entries.append((fullfilename,label,hint_text,position,callback,help_topic,icontype))
+            else:
+                raise AttributeError,"position should be an integer"
+        else:
+            txt = "Cannot find file "+iconfile+'.  Either the full\n'
+            txt = txt+"path must be specified, or "+iconfile+ " must be\n"
+            txt = txt+"placed in the tools or pics directory."
+            raise AttributeError,txt

Added: packages/openev/branches/upstream/current/pymod/gvlabeledit.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvlabeledit.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvlabeledit.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,313 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: gvlabeledit.py,v 1.8 2002/12/10 15:49:11 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Label Edit Tool.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvlabeledit.py,v $
+#  Revision 1.8  2002/12/10 15:49:11  gmwalter
+#  Avoid checking "tool_name" on None object.
+#
+#  Revision 1.7  2001/08/23 16:52:15  pgs
+#  added default_ogrfs to __init__ to override built in default
+#
+#  Revision 1.6  2001/08/07 18:17:25  pgs
+#  pick up default color if avail and added\nmissing signal in close()
+#
+#  Revision 1.5  2001/05/05 18:26:40  pgs
+#  added optional interactive editing
+#
+#  Revision 1.4  2001/05/04 19:03:40  pgs
+#  split code for new label into separate method to support starting labels from another location
+#
+#  Revision 1.3  2001/05/04 02:58:31  pgs
+#  some bug fixes
+#
+#  Revision 1.2  2001/04/24 14:24:46  warmerda
+#  added indirect text application, various cleanup
+#
+#  Revision 1.1  2001/04/23 21:20:28  warmerda
+#  New
+#
+#
+
+from gtk import *
+import gview
+import string
+import gvselbrowser
+import pgucolorsel
+import gvutils
+import gvogrfs
+import gvogrfsgui
+import pgufont
+
+def launch(interactive=FALSE):
+    try:
+        gview.gvlabeledit.get_window()._raise()
+    except:
+        gview.gvlabeledit = GvLabelEdit(interactive = interactive)
+
+    return gview.gvlabeledit
+
+
+class GvLabelEdit(GtkWindow):
+
+    def __init__(self, interactive=FALSE, default_ogrfs = None):
+        GtkWindow.__init__(self)
+        self.set_title('Label Edit Tool')
+        gview.app.sel_manager.subscribe( 'active-layer-changed',
+                                         self.gui_update )
+        gview.app.sel_manager.subscribe( 'selection-changed',
+                                         self.gui_update )
+        gview.app.sel_manager.subscribe( 'subselection-changed',
+                                         self.gui_update )
+
+        if default_ogrfs is None:
+            font = pgufont.XLFDFontSpec()
+            default_font = gview.get_preference('label-font')
+            if default_font is None:
+                font.set_font_part('Family', 'fixed')
+            else:
+                font.parse_font_spec(default_font)
+
+            color = gview.get_preference('label-color')
+
+            if color is None:
+                color = "#88FF88"
+            else:
+                try:
+                    color = string.replace( color, "(", "" )
+                    color = string.replace( color, ")", "" )
+                    r, g, b, a = string.split( color, "," )
+                    r = float(r)
+                    g = float(g)
+                    b = float(b)
+                    a = float(a)
+                    color = ( r, g, b, a )
+                    color = gvogrfs.gv_to_ogr_color( color )
+                except:
+                    color = "#88FF88"
+            default_ogrfs = 'LABEL(t:"",f:"%s",c:%s)' % (font, color)
+
+
+        self.default_ogrfs = default_ogrfs
+        self.selected_shape = None
+        self.layer = None
+        self.interactive = interactive
+        self.create_gui()
+        self.show()
+
+        self.edit_mode = 0
+
+        self.view = gview.app.view_manager.get_active_view()
+        self.key_sig = self.view.connect('key-press-event', self.key_press_cb)
+        self.connect('delete-event', self.close)
+
+        self.gui_update()
+
+    def close(self, window, event):
+        gview.app.sel_manager.unsubscribe( 'selection-changed',
+                                           self.gui_update )
+        gview.app.sel_manager.unsubscribe( 'subselection-changed',
+                                           self.gui_update )
+        gview.app.sel_manager.unsubscribe( 'active-layer-changed',
+                                           self.gui_update )
+        #remember to disconnect from the view's key press event
+        self.view.disconnect(self.key_sig)
+
+    def create_gui(self):
+        box1 = GtkVBox(spacing=10)
+        box1.set_border_width(10)
+        self.add(box1)
+        box1.show()
+
+        #######################################################################
+        # Create a control box for the text related widgets.
+        self.text_frame = GtkFrame('Text Style')
+        box1.pack_start(self.text_frame,expand=FALSE)
+
+        self.label_style = gvogrfsgui.GvLabelStyle(text_entry = TRUE,
+                                                   label_field = FALSE,
+                                                   interactive = self.interactive)
+        self.label_style.subscribe('ogrfs-changed', self.label_ogrfs_cb)
+        self.label_style.subscribe('apply-text-to-field',
+                                   self.text_apply_cb)
+        self.text_frame.add(self.label_style)
+        self.text_frame.show()
+
+    def set_default_ogrfs(self, prototype):
+        if prototype is None or len(prototype) == 0:
+            return
+
+        try:
+            ogrfs_obj = gvogrfs.OGRFeatureStylePart()
+            ogrfs_obj.parse( prototype )
+        except:
+            return
+
+        text_parm = gvogrfs.OGRFeatureStyleParam()
+        text_parm.parse('t:""')
+        ogrfs_obj.set_parm( text_parm )
+
+        self.default_ogrfs = ogrfs_obj.unparse()
+
+    def text_apply_cb(self, widget, field, value ):
+        try:
+            self.layer = gview.app.sel_manager.get_active_layer()
+            shapes = self.layer.get_parent()
+            self.selected_shape = self.layer.get_subselected()
+            shape_obj = shapes[self.selected_shape]
+        except:
+            self.selected_shape = -1
+            shape_obj = None
+
+        if shape_obj is None:
+            return
+
+        shape_obj = shape_obj.copy()
+        shape_obj.set_property( field, value )
+        shapes[self.selected_shape] = shape_obj
+
+        self.gui_update()
+
+    def label_ogrfs_cb(self, *args):
+        self.edit_mode = 0
+
+        try:
+            shape_obj = (self.layer.get_parent())[self.selected_shape]
+        except:
+            shape_obj = None
+
+        if shape_obj is None:
+            return
+
+        if self.label_style.ogrfs_obj is None:
+            ogrfs = ''
+        else:
+            ogrfs = self.label_style.ogrfs_obj.unparse()
+            self.set_default_ogrfs( ogrfs )
+
+        style = gvogrfs.OGRFeatureStyle()
+        try:
+            #the shape may or may not have a property already.
+            style.parse(shape_obj._gv_ogrfs)
+        except:
+            style.parse(self.layer.get_property('_gv_ogrfs'))
+
+        style.parse_part(ogrfs)
+
+        shape_obj = shape_obj.copy()
+        shape_obj._gv_ogrfs = style.unparse()
+        (self.layer.get_parent())[self.selected_shape] = shape_obj
+
+    def gui_update(self,*args):
+        self.edit_mode = 0
+
+        try:
+            self.layer = gview.app.sel_manager.get_active_layer()
+            shapes = self.layer.get_parent()
+            self.selected_shape = self.layer.get_subselected()
+            shape_obj = shapes[self.selected_shape]
+        except:
+            self.selected_shape = -1
+            shape_obj = None
+
+        if shape_obj is None:
+            # add stuff to grey out interface.
+            self.label_style.set_sensitive(FALSE)
+            return
+        else:
+            self.label_style.set_sensitive(TRUE)
+
+        try:
+            ogrfs = shape_obj._gv_ogrfs
+        except:
+            ogrfs = None
+
+        change_enabled = 1
+        if ogrfs is None:
+            ogrfs = self.layer.get_property('_gv_ogrfs')
+            if ogrfs is not None:
+                change_enabled = 0
+
+        # Parse the ogrfs.
+        try:
+            ogrfs_obj = gvogrfs.OGRFeatureStyle()
+            ogrfs_obj.parse( ogrfs )
+            label_obj = ogrfs_obj.get_part('LABEL')
+            if label_obj is not None:
+                self.set_default_ogrfs( label_obj.unparse() )
+        except:
+            ogrfs_obj = None
+
+        # Display type specific information.
+        if ((ogrfs_obj is None) or
+            ((label_obj is not None) and label_obj.tool_name == 'LABEL')):
+            self.label_style.set_ogrfs( label_obj, layer = self.layer,
+                                        shape_obj = shape_obj )
+            self.label_style.show()
+
+    def key_press_cb(self, view, event, *args):
+        if self.edit_mode:
+            self.label_style.text_input(event.keyval)
+            if event.keyval == GDK.Return:
+                self.edit_mode = 0
+            return TRUE
+
+        #######################################################################
+        # We only want to respond to <ENTER> keystrokes.
+        if event.keyval != GDK.Return:
+            return
+
+        if self.layer is None:
+            print 'gvlabeledit: no layer'
+            return
+
+        self.view = gview.app.view_manager.get_active_view()
+        self.new_label()
+
+
+    def new_label(self):
+        #######################################################################
+        # Create a new point shape at the current pointer location.
+
+        pointer_loc = self.view.get_pointer()
+        new_label = gview.GvShape(type=gview.GVSHAPE_POINT)
+        new_label.set_node(pointer_loc[0], pointer_loc[1])
+        new_label._gv_ogrfs = self.default_ogrfs
+
+        #######################################################################
+        # Attach it to the current layer, and make it selected.
+
+        shapes = self.layer.get_parent()
+        id = shapes.append(new_label)
+        self.layer.clear_selection()
+        self.layer.select_shape(id)
+
+        self.edit_mode = 1
+
+        self.get_window()._raise()
+        self.label_style.text_entry.grab_focus()
+
+

Added: packages/openev/branches/upstream/current/pymod/gvlegenddlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvlegenddlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvlegenddlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,424 @@
+###############################################################################
+# $Id: gvlegenddlg.py,v 1.21 2004/01/20 19:45:51 gmwalter Exp $
+#
+# Project:  CIETMap / OpenEV
+# Purpose:  Implement Legend Display Dialog
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Frank Warmerdam <warmerda at home.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvlegenddlg.py,v $
+#  Revision 1.21  2004/01/20 19:45:51  gmwalter
+#  Get a default font if preferred one can't
+#  be loaded, fix window title.
+#
+#  Revision 1.20  2002/09/10 15:43:54  pgs
+#  updated for new gvclassification (removed deprecated calls)
+#
+#  Revision 1.19  2002/08/28 18:04:26  pgs
+#  removed sys.stdout.flush() to prevent problems on windows when running
+#  with no console.
+#
+#  Revision 1.18  2002/08/13 17:43:31  pgs
+#  fixed raster classification and fixed legend for point layers
+#
+#  Revision 1.17  2002/08/01 20:11:45  warmerda
+#  minor changes for vector capable classifications
+#
+#  Revision 1.16  2001/07/06 17:02:53  warmerda
+#  Use Pixel Size instead of Point Size for defaults
+#
+#  Revision 1.15  2001/07/01 03:05:34  pgs
+#  modified resize logic
+#
+#  Revision 1.14  2001/05/25 16:22:09  pgs
+#  resize legend to accomodate all classes
+#
+#  Revision 1.13  2001/05/14 15:40:05  pgs
+#  handle multi-line titles
+#
+#  Revision 1.12  2001/05/12 16:15:22  pgs
+#  made sample size configurable
+#
+#  Revision 1.11  2001/05/08 17:03:33  pgs
+#  fixed bugs in font handling
+#
+#  Revision 1.10  2001/04/14 14:53:38  pgs
+#  added ability to control legend title and label font, color and size
+#
+#  Revision 1.9  2001/03/21 05:35:45  warmerda
+#  don't print messages in view_state_cb
+#
+#  Revision 1.8  2001/02/20 20:40:21  pgs
+#  fixed error from changing gvclassification
+#
+#  Revision 1.7  2000/10/19 16:44:42  warmerda
+#  made changes to organize the legend into columns to fit better
+#
+#  Revision 1.6  2000/10/17 19:00:51  pgs
+#  fixed legend refresh problem
+#
+#  Revision 1.5  2000/10/09 18:52:30  pgs
+#  modified show_legend to return the legend just opened and added find_legend to find
+#  a legend attached to a raster (for printing).
+#
+#  Revision 1.4  2000/10/06 16:48:56  warmerda
+#  added GvViewArea background color
+#
+#  Revision 1.3  2000/10/06 15:58:19  warmerda
+#  ensure only one legend per layer, and destroy when layer is destroyed
+#
+#  Revision 1.2  2000/09/27 14:35:27  warmerda
+#  added auto update of legend, and classification serial numbers
+#
+#  Revision 1.1  2000/09/21 03:02:01  warmerda
+#  New
+#
+#
+
+from gtk import *
+import gview
+import gvclassification
+from gvogrfs import gv_to_ogr_color
+from pgucolor import color_string_to_tuple
+import pgufont
+import string
+import sys
+import gdal
+
+legend_dialogs = []
+
+def show_legend( layer ):
+
+    for ld in legend_dialogs:
+        if ld.layer._o == layer._o:
+            ld.show()
+            ld.get_window()._raise()
+            return ld
+
+    ld = GvLegendDialog()
+    ld.set_raster( layer )
+    legend_dialogs.append( ld )
+    return ld
+
+def find_legend( layer ):
+
+    for ld in legend_dialogs:
+        if ld.layer._o == layer._o:
+            return ld
+    return None
+
+#
+# It was my goal to have the GvLegendDialog use the GvLegendView to
+# implement the legend, and that the GvLegendView would manage a legend
+# at some assign region of an existing GvViewArea so that legend could
+# eventually be embedded in other views.  I haven't gotten around to that
+# yet, but it is still my eventual goal.
+#
+class GvLegendView:
+    def __init__(self):
+        pass
+
+class GvLegendDialog(GtkWindow):
+
+    def __init__(self):
+        GtkWindow.__init__(self)
+
+        self.layer = None
+        self.teardown_id = None
+        self.changed_id = None
+        self.resizing = FALSE
+        self.resize_count = 0
+        
+        self.set_title('Legend: Empty')
+        self.set_policy(TRUE,TRUE,FALSE)
+        self.set_usize(300, 300)
+        self.viewarea = gview.GvViewArea()
+        back_color = gview.get_preference('legend-background-color')
+        if back_color is None:
+            back_color = (1.0, 1.0, 1.0, 1.0 )
+        else:
+            back_color = color_string_to_tuple(back_color)
+
+        self.viewarea.set_background_color( back_color )
+        self.shapes = gview.GvShapes()
+        self.vlayer = gview.GvShapesLayer(self.shapes)
+        self.viewarea.add_layer( self.vlayer )
+        self.add( self.viewarea )
+
+        self.connect( 'delete-event', self.close )
+        self.show_all()
+
+        self.viewarea.fit_extents(0,
+                                  self.viewarea.get_height(),
+                                  self.viewarea.get_width(),
+                                  -self.viewarea.get_height() )
+
+        self.changing_view_state = 0
+        self.viewarea.connect('view-state-changed', self.view_state_cb )
+
+    def set_raster(self, layer ):
+        self.layer = layer
+
+        self.changed_id = self.layer.connect('changed',
+                                              self.check_for_legend_change_cb )
+        self.teardown_id = self.layer.connect('teardown', self.close )
+
+        self.classification = gvclassification.GvClassification()
+        self.classification.add_layer( layer )
+
+        self.prepare_legend()
+
+    def prepare_legend(self):
+        
+        if self.resizing:
+            return
+      
+        if self.layer.get_property('Class_sn') is not None:
+            self.Class_sn = int(self.layer.get_property('Class_sn'))
+
+        #remove any existing shapes
+        self.shapes.delete_shapes(range(len(self.shapes)))
+
+        samp_x_size = gview.get_preference('legend-sample-x-size')
+        if samp_x_size is None:
+            samp_x_size = 20
+        else:
+            samp_x_size = int(samp_x_size)
+
+        samp_y_size = gview.get_preference('legend-sample-y-size')
+        if samp_y_size is None:
+            samp_y_size = 20
+        else:
+            samp_y_size = int(samp_y_size)
+
+
+        title_font = pgufont.XLFDFontSpec()
+        font_spec = gview.get_preference('legend-title-font')
+        if font_spec is None:
+            title_font.set_font_part('Family', 'times')
+            title_font.set_font_part('Pixel Size', '20')
+        else:
+            title_font.parse_font_spec(font_spec)
+
+
+        title_font_color = gview.get_preference('legend-title-font-color')
+        if title_font_color is None:
+            title_font_color =  (0.0, 0.0, 0.0, 1.0)
+        else:
+            title_font_color = color_string_to_tuple(title_font_color)
+        title_ogr_color = gv_to_ogr_color(title_font_color)
+
+        label_font = pgufont.XLFDFontSpec()
+        font_spec = gview.get_preference('legend-label-font')
+        if font_spec is None:
+            label_font.set_font_part('Family', 'times')
+            label_font.set_font_part('Pixel Size', '14')
+        else:
+            label_font.parse_font_spec(font_spec)
+
+        label_font_color = gview.get_preference('legend-label-font-color')
+        if label_font_color is None:
+            label_font_color = (0.0, 0.0, 0.0, 1.0)
+        else:
+            label_font_color = color_string_to_tuple(label_font_color)
+        label_ogr_color = gv_to_ogr_color(label_font_color)
+
+        #handle large fonts in the sample text
+        try:
+            gdk_font = load_font(str(label_font))
+        except:
+            # get a default font if preferred one
+            # can't be loaded.
+            gdk_font = load_font('*')
+            
+        h = gdk_font.height("Wj")
+        samp_offset = max(samp_y_size, h) + 10
+
+        #handle multi-line text for the title.
+        try:
+            gdk_title_font = load_font(str(title_font))
+        except:
+            gdk_title_font = load_font('*')
+
+        lines = string.split(self.classification.title, '\\n')
+
+        x_offset = 10  #start title 10 pixels in from left edge
+        col_offset = 30 #space columns apart
+        y_offset = 35  #start title 35 pixels down from top edge
+        title_width = 0
+        max_height = 0
+
+        #resize the window appropriately
+
+        title_height = y_offset
+        title_width = 0
+        for idx in range(len(lines)):
+            line = lines[idx]
+            title_height = title_height + gdk_title_font.height(line)
+            title_width = max(title_width, gdk_title_font.width(line))
+
+        title_height = title_height + 10
+        title_width = x_offset + title_width + 10
+
+        cols = int (self.classification.count / 8)
+        samps = min( 8, self.classification.count )
+
+        samp_height = samps * (samp_offset) + 10
+        samp_width = x_offset
+
+        for i in range( cols + 1):
+            idx = 8 * i
+            col_width = 0
+            while idx < self.classification.count and idx < 8 * (i + 1):
+                name = self.classification.name[idx]
+                width = samp_x_size + 20 + gdk_font.width(name)
+                col_width = max(col_width, width)
+                idx = idx + 1
+            samp_width = samp_width + col_width + col_offset
+        samp_width = samp_width + 10
+
+        total_width = max(title_width, samp_width)
+        total_height = title_height + samp_height
+
+        self.resizing = TRUE
+
+        if (self.get_window().width < total_width) or \
+           (self.get_window().height < total_height):
+            self.resize_count = self.resize_count + 1
+            if self.resize_count < 2:
+                self.set_usize(total_width, total_height)
+
+        self.resizing = FALSE
+
+        for idx in range(len(lines)):
+            line = lines[idx]
+            w = gdk_title_font.width(line)
+            h = gdk_title_font.height(line)
+            title_width = max(title_width, w)
+
+            samp_text = gview.GvShape()
+            samp_text.add_node( x_offset, y_offset )
+            samp_text.set_property( '_gv_ogrfs',
+                                'LABEL(c:' + title_ogr_color + \
+                                ',f:"' + str(title_font) + '",' \
+                                + 't:"' + line + '")' )
+
+            self.shapes.append(samp_text)
+            y_offset = y_offset + h
+
+        if ((len(lines[0]) > 6) and (lines[0][:6] != 'Legend') and
+            (lines[0][:6] != 'legend')):
+            self.set_title('Legend: ' + lines[0] + '...')
+        else:
+            self.set_title(lines[0] + '...')
+            
+        y_offset = y_offset + 10
+        title_offset = y_offset
+
+        max_width = 0
+        max_height = 0
+
+        for class_id in range(self.classification.count):
+            color = self.classification.get_color( class_id )
+            symbol = self.classification.get_symbol( class_id )
+            scale = self.classification.get_scale( class_id )
+            if symbol is not None:
+                samp = gview.GvShape( type = gview.GVSHAPE_POINT )
+                samp.add_node( x_offset + (samp_x_size/2), 
+                               y_offset + (samp_y_size/2) )
+                ogrfs_color = '#%02x%02x%02x%02x' % (int(color[0] * 255.999),
+                                                 int(color[1] * 255.999),
+                                                 int(color[2] * 255.999),
+                                                 int(color[3] * 255.999))
+                ogrfs = "SYMBOL(id:%s,c:%s,s:%s)" % (symbol, ogrfs_color, 
+                                              scale)
+                samp.set_property( "_gv_ogrfs", ogrfs )
+            else:
+                samp = gview.GvShape( type = gview.GVSHAPE_AREA )
+                samp.add_node( x_offset, y_offset )
+                samp.add_node( x_offset+samp_x_size, y_offset )
+                samp.add_node( x_offset+samp_x_size, y_offset+samp_y_size )
+                samp.add_node( x_offset, y_offset+samp_y_size )
+                samp.add_node( x_offset, y_offset )
+
+                color = '%f %f %f %f' % color
+
+                samp.set_property( '_gv_color', color )
+                samp.set_property( '_gv_fill_color', color )
+
+            self.shapes.append( samp )
+
+            name = self.classification.name[class_id]
+            samp_text = gview.GvShape()
+            samp_text.add_node( x_offset+samp_x_size+10, y_offset + 17 )
+            font = str(label_font)
+            samp_text.set_property( '_gv_ogrfs',
+                      'LABEL(c:' + label_ogr_color + \
+                      ',f:"'+font+'",t:"'+name+'")'  )
+            self.shapes.append( samp_text )
+
+            this_width = samp_x_size + 20 + gdk_font.width(name)
+            if max_width < this_width:
+                max_width = this_width
+
+            y_offset = y_offset + samp_offset
+
+            if y_offset+samp_offset > self.viewarea.get_height():
+                max_height = max(max_height, y_offset + samp_offset)
+                y_offset = title_offset
+                x_offset = x_offset + col_offset + max_width
+                max_width = 0
+
+        self.vlayer.changed()
+
+    def check_for_legend_change_cb(self,*args):
+        if self.layer.get_property('Class_sn') is not None:
+            if int(self.layer.get_property('Class_sn')) == self.Class_sn:
+                return
+        self.classification.remove_layer( self.layer )
+        self.classification.add_layer( self.layer )
+        self.prepare_legend()
+
+    def view_state_cb( self, *args ):
+        if self.changing_view_state != 0:
+            return
+
+        self.changing_view_state = 1
+        self.viewarea.fit_extents(0,
+                                  self.viewarea.get_height(),
+                                  self.viewarea.get_width(),
+                                  -self.viewarea.get_height() )
+        self.prepare_legend()
+        self.changing_view_state = 0
+
+    def close( self, *args ):
+        if self.teardown_id is not None:
+            self.layer.disconnect(self.teardown_id )
+            self.layer.disconnect(self.changed_id )
+
+        try:
+            legend_dialogs.remove(self)
+        except:
+            print 'GvLegendDialog.remove failed.'
+
+        self.layer = None
+        self.destroy()
+

Added: packages/openev/branches/upstream/current/pymod/gvmaptools.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvmaptools.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvmaptools.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,754 @@
+#! /usr/bin/env python
+##############################################################################
+# $Id: gvmaptools.py,v 1.1 2004/01/07 20:41:48 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Grid layers, north arrows, etc.
+# Author:   Gillian Walter <gillian.walter at atlantis-scientific.com>
+#
+# Developed by Atlantis Scientific Inc. (www.atlantis-scientific.com) for
+# DRDC Ottawa
+#
+###############################################################################
+# Copyright (c) Her majesty the Queen in right of Canada as represented
+# by the Minister of National Defence, 2003.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the 
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvmaptools.py,v $
+#  Revision 1.1  2004/01/07 20:41:48  gmwalter
+#  Initial check-in of plotting and
+#  annotation layer modules developed
+#  for DRDC Ottawa.
+#
+#
+
+import gview
+import Numeric
+import gtk
+import os
+import gvogrfs
+import string
+
+#########################################################################
+# Grid/graticule layers                                                 #
+#########################################################################
+
+def SimpleReferenceGrid(min_x,min_y,max_x,max_y,x_divisions,y_divisions,
+                        color=(0.5,1.0,0.5,1.0),xoff=-0.15,yoff=-0.04,
+                        label_type=None,shapes_name="Grid"):
+    """ Create a reference grid for an unprojected raster.
+        min_x, min_y- minimum lat/longs, in decimal degrees
+        max_x, max_y- minimum lat/longs, in decimal degrees
+        x_divisions- number of divisions in horizontal direction
+        y_divisions- number of divisions in vertical direction
+        xoff- horizontal offset of vertical labels, as a fraction
+              of max_x-min_x.  Offset is relative to min_x.
+        yoff- vertical offset of horizontal labels, as a fraction
+              of max_y-min_y.  Offset is relative to min_y.
+        color- start color for the grid
+        label_type- not used yet; might be later for formatting.
+
+    """
+
+    shps=gview.GvShapes(name=shapes_name)
+    gview.undo_register( shps )
+    shps.add_field('position','string',20)
+
+    if os.name == 'nt':
+        font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+    else:
+        #font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+        #font="-urw-helvetica-medium-r-normal-*-9-*-*-*-p-*-iso8859-2"
+        font="-adobe-helvetica-medium-r-normal-*-8-*-*-*-p-*-iso10646-1"
+        #font="-misc-fixed-medium-r-*-*-9-*-*-*-*-*-*-*"
+
+    
+    lxoff=(max_x-min_x)*xoff  # horizontal label placement
+    lyoff=(max_y-min_y)*yoff # vertical label placement
+   
+    hspc=(max_x-min_x)/x_divisions
+    vspc=(max_y-min_y)/y_divisions
+
+    for hval in Numeric.arange(min_x,max_x+hspc/100.0,hspc):
+        nshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        nshp.set_node(hval,max_y,0,0)
+        nshp.set_node(hval,min_y,0,1)
+        shps.append(nshp)
+        pshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        pshp.set_node(hval,min_y+lyoff)
+        pshp.set_property('position',"%.1f" % hval)
+        shps.append(pshp)
+                          
+    for vval in Numeric.arange(min_y,max_y+vspc/100.0,vspc):
+        nshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        nshp.set_node(min_x,vval,0,0)
+        nshp.set_node(max_x,vval,0,1)
+        shps.append(nshp)
+        pshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        pshp.set_node(min_x+lxoff,vval)
+        pshp.set_property('position',"%.1f" % vval)
+        shps.append(pshp)
+
+    cstr=gvogrfs.gv_to_ogr_color(color)
+    if len(cstr) < 9:
+        cstr=cstr+"FF"
+    clstr=str(color[0])+' '+str(color[1])+' '+str(color[2])+' '+str(color[3])
+    
+    layer=gview.GvShapesLayer(shps)
+    layer.set_property('_line_color',clstr)
+    layer.set_property('_point_color',clstr)
+    # Set antialias property so that lines look nice
+    # when rotated.
+    layer.set_property('_gl_antialias','1')
+    layer.set_property('_gv_ogrfs_point',
+                       'LABEL(t:{position},f:"'+font+'",c:'+cstr+')')
+    layer.set_read_only(gtk.TRUE)    
+    
+    return layer
+
+    
+def SimpleMeasuredGrid(min_x,min_y,max_x,max_y,x_spacing,y_spacing,
+                        color=(0.5,1.0,0.5,1.0),xoff=-0.14,yoff=1.04,
+                        label_type=None,shapes_name="Grid"):
+    """ Create a reference grid for a utm-projected raster.
+        min_x, min_y, max_x, max_y- extents that grid should cover
+        x_spacing- line spacing in horizontal direction
+        y_spacing- line spacing in vertical direction
+        xoff- horizontal offset of vertical labels, as a fraction
+              of max_x-min_x.  Offset is relative to min_x.
+        yoff- vertical offset of horizontal labels, as a fraction
+              of max_y-min_y.  Offset is relative to min_y.
+        color- start color for the grid
+        label_type- not used yet; might be later for formatting.
+        shapes_name- name to give the shapes forming the layer.
+        
+    """
+
+    shps=gview.GvShapes(name=shapes_name)
+    gview.undo_register( shps )
+    shps.add_field('position','string',20)
+
+    if os.name == 'nt':
+        font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+    else:
+        #font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+        #font="-urw-helvetica-medium-r-normal-*-9-*-*-*-p-*-iso8859-2"
+        font="-adobe-helvetica-medium-r-normal-*-8-*-*-*-p-*-iso10646-1"
+        #font="-misc-fixed-medium-r-*-*-9-*-*-*-*-*-*-*"
+     
+    
+    # Round to nearest integer space
+    max_x=min_x+Numeric.floor((max_x-min_x)/x_spacing)*x_spacing
+    max_y=min_y+Numeric.floor((max_y-min_y)/y_spacing)*y_spacing
+
+    lxoff=(max_x-min_x)*xoff  # horizontal label placement
+    lyoff=(max_y-min_y)*yoff # vertical label placement
+    
+    for hval in Numeric.arange(min_x,
+                               max_x+x_spacing/100.0,
+                               x_spacing):
+        nshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        nshp.set_node(hval,max_y,0,0)
+        nshp.set_node(hval,min_y,0,1)
+        shps.append(nshp)
+        pshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        pshp.set_node(hval,min_y+lyoff)
+        pshp.set_property('position',"%d" % int(hval+0.5))
+        shps.append(pshp)
+                
+    for vval in Numeric.arange(min_y,
+                               max_y+y_spacing/100.0,
+                               y_spacing):
+        nshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        nshp.set_node(min_x,vval,0,0)
+        nshp.set_node(max_x,vval,0,1)
+        shps.append(nshp)
+        pshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        pshp.set_node(min_x+lxoff,vval)
+        pshp.set_property('position',"%d" % int(vval+0.5))
+        shps.append(pshp)
+
+    cstr=gvogrfs.gv_to_ogr_color(color)
+    if len(cstr) < 9:
+        cstr=cstr+"FF"
+    clstr=str(color[0])+' '+str(color[1])+' '+str(color[2])+' '+str(color[3])
+    
+    layer=gview.GvShapesLayer(shps)
+    layer.set_property('_line_color',clstr)
+    layer.set_property('_point_color',clstr)
+    # Set antialias property so that lines look nice
+    # when rotated.
+    layer.set_property('_gl_antialias','1')
+    layer.set_property('_gv_ogrfs_point',
+                       'LABEL(t:{position},f:"'+font+'",c:'+cstr+')')
+    layer.set_read_only(gtk.TRUE)    
+    
+    return layer
+
+
+def SimpleLatLongGrid(min_x,min_y,max_x,max_y,hdeg,hmin,hsec,vdeg,vmin,vsec,
+                        color=(0.5,1.0,0.5,1.0),xoff=-0.18,yoff=1.04,
+                        label_type=None,shapes_name="Grid"):
+    """ Create a reference graticule.
+        min_x, min_y- minimum lat/longs, in decimal degrees
+        max_x, max_y- minimum lat/longs, in decimal degrees
+        hdeg/hmin/hsec- horizontal spacing (degrees/min/sec)
+        vdeg/vmin/vsec- vertical spacing (degrees/min/sec)
+
+        decimal degrees=degrees+(minutes/60.0)+(seconds/3600.0)
+
+        xoff- horizontal offset of vertical labels, as a fraction
+              of max_x-min_x.  Offset is relative to min_x.
+        yoff- vertical offset of horizontal labels, as a fraction
+              of max_y-min_y.  Offset is relative to min_y.
+              
+        color- start color for the grid
+        label_type- not used yet; might be later for formatting.
+
+        extents may be shifted slightly to generate 'nice' labels.
+
+        Note that the min_x, min_y, max_x, max_y extents include
+        a border 5% in from each side, and room for labels.        
+    """
+
+    shps=gview.GvShapes(name=shapes_name)
+    gview.undo_register( shps )
+    shps.add_field('position','string',20)
+
+    if os.name == 'nt':
+        font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+    else:
+        #font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+        #font="-urw-helvetica-medium-r-normal-*-9-*-*-*-p-*-iso8859-2"
+        font="-adobe-helvetica-medium-r-normal-*-8-*-*-*-p-*-iso10646-1"
+        #font="-misc-fixed-medium-r-*-*-9-*-*-*-*-*-*-*"
+
+    x_spacing=float(hdeg)+(float(hmin)+(float(hsec)/60.0))/60.0
+    y_spacing=float(vdeg)+(float(vmin)+(float(vsec)/60.0))/60.0
+
+
+    # Round to nearest integer space
+    max_x=min_x+Numeric.floor((max_x-min_x)/x_spacing)*x_spacing
+    max_y=min_y+Numeric.floor((max_y-min_y)/y_spacing)*y_spacing
+
+    lxoff=(max_x-min_x)*xoff  # horizontal label placement
+    lyoff=(max_y-min_y)*yoff # vertical label placement
+    
+    for hval in Numeric.arange(min_x,
+                               max_x+x_spacing/100.0,
+                               x_spacing):
+        nshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        nshp.set_node(hval,max_y,0,0)
+        nshp.set_node(hval,min_y,0,1)
+        shps.append(nshp)
+        pshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        pshp.set_node(hval,min_y+lyoff)
+        hstr=GetLatLongString(hval,'longitude')
+        pshp.set_property('position',hstr)
+        shps.append(pshp)
+        
+    for vval in Numeric.arange(min_y,
+                               max_y+y_spacing/100.0,
+                               y_spacing):
+        nshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        nshp.set_node(min_x,vval,0,0)
+        nshp.set_node(max_x,vval,0,1)
+        shps.append(nshp)
+        pshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        pshp.set_node(min_x+lxoff,vval)
+        vstr=GetLatLongString(vval,'latitude')
+        pshp.set_property('position',vstr)
+        shps.append(pshp)
+
+    cstr=gvogrfs.gv_to_ogr_color(color)
+    if len(cstr) < 9:
+        cstr=cstr+"FF"
+    clstr=str(color[0])+' '+str(color[1])+' '+str(color[2])+' '+str(color[3])
+    
+    layer=gview.GvShapesLayer(shps)
+    layer.set_property('_line_color',clstr)
+    layer.set_property('_point_color',clstr)
+    # Set antialias property so that lines look nice
+    # when rotated.
+    layer.set_property('_gl_antialias','1')
+    layer.set_property('_gv_ogrfs_point',
+                       'LABEL(t:{position},f:"'+font+'",c:'+cstr+')')
+    layer.set_read_only(gtk.TRUE)
+
+    return layer
+
+#########################################################################
+# North Arrow layers                                                    #
+#########################################################################
+
+if os.name == "nt":
+    GVNORTHSYM1 = "\\North1"
+else:
+    GVNORTHSYM1 = "/North1"
+
+def CreateNorthSymbol(ntype=GVNORTHSYM1,color1=(0.0,0.0,0.0,1.0),
+                      color2=(1.0,1.0,1.0,1.0),scale=1.0,symbol_manager=None):
+    """ Create the North Symbol and put it in a symbol manager.
+        Input:
+            ntype- type of north arrow to create
+            color1- first color
+            color2- second color (if needed)
+            scale- amount to scale size by.
+            symbol_manager- symbol manager to inject symbol into (a
+                            new one will be created if this is set
+                            to None).
+    """
+
+    if symbol_manager is None:
+        sm=gview.GvSymbolManager()
+    else:
+        sm=symbol_manager
+        
+    cstr1=gvogrfs.gv_to_ogr_color(color1)
+    if len(cstr1) < 9:
+        cstr1=cstr1+"FF"
+        
+    cstr2=gvogrfs.gv_to_ogr_color(color2)
+    if len(cstr2) < 9:
+        cstr2=cstr2+"FF"
+
+    sstr=string.replace(str(scale),'.','_')
+    
+    refname=ntype+cstr1[1:]+cstr2[1:]+sstr
+    if ntype==GVNORTHSYM1:   
+        shape=gview.GvShape(type=gview.GVSHAPE_AREA)
+        shape.set_node(1.0*scale,-2.6*scale,node=0)
+        shape.set_node(0.0,-0.8*scale,node=1)
+        shape.set_node(-1.0*scale,-2.6*scale,node=2)
+        shape.set_node(0.0,2.6*scale,node=3)
+        shape.set_node(1.0*scale,-2.6*scale,node=4)
+        shape.set_property('_gv_ogrfs','PEN(c:'+cstr1+');BRUSH(c:'+\
+                           cstr2+')')
+        sm.inject_vector_symbol(refname,shape)
+
+    return (refname,sm)
+
+def CreateNorthSymbols(color1=(0.0,0.0,0.0,1.0),color2=(1.0,1.0,1.0,1.0),
+                       scale=1.0,symbol_manager=None):
+    """ Create North symbols of all types using two specified colors,
+        and inject them into a symbol manager.
+        Input:
+            color1- first color
+            color2- second color (if needed)
+            scale- amount to scale size by.
+            symbol_manager- symbol manager to inject symbols into (a
+                            new one will be created if this is set
+                            to None).
+    """
+
+    if symbol_manager is None:
+        sm=gview.GvSymbolManager()
+    else:
+        sm=symbol_manager
+
+    refnames=[]
+    for item in [GVNORTHSYM1]:
+        rname,junk=CreateNorthSymbol(color1,color2,scale,sm)
+        refnames.append(rname)
+
+    return (refnames,sm)
+        
+
+def SimpleNorthLayer(xoffset,yoffset,ntype=GVNORTHSYM1,
+                        color1=(0.0,0.0,0.0,1.0),
+                        color2=(1.0,1.0,1.0,1.0),
+                        scale=1.0,
+                     shapes_name="North Arrow"):
+    """ Create a layer with a North arrow symbol,
+        with the North arrow located at (xoffset,yoffset).
+        The 'ntype' parameter will eventually be used for
+        different types of north arrows.  The layer
+        will contain two shapes: an area or line, and
+        label.
+        Input:
+            xoffset,yoffset- where to center North Arrow (in
+                             display coordinates).
+
+            ntype- index of north arrow type (currently only one type).
+            color1- First color and outline color for north arrow. A tuple
+                    of 4 values between 0 and 1.
+            color2- Second color (not used yet).
+            scale- amount to scale size of symbol by.
+            shapes_name- name to give the shapes that form
+                         the north arrow.
+    """
+    
+    shps=gview.GvShapes(name=shapes_name)
+    gview.undo_register( shps )
+    
+    nshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+    nshp.set_node(xoffset,yoffset)
+
+    cstr1=gvogrfs.gv_to_ogr_color(color1)
+    if len(cstr1) < 9:
+        cstr1=cstr1+"FF"
+        
+    refname,sm=CreateNorthSymbol(ntype,color1,color2,scale)
+    dxstr=str(-1.5*scale)
+    dystr=str(-15.0*scale)
+    nshp.set_property('_gv_ogrfs',
+                      'SYMBOL(c:'+cstr1+',s:4,id:"'+refname+'");'+\
+                      'LABEL(c:'+cstr1+',t:"N",dx:'+dxstr+\
+                      ',dy:'+dystr+')' )
+    shps.append(nshp)
+    
+    layer=gview.GvShapesLayer(shps)
+    # Set antialias property so that lines look nice
+    # when rotated.
+    layer.set_property('_gl_antialias','1')
+    
+    return layer
+
+#############################################################
+# Scale bar layers                                          #
+#############################################################
+
+# Types of scale bars
+GVSCALE1=0
+
+def SimpleScalebarLayer(xoffset,yoffset,dwidth,swidth,
+                        angle,units_label=None,stype=GVSCALE1,
+                        color1=(0.0,0.0,0.0,1.0),
+                        color2=(1.0,1.0,1.0,1.0),
+                        offset=-0.2,
+                        shapes_name="Scale Bar"):
+    """ Create a layer with a Scale bar located at
+        stretching from dmin to dmax on the display
+        Input:
+            xoffset,yoffset- where to center scale bar (in
+                             display coordinates)
+
+            dwidth- width of scale bar in display coordinates
+            swidth- width of scale bar in scale coordinates
+                    (same if scale units and geocoding units
+                    are the same; different if for example
+                    display is UTM- meters- and scale bar is
+                    in km)
+            angle- angle of scale bar relative to display
+                   (in RADIANS)
+            units_label- label for units (left out if None)
+            stype- index of scale bar type (currently must be 0)
+            color1- First color and outline color for scale bar. A tuple
+                    of 4 values between 0 and 1.
+            color2- Second color (only used in alternating scale bars)
+            offset- Vertical offset of labels for scale bar as a
+                    fraction of scale bar width.
+            shapes_name- name to give the shapes that form
+                         the scalebar.
+    """
+    
+    shps=gview.GvShapes(name=shapes_name)
+    gview.undo_register( shps )
+    shps.add_field('label','string',20)
+
+    if os.name == 'nt':
+        font="-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-*-*"
+    else:
+        #font="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+        #font="-urw-helvetica-medium-r-normal-*-9-*-*-*-p-*-iso8859-2"
+        font="-adobe-helvetica-medium-r-normal-*-8-*-*-*-p-*-iso10646-1"
+        #font="-misc-fixed-medium-r-*-*-9-*-*-*-*-*-*-*"
+
+    sc=dwidth/swidth
+    svals,labels=GetScaleBlocks(swidth)
+
+    cstr1=str(color1[0])+' '+str(color1[1])+' '+str(color1[2])+\
+           ' '+str(color1[3])
+    cstr2=str(color2[0])+' '+str(color2[1])+' '+str(color2[2])+\
+           ' '+str(color2[3])
+    
+    if stype == GVSCALE1:
+        # Rectangle with alternating filled/unfilled
+        # sections.
+        smax=svals[len(svals)-1]
+
+        # rectangle nodes before rotation
+        hbr=(svals-(smax/2.0))*sc # horizontal- shift to -smax/2:+smax/2
+                                  # so that rectangle is centered about 0.
+        # rectangle extends from -smax/20 (bbr) to +smax/20 (tbr) vertically,
+        # labels are placed at + or - smax/5
+        tbr=smax/20.0*Numeric.ones(Numeric.shape(hbr))
+        bbr=-1*smax/20.0*Numeric.ones(Numeric.shape(hbr))
+        lbr=offset*smax*Numeric.ones(Numeric.shape(hbr))
+
+        # units label location before rotation
+        uxbr=(hbr[len(hbr)-1]-hbr[0])*0.05+hbr[len(hbr)-1]
+
+        # rotate
+        ctheta=Numeric.cos(angle)
+        stheta=Numeric.sin(angle)
+        tx=hbr*ctheta-tbr*stheta + xoffset
+        ty=hbr*stheta+tbr*ctheta + yoffset
+        bx=hbr*ctheta-bbr*stheta + xoffset
+        by=hbr*stheta+bbr*ctheta + yoffset
+        lx=hbr*ctheta-lbr*stheta + xoffset
+        ly=hbr*stheta+lbr*ctheta + yoffset
+        ux=uxbr*ctheta + xoffset
+        uy=uxbr*stheta + yoffset
+        
+        # LATER: once shape collections are working, use them instead
+        # so that entire scale bar can be selected and shifted as
+        # a whole rather than separate shapes...
+        
+        #shp=gview.GvShape(type=gview.GVSHAPE_COLLECTION)
+        #shp.set_property('_gv_ogrfs_point',
+        #               'LABEL(t:{label},f:"%s",c:#000000FF)' % font)
+        #shps.append(shp)
+        for idx in range(len(tx)-1):
+            nshp=gview.GvShape(type=gview.GVSHAPE_AREA)
+            nshp.add_node(tx[idx],ty[idx],0)
+            nshp.add_node(tx[idx+1],ty[idx+1],0)
+            nshp.add_node(bx[idx+1],by[idx+1],0)
+            nshp.add_node(bx[idx],by[idx],0)
+            nshp.add_node(tx[idx],ty[idx],0)
+            if idx % 2:
+                nshp.set_property('_gv_color',cstr1)
+                nshp.set_property('_gv_fill_color',cstr2)
+            else:
+                nshp.set_property('_gv_color',cstr1)
+                nshp.set_property('_gv_fill_color',cstr1)
+                
+            shps.append(nshp)
+      
+        for idx in range(len(lx)):      
+            if labels[idx] is not None:
+                lshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+                lshp.set_node(lx[idx],ly[idx])
+                lshp.set_property('label',labels[idx])
+                shps.append(lshp)
+                
+        if units_label is not None:
+            lshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+            lshp.set_node(ux,uy)
+            lshp.set_property('label',units_label)
+            shps.append(lshp)
+            
+    layer=gview.GvShapesLayer(shps)
+    # Set antialias property so that lines look nice
+    # when rotated.
+    layer.set_property('_gl_antialias','1')
+    cstr3=gvogrfs.gv_to_ogr_color(color1)
+    if len(cstr3) < 9:
+        cstr3=cstr3+"FF"
+        
+    layer.set_property('_gv_ogrfs_point',
+                       'LABEL(t:{label},f:"%s",c:%s)' % (font,cstr3))
+    return layer
+
+def GetScaleBlocks(width):
+    """ Get 'nice' scale bar block sizes and start/end
+        values.
+        Input:
+            width- geocoded width
+            
+        Output:
+            values- geocoded values at divisions (array)
+            labels- labels for divisions
+    """
+
+    rord=Numeric.log10(abs(width)/2.0)
+    nrord=rord % 1
+        
+    if nrord < Numeric.log10(2):
+        spc=0.2*pow(10,Numeric.floor(rord))
+        smallspc=spc
+        bigspc=5*spc
+        newspc=[0,smallspc,smallspc*2,smallspc*3,smallspc*4,smallspc*5]
+    elif nrord < Numeric.log10(5):
+        spc=0.5*pow(10,Numeric.floor(rord))
+        smallspc=spc
+        bigspc=5*spc
+        newspc=[0,smallspc,smallspc*2,smallspc*3,smallspc*4]
+    else:
+        spc=pow(10,Numeric.floor(rord))
+        smallspc=spc
+        bigspc=spc*5
+        newspc=[0,smallspc,smallspc*2,smallspc*3,smallspc*4,smallspc*5]
+
+    if len(newspc) == 5:
+        #labels=['0',None,"%g" % smallspc*2,None,"%g" % (smallspc*4)]
+        labels=['0',None,None,None,"%g" % (smallspc*4)]
+    else:
+        labels=['0',None,None,None,None,"%g" % (smallspc*5)]
+        
+    temp_max=newspc[len(newspc)-1]
+    start=temp_max
+    for temp in Numeric.arange(start,width-bigspc/2,bigspc):
+        temp_max=temp_max+bigspc
+        newspc.append(temp_max)
+        labels.append("%g" % temp_max)
+
+    #start=temp_max
+    #for temp in Numeric.arange(start,width-smallspc/2,smallspc):
+    #    labels.append(None)
+    #    temp_max=temp_max+smallspc 
+    #    newspc.append(temp_max)       
+                              
+    return (Numeric.array(newspc,Numeric.Float32),labels)
+
+
+#############################################################
+# Utility functions                                         #
+#############################################################
+
+# "Nice" function logic:
+#
+# spacings: restrict to three significant digits; if
+#           a third one is present, it must be 5.
+#
+# min/max values: there should be an integer number of
+# "spacings" between min and max values.  min and max
+# values may each be rounded by up to tolerance*spacing
+# or tolerance*(max-min)/divisions in either direction
+# in order to be "nicer".  Tolerance defaults to 0.5.
+#
+# "Nice" numbers have fewer significant digits
+# (eg. 3000 is "nicer" than 3013), and having 5 as
+# the last significant digit is "nicer" than having
+# 1,2,3,4,6,7,8,9.
+#
+# TO DO: Create "nice" functions, and use them in the grids.
+
+def GetNiceExtentsByDivisions(minval,maxval,divisions,tolerance):
+    """ Try to find nice default extents based on 
+        approximate min/max values and a number of divisions.
+
+        Input:
+            minval- minimum value
+            maxval- maximum value
+            divisions- number of divisions
+            tolerance- minval and maxval may each be altered by
+                       up to tolerance*(maxval-minval)/divisions
+
+        Output:
+            newmin- new minimum value
+            newmax- new maximum value
+            spacing- spacing
+    """
+    pass
+
+def GetNiceExtentsBySpacing(minval,maxval,spacing,tolerance):
+    """ Try to find nice default extents based on 
+        approximate min/max values and a spacing.
+        If the spacing is itself not a nice value,
+        it will also be somewhat adjusted.
+
+        Input:
+            minval- minimum value
+            maxval- maximum value
+            spacing- space between values
+            tolerance- minval and maxval may each be altered by
+                       up to tolerance*spacing
+
+
+        Output:
+            newmin- new minimum value
+            newmax- new maximum value
+            newspacing- new spacing
+    """
+    pass
+
+
+def GetLatLongString(ddvalue,lltype='latitude'):
+    """ Convert a decimal degree value to a
+        string appropriate for Lat/Long
+        display.
+
+        ddvalue- position in decimal degrees
+        lltype- latitude or longitude
+
+        returns: lat/long string
+    """
+    import Numeric
+    
+    deg=int(abs(ddvalue))
+    min=int((abs(ddvalue)-deg)*60)
+    sec=int((abs(ddvalue)-deg-(float(min)/60.0))*3600.0)
+    if lltype == 'latitude':
+        if Numeric.sign(ddvalue) == -1:
+            ch='S'
+        else:
+            ch='N'
+    else:
+        if Numeric.sign(ddvalue) == -1:
+            ch='W'
+        else:
+            ch='E'
+
+    nstr="%dd%d'%.1f''%s" % (deg,min,sec,ch)
+    return nstr
+
+
+def GetLatLongSpacings(min_ddvalue,max_ddvalue,approx_divs):
+    """ Get spacing values for latitude or longitude
+        min/max values.
+
+        Input:
+            min_ddvalue- minimum extent (decimal degrees)
+            max_ddvalue- maximum extent (decimal degrees)
+            approx_divs- An approximate number of divisions.
+
+        Output:
+            (degspc,minspc,secspc)
+            degspc- degree spacing (integer)
+            minspc- minute spacing (integer)
+            secspc- second spacing (float)
+    """
+
+    diff=max_ddvalue-min_ddvalue
+    degspc=int(diff/approx_divs)
+    minspc=int((diff-float(degspc*approx_divs))*60.0/approx_divs)
+    sdiff=diff-float(degspc*approx_divs)-(float(minspc*approx_divs)/60.0)
+
+    # second spacing is rounded down to the nearest 0.1
+    secspc=float("%.1f" % (sdiff*3600.0/approx_divs))
+    if secspc > (sdiff*3600.0/approx_divs):
+        secspc=secspc-0.1
+        
+    return (degspc,minspc,secspc)
+
+def GetAlphabeticGridString(index):
+    """ Get string for reference grid horizontal
+        index (1='A',2='B',...,27='AA',...)
+    """
+    # This function doesn't work yet.
+    alphabet=['A','B','C','D','E','F','G','H','I','J','K',
+              'L','M','N','O','P','Q','R','S','T','U','V',
+              'W','X','Y','Z']
+    alen=len(alphabet)
+    sc=Numeric.log(alen)
+    nletters=int(Numeric.log(index)/sc)
+    str=''
+    rem=index
+    for idx in range(nletters,-1,-1):
+        rint=int(rem/pow(alen,idx-1))
+        rem=rem-rint*pow(alen,idx-1)
+        print 'rint ',rint,' rem: ',rem
+        if (rint == 0) and (len(str) == 0):
+            continue
+        if rem == 0:
+            str=str+alphabet[0]
+        else:    
+            str=str+alphabet[rint-1]
+        
+    return str

Added: packages/openev/branches/upstream/current/pymod/gvmodule.c
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvmodule.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvmodule.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4825 @@
+/******************************************************************************
+ * $Id: gvmodule.c,v 1.120 2005/10/17 19:19:15 gmwalter Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Hand generated python bindings for OpenEV C functions.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: gvmodule.c,v $
+ * Revision 1.120  2005/10/17 19:19:15  gmwalter
+ * Wrap gvshape delete_ring function.
+ *
+ * Revision 1.119  2005/09/12 15:33:10  gmwalter
+ * Update autopan tool for line paths.
+ *
+ * Revision 1.118  2005/08/30 13:00:05  andrey_kiselev
+ * Signedness fixes in LUT related code.
+ *
+ * Revision 1.117  2005/04/25 05:37:01  warmerda
+ * progress callback should now be CPL_STDCALL if defined
+ *
+ * Revision 1.116  2005/02/22 13:22:39  gmwalter
+ * Add autopan tool.
+ *
+ * Revision 1.115  2005/01/14 16:52:02  gmwalter
+ * Checked in Aude's gv_shapes_add_shape_last function
+ * (allows shapes to be added without repeating
+ * indices if others have been deleted).
+ *
+ * Revision 1.114  2005/01/04 18:50:33  gmwalter
+ * Checked in Aude's new gvshape function changes.
+ *
+ * Revision 1.113  2004/09/28 19:45:22  warmerda
+ * slight change in PyProxy code to avoid uninit data reference
+ *
+ * Revision 1.112  2004/08/18 20:55:21  pgs
+ * added ability to return columns of data in a python dictionary
+ *
+ * Revision 1.111  2004/06/23 14:35:17  gmwalter
+ * Added support for multi-band complex imagery.
+ *
+ * Revision 1.110  2004/05/12 10:00:27  dem
+ *
+ * Fix a bug in _wrap_gv_raster_layer_get_mesh_lod : this wrapper created
+ * a python double from a C int and then the mesh_lod was wrong (example 223.454).
+ * The wrapper now returns an int.
+ * This bug crashed OpenEV when for example we save a project, then reload it :
+ * if the wrong mesh is big on a lot of images of the project, this uses a lot of
+ * memory and done an allocation error...
+ * Note that all projects saved until this fix have wrong mesh_lods.
+ *
+ * Revision 1.109  2004/02/12 22:36:08  gmwalter
+ * Add functions for easily creating a line from three
+ * lists of nodes (x,y,z), avoiding python-level for
+ * loop.
+ *
+ * Revision 1.108  2004/02/10 15:48:56  andrey_kiselev
+ * Added wrapper for gv_manager_add_dataset() function.
+ *
+ * Revision 1.107  2004/01/22 20:47:43  andrey_kiselev
+ * Added wrappers for gv_raster_layer_nodata_set(), gv_raster_layer_nodata_get()
+ * and gv_raster_layer_type_get().
+ *
+ * Revision 1.106  2003/09/02 17:29:10  warmerda
+ * added get_names() method
+ *
+ * Revision 1.105  2003/08/29 20:52:43  warmerda
+ * added to/from xml translation for GvShape
+ *
+ * Revision 1.104  2003/08/23 04:02:39  warmerda
+ * added gv_records_recode
+ *
+ * Revision 1.103  2003/08/20 20:04:18  warmerda
+ * Added collection methods on GvShape
+ *
+ * Revision 1.102  2003/08/08 18:10:49  warmerda
+ * added gv_ciet.c
+ *
+ * Revision 1.101  2003/08/06 22:23:14  warmerda
+ * added progress monitor to gv_records load/save funcs
+ *
+ * Revision 1.100  2003/08/06 17:17:53  warmerda
+ * gv_records_to_dbf() now supports passing in a list of selected items to write.
+ * gv_records_get_typed_properties() and gv_records_get_properties() will no
+ * longer put NULL properties in the dictionary.
+ *
+ * Revision 1.99  2003/08/05 15:25:22  warmerda
+ * fixed formatting for integer fields when fetching from GvRecords
+ *
+ * Revision 1.98  2003/07/27 05:00:47  warmerda
+ * fleshed out gvrecords support
+ *
+ * Revision 1.97  2003/05/23 16:18:18  warmerda
+ * added GvRecords for CIETMap
+ *
+ * Revision 1.96  2003/04/08 18:11:40  andrey_kiselev
+ * FAdded missed return value in _wrap_gv_symbol_manager_save_vector_symbol()
+ *
+ * Revision 1.95  2003/04/08 11:58:33  andrey_kiselev
+ * Added wrapper for gv_symbol_manager_save_vector_symbol() function.
+ *
+ * Revision 1.94  2003/04/02 15:46:59  pgs
+ * added wrapper for gv_format_point_query
+ *
+ * Revision 1.93  2003/03/07 22:24:47  warmerda
+ * added example MyGDALOperator for Diana
+ *
+ * Revision 1.92  2003/03/02 17:06:24  warmerda
+ * new symbolmanager support
+ *
+ * Revision 1.91  2003/02/20 19:27:20  gmwalter
+ * Updated link tool to include Diana's ghost cursor code, and added functions
+ * to allow the cursor and link mechanism to use different gcps
+ * than the display for georeferencing.  Updated raster properties
+ * dialog for multi-band case.  Added some signals to layerdlg.py and
+ * oeattedit.py to make it easier for tools to interact with them.
+ * A few random bug fixes.
+ *
+ * Revision 1.90  2003/01/06 21:39:50  warmerda
+ * added gv_shapes_from_ogr_layer
+ *
+ * Revision 1.89  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.88  2002/09/11 20:40:50  warmerda
+ * fixed so that 3d views can be saved
+ *
+ * Revision 1.87  2002/07/29 21:01:31  warmerda
+ * return missing properties as None
+ *
+ * Revision 1.86  2002/07/24 20:33:22  warmerda
+ * added gv_shape_get_property
+ *
+ * Revision 1.85  2002/07/18 19:43:53  warmerda
+ * added gv_shapes_get_typed_properties
+ *
+ * Revision 1.84  2002/07/18 19:34:40  pgs
+ * added wrapper for gv_shapes_to_dbf
+ *
+ * Revision 1.83  2002/07/16 14:17:06  warmerda
+ * added support for getting background color
+ *
+ * Revision 1.82  2002/03/07 18:31:56  warmerda
+ * added preliminary gv_shape_clip_to_rect() implementation
+ *
+ * Revision 1.81  2002/02/28 18:52:22  gmwalter
+ * Added a point-of-interest tool similar to the region-of-interest
+ * tool (allows a user to select a temporary point without having to add a
+ * new layer).  Added a mechanism to allow some customization of openev
+ * via a textfile defining external modules.
+ *
+ * Revision 1.80  2002/01/18 05:48:13  warmerda
+ * added GvShapes.get_extents() method in python
+ *
+ * Revision 1.79  2001/12/08 04:49:39  warmerda
+ * added point in polygon test
+ *
+ * Revision 1.78  2001/11/28 19:18:30  warmerda
+ * Added set_gcps(), and get_gcps() methods on GvRaster, and the
+ * geotransform-changed signal generated when the gcps change.
+ *
+ * Revision 1.77  2001/11/07 15:20:38  warmerda
+ * fixed other similar memory leaks
+ *
+ * Revision 1.76  2001/11/07 14:55:44  warmerda
+ * fixed serious memory leak in gv_shape_get_properties() implementation
+ *
+ * Revision 1.75  2001/10/16 18:52:14  warmerda
+ * added autoscale and histogram methods
+ *
+ * Revision 1.74  2001/09/17 15:31:23  pgs
+ * removed extraneous initialization in _wrap_gtk_color_well_get_d
+ *
+ * Revision 1.73  2001/09/17 03:44:14  pgs
+ * removed extra declaration from _wrap_gtk_color_well_get_d
+ *
+ * Revision 1.72  2001/09/16 04:40:55  warmerda
+ * removed extra Py_INCREF call
+ *
+ * Revision 1.71  2001/09/16 03:29:10  pgs
+ * added gtk_color_well_get_d binding.
+ *
+ * Revision 1.70  2001/09/14 14:22:01  warmerda
+ * added GtkColorWell bindings
+ *
+ * Revision 1.69  2001/08/14 17:03:24  warmerda
+ * added standard deviation autoscaling support
+ *
+ * Revision 1.68  2001/08/08 17:46:52  warmerda
+ * added GvShape reference counting support
+ *
+ * Revision 1.67  2001/07/24 02:59:25  warmerda
+ * added force_load method on GvRaster
+ *
+ * Revision 1.66  2001/07/13 22:13:35  warmerda
+ * added function to get height from mesh
+ *
+ * Revision 1.65  2001/07/09 20:40:48  warmerda
+ * added skirt prototype
+ *
+ * Revision 1.64  2001/04/23 18:51:51  warmerda
+ * added set_properties() and __delattr__ to GvShape
+ *
+ * Revision 1.63  2001/04/22 17:35:25  pgs
+ * added get_short_path_name and changed wid_interpolate to a variable exponent for d
+ *
+ * Revision 1.62  2001/04/02 18:10:46  warmerda
+ * expose gv_raster_autoscale() to python
+ *
+ * Revision 1.61  2001/03/29 14:59:56  warmerda
+ * added fill_short flag to control handling of slivers
+ *
+ * Revision 1.60  2001/03/29 04:49:36  warmerda
+ * added gv_view_area_get_volume access
+ *
+ * Revision 1.59  2001/03/21 04:32:04  warmerda
+ * fixed out-of-range __getitem__ on GvShape
+ *
+ * Revision 1.58  2001/01/30 15:18:01  warmerda
+ * added queue_task() access from python
+ *
+ * Revision 1.57  2000/10/06 16:48:56  warmerda
+ * added GvViewArea background color
+ *
+ * Revision 1.56  2000/09/29 16:09:20  srawlin
+ * added Goto function requring fuction to map lat/long to view coordinates
+ *
+ * Revision 1.55  2000/09/29 04:27:58  warmerda
+ * fix type of nodata arguments
+ *
+ * Revision 1.54  2000/09/29 00:59:46  warmerda
+ * take care to return None from GvShapes.__getitem__ on deleted shapes
+ *
+ * Revision 1.53  2000/09/21 03:01:09  warmerda
+ * added gv_data_set_properties
+ *
+ * Revision 1.52  2000/09/15 15:12:45  warmerda
+ * added gv_shape_destroy
+ *
+ * Revision 1.51  2000/09/15 01:30:02  warmerda
+ * added gv_raster_rasterize_shapes cover
+ *
+ * Revision 1.50  2000/09/12 19:19:33  warmerda
+ * added WIDInterpolate
+ *
+ * Revision 1.49  2000/08/25 20:14:31  warmerda
+ * added appcurlayer, and raster layer nodata support
+ *
+ * Revision 1.48  2000/07/31 21:15:50  srawlin
+ * added functions in GvRaster and GvShapes to convert a C change_info struct into a Python tuple
+ *
+ * Revision 1.47  2000/07/27 20:06:23  warmerda
+ * added boundary constraints
+ *
+ * Revision 1.46  2000/07/25 17:04:49  warmerda
+ * Fixed bug with reference leak in gv_view_area_list_layers().
+ *
+ * Revision 1.45  2000/07/24 14:39:19  warmerda
+ * added GvRasterLayer pixel_to_view, view_to_pixel methods
+ *
+ * Revision 1.44  2000/07/20 19:21:24  warmerda
+ * fixed delete_shapes bug for Ahmed
+ *
+ * Revision 1.43  2000/07/13 19:17:12  warmerda
+ * added gv_shape_copy, gv_shapes_replace_shapes
+ *
+ * Revision 1.41  2000/07/11 20:54:59  srawlin
+ * added GvViewArea methods to get and set viewing direction relative to z-plane in 3D
+ *
+ * Revision 1.40  2000/07/11 18:24:38  warmerda
+ * added shape deletion
+ *
+ * Revision 1.39  2000/07/03 20:59:05  warmerda
+ * added some 3d view methods
+ *
+ * Revision 1.38  2000/06/30 18:05:19  srawlin
+ * added ability to set ROI constraints
+ *
+ * Revision 1.37  2000/06/29 16:13:28  srawlin
+ * added ROI Tool creation function
+ *
+ * Revision 1.36  2000/06/26 15:14:22  warmerda
+ * added GvManager dataset support
+ *
+ * Revision 1.35  2000/06/23 12:57:32  warmerda
+ * added GvRasterSource support
+ *
+ * Revision 1.34  2000/06/20 13:39:06  warmerda
+ * added standard headers
+ *
+ */
+
+#include "gview.h"
+#include "gvutils.h"
+#include "invdistance.h"
+#include "gvrasterize.h"
+#include "gtkcolorwell.h"
+#include "cpl_conv.h"
+#include "cpl_string.h"
+#include "gvtypes.h"         // define GV_USE_DOUBLE_PRECISION_COORD
+
+#ifndef CPL_STDCALL
+#define CPL_STDCALL
+#endif
+
+// SD select float or double
+#ifdef GV_USE_DOUBLE_PRECISION_COORD
+#
+#   define Ccast   "d"
+#   define CC      "dd"
+#   define CCC     "ddd"
+#   define CCCC    "dddd"
+#   define CCCCCC  "dddddd"
+#
+#else
+#
+#   define Ccast   "f"
+#   define CC      "ff"
+#   define CCC     "fff"
+#   define CCCC    "ffff"
+#   define CCCCCC  "ffffff"
+#
+#endif
+
+
+
+GvLayer *gv_build_skirt( GvRasterLayer *, double base_z );
+
+#ifdef WIN32
+#  include <pygtk.h>
+#else
+#  include <pygtk/pygtk.h>
+#endif
+
+
+/*
+ * This is a function for extracting a raw pointer from a SWIG pointer
+ * string.
+ */
+static
+void *SWIG_SimpleGetPtr(char *_c, char *_t)
+{
+  unsigned long _p;
+  if( _c == NULL || _c[0] != '_' )
+      return NULL;
+
+  if( _t != NULL && strstr(_c,_t) == NULL )
+      return NULL;
+
+  _c++;
+  /* Extract hex value from pointer */
+  _p = 0;
+  while (*_c) {
+      if ((*_c >= '0') && (*_c <= '9'))
+          _p = (_p << 4) + (*_c - '0');
+      else if ((*_c >= 'a') && (*_c <= 'f'))
+          _p = (_p << 4) + ((*_c - 'a') + 10);
+      else
+          break;
+      _c++;
+  }
+
+  return (void *) _p;
+}
+
+static
+void SWIG_SimpleMakePtr(char *_c, const void *_ptr, char *type) {
+  static char _hex[16] =
+  {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+   'a', 'b', 'c', 'd', 'e', 'f'};
+  unsigned long _p, _s;
+  char _result[20], *_r;    /* Note : a 64-bit hex number = 16 digits */
+  _r = _result;
+  _p = (unsigned long) _ptr;
+  if (_p > 0) {
+    while (_p > 0) {
+      _s = _p & 0xf;
+      *(_r++) = _hex[_s];
+      _p = _p >> 4;
+    }
+    *_r = '_';
+    while (_r >= _result)
+      *(_c++) = *(_r--);
+  } else {
+    strcpy (_c, "NULL");
+  }
+  if (_ptr)
+    strcpy (_c, type);
+}
+
+/*
+** Stuff to support progress reporting callbacks.
+*/
+
+typedef struct {
+    PyObject *psPyCallback;
+    PyObject *psPyCallbackData;
+    int nLastReported;
+} PyProgressData;
+
+/************************************************************************/
+/*                          PyProgressProxy()                           */
+/*                                                                      */
+/*      Copied from gdal.i                                              */
+/************************************************************************/
+
+static int CPL_STDCALL
+PyProgressProxy( double dfComplete, const char *pszMessage, void *pData )
+
+{
+    PyProgressData *psInfo = (PyProgressData *) pData;
+    PyObject *psArgs, *psResult;
+    int      bContinue = TRUE;
+
+    if( psInfo->psPyCallback == NULL || psInfo->psPyCallback == Py_None )
+        return TRUE;
+
+    if( psInfo->nLastReported == (int) (100.0 * dfComplete) )
+        return TRUE;
+
+    psInfo->nLastReported = (int) 100.0 * dfComplete;
+
+    if( pszMessage == NULL )
+        pszMessage = "";
+
+    if( psInfo->psPyCallbackData == NULL )
+        psArgs = Py_BuildValue("(dsO)", dfComplete, pszMessage, Py_None );
+    else
+        psArgs = Py_BuildValue("(dsO)", dfComplete, pszMessage,
+                           psInfo->psPyCallbackData );
+
+    psResult = PyEval_CallObject( psInfo->psPyCallback, psArgs);
+    Py_XDECREF(psArgs);
+
+    if( psResult == NULL )
+    {
+        return TRUE;
+    }
+
+    if( psResult == Py_None )
+    {
+    Py_XDECREF(Py_None);
+        return TRUE;
+    }
+
+    if( !PyArg_Parse( psResult, "i", &bContinue ) )
+    {
+        PyErr_SetString(PyExc_ValueError, "bad progress return value");
+    return FALSE;
+    }
+
+    Py_XDECREF(psResult);
+
+    return bContinue;
+}
+
+/************************************************************************/
+/*                          PyListToXMLTree()                           */
+/************************************************************************/
+
+static CPLXMLNode *PyListToXMLTree( PyObject *pyList )
+
+{
+    int      nChildCount = 0, iChild, nType;
+    CPLXMLNode *psThisNode;
+    CPLXMLNode *psChild;
+    char       *pszText = NULL;
+
+    nChildCount = PyList_Size(pyList) - 2;
+    if( nChildCount < 0 )
+    {
+        PyErr_SetString(PyExc_TypeError,"Error in input XMLTree." );
+    return NULL;
+    }
+
+    PyArg_Parse( PyList_GET_ITEM(pyList,0), "i", &nType );
+    PyArg_Parse( PyList_GET_ITEM(pyList,1), "s", &pszText );
+    psThisNode = CPLCreateXMLNode( NULL, (CPLXMLNodeType) nType, pszText );
+
+    for( iChild = 0; iChild < nChildCount; iChild++ )
+    {
+        psChild = PyListToXMLTree( PyList_GET_ITEM(pyList,iChild+2) );
+        CPLAddXMLChild( psThisNode, psChild );
+    }
+
+    return psThisNode;
+}
+
+/************************************************************************/
+/*                          XMLTreeToPyList()                           */
+/************************************************************************/
+
+static PyObject *XMLTreeToPyList( CPLXMLNode *psTree )
+
+{
+    PyObject *pyList;
+    int      nChildCount = 0, iChild;
+    CPLXMLNode *psChild;
+
+    for( psChild = psTree->psChild; 
+         psChild != NULL; 
+         psChild = psChild->psNext )
+        nChildCount++;
+
+    pyList = PyList_New(nChildCount+2);
+
+    PyList_SetItem( pyList, 0, Py_BuildValue( "i", (int) psTree->eType ) );
+    PyList_SetItem( pyList, 1, Py_BuildValue( "s", psTree->pszValue ) );
+
+    for( psChild = psTree->psChild, iChild = 2; 
+         psChild != NULL; 
+         psChild = psChild->psNext, iChild++ )
+    {
+        PyList_SetItem( pyList, iChild, XMLTreeToPyList( psChild ) );
+    }
+
+    return pyList; 
+}
+
+/*
+ * Functions not handled by the wrapper generator
+ */
+
+static PyObject *
+_wrap_gv_shape_new(PyObject *self, PyObject *args)
+{
+    int          type;
+    char         swig_ptr[32];
+    GvShape *shape;
+
+    if (!PyArg_ParseTuple(args, "i:gv_shape_new",
+                          &type) )
+    return NULL;
+
+    shape = gv_shape_new(type);
+    SWIG_SimpleMakePtr( swig_ptr, shape, "_GvShape" );
+
+    return Py_BuildValue("s",swig_ptr);
+}
+
+static PyObject *
+_wrap_gv_shape_from_xml(PyObject *self, PyObject *args)
+{
+    GvShape     *shape;
+    CPLXMLNode  *cpl_tree;
+    PyObject    *py_tree = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shape_from_xml",
+                          &PyList_Type, &py_tree ) )
+        return NULL;
+
+    cpl_tree = PyListToXMLTree( py_tree );
+    
+    shape = gv_shape_from_xml_tree( cpl_tree );
+    if( shape == NULL )
+    {
+        PyErr_SetString( PyExc_ValueError, 
+                         "XML translation to GvShape filed." );
+        return NULL;
+    }
+    else
+    {
+        char         swig_ptr[32];
+        SWIG_SimpleMakePtr( swig_ptr, shape, "_GvShape" );
+        return Py_BuildValue("s",swig_ptr);
+    }
+}
+
+static PyObject *
+_wrap_gv_shape_to_xml(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    CPLXMLNode *psTree;
+    PyObject *py_xml = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_to_xml",
+                          &swig_shape_ptr))
+        return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape == NULL )
+        return NULL;
+
+    psTree = gv_shape_to_xml_tree( shape );
+    py_xml = XMLTreeToPyList( psTree );
+    CPLDestroyXMLNode( psTree );
+
+    return py_xml;
+}
+
+static PyObject *
+_wrap_gv_shape_destroy(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_destroy",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        gv_shape_delete( shape );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_ref(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_ref",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        gv_shape_ref( shape );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_unref(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_unref",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        gv_shape_unref( shape );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_get_ref(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_get_ref",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+    {
+        return Py_BuildValue( "i", gv_shape_get_ref( shape ) );
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *
+_wrap_gv_shape_copy(PyObject *self, PyObject *args)
+{
+    char  swig_ptr[32];
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL, *copy = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_copy",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        copy = gv_shape_copy( shape );
+
+    SWIG_SimpleMakePtr( swig_ptr, copy, "_GvShape" );
+
+    return Py_BuildValue("s",swig_ptr);
+}
+
+static PyObject *
+_wrap_gv_shape_delete_ring(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    int ring = 0;
+
+    if (!PyArg_ParseTuple(args, "si:gv_shape_ref",
+                          &swig_shape_ptr, &ring))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+      gv_shape_delete_ring( shape, ring );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_line_from_nodelists(PyObject *self, PyObject *args)
+{
+    PyObject *pyxlist=NULL;
+    PyObject *pyylist=NULL;
+    PyObject *pyzlist=NULL;
+
+    int       node_count, i;
+    char         swig_ptr[32];
+    GvShape *shape;
+    gvgeocoord xnode, ynode, znode;
+    int ring=0;
+
+    if (!PyArg_ParseTuple(args, "O!O!O!:gv_shape_line_from_nodelist",
+                          &PyList_Type,&pyxlist,
+                          &PyList_Type,&pyylist,
+                          &PyList_Type,&pyzlist) )
+    return NULL;
+
+    node_count=PyList_Size(pyxlist);
+    if (node_count < 1)
+    {
+        PyErr_SetString(PyExc_ValueError,
+              "require at least one node in list for gv_shape_line_from_nodelist");
+        return NULL;
+    }
+    if ((node_count != PyList_Size(pyylist)) ||
+        (node_count != PyList_Size(pyzlist)))
+    {
+        PyErr_SetString(PyExc_ValueError,
+              "x, y, and z node lists must have identical lengths for gv_shape_line_from_nodelist");
+        return NULL;
+    }
+
+    shape = gv_shape_new(GVSHAPE_LINE);
+
+    for( i = 0; i < node_count; i++ )
+    {
+        if ( ( !PyArg_Parse( PyList_GET_ITEM(pyxlist,i), 
+                Ccast ":gv_shape_line_from_nodelist" , &xnode ) ) ||
+             ( !PyArg_Parse( PyList_GET_ITEM(pyylist,i), 
+                Ccast ":gv_shape_line_from_nodelist" , &ynode ) ) ||
+             ( !PyArg_Parse( PyList_GET_ITEM(pyzlist,i), 
+                Ccast ":gv_shape_line_from_nodelist" , &znode ) ))
+        {
+            PyErr_SetString(PyExc_ValueError,
+          "expecting floats in gv_shape_line_from_nodelist arguments");
+            gv_shape_delete(shape);
+            return NULL;
+        }
+        gv_shape_add_node(shape,ring,xnode,ynode,znode);
+        
+    }
+
+    SWIG_SimpleMakePtr( swig_ptr, shape, "_GvShape" );
+
+    return Py_BuildValue("s",swig_ptr);
+}
+
+
+static PyObject *
+_wrap_gv_shapes_lines_for_vecplot(PyObject *self, PyObject *args)
+{
+    PyObject *pyxlist=NULL;
+    PyObject *pyylist=NULL;
+    PyObject *pyzlist=NULL;
+    PyObject *pyoklist=NULL;
+
+    int       node_count, i, j, last_ok, shape_count,last_shape_nodes, oknode;
+    GvShape *shape;
+    GvShapes *shapes;
+    gvgeocoord xnode, ynode, znode;
+    int ring=0;
+    int *shape_ids=NULL;
+
+    if (!PyArg_ParseTuple(args, "O!O!O!O!:gv_shape_lines_for_vecplot",
+                          &PyList_Type,&pyxlist,
+                          &PyList_Type,&pyylist,
+                          &PyList_Type,&pyzlist,
+                          &PyList_Type,&pyoklist) )
+    return NULL;
+
+    node_count=PyList_Size(pyxlist);
+    if (node_count < 1)
+    {
+        PyErr_SetString(PyExc_ValueError,
+              "require at least one node in list for gv_shapes_lines_for_vecplot");
+        return NULL;
+    }
+    if ((node_count != PyList_Size(pyylist)) ||
+        (node_count != PyList_Size(pyzlist)) ||
+        (node_count != PyList_Size(pyoklist)))
+    {
+        PyErr_SetString(PyExc_ValueError,
+              "x, y, z, ok lists must have identical lengths for gv_shapes_lines_for_vecplot");
+        return NULL;
+    }
+    shapes = (GvShapes *) gv_shapes_new();
+    shape = gv_shape_new(GVSHAPE_LINE);
+    gv_shapes_add_shape(shapes,shape);
+    last_ok=1;
+    shape_count=1;
+    last_shape_nodes=0;
+    for( i = 0; i < node_count; i++ )
+    {
+        if(( !PyArg_Parse( PyList_GET_ITEM(pyxlist,i), 
+              Ccast ":gv_shapes_lines_for_vecplot" , &xnode ) ) ||
+           ( !PyArg_Parse( PyList_GET_ITEM(pyylist,i), 
+              Ccast ":gv_shapes_lines_for_vecplot" , &ynode ) ) ||
+           ( !PyArg_Parse( PyList_GET_ITEM(pyzlist,i), 
+              Ccast ":gv_shapes_lines_for_vecplot" , &znode ) ) ||
+           ( !PyArg_Parse( PyList_GET_ITEM(pyoklist,i), 
+              "i:gv_shapes_lines_for_vecplot" , &oknode ) ))
+        {
+            PyErr_SetString(PyExc_ValueError,
+          "expecting floats for nodes, ints for ok in gv_shapes_lines_for_vecplot arguments");
+            shape_ids=g_new(int,shape_count);
+            for ( j= 0 ; j < shape_count; j++ )
+            {
+                *(shape_ids+sizeof(int)) = j;
+            }
+            gv_shapes_delete_shapes(shapes,shape_count,shape_ids);
+            g_free(shape_ids);
+            return NULL;
+        }
+        if (oknode == 1)
+        {
+            gv_shape_add_node(shape,ring,xnode,ynode,znode);
+            last_ok = 1;
+            last_shape_nodes=last_shape_nodes+1;
+        }
+        else if (last_ok == 1)
+        {
+            shape=gv_shape_new(GVSHAPE_LINE);
+            gv_shapes_add_shape(shapes,shape);
+            shape_count=shape_count+1;
+            last_shape_nodes=0;
+            last_ok = 0;
+        } 
+    }
+    if (last_shape_nodes == 0)
+    {
+        shape_ids=g_new(int,1);
+        *shape_ids=shape_count-1;
+        gv_shapes_delete_shapes(shapes,1,shape_ids);
+        g_free(shape_ids);
+    }
+
+    return PyGtk_New((GtkObject *) shapes);
+}
+
+
+
+static PyObject *
+_wrap_gv_shape_get_property(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    char *key;
+    GvShape *shape = NULL;
+    const char *value = NULL;
+
+    if (!PyArg_ParseTuple(args, "ss:gv_shape_get_property",
+                          &swig_shape_ptr, &key))
+        return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        value = gv_properties_get( gv_shape_get_properties( shape ), key );
+
+    if( value != NULL )
+        return Py_BuildValue( "s", value );
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *
+_wrap_gv_shape_get_properties(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    GvProperties *properties = NULL;
+    PyObject *psDict = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_get_properties",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        properties = gv_shape_get_properties( shape );
+
+    psDict = PyDict_New();
+    if( properties != NULL )
+    {
+        int        i, count;
+
+        count = gv_properties_count( properties );
+        for( i = 0; i < count; i++ )
+        {
+            const char *value, *name;
+            PyObject *py_name, *py_value;
+
+            value = gv_properties_get_value_by_index(properties,i);
+            name = gv_properties_get_name_by_index(properties,i);
+
+            py_name = Py_BuildValue("s",name);
+            py_value = Py_BuildValue("s",value);
+            PyDict_SetItem( psDict, py_name, py_value );
+
+            Py_DECREF(py_name);
+            Py_DECREF(py_value);
+        }
+    }
+
+    return psDict;
+}
+
+
+/************************************************************************/
+/*                      gv_shape_get_typed_properties()                 */
+/*                                                                      */
+/*      This function fetches a dictionary of property values for a     */
+/*      given GvShape.  It operates similarly to the normal             */
+/*      get_properties() call on the GvShape with the following         */
+/*      changes:                                                        */
+/*       o The list of fields to extract are passed in.                 */
+/*       o The passed in field list includes an indicator of whether    */
+/*         the field should be auto-converted to a numeric type.        */
+/*                                                                      */
+/*      The input argument (beside the shape) is a list of tuples       */
+/*      for the field. The tuples consist of the name, and an           */
+/*      integer flag indicating (non-zero) if the field should be       */
+/*      converted to a numeric value.                                   */
+/************************************************************************/
+
+static PyObject *
+_wrap_gv_shape_get_typed_properties(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    GvProperties *properties = NULL;
+    PyObject *psDict = NULL;
+    PyObject *pyFieldList = NULL;
+    int      nCount, i;
+
+    if (!PyArg_ParseTuple(args, "sO!:get_typed_properties",
+                          &swig_shape_ptr, &PyList_Type, &pyFieldList))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape != NULL )
+        properties = gv_shape_get_properties( shape );
+
+    psDict = PyDict_New();
+    if( properties == NULL )
+        return psDict;
+
+    nCount = PyList_Size(pyFieldList);
+    for( i = 0; i < nCount; i++ )
+    {
+        char *pszFieldName = NULL;
+        int nNumericFlag = 0;
+        const char *value;
+        PyObject *py_name, *py_value;
+
+        if( !PyArg_Parse( PyList_GET_ITEM(pyFieldList,i), "(si)",
+                          &pszFieldName, &nNumericFlag ) )
+        {
+            PyErr_SetString(PyExc_ValueError,
+                            "expecting (name,flag) tuples in list.");
+            return NULL;
+        }
+
+        value = gv_properties_get(properties,pszFieldName);
+        if( value == NULL )
+        {
+            py_value = Py_None;
+            Py_INCREF( Py_None );
+        }
+        else if( nNumericFlag )
+            py_value = Py_BuildValue("f",atof(value));
+        else
+            py_value = Py_BuildValue("s",value);
+
+        py_name = Py_BuildValue("s",pszFieldName);
+
+        PyDict_SetItem( psDict, py_name, py_value );
+
+        Py_DECREF(py_name);
+        Py_DECREF(py_value);
+    }
+
+    return psDict;
+}
+
+static PyObject *
+_wrap_gv_shape_set_property(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL, *name=NULL, *value=NULL;
+    GvShape *shape = NULL;
+    GvProperties *properties = NULL;
+
+    if (!PyArg_ParseTuple(args, "sss:gv_shape_set_property",
+                          &swig_shape_ptr, &name, &value))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+    {
+        properties = gv_shape_get_properties( shape );
+        gv_properties_set( properties, name, value );
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_set_properties(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    GvProperties *properties = NULL;
+    PyObject *psDict = NULL;
+    PyObject    *pyKey = NULL, *pyValue = NULL;
+
+    if (!PyArg_ParseTuple(args, "sO!:gv_shape_set_properties",
+                          &swig_shape_ptr, &PyDict_Type, &psDict))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+    {
+        int i;
+
+        properties = gv_shape_get_properties( shape );
+
+        gv_properties_clear( properties );
+
+        i = 0;
+        while( PyDict_Next( psDict, &i, &pyKey, &pyValue ) )
+        {
+            char            *key = NULL, *value = NULL;
+
+            if( !PyArg_Parse( pyKey, "s", &key )
+                || !PyArg_Parse( pyValue, "s", &value ))
+                continue;
+
+            gv_properties_set( properties, key, value );
+
+            pyKey = pyValue = NULL;
+        }
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_get_type(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_get_rings",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        return Py_BuildValue("i",gv_shape_type(shape));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_get_rings(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_get_rings",
+                          &swig_shape_ptr))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        return Py_BuildValue("i",gv_shape_get_rings(shape));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_get_nodes(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    int      ring = 0;
+
+    if (!PyArg_ParseTuple(args, "si:gv_shape_get_nodes",
+                          &swig_shape_ptr, &ring))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        return Py_BuildValue("i",gv_shape_get_nodes(shape,ring));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_add_node(PyObject *self, PyObject *args)
+{
+    char      *swig_shape_ptr = NULL;
+    GvShape   *shape = NULL;
+    int        ring = 0;
+    gvgeocoord x=0.0, y=0.0, z=0.0;
+
+    if (!PyArg_ParseTuple(args, "s" CCC "i:gv_shape_add_node", &swig_shape_ptr,
+                          &x, &y, &z, &ring ))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        return Py_BuildValue("i",gv_shape_add_node(shape,ring, x,y,z));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_set_node(PyObject *self, PyObject *args)
+{
+    char      *swig_shape_ptr = NULL;
+    GvShape   *shape = NULL;
+    int        ring = 0, node = 0;
+    gvgeocoord x=0.0, y=0.0, z=0.0;
+
+    if (!PyArg_ParseTuple(args, "s" CCC "ii:gv_shape_set_node", &swig_shape_ptr,
+                          &x, &y, &z, &node, &ring ))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL )
+        return Py_BuildValue("i",
+                       gv_shape_set_xyz(shape, ring, node, x,y,z));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_get_node(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    int      ring = 0, node = 0;
+
+    if (!PyArg_ParseTuple(args, "sii:gv_shape_get_node",
+                          &swig_shape_ptr, &node, &ring ))
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( shape != NULL ){
+        return Py_BuildValue("(" CCC ")",
+                             gv_shape_get_x(shape,ring,node),
+                             gv_shape_get_y(shape,ring,node),
+                             gv_shape_get_z(shape,ring,node) );
+    }else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_point_in_polygon(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    double  x, y;
+
+    if (!PyArg_ParseTuple(args, "sdd:gv_shape_point_in_polygon",
+                          &swig_shape_ptr, &x, &y ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape != NULL )
+        return Py_BuildValue("i", gv_shape_point_in_polygon(shape, x, y ));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_distance_from_polygon(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    double  x, y;
+
+    if (!PyArg_ParseTuple(args, "sdd:gv_shape_distance_from_polygon",
+                          &swig_shape_ptr, &x, &y ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape != NULL )
+        return Py_BuildValue("d", gv_shape_distance_from_polygon(shape, x, y ));
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_clip_to_rect(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    double  x, y, width, height;
+
+    if (!PyArg_ParseTuple(args, "sdddd:gv_shape_clip_to_rect",
+                          &swig_shape_ptr, &x, &y, &width, &height ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape != NULL )
+    {
+        GvRect      rect;
+        GvShape *new_shape;
+
+        rect.x = x;
+        rect.y = y;
+        rect.width = width;
+        rect.height = height;
+
+        new_shape = gv_shape_clip_to_rect( shape, &rect );
+
+        if( new_shape == NULL )
+        {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+        else
+        {
+            char swig_ptr[128];
+
+            SWIG_SimpleMakePtr( swig_ptr, new_shape, "_GvShape" );
+
+            return Py_BuildValue("s",swig_ptr);
+        }
+    }
+    else
+        return NULL;
+}
+
+static PyObject *
+_wrap_gv_shape_add_shape(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    char *swig_sub_shape_ptr = NULL;
+    GvShape *shape = NULL, *sub_shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "ss:gv_shape_add_shape",
+                          &swig_shape_ptr, &swig_sub_shape_ptr ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( swig_sub_shape_ptr )
+        sub_shape = SWIG_SimpleGetPtr( swig_sub_shape_ptr, "_GvShape" );
+
+    if( shape != NULL && sub_shape != NULL )
+        gv_shape_collection_add_shape( shape, sub_shape );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_get_shape(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+    int shape_index;
+
+    if (!PyArg_ParseTuple(args, "si:gv_shape_get_shape",
+                          &swig_shape_ptr, &shape_index ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape == NULL )
+    {
+        PyErr_SetString(PyExc_ValueError, "no shape passed into get_shape().");
+        return NULL;
+    }
+
+    shape = gv_shape_collection_get_shape( shape, shape_index );
+    if( shape == NULL )
+    {
+        PyErr_SetString(PyExc_IndexError, "shape index out of range for collection");
+        return NULL;
+    }
+    else
+    {
+        char swig_ptr[128];
+        
+        SWIG_SimpleMakePtr( swig_ptr, shape, "_GvShape" );
+        
+        return Py_BuildValue("s",swig_ptr);
+    }
+}
+
+static PyObject *
+_wrap_gv_shape_collection_get_count(PyObject *self, PyObject *args)
+{
+    char *swig_shape_ptr = NULL;
+    GvShape *shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shape_get_shape",
+                          &swig_shape_ptr ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape == NULL )
+    {
+        PyErr_SetString(PyExc_ValueError, "no shape passed into get_shape().");
+        return NULL;
+    }
+
+    return Py_BuildValue( "i", gv_shape_collection_get_count( shape ) );
+}
+
+static PyObject *
+_wrap_gv_symbol_manager_get_names(PyObject *self, PyObject *args)
+{
+    PyObject *manager, *py_name_list;
+    char **name_list;
+    int i, count;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_symbol_manager_get_names",
+                          &PyGtk_Type, &manager ))
+        return NULL;
+
+    name_list = 
+        gv_symbol_manager_get_names( GV_SYMBOL_MANAGER(PyGtk_Get(manager)) );
+    
+    count = CSLCount( name_list );
+    py_name_list = PyList_New( count );
+    for( i = 0; i < count; i++ )
+        PyList_SetItem( py_name_list, i, Py_BuildValue( "s", name_list[i] ) );
+    
+    g_free( name_list );
+
+    return py_name_list;
+}
+
+static PyObject *
+_wrap_gv_symbol_manager_inject_vector_symbol(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    GvShape  *shape = NULL;
+    char     *swig_shape_ptr = NULL;
+    char     *symbol_name = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!ss:gv_symbol_manager_inject_vector_symbol",
+                          &PyGtk_Type, &manager, &symbol_name,
+                          &swig_shape_ptr ))
+        return NULL;
+
+    if( swig_shape_ptr )
+        shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+
+    if( shape )
+        gv_symbol_manager_inject_vector_symbol(
+                GV_SYMBOL_MANAGER(PyGtk_Get(manager)),
+                symbol_name, shape );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_symbol_manager_inject_raster_symbol(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    char     *rgba_string = NULL;
+    char     *symbol_name = NULL;
+    int      width, height, rgba_len;
+
+    if (!PyArg_ParseTuple(args,
+                          "O!siiz#:gv_symbol_manager_inject_raster_symbol",
+                          &PyGtk_Type, &manager, &symbol_name,
+                          &width, &height, &rgba_string, &rgba_len ))
+        return NULL;
+
+    if( width*height*4 > rgba_len )
+    {
+        PyErr_SetString(PyExc_TypeError,
+                        "rgba raster symbol buffer seems to be too small (width*height*4)\nin gv_symbol_manager_inject_raster_symbol()." );
+        return NULL;
+    }
+
+    gv_symbol_manager_inject_raster_symbol(
+                GV_SYMBOL_MANAGER(PyGtk_Get(manager)),
+                symbol_name, width, height, rgba_string );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_symbol_manager_get_symbol(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    char     *symbol_name = NULL;
+    GvSymbolObj *symbol;
+
+    if (!PyArg_ParseTuple(args,"O!s:gv_symbol_manager_get_symbol",
+                          &PyGtk_Type, &manager, &symbol_name ) )
+        return NULL;
+
+    symbol = gv_symbol_manager_get_symbol(
+        GV_SYMBOL_MANAGER(PyGtk_Get(manager)),
+        symbol_name );
+
+    if( symbol == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if( symbol->type == GV_SYMBOL_VECTOR )
+    {
+        char      swig_shape_ptr[32];
+
+        SWIG_SimpleMakePtr( swig_shape_ptr, symbol->buffer, "_GvShape" );
+
+        return Py_BuildValue("(is)", symbol->type, swig_shape_ptr );
+    }
+    else
+    {
+        PyObject *py_rgba_buffer;
+        PyObject *py_result;
+
+        py_rgba_buffer =
+            PyString_FromStringAndSize( symbol->buffer,
+                                        symbol->width * symbol->height * 4 );
+
+        py_result = Py_BuildValue("(iiiO)",
+                                  symbol->type, symbol->width, symbol->height,
+                                  py_rgba_buffer );
+        Py_DECREF( py_rgba_buffer );
+
+        return py_result;
+    }
+}
+
+static PyObject *
+_wrap_gv_symbol_manager_save_vector_symbol(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    char     *symbol_name = NULL, *new_name = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!ss:gv_symbol_manager_save_vector_symbol",
+                          &PyGtk_Type, &manager, &symbol_name, &new_name ))
+        return NULL;
+
+    if( symbol_name && new_name )
+    {
+        if ( gv_symbol_manager_save_vector_symbol(
+                GV_SYMBOL_MANAGER(PyGtk_Get(manager)),
+                symbol_name, new_name ) )
+        {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+        else
+        {
+            PyErr_SetString(PyExc_TypeError,
+                "error while saving new symbol in gv_symbol_manager_save_vector_symbol()." );
+        return NULL;
+        }
+    }
+
+    return NULL;
+}
+
+static PyObject *
+_wrap_gv_data_get_properties(PyObject *self, PyObject *args)
+{
+    PyObject *data;
+    GvProperties *properties = NULL;
+    PyObject *psDict = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_get_properties",
+                          &PyGtk_Type, &data))
+    return NULL;
+
+    if( data != NULL )
+        properties = gv_data_get_properties( GV_DATA(PyGtk_Get(data)) );
+
+    psDict = PyDict_New();
+    if( properties != NULL )
+    {
+        int        i, count;
+
+        count = gv_properties_count( properties );
+        for( i = 0; i < count; i++ )
+        {
+            const char *value, *name;
+            PyObject *py_name, *py_value;
+
+            value = gv_properties_get_value_by_index(properties,i);
+            name = gv_properties_get_name_by_index(properties,i);
+
+            py_name = Py_BuildValue("s",name);
+            py_value = Py_BuildValue("s",value);
+            PyDict_SetItem( psDict, py_name, py_value );
+
+            Py_DECREF(py_name);
+            Py_DECREF(py_value);
+        }
+    }
+
+    return psDict;
+}
+
+static PyObject *
+_wrap_gv_data_set_properties(PyObject *self, PyObject *args)
+{
+    PyObject *data;
+    GvProperties *properties = NULL;
+    PyObject *psDict = NULL;
+    int      i;
+    PyObject    *pyKey = NULL, *pyValue = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_data_set_properties",
+                          &PyGtk_Type, &data,
+                          &PyDict_Type, &psDict))
+    return NULL;
+
+    if( data != NULL )
+        properties = gv_data_get_properties( GV_DATA(PyGtk_Get(data)) );
+
+    gv_properties_clear( properties );
+
+    i = 0;
+    while( PyDict_Next( psDict, &i, &pyKey, &pyValue ) )
+    {
+        char            *key = NULL, *value = NULL;
+
+        if( !PyArg_Parse( pyKey, "s", &key )
+            || !PyArg_Parse( pyValue, "s", &value ))
+            continue;
+
+        gv_properties_set( properties, key, value );
+
+        pyKey = pyValue = NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_data_changed(PyObject *self, PyObject *args)
+{
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_changed", &PyGtk_Type, &data))
+    return NULL;
+    gv_data_changed(GV_DATA(PyGtk_Get(data)), NULL);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_layer_extents(PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_layer_extents", &PyGtk_Type, &layer))
+    return NULL;
+    gv_layer_extents(GV_LAYER(PyGtk_Get(layer)), &rect);
+    return Py_BuildValue( "(" CCCC ")", rect.x, rect.y, rect.width, rect.height);
+}
+
+static PyObject *
+_wrap_gv_layer_display_change(PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_layer_display_change",
+                          &PyGtk_Type, &layer))
+    return NULL;
+
+    gv_layer_display_change( GV_LAYER(PyGtk_Get(layer)), NULL );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_view_area_list_layers(PyObject *self, PyObject *args)
+{
+    PyObject *view, *py_list;
+    GList *list;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_list_layers", &PyGtk_Type,
+                          &view))
+        return NULL;
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+    list = gv_view_area_list_layers(GV_VIEW_AREA(PyGtk_Get(view)));
+    py_list = PyList_New(0);
+    for ( ; list != NULL; list = list->next)
+    {
+        PyObject *layer = PyGtk_New(GTK_OBJECT(list->data));
+        PyList_Append(py_list, layer);
+        Py_DECREF(layer);
+    }
+    g_list_free(list);
+    return py_list;
+}
+
+static PyObject *
+_wrap_gv_view_area_get_fontnames(PyObject *self, PyObject *args)
+{
+    PyObject *view;
+    GPtrArray *g_list;
+    PyObject  *py_list;
+    int       i;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_fontnames",
+              &PyGtk_Type, &view) )
+    return NULL;
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    g_list = gv_view_area_get_fontnames( GV_VIEW_AREA(PyGtk_Get(view)) );
+
+    py_list = PyList_New(0);
+    for( i = 0; i < g_list->len; i++ )
+    {
+        const char  *item = (const char *) g_ptr_array_index(g_list,i);
+        PyObject    *py_item;
+
+        py_item = Py_BuildValue( "s", item );
+        PyList_Append( py_list, py_item );
+        Py_DECREF( py_item );
+    }
+
+    g_ptr_array_free( g_list, FALSE );
+
+    return py_list;
+}
+
+static PyObject *
+_wrap_gv_view_area_set_background_color(PyObject *self, PyObject *args)
+{
+    PyObject *view;
+    GvColor color;
+
+    if (!PyArg_ParseTuple(args, "O!(ffff):gv_view_area_set_background_color",
+              &PyGtk_Type, &view,
+              &color[0], &color[1], &color[2], &color[3]))
+    return NULL;
+
+    gv_view_area_set_background_color(GV_VIEW_AREA(PyGtk_Get(view)), color);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_view_area_get_background_color(PyObject *self, PyObject *args)
+{
+    PyObject *view;
+    GvColor color;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_background_color",
+              &PyGtk_Type, &view))
+        return NULL;
+
+    gv_view_area_get_background_color(GV_VIEW_AREA(PyGtk_Get(view)), color);
+
+    return Py_BuildValue("(ffff)", color[0], color[1], color[2], color[3] );
+}
+
+static PyObject *
+_wrap_gv_view_area_create_thumbnail(PyObject *self, PyObject *args)
+{
+#ifdef WIN32
+    PyErr_SetString(PyExc_RuntimeError, "not supported on this platform");
+    return NULL;
+#else
+
+    PyObject *view, *layer, *ret;
+    GdkPixmap *pixmap;
+    int width, height;
+
+    if (!PyArg_ParseTuple(args, "O!O!ii:gv_view_area_create_thumbnail",
+              &PyGtk_Type, &view, &PyGtk_Type, &layer,
+              &width, &height))
+    return NULL;
+    pixmap = gv_view_area_create_thumbnail(GV_VIEW_AREA(PyGtk_Get(view)),
+                       PyGtk_Get(layer), width, height);
+    if (!pixmap)
+    {
+    PyErr_SetString(PyExc_RuntimeError, "could not create pixmap");
+    return NULL;
+    }
+    ret = PyGdkWindow_New(pixmap);
+    gdk_pixmap_unref(pixmap);
+    return ret;
+#endif
+}
+
+static PyObject *
+_wrap_gv_view_area_set_3d_view(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    vec3_t     eye_pos, eye_dir;
+
+    if (!PyArg_ParseTuple(args,  "O!(" CCC ")(" CCC "):gv_view_area_set_3d_view",
+                          &PyGtk_Type, &py_view,
+                          eye_pos+0, eye_pos+1, eye_pos+2,
+                          eye_dir+0, eye_dir+1, eye_dir+2))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    gv_view_area_set_3d_view( view, eye_pos, eye_dir );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_view_area_set_3d_view_look_at(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    vec3_t     eye_pos;
+    gvgeocoord eye_look_at[2];
+
+    if (!PyArg_ParseTuple(args, "O!(" CCC ")(" CC "):gv_view_area_set_3d_view_look_at",
+                          &PyGtk_Type, &py_view,
+                          eye_pos+0, eye_pos+1, eye_pos+2,
+                          eye_look_at+0, eye_look_at+1))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    gv_view_area_set_3d_view_look_at( view, eye_pos, eye_look_at );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_view_area_get_eye_pos(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_eye_pos",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    return Py_BuildValue( "(" CCC ")",
+                          view->state.eye_pos[0],
+                          view->state.eye_pos[1],
+                          view->state.eye_pos[2] );
+}
+
+static PyObject *
+_wrap_gv_view_area_get_eye_dir(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_eye_dir",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    return Py_BuildValue( "(" CCC ")",
+                          view->state.eye_dir[0],
+                          view->state.eye_dir[1],
+                          view->state.eye_dir[2] );
+}
+
+
+static PyObject *
+_wrap_gv_view_area_get_look_at_pos(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    gvgeocoord  x, y;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_look_at_pos",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+
+    if (!gv_view_area_get_look_at_pos( view, &x, &y))
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return Py_BuildValue("(" CC ")", x, y);
+
+}
+
+static PyObject *
+_wrap_gv_view_area_map_location(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    gvgeocoord  x, y;
+
+    if (!PyArg_ParseTuple(args, "O!(" CC "):gv_view_area_map_location",
+                          &PyGtk_Type, &py_view, &x, &y))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    gv_view_area_map_location( view, x, y, &x, &y );
+
+    return Py_BuildValue("(" CC ")", x, y );
+}
+
+static PyObject *
+_wrap_gv_view_area_get_pointer(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    gvgeocoord  x, y;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_pointer",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    gv_view_area_map_pointer( view,
+                              view->state.mpos_x, view->state.mpos_y,
+                              &x, &y );
+
+    return Py_BuildValue("(" CC ")", x, y );
+}
+
+static PyObject *
+_wrap_gv_view_area_map_pointer(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    gvgeocoord  x, y;
+
+    if (!PyArg_ParseTuple(args, "O!(" CC "):gv_view_area_map_pointer",
+                          &PyGtk_Type, &py_view, &x, &y))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    gv_view_area_map_pointer( view, x, y, &x, &y );
+
+    return Py_BuildValue("(" CC ")", x, y );
+}
+
+static PyObject *
+_wrap_gv_view_area_inverse_map_pointer(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    gvgeocoord x, y;
+
+    if (!PyArg_ParseTuple(args, "O!(" CC "):gv_view_area_inverse_map_pointer",
+                          &PyGtk_Type, &py_view, &x, &y))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    gv_view_area_inverse_map_pointer( view, x, y, &x, &y );
+
+    return Py_BuildValue("(" CC ")", x, y );
+}
+
+static PyObject *
+_wrap_gv_view_area_get_translation(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_translation",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+    return Py_BuildValue( "(" CC ")", view->state.tx, view->state.ty );
+}
+
+static PyObject *
+_wrap_gv_view_area_get_extents(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    gvgeocoord xmin, ymin, xmax, ymax;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_extents",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+
+    gv_view_area_get_extents( view, &xmin, &ymin, &xmax, &ymax );
+
+    return Py_BuildValue("(" CCCC ")", xmin, ymin, xmax, ymax );
+}
+
+
+static PyObject *
+_wrap_gv_view_area_get_volume(PyObject *self, PyObject *args)
+{
+    PyObject *py_view;
+    GvViewArea *view;
+    double  volume[6];
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_volume",
+                          &PyGtk_Type, &py_view))
+    {
+    return NULL;
+    }
+
+    if (!GV_IS_VIEW_AREA(PyGtk_Get(py_view)))
+    {
+        PyErr_SetString(PyExc_TypeError, "argument must be a GvViewArea");
+        return NULL;
+    }
+
+    view = GV_VIEW_AREA( PyGtk_Get(py_view) );
+
+    gv_view_area_get_volume( view, volume );
+
+    return Py_BuildValue("(" CCCCCC ")",
+                         volume[0], volume[1],
+                         volume[2], volume[3],
+                         volume[4], volume[5] );
+}
+
+
+static PyObject *
+_wrap_gv_tool_set_boundary(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!(" CCCC "):gv_tool_new_rect",
+                          &PyGtk_Type, &tool, &rect.x, &rect.y,
+                          &rect.width, &rect.height))
+    return NULL;
+
+    if (!gv_tool_set_boundary(GV_TOOL(PyGtk_Get(tool)), &rect))
+    {
+    PyErr_SetString(PyExc_RuntimeError,
+                        "invalid ROI constraining region specified, width or height <= 0.0");
+    return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_roi_tool_get_rect(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_roi_tool_get_rect", &PyGtk_Type, &tool))
+    return NULL;
+    if (!gv_roi_tool_get_rect(GV_ROI_TOOL(PyGtk_Get(tool)), &rect))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "no ROI marked");
+    return NULL;
+    }
+    return Py_BuildValue("(" CCCC ")", rect.x, rect.y, rect.width, rect.height);
+}
+
+static PyObject *
+_wrap_gv_roi_tool_new_rect(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!(" CCCC "):gv_roi_tool_new_rect", &PyGtk_Type, &tool, &rect.x, &rect.y,
+                          &rect.width, &rect.height))
+    return NULL;
+
+    if (!gv_roi_tool_new_rect(GV_ROI_TOOL(PyGtk_Get(tool)), &rect))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "invalid ROI specified");
+    return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_poi_tool_get_point(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvVertex point;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_poi_tool_get_point", &PyGtk_Type, &tool))
+    return NULL;
+    if (!gv_poi_tool_get_point(GV_POI_TOOL(PyGtk_Get(tool)), &point))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "no POI marked");
+    return NULL;
+    }
+    return Py_BuildValue("(" CC ")", point.x, point.y);
+}
+
+static PyObject *
+_wrap_gv_poi_tool_new_point(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvVertex point;
+
+    if (!PyArg_ParseTuple(args, "O!(" CC "):gv_poi_tool_new_point", &PyGtk_Type, &tool, &point.x, &point.y))
+    return NULL;
+
+    if (!gv_poi_tool_new_point(GV_POI_TOOL(PyGtk_Get(tool)), &point))
+    {
+        PyErr_SetString(PyExc_RuntimeError, "invalid POI specified");
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_records_get_typed_properties(PyObject *self, PyObject *args)
+{
+    PyObject *py_records;
+    int       shp_index = -1, iField, ii;
+    GvRecords *records;
+    const char *pachRecData;
+    PyObject *psDict = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_records_get_typed_properties",
+                          &PyGtk_Type, &py_records, &shp_index) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+
+    if( shp_index < 0 || shp_index >= records->nRecordCount )
+        return NULL;
+
+    psDict = PyDict_New();
+    pachRecData = gv_records_get_raw_record_data( records, shp_index );
+    for( ii = 0; ii < records->nUsedFieldCount; ii++ )
+    {
+        PyObject *py_name, *py_value;
+        const char *value;
+
+        iField = records->panUsedFieldList[ii];
+
+        value = pachRecData + records->panFieldOffset[iField];
+
+        if( *value == GV_NULL_MARKER )
+        {
+            py_value = Py_None;
+            Py_INCREF( Py_None );
+        }
+        else if( records->panFieldType[iField] == GV_RFT_INTEGER )
+            py_value = Py_BuildValue("i",atoi(value));
+        else if( records->panFieldType[iField] == GV_RFT_FLOAT )
+            py_value = Py_BuildValue("f",atof(value));
+        else
+            py_value = Py_BuildValue("s",value);
+        
+        py_name = Py_BuildValue("s",records->papszFieldName[iField]);
+
+        PyDict_SetItem( psDict, py_name, py_value );
+
+        Py_DECREF(py_name);
+        Py_DECREF(py_value);
+    }
+
+    return psDict;
+}
+
+static PyObject *
+_wrap_gv_records_get_properties(PyObject *self, PyObject *args)
+{
+    PyObject *py_records;
+    int       shp_index = -1, iField;
+    GvRecords *records;
+    const char *pachRecData;
+    PyObject *psDict = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_records_get_properties",
+                          &PyGtk_Type, &py_records, &shp_index) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+
+    if( shp_index < 0 || shp_index >= records->nRecordCount )
+        return NULL;
+
+    psDict = PyDict_New();
+    pachRecData = gv_records_get_raw_record_data( records, shp_index );
+    for( iField = 0; iField < records->nFieldCount; iField++ )
+    {
+        PyObject *py_name, *py_value;
+        const char *value;
+
+        value = pachRecData + records->panFieldOffset[iField];
+        if( *value == GV_NULL_MARKER )
+            continue;
+
+        py_value = Py_BuildValue("s",value);
+        py_name = Py_BuildValue("s",records->papszFieldName[iField]);
+
+        PyDict_SetItem( psDict, py_name, py_value );
+
+        Py_DECREF(py_name);
+        Py_DECREF(py_value);
+    }
+
+    return psDict;
+}
+
+static PyObject *
+_wrap_gv_records_set_used_properties(PyObject *self, PyObject *args)
+{
+    PyObject *py_records;
+    PyObject *py_list;
+    GvRecords *records;
+    int       nFieldCount, *panFieldList = NULL, i;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_records_set_used_properties",
+                          &PyGtk_Type, &py_records, &PyList_Type, &py_list) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+    
+    nFieldCount = PyList_Size(py_list);
+    panFieldList = g_new( int, nFieldCount );
+
+    for( i = 0; i < nFieldCount; i++ )
+    {
+        if( !PyArg_Parse( PyList_GET_ITEM(py_list,i), "i", panFieldList + i ) )
+        {
+            PyErr_SetString(PyExc_ValueError,
+                            "expecting ints in gv_records_set_used_fields argument");
+            return NULL;
+        }
+    }
+
+    gv_records_set_used_properties( records, nFieldCount, panFieldList );
+
+    g_free( panFieldList );
+    
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_records_to_dbf(PyObject *self, PyObject *args)
+{
+    PyObject *py_records;
+    PyObject *py_list;
+    char      *filename = NULL;
+    GvRecords *records;
+    int       nSelectionCount, *panSelectionList = NULL, i, result;
+    PyProgressData sProgressInfo;
+
+    sProgressInfo.psPyCallback = NULL;
+    sProgressInfo.psPyCallbackData = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!sO!OO:gv_records_to_dbf",
+                          &PyGtk_Type, &py_records, &filename, 
+                          &PyList_Type, &py_list,
+                          &(sProgressInfo.psPyCallback),
+                          &(sProgressInfo.psPyCallbackData) ) )
+        return NULL;
+
+    if( !GV_IS_RECORDS(PyGtk_Get(py_records)) )
+        return NULL;
+
+    records = GV_RECORDS(PyGtk_Get(py_records));
+
+    nSelectionCount = PyList_Size(py_list);
+    if( nSelectionCount != 0 )
+        panSelectionList = g_new( int, nSelectionCount );
+
+    for( i = 0; i < nSelectionCount; i++ )
+    {
+        if( !PyArg_Parse( PyList_GET_ITEM(py_list,i), "i", panSelectionList + i ) )
+        {
+            PyErr_SetString(PyExc_ValueError,
+                            "expecting ints in gv_records_to_dbf argument");
+            return NULL;
+        }
+    }
+
+    result = gv_records_to_dbf( records, filename, 
+                                nSelectionCount, panSelectionList,
+                                PyProgressProxy, &sProgressInfo );
+
+    g_free( panSelectionList );
+    
+    return Py_BuildValue("i", result );
+}
+    
+#include "gv_ciet.c"
+
+static PyObject *
+_wrap_gv_shapes_get_shape(PyObject *self, PyObject *args)
+{
+    PyObject *py_shapes;
+    int       shp_index = -1;
+    char      swig_shape_ptr[32];
+    GvShape   *gv_shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_shapes_get_shape",
+                          &PyGtk_Type, &py_shapes, &shp_index) )
+        return NULL;
+
+    if( GV_IS_SHAPES(PyGtk_Get(py_shapes))
+        && shp_index >= 0
+        && shp_index < gv_shapes_num_shapes(GV_SHAPES(PyGtk_Get(py_shapes))) )
+    {
+        gv_shape = gv_shapes_get_shape( GV_SHAPES(PyGtk_Get(py_shapes)),
+                                        shp_index );
+    }
+
+    if( gv_shape == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    SWIG_SimpleMakePtr( swig_shape_ptr, gv_shape, "_GvShape" );
+
+    return Py_BuildValue("s", swig_shape_ptr );
+}
+
+static PyObject *
+_wrap_gv_shapes_from_ogr_layer(PyObject *self, PyObject *args)
+{
+    GvData    *data = NULL;
+    char      *ogrlayer_in = NULL;
+    void      *hLayer;
+    PyObject *py_ret = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shapes_from_ogr_layer",
+                          &ogrlayer_in) )
+        return NULL;
+
+    hLayer = SWIG_SimpleGetPtr(ogrlayer_in, "OGRLayerH" );
+    if( hLayer == NULL )
+    {
+        PyErr_SetString(PyExc_IOError,
+                        "Unable to extract OGRLayerH handle in gv_shapes_from_ogr_layer()");
+        return NULL;
+    }
+
+    data = gv_shapes_from_ogr_layer( hLayer );
+    if( data == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    py_ret = PyGtk_New( GTK_OBJECT(data) );
+    gtk_object_sink( GTK_OBJECT(data) );
+
+    return py_ret;
+}
+
+static PyObject *
+_wrap_gv_shapes_add_shape(PyObject *self, PyObject *args)
+{
+    PyObject *py_shapes;
+    char     *swig_shape_ptr;
+    int       shp_index = -1;
+    GvShape   *gv_shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_shapes_add_shape",
+                          &PyGtk_Type, &py_shapes, &swig_shape_ptr) )
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        gv_shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( GV_IS_SHAPES(PyGtk_Get(py_shapes)) && gv_shape )
+        shp_index = gv_shapes_add_shape( GV_SHAPES(PyGtk_Get(py_shapes)),
+                                         gv_shape );
+
+    return Py_BuildValue("i", shp_index );
+}
+
+static PyObject *
+_wrap_gv_shapes_add_shape_last(PyObject *self, PyObject *args)
+{
+    PyObject *py_shapes;
+    char     *swig_shape_ptr;
+    int       shp_index = -1;
+    GvShape   *gv_shape = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_shapes_add_shape_last",
+                          &PyGtk_Type, &py_shapes, &swig_shape_ptr) )
+    return NULL;
+
+    if( swig_shape_ptr )
+    {
+        gv_shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+    }
+
+    if( GV_IS_SHAPES(PyGtk_Get(py_shapes)) && gv_shape )
+        shp_index = gv_shapes_add_shape_last( GV_SHAPES(PyGtk_Get(py_shapes)),
+                                         gv_shape );
+
+    return Py_BuildValue("i", shp_index );
+}
+
+static PyObject *
+_wrap_gv_shapes_delete_shapes(PyObject *self, PyObject *args)
+{
+    PyObject *py_shapes;
+    int       shape_count, i;
+    PyObject *pylist = NULL;
+    GvShapes *shapes = NULL;
+    int      *shape_ids = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_shapes_delete_shapes",
+                          &PyGtk_Type, &py_shapes,
+                          &PyList_Type, &pylist) )
+    return NULL;
+
+    if( GV_IS_SHAPES(PyGtk_Get(py_shapes))  )
+        shapes = GV_SHAPES(PyGtk_Get(py_shapes));
+
+    shape_count = PyList_Size(pylist);
+    shape_ids = g_new(int,shape_count);
+
+    for( i = 0; i < shape_count; i++ )
+    {
+        if( !PyArg_Parse( PyList_GET_ITEM(pylist,i), "i", shape_ids + i ) )
+        {
+        PyErr_SetString(PyExc_ValueError,
+                       "expecting ints in gv_shapes_delete_shapes argument");
+        return NULL;
+        }
+    }
+
+    gv_shapes_delete_shapes( shapes, shape_count, shape_ids );
+
+    g_free( shape_ids );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shapes_replace_shapes(PyObject *self, PyObject *args)
+{
+    PyObject *py_shapes;
+    int       shape_count, i;
+    PyObject *pyindex_list = NULL, *pyshape_list = NULL;
+    GvShapes *shapes = NULL;
+    GvShape **shape_list = NULL;
+    int      *shape_ids = NULL;
+    int      copy_flag = FALSE;
+
+    if (!PyArg_ParseTuple(args, "O!O!O!|d:gv_shapes_replace_shapes",
+                          &PyGtk_Type, &py_shapes,
+                          &PyList_Type, &pyindex_list,
+                          &PyList_Type, &pyshape_list,
+                          &copy_flag ) )
+    return NULL;
+
+    if( GV_IS_SHAPES(PyGtk_Get(py_shapes))  )
+        shapes = GV_SHAPES(PyGtk_Get(py_shapes));
+
+    if( PyList_Size(pyindex_list) != PyList_Size(pyshape_list) )
+    {
+    PyErr_SetString(PyExc_RuntimeError,
+          "Size of index & shape lists differ in gv_shapes_replace_shapes().");
+    return NULL;
+    }
+
+    shape_count = PyList_Size(pyindex_list);
+    shape_ids = g_new(int,shape_count);
+    shape_list = g_new(GvShape*,shape_count);
+
+    for( i = 0; i < shape_count; i++ )
+    {
+        char   *shape_swigid = NULL;
+
+         if( !PyArg_Parse( PyList_GET_ITEM(pyindex_list,i), "i", shape_ids+i ))
+        {
+        PyErr_SetString(PyExc_ValueError,
+                       "expecting ints in gv_shapes_replace_shapes argument");
+        return NULL;
+        }
+
+        if( !PyArg_Parse( PyList_GET_ITEM(pyshape_list,i), "s",&shape_swigid ))
+        {
+        PyErr_SetString(PyExc_ValueError,
+                    "expecting GvShape in gv_shapes_replace_shapes argument");
+        return NULL;
+        }
+
+        shape_list[i] = SWIG_SimpleGetPtr( shape_swigid, "_GvShape" );
+    }
+
+
+    gv_shapes_replace_shapes( shapes, shape_count, shape_ids, shape_list,
+                              copy_flag );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shapes_get_extents(PyObject *self, PyObject *args)
+{
+    PyObject *shapes;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shapes_extents", &PyGtk_Type, &shapes))
+        return NULL;
+
+    gv_shapes_get_extents(GV_SHAPES(PyGtk_Get(shapes)), &rect);
+    return Py_BuildValue("(" CCCC ")", rect.x, rect.y, rect.width, rect.height);
+}
+
+static PyObject *
+_wrap_gv_shapes_get_change_info(PyObject *self, PyObject *args)
+{
+    GvShapeChangeInfo *change_info;
+    PyObject *c_change_info;
+    PyObject *id_list;
+    int i;
+
+    if (!PyArg_ParseTuple(args, "O:gv_shapes_get_change_info", &c_change_info))
+    return NULL;
+
+    if (!PyCObject_Check (c_change_info))
+        return NULL;
+
+    change_info = (GvShapeChangeInfo *) PyCObject_AsVoidPtr(c_change_info);
+
+    id_list = PyTuple_New(change_info->num_shapes);
+
+    for(i=0; i<change_info->num_shapes; i++)
+    {
+        PyTuple_SetItem(id_list, i, PyInt_FromLong((long)change_info->shape_id[i]));
+    }
+
+    return Py_BuildValue( "(iiO)",
+                         change_info->change_type,
+                         change_info->num_shapes,
+                         id_list );
+}
+
+static PyObject *
+_wrap_gv_points_get_point(PyObject *self, PyObject *args)
+{
+    PyObject *pypoints;
+    GvPoints *points;
+    GvPoint *point;
+    int index;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_points_get_point", &PyGtk_Type,
+              &pypoints, &index))
+        return NULL;
+
+    points = GV_POINTS(PyGtk_Get(pypoints));
+    if (index < 0 || index >= gv_points_num_points(points))
+    {
+        PyErr_SetString(PyExc_IndexError, "point index out of range");
+        return NULL;
+    }
+    point = gv_points_get_point(points, index);
+    return Py_BuildValue("(" CC ")", point->v.x, point->v.y);
+}
+
+static PyObject *
+_wrap_gv_points_new_point(PyObject *self, PyObject *args)
+{
+    PyObject *pypoints;
+    GvVertex vertex;
+
+    if (!PyArg_ParseTuple(args, "O!(" CC "):gv_points_new_point", &PyGtk_Type,
+              &pypoints, &vertex.x, &vertex.y))
+    return NULL;
+    return PyInt_FromLong(gv_points_new_point(GV_POINTS(PyGtk_Get(pypoints)),
+                          &vertex));
+}
+
+static PyObject *
+build_py_line(GArray *line)
+{
+    PyObject *pylist;
+    GvVertex *v;
+    int i;
+
+    pylist = PyList_New(line->len);
+    for (i=0; i < line->len; i++)
+    {
+        PyObject *py_value;
+
+        v = &g_array_index(line, GvVertex, i);
+        py_value = Py_BuildValue("(" CC ")", v->x, v->y);
+        PyList_SetItem(pylist, i, py_value);
+    }
+    return pylist;
+}
+
+static GArray *
+build_gv_line(PyObject *pylist, int min_len)
+{
+    GArray *line;
+    GvVertex *v;
+    int  i;
+
+    if (PyList_Size(pylist) < min_len)
+    {
+    PyErr_SetString(PyExc_ValueError, "line too short");
+    return NULL;
+    }
+
+    line = g_array_new(FALSE, FALSE, sizeof(GvVertex));
+    g_array_set_size(line, PyList_Size(pylist));
+    for (i=0; i < line->len; i++)
+    {
+    v = &g_array_index(line, GvVertex, i);
+    if (!PyArg_ParseTuple(PyList_GET_ITEM(pylist, i), CC, &v->x, &v->y))
+    {
+        PyErr_SetString(PyExc_ValueError, "bad line format");
+        g_array_free(line, TRUE);
+        return NULL;
+    }
+    }
+    return line;
+}
+
+static PyObject *
+_wrap_gv_polylines_get_line(PyObject *self, PyObject *args)
+{
+    PyObject *pypline;
+    GvPolylines *pline;
+    int index;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_polylines_get_line", &PyGtk_Type,
+              &pypline, &index))
+    return NULL;
+    pline = GV_POLYLINES(PyGtk_Get(pypline));
+    if (index < 0 || index >= gv_polylines_num_lines(pline))
+    {
+    PyErr_SetString(PyExc_IndexError, "line index out of range");
+    return NULL;
+    }
+    return build_py_line(gv_polylines_get_line(pline, index));
+}
+
+static PyObject *
+_wrap_gv_polylines_new_line(PyObject *self, PyObject *args)
+{
+    PyObject *pypline, *pylist;
+    GArray *line;
+    int index;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_polylines_new_line", &PyGtk_Type,
+              &pypline, &PyList_Type, &pylist))
+    return NULL;
+    line = build_gv_line(pylist, 2);
+    if (!line) return NULL;
+    index = gv_polylines_new_line_with_data(GV_POLYLINES(PyGtk_Get(pypline)),
+                        line->len, (GvVertex*)line->data);
+    g_array_free(line, TRUE);
+    return PyInt_FromLong(index);
+}
+
+static PyObject *
+_wrap_gv_areas_get_area(PyObject *self, PyObject *args)
+{
+    PyObject *pyareas, *pyarea;
+    GvAreas *areas;
+    GvArea *area;
+    int index, ring;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_areas_get_area", &PyGtk_Type,
+              &pyareas, &index))
+    return NULL;
+    areas = GV_AREAS(PyGtk_Get(pyareas));
+    if (index < 0 || index >= gv_areas_num_areas(areas))
+    {
+    PyErr_SetString(PyExc_IndexError, "area index out of range");
+    return NULL;
+    }
+    area = gv_areas_get_area(areas, index);
+
+    pyarea = PyList_New(gv_areas_num_rings(area));
+    for (ring = 0; ring < gv_areas_num_rings(area); ring++)
+    {
+        PyObject *pyline = build_py_line(gv_areas_get_ring(area, ring));
+        PyList_SetItem(pyarea, ring, pyline);
+    }
+    return pyarea;
+}
+
+static PyObject *
+_wrap_gv_areas_new_area(PyObject *self, PyObject *args)
+{
+    PyObject *pyareas, *pyarea;
+    GvAreas *areas;
+    GvArea *area;
+    int index, ring, num_rings;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_areas_new_area", &PyGtk_Type,
+              &pyareas, &PyList_Type, &pyarea))
+    return NULL;
+    areas = GV_AREAS(PyGtk_Get(pyareas));
+    num_rings = PyList_Size(pyarea);
+    if (num_rings < 1)
+    {
+    PyErr_SetString(PyExc_ValueError, "empty ring list");
+    return NULL;
+    }
+
+    area = gv_area_new(FALSE);
+    for (ring = 0; ring < num_rings; ring++)
+    {
+    GArray *line = build_gv_line(PyList_GET_ITEM(pyarea, ring), 3);
+    if (!line) break;
+    g_ptr_array_add(area->rings, line);
+    }
+    if (PyErr_Occurred())
+    {
+    gv_area_delete(area);
+    return NULL;
+    }
+
+    index = gv_areas_new_area_with_data(areas, area);
+    gv_area_delete(area);
+    return PyInt_FromLong(index);
+}
+
+static PyObject *
+_wrap_gv_shape_layer_set_color(PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    GvColor color;
+
+    if (!PyArg_ParseTuple(args, "O!(ffff):gv_shape_layer_set_color",
+              &PyGtk_Type, &layer,
+              &color[0], &color[1], &color[2], &color[3]))
+    return NULL;
+    gv_shape_layer_set_color(GV_SHAPE_LAYER(PyGtk_Get(layer)), color);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_shape_layer_get_selected(PyObject *self, PyObject *args)
+{
+    PyObject  *layer, *list;
+    GArray    *array;
+    int       i;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shape_layer_set_color",
+              &PyGtk_Type, &layer ) )
+    return NULL;
+
+    array = g_array_new(FALSE,TRUE,sizeof(gint));
+    gv_shape_layer_selected(GV_SHAPE_LAYER(PyGtk_Get(layer)),
+                            GV_ALL, (void *) array );
+
+    list = PyList_New(array->len);
+    for( i = 0; i < array->len; i++ )
+    {
+        PyList_SetItem( list, i,
+                        Py_BuildValue("i", g_array_index(array,gint,i)) );
+    }
+
+    g_array_free( array, TRUE );
+
+    return list;
+}
+
+static PyObject *
+_wrap_gv_shape_layer_pick_shape( PyObject *self, PyObject *args )
+{
+    PyObject *layer;
+    PyObject *view;
+    gint shape_id;
+    float x, y;
+
+    if (!PyArg_ParseTuple(args, "O!O!ff:gv_shape_layer_pick_shape",
+                          &PyGtk_Type, &layer, &PyGtk_Type, &view, &x, &y ))
+    {
+        return NULL;
+    }
+
+    if (gv_shape_layer_pick_shape( GV_SHAPE_LAYER(PyGtk_Get(layer)),
+                                   GV_VIEW_AREA(PyGtk_Get(view)),
+                                   x, y, &shape_id ))
+    {
+        return PyInt_FromLong( (long)shape_id );
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+/******************
+ * gv_raster_new
+ *
+ * This function makes assumptions about the dataio lib and should
+ * be modified once the dataio situation is sorted out.
+ *
+ * Args: filename, format (optional)
+ ******************/
+
+static PyObject *
+_wrap_gv_raster_new(PyObject *self, PyObject *args, PyObject *keywds)
+{
+    PyObject * py_ret;
+    char *filename = NULL, *dataset_string = NULL;
+    GDALDatasetH  dataset;
+    static int gdal_initialized = 0;
+    GvSampleMethod sm = GvSMAverage;
+    int   rband = 1;
+    static char *kwlist[] = {"filename", "sample", "real",
+                             "dataset", NULL};
+    GvRaster *raster;
+
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "|ziiz", kwlist,
+                                     &filename, &sm, &rband,
+                                     &dataset_string))
+    return NULL;
+
+    if( !gdal_initialized )
+    {
+        GDALAllRegister();
+        gdal_initialized = 1;
+    }
+
+    if( dataset_string != NULL )
+    {
+        dataset = (GDALDatasetH)
+            SWIG_SimpleGetPtr(dataset_string, "GDALDatasetH" );
+        if (dataset == NULL)
+        {
+            PyErr_SetString(PyExc_IOError,
+                 "Unable to extract GDALDatasetH handle in gv_raster_new()");
+            return NULL;
+        }
+    }
+    else if( filename != NULL )
+    {
+        dataset = GDALOpen( filename, GA_ReadOnly );
+
+        if (dataset == NULL)
+        {
+            PyErr_SetString(PyExc_IOError, "failed to open data file");
+            return NULL;
+        }
+
+        GDALDereferenceDataset( dataset );
+    }
+    else
+    {
+        PyErr_SetString(PyExc_IOError,
+                        "gv_raster_new: either a filename, or dataset handle"
+                        " is required.  Neither provided." );
+        return NULL;
+    }
+
+    raster = GV_RASTER(gv_raster_new(dataset,rband,sm));
+    if( filename != NULL )
+        gv_data_set_name(GV_DATA(raster), filename);
+
+    py_ret = PyGtk_New( GTK_OBJECT(raster) );
+
+    gtk_object_sink( GTK_OBJECT(raster) );
+
+    return py_ret;
+}
+
+static PyObject *
+_wrap_gv_raster_autoscale(PyObject *self, PyObject *args)
+
+{
+    int alg = GvASAAutomatic, assign = 0, success;
+    double alg_param = -1.0;
+    PyObject *py_raster;
+    GvRaster *raster = NULL;
+    double   out_min, out_max;
+
+    if (!PyArg_ParseTuple(args, "O!idi:gv_raster_autoscale", &PyGtk_Type,
+                          &py_raster, &alg, &alg_param, &assign))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( assign == 0 )
+    {
+        success = gv_raster_autoscale(raster, alg, alg_param, 0, NULL,
+                                      NULL, NULL);
+        if( !success )
+        {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "autoscale() failed, failed to get samples?" );
+            return NULL;
+        }
+
+        return Py_BuildValue("(dd)", raster->min, raster->max);
+    }
+    else
+    {
+        success = gv_raster_autoscale(raster, alg, alg_param,
+                                      0, NULL,
+                                      &out_min, &out_max);
+
+        if( !success )
+        {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "autoscale() failed, failed to get samples?" );
+            return NULL;
+        }
+
+        return Py_BuildValue("(dd)", out_min, out_max );
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_get_gdal_band(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    char         swig_ptr[32];
+    GDALRasterBandH band;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_get_gdal_band",
+              &PyGtk_Type, &py_raster ) )
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+    if( raster == NULL )
+        return NULL;
+
+    band = raster->gdal_band;
+
+    if( band == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    SWIG_SimpleMakePtr( swig_ptr, band, "_GDALRasterBandH" );
+    return Py_BuildValue("s",swig_ptr);
+}
+
+static PyObject *
+_wrap_gv_raster_get_gdal_dataset(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    char         swig_ptr[32];
+    GDALDatasetH dataset;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_get_gdal_dataset",
+              &PyGtk_Type, &py_raster ) )
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+    if( raster == NULL )
+        return NULL;
+
+    dataset = raster->dataset;
+
+    if( dataset == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    SWIG_SimpleMakePtr( swig_ptr, dataset, "_GDALDatasetH" );
+    return Py_BuildValue("s",swig_ptr);
+}
+
+static PyObject *
+_wrap_gv_raster_force_load(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    int       i;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_force_load",
+              &PyGtk_Type, &py_raster ) )
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+    if( raster == NULL )
+        return NULL;
+
+    for( i = 0; i < raster->max_tiles; i++ )
+        gv_raster_tile_get( raster, i, 0 );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_raster_data_changing(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    int      x_off=0, y_off=0, width=0, height=0;
+    GvRasterChangeInfo change_info;
+
+    if (!PyArg_ParseTuple(args, "O!iiii:gv_raster_data_changing",
+              &PyGtk_Type, &py_raster,
+                          &x_off, &y_off, &width, &height ))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( height > 0 )
+    {
+        change_info.change_type = GV_CHANGE_REPLACE;
+        change_info.x_off = x_off;
+        change_info.y_off = y_off;
+        change_info.width = width;
+        change_info.height = height;
+
+        gv_data_changing( GV_DATA(raster), &change_info );
+    }
+    else
+    {
+        gv_data_changing( GV_DATA(raster), NULL );
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_raster_data_changed(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    int      x_off=0, y_off=0, width=0, height=0;
+    GvRasterChangeInfo change_info;
+
+    if (!PyArg_ParseTuple(args, "O!iiii:gv_raster_data_changed",
+              &PyGtk_Type, &py_raster,
+                          &x_off, &y_off, &width, &height ))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( height > 0 )
+    {
+        change_info.change_type = GV_CHANGE_REPLACE;
+        change_info.x_off = x_off;
+        change_info.y_off = y_off;
+        change_info.width = width;
+        change_info.height = height;
+
+        gv_data_changed( GV_DATA(raster), &change_info );
+    }
+    else
+    {
+        gv_data_changed( GV_DATA(raster), NULL );
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_raster_get_sample(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    double x, y, real, imaginary;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_get_sample",
+              &PyGtk_Type, &py_raster, &x, &y ))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( !gv_raster_get_sample(raster, x, y, &real, &imaginary ) )
+        return NULL;
+    else if( GDALDataTypeIsComplex(raster->gdal_type) )
+        return Py_BuildValue( "(ff)", real, imaginary );
+    else
+        return Py_BuildValue( "f", real );
+}
+
+static PyObject *
+_wrap_gv_raster_georef_to_pixel(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_georef_to_pixel",
+              &PyGtk_Type, &py_raster, &x, &y ))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( gv_raster_georef_to_pixel(raster, &x, &y, NULL) )
+    {
+        return Py_BuildValue( "(ff)", x, y );
+    }
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "georef_to_pixel transformation failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_pixel_to_georef(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_pixel_to_georef",
+              &PyGtk_Type, &py_raster, &x, &y ))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( gv_raster_pixel_to_georef(raster, &x, &y, NULL ) )
+    {
+        return Py_BuildValue("(ff)", x, y);
+    }
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "georef_to_pixel transformation failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_get_gcps(PyObject *self, PyObject *args) {
+
+    PyObject *py_raster;
+    GvRaster *raster;
+    const GDAL_GCP * pasGCPList;
+    PyObject *psList;
+    int iGCP;
+
+    self = self;
+    if(!PyArg_ParseTuple(args,"O!:gv_raster_get_gcps",
+                         &PyGtk_Type, &py_raster ))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+    pasGCPList = gv_raster_get_gcps( raster );
+
+    psList = PyList_New(raster->gcp_count);
+    for( iGCP = 0; pasGCPList != NULL && iGCP < raster->gcp_count; iGCP++)
+    {
+        PyObject *py_item;
+
+        py_item = Py_BuildValue("(ssddddd)",
+                                pasGCPList[iGCP].pszId,
+                                pasGCPList[iGCP].pszInfo,
+                                pasGCPList[iGCP].dfGCPPixel,
+                                pasGCPList[iGCP].dfGCPLine,
+                                pasGCPList[iGCP].dfGCPX,
+                                pasGCPList[iGCP].dfGCPY,
+                                pasGCPList[iGCP].dfGCPZ );
+    PyList_SetItem(psList, iGCP, py_item );
+    }
+
+    return psList;
+}
+
+static PyObject *
+_wrap_gv_raster_georef_to_pixelCL(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_georef_to_pixelCL",
+              &PyGtk_Type, &py_raster, &x, &y ))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( gv_raster_georef_to_pixelCL(raster, &x, &y, NULL) )
+    {
+        return Py_BuildValue( "(ff)", x, y );
+    }
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "georef_to_pixelCL transformation failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_pixel_to_georefCL(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRaster *raster;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_pixel_to_georefCL",
+              &PyGtk_Type, &py_raster, &x, &y ))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( gv_raster_pixel_to_georefCL(raster, &x, &y, NULL ) )
+    {
+        return Py_BuildValue("(ff)", x, y);
+    }
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "georef_to_pixelCL transformation failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_get_gcpsCL(PyObject *self, PyObject *args) {
+
+    PyObject *py_raster;
+    GvRaster *raster;
+    const GDAL_GCP * pasGCPList;
+    PyObject *psList;
+    int iGCP;
+
+    self = self;
+    if(!PyArg_ParseTuple(args,"O!:gv_raster_get_gcps",
+                         &PyGtk_Type, &py_raster ))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+    pasGCPList = gv_raster_get_gcpsCL( raster );
+
+    psList = PyList_New(raster->gcp_countCL);
+    for( iGCP = 0; pasGCPList != NULL && iGCP < raster->gcp_countCL; iGCP++)
+    {
+        PyObject *py_item;
+
+        py_item = Py_BuildValue("(ssddddd)",
+                                pasGCPList[iGCP].pszId,
+                                pasGCPList[iGCP].pszInfo,
+                                pasGCPList[iGCP].dfGCPPixel,
+                                pasGCPList[iGCP].dfGCPLine,
+                                pasGCPList[iGCP].dfGCPX,
+                                pasGCPList[iGCP].dfGCPY,
+                                pasGCPList[iGCP].dfGCPZ );
+    PyList_SetItem(psList, iGCP, py_item );
+    }
+
+    return psList;
+}
+
+static PyObject *
+_wrap_gv_raster_set_gcps(PyObject *self, PyObject *args) {
+
+    PyObject *py_raster;
+    GvRaster *raster;
+    GDAL_GCP * pasGCPList;
+    PyObject *psList;
+    int iGCP, nGCPCount, success;
+
+    self = self;
+    if(!PyArg_ParseTuple(args,"O!O!:gv_raster_set_gcps",
+                         &PyGtk_Type, &py_raster,
+                         &PyList_Type, &psList))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    nGCPCount = PyList_Size(psList);
+    pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount);
+    GDALInitGCPs( nGCPCount, pasGCPList );
+
+    for( iGCP = 0; iGCP < nGCPCount; iGCP++ )
+    {
+        char *pszId = NULL, *pszInfo = NULL;
+
+    if( !PyArg_Parse( PyList_GET_ITEM(psList,iGCP), "(ssddddd)",
+                      &pszId, &pszInfo,
+                      &(pasGCPList[iGCP].dfGCPPixel),
+                      &(pasGCPList[iGCP].dfGCPLine),
+                      &(pasGCPList[iGCP].dfGCPX),
+                      &(pasGCPList[iGCP].dfGCPY),
+                      &(pasGCPList[iGCP].dfGCPZ) ) )
+        {
+        PyErr_SetString(PyExc_ValueError, "improper GCP tuple");
+        return NULL;
+        }
+
+        CPLFree( pasGCPList[iGCP].pszId );
+    pasGCPList[iGCP].pszId = CPLStrdup(pszId);
+        CPLFree( pasGCPList[iGCP].pszInfo );
+    pasGCPList[iGCP].pszInfo = CPLStrdup(pszInfo);
+    }
+
+    success = gv_raster_set_gcps( raster, nGCPCount, pasGCPList );
+
+    GDALDeinitGCPs( nGCPCount, pasGCPList );
+    CPLFree( pasGCPList );
+
+    return Py_BuildValue("d", success);
+}
+
+static PyObject *
+_wrap_gv_raster_set_gcpsCL(PyObject *self, PyObject *args) {
+
+    PyObject *py_raster;
+    GvRaster *raster;
+    GDAL_GCP * pasGCPList;
+    PyObject *psList;
+    int iGCP, nGCPCount, success, poly_order;
+
+    self = self;
+    if(!PyArg_ParseTuple(args,"O!O!i:gv_raster_set_gcpsCL",
+                         &PyGtk_Type, &py_raster,
+                         &PyList_Type, &psList,
+                         &poly_order))
+        return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    nGCPCount = PyList_Size(psList);
+    pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount);
+    GDALInitGCPs( nGCPCount, pasGCPList );
+
+    for( iGCP = 0; iGCP < nGCPCount; iGCP++ )
+    {
+        char *pszId = NULL, *pszInfo = NULL;
+
+    if( !PyArg_Parse( PyList_GET_ITEM(psList,iGCP), "(ssddddd)",
+                      &pszId, &pszInfo,
+                      &(pasGCPList[iGCP].dfGCPPixel),
+                      &(pasGCPList[iGCP].dfGCPLine),
+                      &(pasGCPList[iGCP].dfGCPX),
+                      &(pasGCPList[iGCP].dfGCPY),
+                      &(pasGCPList[iGCP].dfGCPZ) ) )
+        {
+        PyErr_SetString(PyExc_ValueError, "improper GCP tuple");
+        return NULL;
+        }
+
+        CPLFree( pasGCPList[iGCP].pszId );
+    pasGCPList[iGCP].pszId = CPLStrdup(pszId);
+        CPLFree( pasGCPList[iGCP].pszInfo );
+    pasGCPList[iGCP].pszInfo = CPLStrdup(pszInfo);
+    }
+
+    success = gv_raster_set_gcpsCL( raster, nGCPCount, pasGCPList,poly_order);
+
+    GDALDeinitGCPs( nGCPCount, pasGCPList );
+    CPLFree( pasGCPList );
+
+    return Py_BuildValue("d", success);
+}
+
+static PyObject *
+_wrap_gv_raster_layer_new(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    PyObject *py_properties = NULL;
+    GvRaster *raster;
+    GvRasterLayer *layer;
+    GvProperties properties = NULL;
+    int      mode = GV_RLM_AUTO;
+
+    if (!PyArg_ParseTuple(args, "O!|iO!:gv_raster_layer_new",
+              &PyGtk_Type, &py_raster, &mode,
+                          &PyList_Type, &py_properties ))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( py_properties != NULL )
+    {
+        int     i;
+
+        for( i = 0; i < PyList_Size(py_properties); i++ )
+        {
+            char *name, *value;
+            PyObject *tuple = PyList_GET_ITEM(py_properties, i);
+
+            if (!PyArg_ParseTuple(tuple, "ss", &name, &value))
+            {
+                PyErr_SetString(PyExc_ValueError, "properties format");
+                return NULL;
+            }
+
+            gv_properties_set( &properties, name, value );
+        }
+    }
+
+    layer = GV_RASTER_LAYER(gv_raster_layer_new( mode, raster, properties ));
+
+    gv_properties_destroy( &properties );
+
+    return PyGtk_New(GTK_OBJECT(layer));
+}
+
+static PyObject *
+_wrap_gv_raster_layer_autoscale_view(PyObject *self, PyObject *args)
+
+{
+    int alg = GvASAAutomatic, isrc = 0, success;
+    double alg_param = -1.0;
+    PyObject *py_raster;
+    GvRasterLayer *rlayer = NULL;
+    double   out_min, out_max;
+
+    if (!PyArg_ParseTuple(args, "O!idi:gv_raster_layer_autoscale_view",
+                          &PyGtk_Type, &py_raster, &alg, &alg_param, &isrc))
+        return NULL;
+
+    rlayer = GV_RASTER_LAYER(PyGtk_Get(py_raster));
+
+    success = gv_raster_layer_autoscale_view(rlayer, isrc, alg, alg_param,
+                                             &out_min, &out_max );
+    if( !success )
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "autoscale() failed, failed to get samples?" );
+        return NULL;
+    }
+
+    return Py_BuildValue("(dd)", out_min, out_max );
+}
+
+static PyObject *
+_wrap_gv_raster_layer_get_mesh_lod(PyObject *self, PyObject *args)
+
+{
+    PyObject *py_raster;
+    GvRasterLayer *rlayer = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_layer_get_mesh_lod",
+                          &PyGtk_Type, &py_raster))
+        return NULL;
+
+    rlayer = GV_RASTER_LAYER(PyGtk_Get(py_raster));
+
+    return Py_BuildValue("i", rlayer->mesh->detail );
+}
+
+static PyObject *
+_wrap_gv_raster_layer_histogram_view(PyObject *self, PyObject *args)
+
+{
+    PyObject *py_rlayer, *py_list;
+    GvRasterLayer *rlayer = NULL;
+    double   scale_min, scale_max;
+    int      hist_size = 256, isrc = 0, hist_count, i;
+    int      *histogram = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!iddi:gv_raster_layer_autoscale_view",
+                          &PyGtk_Type, &py_rlayer, &isrc,
+                          &scale_min, &scale_max, &hist_size))
+        return NULL;
+
+    rlayer = GV_RASTER_LAYER(PyGtk_Get(py_rlayer));
+
+    histogram = g_new(int, hist_size);
+
+    hist_count =
+        gv_raster_layer_histogram_view(rlayer, isrc,
+                                       scale_min, scale_max, TRUE,
+                                       hist_size, histogram );
+    if( hist_count == 0 )
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "histogram() failed, failed to get samples?" );
+        return NULL;
+    }
+
+    py_list = PyList_New(0);
+    for( i = 0; i < hist_size; i++ )
+    {
+        PyObject *py_value = Py_BuildValue("i", histogram[i]);
+        PyList_Append(py_list, py_value);
+        Py_DECREF( py_value );
+    }
+    g_free( histogram );
+
+    return py_list;
+}
+
+static PyObject *
+_wrap_gv_raster_layer_pixel_to_view(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRasterLayer *raster;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_layer_pixel_to_view",
+              &PyGtk_Type, &py_raster, &x, &y ))
+    return NULL;
+
+    raster = GV_RASTER_LAYER(PyGtk_Get(py_raster));
+
+    if( gv_raster_layer_pixel_to_view(raster, &x, &y, NULL ) )
+    {
+        return Py_BuildValue("(ff)", x, y);
+    }
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "pixel_to_view transformation failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_view_to_pixel(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRasterLayer *raster;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_layer_view_to_pixel",
+              &PyGtk_Type, &py_raster, &x, &y ))
+    return NULL;
+
+    raster = GV_RASTER_LAYER(PyGtk_Get(py_raster));
+
+    if( gv_raster_layer_view_to_pixel(raster, &x, &y, NULL ) )
+    {
+        return Py_BuildValue("(ff)", x, y);
+    }
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "view_to_pixel transformation failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_texture_mode_set(PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    int texture_mode;
+    GvColor color;
+
+    if (!PyArg_ParseTuple(args, "O!i(ffff):gv_raster_layer_texture_mode_set",
+              &PyGtk_Type, &layer, &texture_mode,
+              &color[0], &color[1], &color[2], &color[3]))
+    return NULL;
+
+    return PyInt_FromLong( gv_raster_layer_texture_mode_set(GV_RASTER_LAYER(PyGtk_Get(layer)), texture_mode, color) );
+}
+
+static PyObject *
+_wrap_gv_raster_layer_alpha_get(PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    float alpha_val;
+    int alpha_mode;
+
+    if (!PyArg_ParseTuple( args, "O!:gv_raster_layer_alpha_get",
+               &PyGtk_Type, &layer ))
+    return NULL;
+
+    if (gv_raster_layer_alpha_get( GV_RASTER_LAYER( PyGtk_Get(layer)), &alpha_mode, &alpha_val ))
+    {
+    Py_INCREF(Py_None);
+    return Py_None;
+    } else {
+    return Py_BuildValue("(if)", alpha_mode, alpha_val );
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_blend_mode_get( PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    int blend_mode;
+    int sfactor;
+    int dfactor;
+
+    if (!PyArg_ParseTuple( args, "O!:gv_raster_layer_blend_mode_get",
+               &PyGtk_Type, &layer ))
+    return NULL;
+
+    if (gv_raster_layer_blend_mode_get( GV_RASTER_LAYER( PyGtk_Get(layer)), &blend_mode, &sfactor, &dfactor ))
+    {
+    Py_INCREF(Py_None);
+    return Py_None;
+    } else {
+    return Py_BuildValue("(iii)", blend_mode, sfactor, dfactor );
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_texture_mode_get( PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    int texture_mode;
+    GvColor color;
+    PyObject *py_color, *py_retval;
+
+    if (!PyArg_ParseTuple( args, "O!:gv_raster_layer_texture_mode_get",
+               &PyGtk_Type, &layer ))
+    return NULL;
+
+    if (gv_raster_layer_texture_mode_get( GV_RASTER_LAYER( PyGtk_Get(layer)), &texture_mode, &color ))
+    {
+    Py_INCREF(Py_None);
+    return Py_None;
+    } else {
+    py_color = Py_BuildValue( "(ffff)", color[0], color[1], color[2], color[3] );
+    py_retval = Py_BuildValue( "(iO)", texture_mode, py_color );
+    Py_DECREF( py_color );
+    return py_retval;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_lut_put( PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    char *lut;
+    int  lut_len, height;
+
+    if (!PyArg_ParseTuple( args, "O!z#:gv_raster_layer_lut_put",
+               &PyGtk_Type, &layer, &lut, &lut_len ))
+    return NULL;
+
+    if( lut != NULL && lut_len != 1024 && lut_len != 1024 * 256 )
+    {
+     PyErr_SetString(PyExc_TypeError,
+       "lut string must be 256x1x4 or 256x256x4 in gv_raster_layer_lut_put");
+     return NULL;
+    }
+
+    height = lut_len / 1024;
+    gv_raster_layer_lut_put( GV_RASTER_LAYER( PyGtk_Get(layer)),
+                             (unsigned char*)lut, height );
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_raster_layer_lut_get( PyObject *self, PyObject *args)
+{
+    PyObject *layer, *py_lut, *py_retval;
+    char *lut;
+    int width, height;
+    int rgba_complex=0;
+
+    if (!PyArg_ParseTuple( args, "O!|i:gv_raster_layer_lut_get",
+                           &PyGtk_Type, &layer, &rgba_complex ))
+        return NULL;
+
+    if ( (lut = (char *)gv_raster_layer_lut_get( GV_RASTER_LAYER( PyGtk_Get(layer)), &width, &height, rgba_complex )) == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    } else {
+
+        if ( ( py_lut = PyString_FromStringAndSize( lut, width * height * 4 ) ) == NULL )
+        {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+
+        py_retval = Py_BuildValue( "(Oii)", py_lut, width, height );
+        Py_DECREF( py_lut );
+        return py_retval;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_source_get_lut( PyObject *self, PyObject *args)
+{
+    PyObject *layer, *py_lut;
+    char *lut;
+    int isource=0;
+
+    if (!PyArg_ParseTuple( args, "O!i:gv_raster_layer_source_get_lut",
+                           &PyGtk_Type, &layer, &isource ))
+    return NULL;
+
+    lut = (char *)gv_raster_layer_source_get_lut(
+                            GV_RASTER_LAYER( PyGtk_Get(layer)), isource );
+    if( lut == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if ( ( py_lut = PyString_FromStringAndSize( lut, 256 ) ) == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return py_lut;
+}
+
+static PyObject *
+_wrap_gv_raster_layer_nodata_get( PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    GvRasterLayer *rlayer;
+    int isource = 0;
+    gint ret;
+    double nodata_real, nodata_imaginary;
+
+    if (!PyArg_ParseTuple( args, "O!i:gv_raster_layer_nodata_get",
+                           &PyGtk_Type, &layer, &isource ))
+        return NULL;
+
+    rlayer = GV_RASTER_LAYER(PyGtk_Get(layer));
+    ret = gv_raster_layer_nodata_get( rlayer, isource,
+                                      &nodata_real, &nodata_imaginary );
+    if( ret )
+    {
+        if( GDALDataTypeIsComplex(gv_raster_layer_type_get(rlayer, isource)) )
+            return Py_BuildValue( "(ff)", nodata_real, nodata_imaginary );
+        else
+            return Py_BuildValue( "f", nodata_real );
+    }
+    else
+    {
+        Py_INCREF( Py_None );
+        return Py_None;
+    }
+}
+
+/* XXX: gv_raster_layer_get_nodata() now DEPRECATED. */
+static PyObject *
+_wrap_gv_raster_layer_get_nodata( PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    GvRasterLayer *rlayer;
+    int isource=0;
+
+    if (!PyArg_ParseTuple( args, "O!i:gv_raster_layer_get_nodata",
+               &PyGtk_Type, &layer, &isource ))
+        return NULL;
+
+    rlayer = GV_RASTER_LAYER( PyGtk_Get(layer) );
+    if( rlayer != NULL && isource >= 0 && isource < rlayer->source_count )
+    {
+        GvRasterSource *source = rlayer->source_list + isource;
+
+        if( source->nodata_active && source->data != NULL
+            && source->data->gdal_type == GDT_CFloat32 )
+            return Py_BuildValue( "(ff)", source->nodata_real,
+                                  source->nodata_imaginary );
+        else
+            return Py_BuildValue( "f", source->nodata_real );
+    }
+    else
+    {
+        Py_INCREF( Py_None );
+        return Py_None;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_set_source( PyObject *self, PyObject *args)
+{
+    PyObject *layer, *py_raster;
+    GvRaster *raster = NULL;
+    char *lut = NULL;
+    int isource, const_value, ret, lut_len=0, nodata_active=FALSE;
+    float min, max;
+    PyObject *nodata = NULL;
+    float nodata_real=-1e8, nodata_imaginary=0.0;
+
+    if (!PyArg_ParseTuple( args, "O!iOffi|z#O:gv_raster_layer_set_source",
+                           &PyGtk_Type, &layer, &isource,
+                           &py_raster, &min, &max, &const_value,
+                           &lut, &lut_len, &nodata ))
+    return NULL;
+
+    if( py_raster == NULL || py_raster == Py_None )
+        raster = NULL;
+    else
+        raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    if( nodata == NULL || nodata == Py_None )
+    {
+        nodata_real = 0.0;
+        nodata_imaginary = 0.0;
+        nodata_active = FALSE;
+    }
+    else if( PyTuple_Check(nodata) )
+    {
+        if( !PyArg_ParseTuple( nodata, "ff", &nodata_real, &nodata_imaginary) )
+            return NULL;
+
+        nodata_active = TRUE;
+    }
+    else
+    {
+        if( !PyArg_Parse( nodata, "f", &nodata_real ) )
+            return NULL;
+
+        nodata_imaginary = 0.0;
+        nodata_active = TRUE;
+    }
+
+    ret = gv_raster_layer_set_source( GV_RASTER_LAYER( PyGtk_Get(layer)),
+                                      isource, raster,
+                                      min, max, const_value,
+                                      (unsigned char*)lut,
+                                      nodata_active,
+                                      nodata_real, nodata_imaginary );
+
+    return Py_BuildValue("i", ret);
+}
+
+static PyObject *
+_wrap_gv_raster_layer_zoom_get( PyObject *self, PyObject *args)
+{
+    PyObject *layer;
+    int mag, min;
+
+    if (!PyArg_ParseTuple( args, "O!:gv_raster_layer_zoom_get",
+                &PyGtk_Type, &layer ))
+    return NULL;
+
+    if( gv_raster_layer_zoom_get( GV_RASTER_LAYER( PyGtk_Get(layer)), &mag, &min ) )
+    {
+    Py_INCREF(Py_None);
+    return Py_None;
+    } else {
+    return Py_BuildValue( "(ii)", min, mag );
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_layer_get_height(PyObject *self, PyObject *args)
+{
+    PyObject *py_raster;
+    GvRasterLayer *layer;
+    GvMesh *mesh;
+    double x, y, result;
+    int    success;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_raster_layer_get_height",
+              &PyGtk_Type, &py_raster, &x, &y ))
+    return NULL;
+
+    layer = GV_RASTER_LAYER(PyGtk_Get(py_raster));
+    mesh = layer->mesh;
+
+    result = gv_mesh_get_height( mesh, x, y, &success );
+
+    if( success )
+        return Py_BuildValue("f", result );
+    else
+    {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "gv_mesh_get_height() failed." );
+        return NULL;
+    }
+}
+
+static PyObject *
+_wrap_gv_raster_get_change_info(PyObject *self, PyObject *args)
+{
+    GvRasterChangeInfo *change_info;
+    PyObject *c_change_info;
+
+    if (!PyArg_ParseTuple(args, "O:gv_raster_get_change_info", &c_change_info))
+    return NULL;
+
+    if (!PyCObject_Check (c_change_info))
+        return NULL;
+
+    change_info = (GvRasterChangeInfo *) PyCObject_AsVoidPtr(c_change_info);
+
+    return Py_BuildValue( "(iiiii)",
+                          change_info->change_type,
+                          change_info->x_off,
+                          change_info->y_off,
+                          change_info->width,
+                          change_info->height
+                         );
+}
+
+
+static PyObject *
+_wrap_gv_manager_get_preferences(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    GvProperties *properties = NULL;
+    PyObject *psDict = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_manager_get_properties",
+                          &PyGtk_Type, &manager))
+    return NULL;
+
+    if( manager != NULL )
+        properties =
+            gv_manager_get_preferences(GV_MANAGER(PyGtk_Get(manager)));
+
+    psDict = PyDict_New();
+    if( properties != NULL )
+    {
+        int        i, count;
+
+        count = gv_properties_count( properties );
+        for( i = 0; i < count; i++ )
+        {
+            const char *value, *name;
+            PyObject *py_name, *py_value;
+
+            value = gv_properties_get_value_by_index(properties,i);
+            name = gv_properties_get_name_by_index(properties,i);
+
+            py_name = Py_BuildValue("s",name);
+            py_value = Py_BuildValue("s",value);
+            PyDict_SetItem( psDict, py_name, py_value );
+
+            Py_DECREF(py_name);
+            Py_DECREF(py_value);
+        }
+    }
+
+    return psDict;
+}
+
+static PyObject *
+_wrap_gv_manager_add_dataset(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    char     *dataset_string=NULL;
+    char      swig_ptr[32];
+    GDALDatasetH dataset = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_manager_get_dataset",
+                          &PyGtk_Type, &manager, &dataset_string))
+        return NULL;
+
+    if( manager == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    dataset = (GDALDatasetH) SWIG_SimpleGetPtr(dataset_string, "GDALDatasetH");
+    if( dataset == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    dataset = gv_manager_add_dataset( GV_MANAGER(PyGtk_Get(manager)), dataset );
+    SWIG_SimpleMakePtr( swig_ptr, dataset, "_GDALDatasetH" );
+    return Py_BuildValue( "s", swig_ptr );
+}
+
+static PyObject *
+_wrap_gv_manager_get_dataset(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    char     *filename = NULL;
+    char      swig_ptr[32];
+    GDALDatasetH dataset = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_manager_get_dataset",
+                          &PyGtk_Type, &manager, &filename))
+        return NULL;
+
+    if( manager != NULL )
+        dataset = gv_manager_get_dataset( GV_MANAGER(PyGtk_Get(manager)),
+                                          filename );
+
+    if( dataset == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    else
+    {
+        SWIG_SimpleMakePtr( swig_ptr, dataset, "_GDALDatasetH" );
+        return Py_BuildValue( "s", swig_ptr );
+    }
+}
+
+static PyObject *
+_wrap_gv_manager_get_dataset_raster(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    char     *dataset_string=NULL;
+    int       band = 0;
+    GDALDatasetH dataset = NULL;
+    GvRaster *raster = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!si:gv_manager_get_dataset_raster",
+                          &PyGtk_Type, &manager, &dataset_string, &band))
+        return NULL;
+
+    if( manager == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    dataset = (GDALDatasetH) SWIG_SimpleGetPtr(dataset_string, "GDALDatasetH");
+    if( dataset == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    raster = gv_manager_get_dataset_raster( GV_MANAGER(PyGtk_Get(manager)),
+                                            dataset, band );
+
+    if( raster == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    else
+    {
+        return PyGtk_New((GtkObject*)raster);
+    }
+}
+
+/* -------------------------------------------------------------------- */
+/*      Idle Task C callback structure.                                 */
+/* -------------------------------------------------------------------- */
+typedef struct {
+    PyObject *psPyCallback;
+    PyObject *psPyCallbackData;
+    PyThreadState *psThreadState;
+} PyTaskData;
+
+int PyIdleTaskProxy( void *task_info )
+
+{
+    PyTaskData *psInfo = (PyTaskData *) task_info;
+    PyObject *psArgs, *psResult;
+    int      bContinue = TRUE;
+    PyThreadState *tstate;
+
+    tstate = PyThreadState_Swap( psInfo->psThreadState );
+
+    psArgs = Py_BuildValue("(O)", psInfo->psPyCallbackData );
+
+    psResult = PyEval_CallObject( psInfo->psPyCallback, psArgs);
+
+    tstate = PyThreadState_Swap( tstate );
+    if( tstate == NULL )
+    {
+        CPLDebug( "OpenEV",
+                  "PyIdleTaskProxy: Thread state unexpectedly disappeared.\n"
+                  "                  Skipping check for error.\n" );
+    }
+    else
+    {
+        tstate = PyThreadState_Swap( tstate );
+        if( PyErr_Occurred() )
+        {
+            PyErr_Print();
+            PyErr_Clear();
+        }
+    }
+
+    Py_XDECREF(psArgs);
+
+    PyThreadState_Swap( tstate );
+
+    if( psResult == NULL
+        || psResult == Py_None
+        || !PyArg_Parse( psResult, "i", &bContinue )
+        || !bContinue )
+    {
+        bContinue = FALSE;
+        Py_XDECREF( psInfo->psPyCallback );
+        Py_XDECREF( psInfo->psPyCallbackData );
+        g_free( task_info );
+    }
+
+    Py_XDECREF(psResult);
+
+    return bContinue;
+}
+
+static PyObject *
+_wrap_gv_manager_queue_task(PyObject *self, PyObject *args)
+{
+    PyObject *manager;
+    int       priority;
+    char      *task_name;
+    PyTaskData *psProgressInfo;
+
+    psProgressInfo = g_new(PyTaskData,1);
+    psProgressInfo->psPyCallback = NULL;
+    psProgressInfo->psPyCallbackData = Py_None;
+
+    if (!PyArg_ParseTuple(args, "O!siO|O:gv_manager_queue_task",
+                          &PyGtk_Type, &manager, &task_name, &priority,
+                          &(psProgressInfo->psPyCallback),
+                          &(psProgressInfo->psPyCallbackData) ) )
+    return NULL;
+
+    if( manager == NULL )
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    Py_XINCREF( psProgressInfo->psPyCallback );
+    Py_XINCREF( psProgressInfo->psPyCallbackData );
+
+    psProgressInfo->psThreadState = PyThreadState_Get();
+
+    gv_manager_queue_task( GV_MANAGER(PyGtk_Get(manager)),
+                           task_name, priority,
+                           PyIdleTaskProxy,
+                           psProgressInfo );
+
+    Py_INCREF(Py_None);
+
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_launch_url(PyObject *self, PyObject *args)
+{
+    char *url = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_launch_url",
+                          &url))
+    return NULL;
+
+    return Py_BuildValue("i",gv_launch_url(url));
+}
+
+static PyObject *
+_wrap_gv_rgba_to_rgb(PyObject *self, PyObject *args)
+{
+    PyObject *rgba_obj = NULL;
+    PyObject *rgb_obj = NULL;
+    const char *rgba;
+    char       *rgb;
+    int length, i;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_rgba_to_rgb",
+                          &PyString_Type, &rgba_obj))
+    return NULL;
+
+    length = PyString_Size( rgba_obj ) / 4;
+    rgba = PyString_AS_STRING( rgba_obj );
+
+    rgb = (char *) malloc(length*3);
+
+    for( i = 0; i < length; i++ )
+    {
+        rgb[i*3  ] = rgba[i*4  ];
+        rgb[i*3+1] = rgba[i*4+1];
+        rgb[i*3+2] = rgba[i*4+2];
+    }
+
+    rgb_obj = PyString_FromStringAndSize( rgb, length * 3 );
+
+    free( rgb );
+
+    return rgb_obj;
+}
+
+/*
+ * Algorithms not very specific to OpenEV.
+ */
+
+static PyObject *
+_wrap_WIDInterpolate(PyObject *self, PyObject *args)
+{
+    PyObject *poPyPoints;
+    PyProgressData sProgressInfo;
+    char *pszSwigBand = NULL;
+    int nPoints, i, nErr;
+    double *padfXYVW;
+    double fExponent;
+    GDALRasterBandH hBand;
+
+    sProgressInfo.psPyCallback = NULL;
+    sProgressInfo.psPyCallbackData = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!s|dOO:WIDInterpolate",
+                          &PyList_Type, &poPyPoints,
+                          &pszSwigBand,
+                          &fExponent,
+                          &(sProgressInfo.psPyCallback),
+                          &(sProgressInfo.psPyCallbackData) ) )
+    return NULL;
+
+    hBand = (GDALRasterBandH)
+        SWIG_SimpleGetPtr(pszSwigBand, "_GDALRasterBandH" );
+
+    if( hBand == NULL )
+    {
+        PyErr_SetString( PyExc_ValueError,
+                         "Couldn't parse GDALRasterBandH argument." );
+        return NULL;
+    }
+
+    nPoints = PyList_Size(poPyPoints);
+    padfXYVW = g_new(double,4*nPoints);
+    for( i = 0; i < nPoints; i++ )
+    {
+        if( !PyArg_Parse( PyList_GET_ITEM(poPyPoints,i), "(dddd)",
+                          padfXYVW + i + 0*nPoints,
+                          padfXYVW + i + 1*nPoints,
+                          padfXYVW + i + 2*nPoints,
+                          padfXYVW + i + 3*nPoints ) )
+        {
+            g_free( padfXYVW );
+        PyErr_SetString(PyExc_ValueError,
+                            "bad point format (x,y,value,weight)" );
+            return NULL;
+        }
+    }
+
+    nErr = WIDInterpolate( nPoints, padfXYVW, padfXYVW+nPoints,
+                           padfXYVW+nPoints*2, padfXYVW+nPoints*3, hBand,
+                           fExponent, PyProgressProxy, &sProgressInfo );
+
+    g_free( padfXYVW );
+
+    return Py_BuildValue( "i", nErr );
+}
+
+static PyObject *
+_wrap_gv_raster_rasterize_shapes(PyObject *self, PyObject *args)
+{
+    PyObject *py_shapelist;
+    PyObject *py_raster;
+    GvRaster *raster;
+    double   burn_value;
+    int      shape_count, i, ret_value, fill_short = 1;
+    GvShape  **shape_list;
+
+    if (!PyArg_ParseTuple(args, "O!O!di:gv_raster_rasterize_shapes",
+              &PyGtk_Type, &py_raster,
+                          &PyList_Type, &py_shapelist,
+                          &burn_value, &fill_short))
+    return NULL;
+
+    raster = GV_RASTER(PyGtk_Get(py_raster));
+
+    shape_count = PyList_Size(py_shapelist);
+    shape_list = g_new(GvShape*,shape_count);
+    for( i = 0; i < shape_count; i++ )
+    {
+        GvShape *gv_shape;
+        char *swig_shape_ptr;
+
+        if( !PyArg_Parse( PyList_GET_ITEM(py_shapelist,i), "s",
+                          &swig_shape_ptr) )
+        {
+            g_free( shape_list );
+            PyErr_SetString( PyExc_ValueError,
+                             "bad item in shapelist" );
+            return NULL;
+        }
+        gv_shape = SWIG_SimpleGetPtr( swig_shape_ptr, "_GvShape" );
+        if( gv_shape == NULL )
+        {
+            g_free( shape_list );
+            PyErr_SetString( PyExc_ValueError,
+                             "bad item in shapelist(2)" );
+            return NULL;
+        }
+
+        shape_list[i] = gv_shape;
+    }
+
+    ret_value = gv_raster_rasterize_shapes( raster, shape_count, shape_list,
+                                            burn_value, fill_short );
+
+    return Py_BuildValue( "i", ret_value );
+}
+
+/*
+ * wrapper function for getting a DOS 8.3 compatible file name on
+ * Windows systems.  On other systems, this will return the
+ * value passed in.
+ */
+static PyObject * _wrap_gv_short_path_name(PyObject *self, PyObject *args)
+{
+    char *lpszLongPath = NULL;
+    PyObject * result = NULL;
+
+    if (!PyArg_ParseTuple(args, "s:gv_short_path_name",
+                          &lpszLongPath))
+        return NULL;
+
+    result = Py_BuildValue("s",gv_short_path_name(lpszLongPath));
+
+    //invalid path in lpszLongPath results in zero length string
+    if (PyString_Size(result) == 0)
+    {
+        PyErr_Format(PyExc_OSError, "path (%s) does not exist.", lpszLongPath);
+        return NULL;
+    }
+
+    return result;
+}
+
+static PyObject * _wrap_gtk_color_well_get_d( PyObject *self, PyObject *args)
+{
+    PyObject *color_well;
+    gdouble r, g, b, a;
+
+    if (!PyArg_ParseTuple(args, "O!:gtk_color_well_get_d",
+                          &PyGtk_Type, &color_well))
+        return NULL;
+
+    gtk_color_well_get_d( GTK_COLOR_WELL(PyGtk_Get(color_well)),
+                          &r, &g, &b, &a );
+
+    return Py_BuildValue( "(dddd)", r, g, b, a );
+}
+
+static PyObject *_wrap_gv_shapes_to_dbf(PyObject *self, PyObject *args) {
+    PyObject *data;
+    char *filename;
+
+    if (!PyArg_ParseTuple(args, "sO!:gv_shapes_to_dbf", &filename, &PyGtk_Type, &data))
+        return NULL;
+    return PyInt_FromLong(gv_shapes_to_dbf(filename, GV_DATA(PyGtk_Get(data))));
+}
+
+static PyObject *_wrap_gv_format_point_query( PyObject *self, PyObject *args )
+{
+    gdouble x, y;
+    char *text;
+    PyObject *py_view;
+    GvViewArea *view = NULL;
+    GvManager *manager;
+
+    if (!PyArg_ParseTuple( args, "O!dd:gv_format_point_query", &PyGtk_Type, &py_view, &x, &y ))
+        return NULL;
+
+    if (py_view != NULL)
+        view = GV_VIEW_AREA(PyGtk_Get( py_view ));
+
+    manager = gv_get_manager();
+
+    text = (char *) gv_format_point_query( view, &(manager->preferences), x, y );
+
+    return Py_BuildValue( "s", text );
+}
+
+
+static PyObject *_wrap_gv_test_entry( PyObject *self, PyObject *args )
+
+{
+    PyObject *py_rlayer;
+    GvRasterLayer *rlayer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_test_entry",
+                          &PyGtk_Type, &py_rlayer ))
+        return NULL;
+
+    rlayer = GV_RASTER_LAYER(PyGtk_Get(py_rlayer));
+
+    return Py_BuildValue( "d", 0.0 );
+}
+
+static PyObject *
+_wrap_MyGDALOperator(PyObject *self, PyObject *args)
+{
+    char *pszSwigDS1 = NULL;
+    char *pszSwigDS2 = NULL;
+    GDALDatasetH hDS1 = NULL, hDS2 = NULL;
+    int  nErr = 1;
+
+    if (!PyArg_ParseTuple(args, "ss:MyGDALOperator",
+                          &pszSwigDS1, &pszSwigDS2 ) )
+        return NULL;
+
+    hDS1 = (GDALDatasetH) SWIG_SimpleGetPtr(pszSwigDS1, "_GDALDatasetH" );
+    hDS2 = (GDALDatasetH) SWIG_SimpleGetPtr(pszSwigDS2, "_GDALDatasetH" );
+
+    if( hDS1 != NULL && hDS2 != NULL )
+    {
+        /* do something with hDS1 and hDS2 */
+        printf( "%s -> %s\n",
+                GDALGetDescription( hDS1 ),
+                GDALGetDescription( hDS2 ) );
+        nErr = 0;
+    }
+
+    return Py_BuildValue( "i", nErr );
+}
+
+
+static PyObject *
+_wrap_gv_autopan_tool_new_rect(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!(" CCCC "):gv_autopan_tool_new_rect", &PyGtk_Type, &tool, &rect.x, &rect.y,
+                          &rect.width, &rect.height))
+    return NULL;
+
+    if (!gv_autopan_tool_new_rect(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), &rect))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "Invalid extents specified");
+    return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_gv_autopan_tool_get_rect(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect rect;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_get_rect", &PyGtk_Type, &tool))
+    return NULL;
+    if (!gv_autopan_tool_get_rect(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), &rect))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "no pan region set");
+    return NULL;
+    }
+    return Py_BuildValue("(" CCCC ")", rect.x, rect.y, rect.width, rect.height);
+}
+
+static PyObject *
+_wrap_gv_autopan_tool_get_location(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    gvgeocoord x, y, z;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_get_location", &PyGtk_Type, &tool))
+    return NULL;
+    if (!gv_autopan_tool_get_location(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), &x, &y, &z))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "no location set");
+    return NULL;
+    }
+    return Py_BuildValue("(" CCC ")", x, y, z);
+}
+
+static PyObject *
+_wrap_gv_autopan_tool_get_state(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    gvgeocoord block_x_size, x_resolution;
+    gint play_flag, path_type, block_size_mode, num_views;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_get_state", &PyGtk_Type, &tool))
+        return NULL;
+    
+    gv_autopan_tool_get_state(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), &play_flag, &path_type, &block_size_mode, &block_x_size, &x_resolution, &num_views);
+
+    return Py_BuildValue("(iii" CC "i)", play_flag, path_type, block_size_mode, block_x_size, x_resolution, num_views);
+}
+
+
+
+static PyObject *
+_wrap_gv_autopan_tool_get_trail_parameters(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect overview_extents;
+    gint overview_width_pixels, num_trail_tiles;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_get_trail_parameters", &PyGtk_Type, &tool))
+        return NULL;
+    
+    gv_autopan_tool_get_trail_parameters(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), &overview_extents, &overview_width_pixels, &num_trail_tiles);
+
+    return Py_BuildValue("(" CCCC "ii)", overview_extents.x, overview_extents.y,overview_extents.width, overview_extents.height, overview_width_pixels, num_trail_tiles);
+}
+
+
+static PyObject *
+_wrap_gv_autopan_tool_set_trail_parameters(PyObject *self, PyObject *args)
+{
+    PyObject *tool;
+    GvRect rect;
+    int overview_width_pixels;
+
+    if (!PyArg_ParseTuple(args, "O!(" CCCC ")i:gv_autopan_tool_set_trail_parameters", &PyGtk_Type, &tool, &rect.x, &rect.y,
+                          &rect.width, &rect.height, &overview_width_pixels))
+    return NULL;
+
+    if (!gv_autopan_tool_set_trail_parameters(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), &rect,overview_width_pixels))
+    {
+    PyErr_SetString(PyExc_RuntimeError, "Invalid parameters specified");
+    return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+/*
+ * Generated wrapper functions
+ */
+
+#include "gvmodule_impl.c"
+
+/*
+ * Method defs
+ */
+
+static PyMethodDef gv_methods[] =
+{
+    {"gv_test_entry", _wrap_gv_test_entry, 1},
+    {"gv_shape_new", _wrap_gv_shape_new, 1},
+    {"gv_shape_from_xml", _wrap_gv_shape_from_xml, 1},
+    {"gv_shape_to_xml", _wrap_gv_shape_to_xml, 1},
+    {"gv_shape_destroy", _wrap_gv_shape_destroy, 1},
+    {"gv_shape_ref", _wrap_gv_shape_ref, 1},
+    {"gv_shape_unref", _wrap_gv_shape_unref, 1},
+    {"gv_shape_get_ref", _wrap_gv_shape_get_ref, 1},
+    {"gv_shape_copy", _wrap_gv_shape_copy, 1},
+    {"gv_shape_delete_ring", _wrap_gv_shape_delete_ring, 1},
+    {"gv_shape_line_from_nodelists",_wrap_gv_shape_line_from_nodelists, 1},
+    {"gv_shapes_lines_for_vecplot",_wrap_gv_shapes_lines_for_vecplot, 1},
+    {"gv_shape_get_property", _wrap_gv_shape_get_property, 1},
+    {"gv_shape_get_properties", _wrap_gv_shape_get_properties, 1},
+    {"gv_shape_get_typed_properties", _wrap_gv_shape_get_typed_properties, 1},
+    {"gv_shape_set_property", _wrap_gv_shape_set_property, 1},
+    {"gv_shape_set_properties", _wrap_gv_shape_set_properties, 1},
+    {"gv_shape_get_rings", _wrap_gv_shape_get_rings, 1},
+    {"gv_shape_get_nodes", _wrap_gv_shape_get_nodes, 1},
+    {"gv_shape_add_node", _wrap_gv_shape_add_node, 1},
+    {"gv_shape_set_node", _wrap_gv_shape_set_node, 1},
+    {"gv_shape_get_node", _wrap_gv_shape_get_node, 1},
+    {"gv_shape_get_type", _wrap_gv_shape_get_type, 1},
+    {"gv_shape_point_in_polygon", _wrap_gv_shape_point_in_polygon, 1},
+    {"gv_shape_distance_from_polygon", _wrap_gv_shape_distance_from_polygon, 1},
+    {"gv_shape_clip_to_rect", _wrap_gv_shape_clip_to_rect, 1},
+    {"gv_shape_add_shape", _wrap_gv_shape_add_shape, 1},
+    {"gv_shape_get_shape", _wrap_gv_shape_get_shape, 1},
+    {"gv_shape_collection_get_count", _wrap_gv_shape_collection_get_count, 1},
+    {"gv_symbol_manager_get_names", _wrap_gv_symbol_manager_get_names, 1 },
+    {"gv_symbol_manager_inject_vector_symbol",
+     _wrap_gv_symbol_manager_inject_vector_symbol, 1},
+    {"gv_symbol_manager_inject_raster_symbol",
+     _wrap_gv_symbol_manager_inject_raster_symbol, 1},
+    {"gv_symbol_manager_get_symbol", _wrap_gv_symbol_manager_get_symbol, 1},
+    {"gv_symbol_manager_save_vector_symbol",
+     _wrap_gv_symbol_manager_save_vector_symbol, 1},
+    {"gv_data_get_properties", _wrap_gv_data_get_properties, 1},
+    {"gv_data_set_properties", _wrap_gv_data_set_properties, 1},
+    {"gv_data_changed", _wrap_gv_data_changed, 1},
+    {"gv_layer_extents", _wrap_gv_layer_extents, 1},
+    {"gv_layer_display_change", _wrap_gv_layer_display_change, 1},
+    {"gv_view_area_list_layers", _wrap_gv_view_area_list_layers, 1},
+    {"gv_view_area_get_translation", _wrap_gv_view_area_get_translation, 1},
+    {"gv_view_area_map_location", _wrap_gv_view_area_map_location, 1},
+    {"gv_view_area_get_pointer", _wrap_gv_view_area_get_pointer, 1},
+    {"gv_view_area_map_pointer", _wrap_gv_view_area_map_pointer, 1},
+    {"gv_view_area_get_extents", _wrap_gv_view_area_get_extents, 1 },
+    {"gv_view_area_get_volume", _wrap_gv_view_area_get_volume, 1 },
+    {"gv_view_area_inverse_map_pointer",
+                                 _wrap_gv_view_area_inverse_map_pointer, 1},
+    {"gv_view_area_get_fontnames", _wrap_gv_view_area_get_fontnames, 1},
+    {"gv_view_area_set_background_color",
+                    _wrap_gv_view_area_set_background_color, 1},
+    {"gv_view_area_get_background_color",
+                    _wrap_gv_view_area_get_background_color, 1},
+    {"gv_view_area_create_thumbnail", _wrap_gv_view_area_create_thumbnail, 1},
+    {"gv_view_area_set_3d_view", _wrap_gv_view_area_set_3d_view, 1 },
+    {"gv_view_area_set_3d_view_look_at", _wrap_gv_view_area_set_3d_view_look_at, 1 },
+    {"gv_view_area_get_eye_pos", _wrap_gv_view_area_get_eye_pos, 1 },
+    {"gv_view_area_get_eye_dir", _wrap_gv_view_area_get_eye_dir, 1 },
+    {"gv_view_area_get_look_at_pos", _wrap_gv_view_area_get_look_at_pos, 1 },
+    {"gv_roi_tool_get_rect", _wrap_gv_roi_tool_get_rect, 1},
+    {"gv_roi_tool_new_rect", _wrap_gv_roi_tool_new_rect, 1},
+    {"gv_poi_tool_get_point", _wrap_gv_poi_tool_get_point, 1},
+    {"gv_poi_tool_new_point", _wrap_gv_poi_tool_new_point, 1},
+    {"gv_tool_set_boundary", _wrap_gv_tool_set_boundary, 1},
+    {"gv_records_set_used_properties", 
+     _wrap_gv_records_set_used_properties, 1 },
+    {"gv_records_get_typed_properties", 
+     _wrap_gv_records_get_typed_properties, 1},
+    {"gv_records_get_properties", _wrap_gv_records_get_properties, 1},
+    {"gv_records_to_dbf", _wrap_gv_records_to_dbf, 1},
+    {"gv_records_MultiStratifiedCollect", 
+     _wrap_gv_records_MultiStratifiedCollect, 1},
+    {"gv_records_recode", _wrap_gv_records_recode, 1},
+    {"gv_records_asdict", _wrap_gv_records_asdict, 1},
+    {"gv_shapes_from_ogr_layer", _wrap_gv_shapes_from_ogr_layer, 1},
+    {"gv_shapes_get_shape", _wrap_gv_shapes_get_shape, 1},
+    {"gv_shapes_add_shape", _wrap_gv_shapes_add_shape, 1},
+    {"gv_shapes_add_shape_last", _wrap_gv_shapes_add_shape_last, 1},
+    {"gv_shapes_delete_shapes", _wrap_gv_shapes_delete_shapes, 1},
+    {"gv_shapes_replace_shapes", _wrap_gv_shapes_replace_shapes, 1},
+    {"gv_shapes_get_extents", _wrap_gv_shapes_get_extents, 1},
+    {"gv_shapes_get_change_info", _wrap_gv_shapes_get_change_info, 1},
+    {"gv_points_get_point", _wrap_gv_points_get_point, 1},
+    {"gv_points_new_point", _wrap_gv_points_new_point, 1},
+    {"gv_polylines_get_line", _wrap_gv_polylines_get_line, 1},
+    {"gv_polylines_new_line", _wrap_gv_polylines_new_line, 1},
+    {"gv_areas_get_area", _wrap_gv_areas_get_area, 1},
+    {"gv_areas_new_area", _wrap_gv_areas_new_area, 1},
+    {"gv_shape_layer_set_color", _wrap_gv_shape_layer_set_color, 1},
+    {"gv_shape_layer_get_selected", _wrap_gv_shape_layer_get_selected, 1},
+    {"gv_shape_layer_pick_shape", _wrap_gv_shape_layer_pick_shape, 1 },
+    {"gv_raster_new", (PyCFunction)_wrap_gv_raster_new,
+                                   METH_VARARGS|METH_KEYWORDS},
+    {"gv_raster_autoscale", _wrap_gv_raster_autoscale, 1},
+    {"gv_raster_get_gdal_band", _wrap_gv_raster_get_gdal_band, 1},
+    {"gv_raster_get_gdal_dataset", _wrap_gv_raster_get_gdal_dataset, 1},
+    {"gv_raster_force_load", _wrap_gv_raster_force_load, 1 },
+    {"gv_raster_get_sample", _wrap_gv_raster_get_sample, 1},
+    {"gv_raster_georef_to_pixel", _wrap_gv_raster_georef_to_pixel, 1},
+    {"gv_raster_pixel_to_georef", _wrap_gv_raster_pixel_to_georef, 1},
+    {"gv_raster_georef_to_pixelCL", _wrap_gv_raster_georef_to_pixelCL, 1},
+    {"gv_raster_pixel_to_georefCL", _wrap_gv_raster_pixel_to_georefCL, 1},
+    {"gv_raster_data_changing", _wrap_gv_raster_data_changing, 1},
+    {"gv_raster_data_changed", _wrap_gv_raster_data_changed, 1},
+    {"gv_raster_get_change_info", _wrap_gv_raster_get_change_info, 1},
+    {"gv_raster_get_gcps", _wrap_gv_raster_get_gcps, 1},
+    {"gv_raster_set_gcps", _wrap_gv_raster_set_gcps, 1},
+    {"gv_raster_get_gcpsCL", _wrap_gv_raster_get_gcpsCL, 1},
+    {"gv_raster_set_gcpsCL", _wrap_gv_raster_set_gcpsCL, 1},
+    {"gv_raster_layer_new", _wrap_gv_raster_layer_new, 1},
+    {"gv_raster_layer_autoscale_view", _wrap_gv_raster_layer_autoscale_view,1},
+    {"gv_raster_layer_get_mesh_lod", _wrap_gv_raster_layer_get_mesh_lod,1},
+    {"gv_raster_layer_histogram_view", _wrap_gv_raster_layer_histogram_view,1},
+    {"gv_raster_layer_view_to_pixel", _wrap_gv_raster_layer_view_to_pixel, 1},
+    {"gv_raster_layer_pixel_to_view", _wrap_gv_raster_layer_pixel_to_view, 1},
+    {"gv_raster_layer_set_source", _wrap_gv_raster_layer_set_source, 1},
+    {"gv_raster_layer_texture_mode_set", _wrap_gv_raster_layer_texture_mode_set, 1},
+    {"gv_raster_layer_texture_mode_get", _wrap_gv_raster_layer_texture_mode_get, 1},
+    {"gv_raster_layer_alpha_get", _wrap_gv_raster_layer_alpha_get, 1},
+    {"gv_raster_layer_blend_mode_get", _wrap_gv_raster_layer_blend_mode_get, 1},
+    {"gv_raster_layer_lut_put", _wrap_gv_raster_layer_lut_put, 1},
+    {"gv_raster_layer_lut_get", _wrap_gv_raster_layer_lut_get, 1},
+    {"gv_raster_layer_nodata_get", _wrap_gv_raster_layer_nodata_get,1},
+    {"gv_raster_layer_get_nodata", _wrap_gv_raster_layer_get_nodata,1},
+    {"gv_raster_layer_source_get_lut", _wrap_gv_raster_layer_source_get_lut,1},
+    {"gv_raster_layer_zoom_get", _wrap_gv_raster_layer_zoom_get, 1},
+    {"gv_raster_layer_get_height", _wrap_gv_raster_layer_get_height, 1},
+    {"gv_manager_get_preferences", _wrap_gv_manager_get_preferences, 1},
+    {"gv_manager_add_dataset", _wrap_gv_manager_add_dataset, 1},
+    {"gv_manager_get_dataset", _wrap_gv_manager_get_dataset, 1},
+    {"gv_manager_get_dataset_raster", _wrap_gv_manager_get_dataset_raster, 1},
+    {"gv_manager_queue_task", _wrap_gv_manager_queue_task, 1},
+    {"gv_launch_url", _wrap_gv_launch_url, 1},
+    {"gv_rgba_to_rgb", _wrap_gv_rgba_to_rgb, 1},
+    {"WIDInterpolate", _wrap_WIDInterpolate, 1},
+    {"gv_raster_rasterize_shapes", _wrap_gv_raster_rasterize_shapes, 1},
+    {"gv_short_path_name", _wrap_gv_short_path_name, 1},
+    {"gtk_color_well_get_d", _wrap_gtk_color_well_get_d, 1},
+    {"gv_shapes_to_dbf", _wrap_gv_shapes_to_dbf, 1},
+    {"gv_format_point_query", _wrap_gv_format_point_query, 1},
+    {"MyGDALOperator", _wrap_MyGDALOperator, 1},
+    {"gv_autopan_tool_new_rect", _wrap_gv_autopan_tool_new_rect, 1},
+    {"gv_autopan_tool_get_rect", _wrap_gv_autopan_tool_get_rect, 1},
+    {"gv_autopan_tool_get_location", _wrap_gv_autopan_tool_get_location, 1},
+    {"gv_autopan_tool_get_state", _wrap_gv_autopan_tool_get_state, 1},
+    {"gv_autopan_tool_get_trail_parameters", _wrap_gv_autopan_tool_get_trail_parameters, 1},
+    {"gv_autopan_tool_set_trail_parameters", _wrap_gv_autopan_tool_set_trail_parameters, 1},
+#include "gvmodule_defs.c"
+    {NULL, NULL, 0}
+};
+
+/*
+ * Module initialization function
+ */
+
+void
+init_gv(void)
+{
+    init_pygtk();
+
+    Py_InitModule("_gv", gv_methods);
+
+    if (PyErr_Occurred())
+    Py_FatalError("can't initialize module _gv");
+}

Added: packages/openev/branches/upstream/current/pymod/gvmodule_defs.c
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvmodule_defs.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvmodule_defs.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,234 @@
+    { "gv_view_area_get_type", _wrap_gv_view_area_get_type, 1 },
+    { "gv_view_area_new", _wrap_gv_view_area_new, 1 },
+    { "gv_view_area_get_width", _wrap_gv_view_area_get_width, 1 },
+    { "gv_view_area_get_height", _wrap_gv_view_area_get_height, 1 },
+    { "gv_view_area_add_layer", _wrap_gv_view_area_add_layer, 1 },
+    { "gv_view_area_remove_layer", _wrap_gv_view_area_remove_layer, 1 },
+    { "gv_view_area_get_named_layer", _wrap_gv_view_area_get_named_layer, 1 },
+    { "gv_view_area_active_layer", _wrap_gv_view_area_active_layer, 1 },
+    { "gv_view_area_set_active_layer", _wrap_gv_view_area_set_active_layer, 1 },
+    { "gv_view_area_swap_layers", _wrap_gv_view_area_swap_layers, 1 },
+    { "gv_view_area_zoom", _wrap_gv_view_area_zoom, 1 },
+    { "gv_view_area_get_zoom", _wrap_gv_view_area_get_zoom, 1 },
+    { "gv_view_area_get_flip_x", _wrap_gv_view_area_get_flip_x, 1 },
+    { "gv_view_area_get_flip_y", _wrap_gv_view_area_get_flip_y, 1 },
+    { "gv_view_area_set_flip_xy", _wrap_gv_view_area_set_flip_xy, 1 },
+    { "gv_view_area_rotate", _wrap_gv_view_area_rotate, 1 },
+    { "gv_view_area_translate", _wrap_gv_view_area_translate, 1 },
+    { "gv_view_area_set_translation", _wrap_gv_view_area_set_translation, 1 },
+    { "gv_view_area_fit_extents", _wrap_gv_view_area_fit_extents, 1 },
+    { "gv_view_area_fit_all_layers", _wrap_gv_view_area_fit_all_layers, 1 },
+    { "gv_view_area_set_projection", _wrap_gv_view_area_set_projection, 1 },
+    { "gv_view_area_get_projection", _wrap_gv_view_area_get_projection, 1 },
+    { "gv_view_area_copy_state", _wrap_gv_view_area_copy_state, 1 },
+    { "gv_view_area_print_to_file", _wrap_gv_view_area_print_to_file, 1 },
+    { "gv_view_area_print_postscript_to_file", _wrap_gv_view_area_print_postscript_to_file, 1 },
+    { "gv_view_area_page_setup", _wrap_gv_view_area_page_setup, 1 },
+    { "gv_view_area_print_to_windriver", _wrap_gv_view_area_print_to_windriver, 1 },
+    { "gv_view_area_set_mode", _wrap_gv_view_area_set_mode, 1 },
+    { "gv_view_area_get_mode", _wrap_gv_view_area_get_mode, 1 },
+    { "gv_view_area_height_scale", _wrap_gv_view_area_height_scale, 1 },
+    { "gv_view_area_get_height_scale", _wrap_gv_view_area_get_height_scale, 1 },
+    { "gv_view_area_queue_draw", _wrap_gv_view_area_queue_draw, 1 },
+    { "gv_view_area_get_raw", _wrap_gv_view_area_get_raw, 1 },
+    { "gv_view_area_set_raw", _wrap_gv_view_area_set_raw, 1 },
+    { "gv_view_area_get_property", _wrap_gv_view_area_get_property, 1 },
+    { "gv_view_area_set_property", _wrap_gv_view_area_set_property, 1 },
+    { "gv_data_get_type", _wrap_gv_data_get_type, 1 },
+    { "gv_data_is_read_only", _wrap_gv_data_is_read_only, 1 },
+    { "gv_data_set_read_only", _wrap_gv_data_set_read_only, 1 },
+    { "gv_data_get_name", _wrap_gv_data_get_name, 1 },
+    { "gv_data_set_name", _wrap_gv_data_set_name, 1 },
+    { "gv_data_get_projection", _wrap_gv_data_get_projection, 1 },
+    { "gv_data_set_projection", _wrap_gv_data_set_projection, 1 },
+    { "gv_data_get_property", _wrap_gv_data_get_property, 1 },
+    { "gv_data_set_property", _wrap_gv_data_set_property, 1 },
+    { "gv_data_freeze", _wrap_gv_data_freeze, 1 },
+    { "gv_data_thaw", _wrap_gv_data_thaw, 1 },
+    { "gv_data_get_parent", _wrap_gv_data_get_parent, 1 },
+    { "gv_data_registry_dump", _wrap_gv_data_registry_dump, 1 },
+    { "gv_records_get_type", _wrap_gv_records_get_type, 1 },
+    { "gv_records_new", _wrap_gv_records_new, 1 },
+    { "gv_records_from_dbf", _wrap_gv_records_from_dbf, 1 },
+    { "gv_records_from_rec", _wrap_gv_records_from_rec, 1 },
+    { "gv_records_create_records", _wrap_gv_records_create_records, 1 },
+    { "gv_records_num_records", _wrap_gv_records_num_records, 1 },
+    { "gv_records_add_field", _wrap_gv_records_add_field, 1 },
+    { "gv_records_set_raw_field_data", _wrap_gv_records_set_raw_field_data, 1 },
+    { "gv_records_get_raw_field_data", _wrap_gv_records_get_raw_field_data, 1 },
+    { "gv_shapes_get_type", _wrap_gv_shapes_get_type, 1 },
+    { "gv_shapes_new", _wrap_gv_shapes_new, 1 },
+    { "gv_shapes_from_shapefile", _wrap_gv_shapes_from_shapefile, 1 },
+    { "gv_shapes_to_shapefile", _wrap_gv_shapes_to_shapefile, 1 },
+    { "gv_shapes_from_ogr", _wrap_gv_shapes_from_ogr, 1 },
+    { "gv_have_ogr_support", _wrap_gv_have_ogr_support, 1 },
+    { "gv_shapes_num_shapes", _wrap_gv_shapes_num_shapes, 1 },
+    { "gv_shapes_add_height", _wrap_gv_shapes_add_height, 1 },
+    { "gv_shape_get_count", _wrap_gv_shape_get_count, 1 },
+    { "gv_points_get_type", _wrap_gv_points_get_type, 1 },
+    { "gv_points_new", _wrap_gv_points_new, 1 },
+    { "gv_points_num_points", _wrap_gv_points_num_points, 1 },
+    { "gv_polylines_get_type", _wrap_gv_polylines_get_type, 1 },
+    { "gv_polylines_new", _wrap_gv_polylines_new, 1 },
+    { "gv_polylines_num_lines", _wrap_gv_polylines_num_lines, 1 },
+    { "gv_areas_get_type", _wrap_gv_areas_get_type, 1 },
+    { "gv_areas_new", _wrap_gv_areas_new, 1 },
+    { "gv_areas_num_areas", _wrap_gv_areas_num_areas, 1 },
+    { "gv_layer_get_type", _wrap_gv_layer_get_type, 1 },
+    { "gv_layer_is_visible", _wrap_gv_layer_is_visible, 1 },
+    { "gv_layer_set_visible", _wrap_gv_layer_set_visible, 1 },
+    { "gv_layer_reproject", _wrap_gv_layer_reproject, 1 },
+    { "gv_layer_get_view", _wrap_gv_layer_get_view, 1 },
+    { "gv_shape_layer_get_type", _wrap_gv_shape_layer_get_type, 1 },
+    { "gv_shape_layer_clear_selection", _wrap_gv_shape_layer_clear_selection, 1 },
+    { "gv_shape_layer_select_all", _wrap_gv_shape_layer_select_all, 1 },
+    { "gv_shape_layer_select_shape", _wrap_gv_shape_layer_select_shape, 1 },
+    { "gv_shape_layer_deselect_shape", _wrap_gv_shape_layer_deselect_shape, 1 },
+    { "gv_shape_layer_subselect_shape", _wrap_gv_shape_layer_subselect_shape, 1 },
+    { "gv_shape_layer_get_subselection", _wrap_gv_shape_layer_get_subselection, 1 },
+    { "gv_shapes_layer_get_type", _wrap_gv_shapes_layer_get_type, 1 },
+    { "gv_shapes_layer_new", _wrap_gv_shapes_layer_new, 1 },
+    { "gv_shapes_layer_get_symbol_manager", _wrap_gv_shapes_layer_get_symbol_manager, 1 },
+    { "gv_point_layer_get_type", _wrap_gv_point_layer_get_type, 1 },
+    { "gv_point_layer_new", _wrap_gv_point_layer_new, 1 },
+    { "gv_line_layer_get_type", _wrap_gv_line_layer_get_type, 1 },
+    { "gv_line_layer_new", _wrap_gv_line_layer_new, 1 },
+    { "gv_area_layer_get_type", _wrap_gv_area_layer_get_type, 1 },
+    { "gv_area_layer_new", _wrap_gv_area_layer_new, 1 },
+    { "gv_pquery_layer_get_type", _wrap_gv_pquery_layer_get_type, 1 },
+    { "gv_pquery_layer_new", _wrap_gv_pquery_layer_new, 1 },
+    { "ip_gcp_layer_get_type", _wrap_ip_gcp_layer_get_type, 1 },
+    { "ip_gcp_layer_new", _wrap_ip_gcp_layer_new, 1 },
+    { "app_cur_layer_get_type", _wrap_app_cur_layer_get_type, 1 },
+    { "app_cur_layer_new", _wrap_app_cur_layer_new, 1 },
+    { "gv_symbol_manager_get_type", _wrap_gv_symbol_manager_get_type, 1 },
+    { "gv_get_symbol_manager", _wrap_gv_get_symbol_manager, 1 },
+    { "gv_symbol_manager_eject_symbol", _wrap_gv_symbol_manager_eject_symbol, 1 },
+    { "gv_symbol_manager_has_symbol", _wrap_gv_symbol_manager_has_symbol, 1 },
+    { "gv_manager_get_type", _wrap_gv_manager_get_type, 1 },
+    { "gv_get_manager", _wrap_gv_get_manager, 1 },
+    { "gv_manager_get_preference", _wrap_gv_manager_get_preference, 1 },
+    { "gv_manager_set_preference", _wrap_gv_manager_set_preference, 1 },
+    { "gv_manager_get_busy", _wrap_gv_manager_get_busy, 1 },
+    { "gv_manager_set_busy", _wrap_gv_manager_set_busy, 1 },
+    { "gv_manager_dump", _wrap_gv_manager_dump, 1 },
+    { "gv_tool_get_type", _wrap_gv_tool_get_type, 1 },
+    { "gv_tool_activate", _wrap_gv_tool_activate, 1 },
+    { "gv_tool_deactivate", _wrap_gv_tool_deactivate, 1 },
+    { "gv_tool_get_view", _wrap_gv_tool_get_view, 1 },
+    { "gv_tool_set_cursor", _wrap_gv_tool_set_cursor, 1 },
+    { "gv_selection_tool_get_type", _wrap_gv_selection_tool_get_type, 1 },
+    { "gv_selection_tool_new", _wrap_gv_selection_tool_new, 1 },
+    { "gv_selection_tool_set_layer", _wrap_gv_selection_tool_set_layer, 1 },
+    { "gv_zoompan_tool_get_type", _wrap_gv_zoompan_tool_get_type, 1 },
+    { "gv_zoompan_tool_new", _wrap_gv_zoompan_tool_new, 1 },
+    { "gv_point_tool_get_type", _wrap_gv_point_tool_get_type, 1 },
+    { "gv_point_tool_new", _wrap_gv_point_tool_new, 1 },
+    { "gv_point_tool_set_layer", _wrap_gv_point_tool_set_layer, 1 },
+    { "gv_point_tool_set_named_layer", _wrap_gv_point_tool_set_named_layer, 1 },
+    { "gv_line_tool_get_type", _wrap_gv_line_tool_get_type, 1 },
+    { "gv_line_tool_new", _wrap_gv_line_tool_new, 1 },
+    { "gv_line_tool_set_layer", _wrap_gv_line_tool_set_layer, 1 },
+    { "gv_line_tool_set_named_layer", _wrap_gv_line_tool_set_named_layer, 1 },
+    { "gv_rect_tool_get_type", _wrap_gv_rect_tool_get_type, 1 },
+    { "gv_rect_tool_new", _wrap_gv_rect_tool_new, 1 },
+    { "gv_rect_tool_set_layer", _wrap_gv_rect_tool_set_layer, 1 },
+    { "gv_rect_tool_set_named_layer", _wrap_gv_rect_tool_set_named_layer, 1 },
+    { "gv_rotate_tool_get_type", _wrap_gv_rotate_tool_get_type, 1 },
+    { "gv_rotate_tool_new", _wrap_gv_rotate_tool_new, 1 },
+    { "gv_rotate_tool_set_layer", _wrap_gv_rotate_tool_set_layer, 1 },
+    { "gv_rotate_tool_set_named_layer", _wrap_gv_rotate_tool_set_named_layer, 1 },
+    { "gv_area_tool_get_type", _wrap_gv_area_tool_get_type, 1 },
+    { "gv_area_tool_new", _wrap_gv_area_tool_new, 1 },
+    { "gv_area_tool_set_layer", _wrap_gv_area_tool_set_layer, 1 },
+    { "gv_area_tool_set_named_layer", _wrap_gv_area_tool_set_named_layer, 1 },
+    { "gv_node_tool_get_type", _wrap_gv_node_tool_get_type, 1 },
+    { "gv_node_tool_new", _wrap_gv_node_tool_new, 1 },
+    { "gv_node_tool_set_layer", _wrap_gv_node_tool_set_layer, 1 },
+    { "gv_roi_tool_get_type", _wrap_gv_roi_tool_get_type, 1 },
+    { "gv_roi_tool_new", _wrap_gv_roi_tool_new, 1 },
+    { "gv_poi_tool_get_type", _wrap_gv_poi_tool_get_type, 1 },
+    { "gv_poi_tool_new", _wrap_gv_poi_tool_new, 1 },
+    { "gv_track_tool_get_type", _wrap_gv_track_tool_get_type, 1 },
+    { "gv_track_tool_new", _wrap_gv_track_tool_new, 1 },
+    { "gv_toolbox_get_type", _wrap_gv_toolbox_get_type, 1 },
+    { "gv_toolbox_new", _wrap_gv_toolbox_new, 1 },
+    { "gv_toolbox_add_tool", _wrap_gv_toolbox_add_tool, 1 },
+    { "gv_toolbox_activate_tool", _wrap_gv_toolbox_activate_tool, 1 },
+    { "gv_undo_register_data", _wrap_gv_undo_register_data, 1 },
+    { "gv_undo_pop", _wrap_gv_undo_pop, 1 },
+    { "gv_undo_clear", _wrap_gv_undo_clear, 1 },
+    { "gv_undo_can_undo", _wrap_gv_undo_can_undo, 1 },
+    { "gv_undo_close", _wrap_gv_undo_close, 1 },
+    { "gv_undo_open", _wrap_gv_undo_open, 1 },
+    { "gv_undo_start_group", _wrap_gv_undo_start_group, 1 },
+    { "gv_undo_end_group", _wrap_gv_undo_end_group, 1 },
+    { "gv_view_link_get_type", _wrap_gv_view_link_get_type, 1 },
+    { "gv_view_link_new", _wrap_gv_view_link_new, 1 },
+    { "gv_view_link_register_view", _wrap_gv_view_link_register_view, 1 },
+    { "gv_view_link_remove_view", _wrap_gv_view_link_remove_view, 1 },
+    { "gv_view_link_enable", _wrap_gv_view_link_enable, 1 },
+    { "gv_view_link_disable", _wrap_gv_view_link_disable, 1 },
+    { "gv_view_link_set_cursor_mode", _wrap_gv_view_link_set_cursor_mode, 1 },
+    { "gv_raster_get_type", _wrap_gv_raster_get_type, 1 },
+    { "gv_raster_flush_cache", _wrap_gv_raster_flush_cache, 1 },
+    { "gv_raster_get_min", _wrap_gv_raster_get_min, 1 },
+    { "gv_raster_get_max", _wrap_gv_raster_get_max, 1 },
+    { "gv_raster_cache_get_max", _wrap_gv_raster_cache_get_max, 1 },
+    { "gv_raster_cache_get_used", _wrap_gv_raster_cache_get_used, 1 },
+    { "gv_raster_cache_set_max", _wrap_gv_raster_cache_set_max, 1 },
+    { "gv_raster_set_poly_order_preference", _wrap_gv_raster_set_poly_order_preference, 1 },
+    { "gv_raster_layer_get_type", _wrap_gv_raster_layer_get_type, 1 },
+    { "gv_raster_layer_lut_color_wheel_new", _wrap_gv_raster_layer_lut_color_wheel_new, 1 },
+    { "gv_raster_layer_lut_color_wheel_new_ev", _wrap_gv_raster_layer_lut_color_wheel_new_ev, 1 },
+    { "gv_raster_layer_lut_color_wheel_1d_new", _wrap_gv_raster_layer_lut_color_wheel_1d_new, 1 },
+    { "gv_raster_layer_alpha_set", _wrap_gv_raster_layer_alpha_set, 1 },
+    { "gv_raster_layer_min_set", _wrap_gv_raster_layer_min_set, 1 },
+    { "gv_raster_layer_min_get", _wrap_gv_raster_layer_min_get, 1 },
+    { "gv_raster_layer_max_set", _wrap_gv_raster_layer_max_set, 1 },
+    { "gv_raster_layer_max_get", _wrap_gv_raster_layer_max_get, 1 },
+    { "gv_raster_layer_nodata_set", _wrap_gv_raster_layer_nodata_set, 1 },
+    { "gv_raster_layer_type_get", _wrap_gv_raster_layer_type_get, 1 },
+    { "gv_raster_layer_get_const_value", _wrap_gv_raster_layer_get_const_value, 1 },
+    { "gv_raster_layer_get_data", _wrap_gv_raster_layer_get_data, 1 },
+    { "gv_raster_layer_get_mode", _wrap_gv_raster_layer_get_mode, 1 },
+    { "gv_raster_layer_texture_clamp_set", _wrap_gv_raster_layer_texture_clamp_set, 1 },
+    { "gv_raster_layer_zoom_set", _wrap_gv_raster_layer_zoom_set, 1 },
+    { "gv_raster_layer_blend_mode_set", _wrap_gv_raster_layer_blend_mode_set, 1 },
+    { "gv_raster_layer_lut_type_get", _wrap_gv_raster_layer_lut_type_get, 1 },
+    { "gv_raster_layer_add_height", _wrap_gv_raster_layer_add_height, 1 },
+    { "gv_raster_layer_clamp_height", _wrap_gv_raster_layer_clamp_height, 1 },
+    { "gv_texture_cache_dump", _wrap_gv_texture_cache_dump, 1 },
+    { "gv_texture_cache_get_max", _wrap_gv_texture_cache_get_max, 1 },
+    { "gv_texture_cache_get_used", _wrap_gv_texture_cache_get_used, 1 },
+    { "gv_texture_cache_set_max", _wrap_gv_texture_cache_set_max, 1 },
+    { "gv_build_skirt", _wrap_gv_build_skirt, 1 },
+    { "gtk_color_well_get_type", _wrap_gtk_color_well_get_type, 1 },
+    { "gtk_color_well_new", _wrap_gtk_color_well_new, 1 },
+    { "gtk_color_well_set_d", _wrap_gtk_color_well_set_d, 1 },
+    { "gtk_color_well_set_i8", _wrap_gtk_color_well_set_i8, 1 },
+    { "gtk_color_well_set_i16", _wrap_gtk_color_well_set_i16, 1 },
+    { "gtk_color_well_set_use_alpha", _wrap_gtk_color_well_set_use_alpha, 1 },
+    { "gtk_color_well_set_continuous", _wrap_gtk_color_well_set_continuous, 1 },
+    { "gtk_color_well_set_title", _wrap_gtk_color_well_set_title, 1 },
+    { "gv_autopan_tool_get_type", _wrap_gv_autopan_tool_get_type, 1 },
+    { "gv_autopan_tool_new", _wrap_gv_autopan_tool_new, 1 },
+    { "gv_autopan_tool_play", _wrap_gv_autopan_tool_play, 1 },
+    { "gv_autopan_tool_pause", _wrap_gv_autopan_tool_pause, 1 },
+    { "gv_autopan_tool_stop", _wrap_gv_autopan_tool_stop, 1 },
+    { "gv_autopan_tool_set_speed", _wrap_gv_autopan_tool_set_speed, 1 },
+    { "gv_autopan_tool_get_speed", _wrap_gv_autopan_tool_get_speed, 1 },
+    { "gv_autopan_tool_set_location", _wrap_gv_autopan_tool_set_location, 1 },
+    { "gv_autopan_tool_set_overlap", _wrap_gv_autopan_tool_set_overlap, 1 },
+    { "gv_autopan_tool_get_overlap", _wrap_gv_autopan_tool_get_overlap, 1 },
+    { "gv_autopan_tool_set_block_x_size", _wrap_gv_autopan_tool_set_block_x_size, 1 },
+    { "gv_autopan_tool_set_x_resolution", _wrap_gv_autopan_tool_set_x_resolution, 1 },
+    { "gv_autopan_tool_set_standard_path", _wrap_gv_autopan_tool_set_standard_path, 1 },
+    { "gv_autopan_tool_set_lines_path", _wrap_gv_autopan_tool_set_lines_path, 1 },
+    { "gv_autopan_tool_clear_trail", _wrap_gv_autopan_tool_clear_trail, 1 },
+    { "gv_autopan_tool_set_trail_color", _wrap_gv_autopan_tool_set_trail_color, 1 },
+    { "gv_autopan_tool_set_trail_mode", _wrap_gv_autopan_tool_set_trail_mode, 1 },
+    { "gv_autopan_tool_save_trail_tiles", _wrap_gv_autopan_tool_save_trail_tiles, 1 },
+    { "gv_autopan_tool_load_trail_tiles", _wrap_gv_autopan_tool_load_trail_tiles, 1 },
+    { "gv_autopan_tool_register_view", _wrap_gv_autopan_tool_register_view, 1 },
+    { "gv_autopan_tool_remove_view", _wrap_gv_autopan_tool_remove_view, 1 },

Added: packages/openev/branches/upstream/current/pymod/gvmodule_impl.c
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvmodule_impl.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvmodule_impl.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2129 @@
+static PyObject *_wrap_gv_view_area_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_view_area_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_type());
+}
+
+static PyObject *_wrap_gv_view_area_new(PyObject *self, PyObject *args) {
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, ":gv_view_area_new"))
+        return NULL;
+    retval = (GtkObject *)gv_view_area_new();
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_width(PyObject *self, PyObject *args) {
+    PyObject *view;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_width", &PyGtk_Type, &view))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_width(GV_VIEW_AREA(PyGtk_Get(view))));
+}
+
+static PyObject *_wrap_gv_view_area_get_height(PyObject *self, PyObject *args) {
+    PyObject *view;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_height", &PyGtk_Type, &view))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_height(GV_VIEW_AREA(PyGtk_Get(view))));
+}
+
+static PyObject *_wrap_gv_view_area_add_layer(PyObject *self, PyObject *args) {
+    PyObject *view, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_area_add_layer", &PyGtk_Type, &view, &PyGtk_Type, &layer))
+        return NULL;
+    gv_view_area_add_layer(GV_VIEW_AREA(PyGtk_Get(view)), GTK_OBJECT(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_remove_layer(PyObject *self, PyObject *args) {
+    PyObject *view, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_area_remove_layer", &PyGtk_Type, &view, &PyGtk_Type, &layer))
+        return NULL;
+    gv_view_area_remove_layer(GV_VIEW_AREA(PyGtk_Get(view)), GTK_OBJECT(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_named_layer(PyObject *self, PyObject *args) {
+    PyObject *view;
+    const char *layer_name;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_view_area_get_named_layer", &PyGtk_Type, &view, &layer_name))
+        return NULL;
+    retval = (GtkObject *)gv_view_area_get_named_layer(GV_VIEW_AREA(PyGtk_Get(view)), layer_name);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_active_layer(PyObject *self, PyObject *args) {
+    PyObject *view;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_active_layer", &PyGtk_Type, &view))
+        return NULL;
+    retval = (GtkObject *)gv_view_area_active_layer(GV_VIEW_AREA(PyGtk_Get(view)));
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_set_active_layer(PyObject *self, PyObject *args) {
+    PyObject *area, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_area_set_active_layer", &PyGtk_Type, &area, &PyGtk_Type, &layer))
+        return NULL;
+    gv_view_area_set_active_layer(GV_VIEW_AREA(PyGtk_Get(area)), GTK_OBJECT(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_swap_layers(PyObject *self, PyObject *args) {
+    PyObject *area;
+    int layer_a, layer_b;
+
+    if (!PyArg_ParseTuple(args, "O!ii:gv_view_area_swap_layers", &PyGtk_Type, &area, &layer_a, &layer_b))
+        return NULL;
+    gv_view_area_swap_layers(GV_VIEW_AREA(PyGtk_Get(area)), layer_a, layer_b);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_zoom(PyObject *self, PyObject *args) {
+    PyObject *area;
+    double zoom;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_view_area_zoom", &PyGtk_Type, &area, &zoom))
+        return NULL;
+    gv_view_area_zoom(GV_VIEW_AREA(PyGtk_Get(area)), zoom);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_zoom(PyObject *self, PyObject *args) {
+    PyObject *view;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_zoom", &PyGtk_Type, &view))
+        return NULL;
+    return PyFloat_FromDouble(gv_view_area_get_zoom(GV_VIEW_AREA(PyGtk_Get(view))));
+}
+
+static PyObject *_wrap_gv_view_area_get_flip_x(PyObject *self, PyObject *args) {
+    PyObject *area;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_flip_x", &PyGtk_Type, &area))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_flip_x(GV_VIEW_AREA(PyGtk_Get(area))));
+}
+
+static PyObject *_wrap_gv_view_area_get_flip_y(PyObject *self, PyObject *args) {
+    PyObject *area;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_flip_y", &PyGtk_Type, &area))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_flip_y(GV_VIEW_AREA(PyGtk_Get(area))));
+}
+
+static PyObject *_wrap_gv_view_area_set_flip_xy(PyObject *self, PyObject *args) {
+    PyObject *area;
+    int flip_x, flip_y;
+
+    if (!PyArg_ParseTuple(args, "O!ii:gv_view_area_set_flip_xy", &PyGtk_Type, &area, &flip_x, &flip_y))
+        return NULL;
+    gv_view_area_set_flip_xy(GV_VIEW_AREA(PyGtk_Get(area)), flip_x, flip_y);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_rotate(PyObject *self, PyObject *args) {
+    PyObject *area;
+    double angle;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_view_area_rotate", &PyGtk_Type, &area, &angle))
+        return NULL;
+    gv_view_area_rotate(GV_VIEW_AREA(PyGtk_Get(area)), angle);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_translate(PyObject *self, PyObject *args) {
+    PyObject *area;
+    double dx, dy;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_view_area_translate", &PyGtk_Type, &area, &dx, &dy))
+        return NULL;
+    gv_view_area_translate(GV_VIEW_AREA(PyGtk_Get(area)), dx, dy);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_set_translation(PyObject *self, PyObject *args) {
+    PyObject *area;
+    double x, y;
+
+    if (!PyArg_ParseTuple(args, "O!dd:gv_view_area_set_translation", &PyGtk_Type, &area, &x, &y))
+        return NULL;
+    gv_view_area_set_translation(GV_VIEW_AREA(PyGtk_Get(area)), x, y);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_fit_extents(PyObject *self, PyObject *args) {
+    PyObject *area;
+    double llx, llyy, width, height;
+
+    if (!PyArg_ParseTuple(args, "O!dddd:gv_view_area_fit_extents", &PyGtk_Type, &area, &llx, &llyy, &width, &height))
+        return NULL;
+    gv_view_area_fit_extents(GV_VIEW_AREA(PyGtk_Get(area)), llx, llyy, width, height);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_fit_all_layers(PyObject *self, PyObject *args) {
+    PyObject *area;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_fit_all_layers", &PyGtk_Type, &area))
+        return NULL;
+    gv_view_area_fit_all_layers(GV_VIEW_AREA(PyGtk_Get(area)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_set_projection(PyObject *self, PyObject *args) {
+    PyObject *area;
+    const char *proj_name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_view_area_set_projection", &PyGtk_Type, &area, &proj_name))
+        return NULL;
+    gv_view_area_set_projection(GV_VIEW_AREA(PyGtk_Get(area)), proj_name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_projection(PyObject *self, PyObject *args) {
+    PyObject *area;
+    const char *ret;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_projection", &PyGtk_Type, &area))
+        return NULL;
+    ret = gv_view_area_get_projection(GV_VIEW_AREA(PyGtk_Get(area)));
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_view_area_copy_state(PyObject *self, PyObject *args) {
+    PyObject *area, *source;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_area_copy_state", &PyGtk_Type, &area, &PyGtk_Type, &source))
+        return NULL;
+    gv_view_area_copy_state(GV_VIEW_AREA(PyGtk_Get(area)), GV_VIEW_AREA(PyGtk_Get(source)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_print_to_file(PyObject *self, PyObject *args) {
+    PyObject *area;
+    int width, height, is_rgb;
+    char *filename, *format;
+
+    if (!PyArg_ParseTuple(args, "O!iissi:gv_view_area_print_to_file", &PyGtk_Type, &area, &width, &height, &filename, &format, &is_rgb))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_print_to_file(GV_VIEW_AREA(PyGtk_Get(area)), width, height, filename, format, is_rgb));
+}
+
+static PyObject *_wrap_gv_view_area_print_postscript_to_file(PyObject *self, PyObject *args) {
+    PyObject *area;
+    int width, height, is_rgb;
+    char *filename;
+    double ulx, uly, lrx, lry;
+
+    if (!PyArg_ParseTuple(args, "O!iiddddis:gv_view_area_print_postscript_to_file", &PyGtk_Type, &area, &width, &height, &ulx, &uly, &lrx, &lry, &is_rgb, &filename))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_print_postscript_to_file(GV_VIEW_AREA(PyGtk_Get(area)), width, height, ulx, uly, lrx, lry, is_rgb, filename));
+}
+
+static PyObject *_wrap_gv_view_area_page_setup(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_view_area_page_setup"))
+        return NULL;
+    gv_view_area_page_setup();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_print_to_windriver(PyObject *self, PyObject *args) {
+    PyObject *area;
+    int width, height, is_rgb;
+    double ulx, uly, lrx, lry;
+
+    if (!PyArg_ParseTuple(args, "O!iiddddi:gv_view_area_print_to_windriver", &PyGtk_Type, &area, &width, &height, &ulx, &uly, &lrx, &lry, &is_rgb))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_print_to_windriver(GV_VIEW_AREA(PyGtk_Get(area)), width, height, ulx, uly, lrx, lry, is_rgb));
+}
+
+static PyObject *_wrap_gv_view_area_set_mode(PyObject *self, PyObject *args) {
+    PyObject *area;
+    int flag_3d;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_view_area_set_mode", &PyGtk_Type, &area, &flag_3d))
+        return NULL;
+    gv_view_area_set_mode(GV_VIEW_AREA(PyGtk_Get(area)), flag_3d);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_mode(PyObject *self, PyObject *args) {
+    PyObject *area;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_mode", &PyGtk_Type, &area))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_mode(GV_VIEW_AREA(PyGtk_Get(area))));
+}
+
+static PyObject *_wrap_gv_view_area_height_scale(PyObject *self, PyObject *args) {
+    PyObject *area;
+    double scale;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_view_area_height_scale", &PyGtk_Type, &area, &scale))
+        return NULL;
+    gv_view_area_height_scale(GV_VIEW_AREA(PyGtk_Get(area)), scale);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_height_scale(PyObject *self, PyObject *args) {
+    PyObject *area;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_get_height_scale", &PyGtk_Type, &area))
+        return NULL;
+    return PyFloat_FromDouble(gv_view_area_get_height_scale(GV_VIEW_AREA(PyGtk_Get(area))));
+}
+
+static PyObject *_wrap_gv_view_area_queue_draw(PyObject *self, PyObject *args) {
+    PyObject *area;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_area_queue_draw", &PyGtk_Type, &area))
+        return NULL;
+    gv_view_area_queue_draw(GV_VIEW_AREA(PyGtk_Get(area)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_area_get_raw(PyObject *self, PyObject *args) {
+    PyObject *area, *ref_layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_area_get_raw", &PyGtk_Type, &area, &PyGtk_Type, &ref_layer))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_get_raw(GV_VIEW_AREA(PyGtk_Get(area)), GTK_OBJECT(PyGtk_Get(ref_layer))));
+}
+
+static PyObject *_wrap_gv_view_area_set_raw(PyObject *self, PyObject *args) {
+    PyObject *area, *ref_layer;
+    int raw_enable;
+
+    if (!PyArg_ParseTuple(args, "O!O!i:gv_view_area_set_raw", &PyGtk_Type, &area, &PyGtk_Type, &ref_layer, &raw_enable))
+        return NULL;
+    return PyInt_FromLong(gv_view_area_set_raw(GV_VIEW_AREA(PyGtk_Get(area)), GTK_OBJECT(PyGtk_Get(ref_layer)), raw_enable));
+}
+
+static PyObject *_wrap_gv_view_area_get_property(PyObject *self, PyObject *args) {
+    PyObject *area;
+    const char *ret;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_view_area_get_property", &PyGtk_Type, &area, &name))
+        return NULL;
+    ret = gv_view_area_get_property(GV_VIEW_AREA(PyGtk_Get(area)), name);
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_view_area_set_property(PyObject *self, PyObject *args) {
+    PyObject *area;
+    const char *name, *value;
+
+    if (!PyArg_ParseTuple(args, "O!ss:gv_view_area_set_property", &PyGtk_Type, &area, &name, &value))
+        return NULL;
+    gv_view_area_set_property(GV_VIEW_AREA(PyGtk_Get(area)), name, value);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_data_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_data_get_type());
+}
+
+static PyObject *_wrap_gv_data_is_read_only(PyObject *self, PyObject *args) {
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_is_read_only", &PyGtk_Type, &data))
+        return NULL;
+    return PyInt_FromLong(gv_data_is_read_only(GV_DATA(PyGtk_Get(data))));
+}
+
+static PyObject *_wrap_gv_data_set_read_only(PyObject *self, PyObject *args) {
+    PyObject *data;
+    int read_only;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_data_set_read_only", &PyGtk_Type, &data, &read_only))
+        return NULL;
+    gv_data_set_read_only(GV_DATA(PyGtk_Get(data)), read_only);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_get_name(PyObject *self, PyObject *args) {
+    PyObject *data;
+    const char *ret;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_get_name", &PyGtk_Type, &data))
+        return NULL;
+    ret = gv_data_get_name(GV_DATA(PyGtk_Get(data)));
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_data_set_name(PyObject *self, PyObject *args) {
+    PyObject *data;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_data_set_name", &PyGtk_Type, &data, &name))
+        return NULL;
+    gv_data_set_name(GV_DATA(PyGtk_Get(data)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_get_projection(PyObject *self, PyObject *args) {
+    PyObject *data;
+    const char *ret;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_get_projection", &PyGtk_Type, &data))
+        return NULL;
+    ret = gv_data_get_projection(GV_DATA(PyGtk_Get(data)));
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_data_set_projection(PyObject *self, PyObject *args) {
+    PyObject *data;
+    char *projection;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_data_set_projection", &PyGtk_Type, &data, &projection))
+        return NULL;
+    gv_data_set_projection(GV_DATA(PyGtk_Get(data)), projection);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_get_property(PyObject *self, PyObject *args) {
+    PyObject *data;
+    const char *ret;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_data_get_property", &PyGtk_Type, &data, &name))
+        return NULL;
+    ret = gv_data_get_property(GV_DATA(PyGtk_Get(data)), name);
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_data_set_property(PyObject *self, PyObject *args) {
+    PyObject *data;
+    const char *name, *value;
+
+    if (!PyArg_ParseTuple(args, "O!ss:gv_data_set_property", &PyGtk_Type, &data, &name, &value))
+        return NULL;
+    gv_data_set_property(GV_DATA(PyGtk_Get(data)), name, value);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_freeze(PyObject *self, PyObject *args) {
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_freeze", &PyGtk_Type, &data))
+        return NULL;
+    gv_data_freeze(GV_DATA(PyGtk_Get(data)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_thaw(PyObject *self, PyObject *args) {
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_thaw", &PyGtk_Type, &data))
+        return NULL;
+    gv_data_thaw(GV_DATA(PyGtk_Get(data)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_get_parent(PyObject *self, PyObject *args) {
+    PyObject *data;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_data_get_parent", &PyGtk_Type, &data))
+        return NULL;
+    retval = (GtkObject *)gv_data_get_parent(GV_DATA(PyGtk_Get(data)));
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_data_registry_dump(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_data_registry_dump"))
+        return NULL;
+    gv_data_registry_dump();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_records_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_records_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_records_get_type());
+}
+
+static PyObject *_wrap_gv_records_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_records_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_records_new());
+}
+
+static PyObject *_wrap_gv_records_from_dbf(PyObject *self, PyObject *args) {
+    char *filename;
+    PyProgressData sProgressInfo = {NULL,NULL,-1};
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "sOO:gv_records_from_dbf", &filename, &(sProgressInfo.psPyCallback), &(sProgressInfo.psPyCallbackData)))
+        return NULL;
+    retval = (GtkObject *)gv_records_from_dbf(filename, PyProgressProxy, &sProgressInfo);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_records_from_rec(PyObject *self, PyObject *args) {
+    char *filename;
+    PyProgressData sProgressInfo = {NULL,NULL,-1};
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "sOO:gv_records_from_rec", &filename, &(sProgressInfo.psPyCallback), &(sProgressInfo.psPyCallbackData)))
+        return NULL;
+    retval = (GtkObject *)gv_records_from_rec(filename, PyProgressProxy, &sProgressInfo);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_records_create_records(PyObject *self, PyObject *args) {
+    PyObject *records;
+    int new_records;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_records_create_records", &PyGtk_Type, &records, &new_records))
+        return NULL;
+    return PyInt_FromLong(gv_records_create_records(GV_RECORDS(PyGtk_Get(records)), new_records));
+}
+
+static PyObject *_wrap_gv_records_num_records(PyObject *self, PyObject *args) {
+    PyObject *records;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_records_num_records", &PyGtk_Type, &records))
+        return NULL;
+    return PyInt_FromLong(gv_records_num_records(GV_RECORDS(PyGtk_Get(records))));
+}
+
+static PyObject *_wrap_gv_records_add_field(PyObject *self, PyObject *args) {
+    PyObject *records;
+    const char *name;
+    int rft, width, precision;
+
+    if (!PyArg_ParseTuple(args, "O!siii:gv_records_add_field", &PyGtk_Type, &records, &name, &rft, &width, &precision))
+        return NULL;
+    return PyInt_FromLong(gv_records_add_field(GV_RECORDS(PyGtk_Get(records)), name, rft, width, precision));
+}
+
+static PyObject *_wrap_gv_records_set_raw_field_data(PyObject *self, PyObject *args) {
+    PyObject *records;
+    int record_index, field_index;
+    const char *value = NULL;
+
+    if (!PyArg_ParseTuple(args, "O!ii|z:gv_records_set_raw_field_data", &PyGtk_Type, &records, &record_index, &field_index, &value))
+        return NULL;
+    gv_records_set_raw_field_data(GV_RECORDS(PyGtk_Get(records)), record_index, field_index, value);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_records_get_raw_field_data(PyObject *self, PyObject *args) {
+    PyObject *records;
+    const char *ret;
+    int record_index, field_index;
+
+    if (!PyArg_ParseTuple(args, "O!ii:gv_records_get_raw_field_data", &PyGtk_Type, &records, &record_index, &field_index))
+        return NULL;
+    ret = gv_records_get_raw_field_data(GV_RECORDS(PyGtk_Get(records)), record_index, field_index);
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_shapes_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_shapes_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_shapes_get_type());
+}
+
+static PyObject *_wrap_gv_shapes_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_shapes_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_shapes_new());
+}
+
+static PyObject *_wrap_gv_shapes_from_shapefile(PyObject *self, PyObject *args) {
+    char *filename;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "s:gv_shapes_from_shapefile", &filename))
+        return NULL;
+    retval = (GtkObject *)gv_shapes_from_shapefile(filename);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shapes_to_shapefile(PyObject *self, PyObject *args) {
+    char *filename;
+    PyObject *data;
+    int shp_type;
+
+    if (!PyArg_ParseTuple(args, "sO!i:gv_shapes_to_shapefile", &filename, &PyGtk_Type, &data, &shp_type))
+        return NULL;
+    return PyInt_FromLong(gv_shapes_to_shapefile(filename, GV_DATA(PyGtk_Get(data)), shp_type));
+}
+
+static PyObject *_wrap_gv_shapes_from_ogr(PyObject *self, PyObject *args) {
+    char *filename;
+    int layer;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "si:gv_shapes_from_ogr", &filename, &layer))
+        return NULL;
+    retval = (GtkObject *)gv_shapes_from_ogr(filename, layer);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_have_ogr_support(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_have_ogr_support"))
+        return NULL;
+    return PyInt_FromLong(gv_have_ogr_support());
+}
+
+static PyObject *_wrap_gv_shapes_num_shapes(PyObject *self, PyObject *args) {
+    PyObject *shapes;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shapes_num_shapes", &PyGtk_Type, &shapes))
+        return NULL;
+    return PyInt_FromLong(gv_shapes_num_shapes(GV_SHAPES(PyGtk_Get(shapes))));
+}
+
+static PyObject *_wrap_gv_shapes_add_height(PyObject *self, PyObject *args) {
+    PyObject *shapes, *raster;
+    double offset, default_height;
+
+    if (!PyArg_ParseTuple(args, "O!O!dd:gv_shapes_add_height", &PyGtk_Type, &shapes, &PyGtk_Type, &raster, &offset, &default_height))
+        return NULL;
+    gv_shapes_add_height(GV_SHAPES(PyGtk_Get(shapes)), GV_DATA(PyGtk_Get(raster)), offset, default_height);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shape_get_count(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_shape_get_count"))
+        return NULL;
+    return PyInt_FromLong(gv_shape_get_count());
+}
+
+static PyObject *_wrap_gv_points_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_points_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_points_get_type());
+}
+
+static PyObject *_wrap_gv_points_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_points_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_points_new());
+}
+
+static PyObject *_wrap_gv_points_num_points(PyObject *self, PyObject *args) {
+    PyObject *points;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_points_num_points", &PyGtk_Type, &points))
+        return NULL;
+    return PyInt_FromLong(gv_points_num_points(GV_POINTS(PyGtk_Get(points))));
+}
+
+static PyObject *_wrap_gv_polylines_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_polylines_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_polylines_get_type());
+}
+
+static PyObject *_wrap_gv_polylines_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_polylines_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_polylines_new());
+}
+
+static PyObject *_wrap_gv_polylines_num_lines(PyObject *self, PyObject *args) {
+    PyObject *lines;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_polylines_num_lines", &PyGtk_Type, &lines))
+        return NULL;
+    return PyInt_FromLong(gv_polylines_num_lines(GV_POLYLINES(PyGtk_Get(lines))));
+}
+
+static PyObject *_wrap_gv_areas_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_areas_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_areas_get_type());
+}
+
+static PyObject *_wrap_gv_areas_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_areas_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_areas_new());
+}
+
+static PyObject *_wrap_gv_areas_num_areas(PyObject *self, PyObject *args) {
+    PyObject *areas;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_areas_num_areas", &PyGtk_Type, &areas))
+        return NULL;
+    return PyInt_FromLong(gv_areas_num_areas(GV_AREAS(PyGtk_Get(areas))));
+}
+
+static PyObject *_wrap_gv_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_layer_get_type());
+}
+
+static PyObject *_wrap_gv_layer_is_visible(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_layer_is_visible", &PyGtk_Type, &layer))
+        return NULL;
+    return PyInt_FromLong(gv_layer_is_visible(GV_LAYER(PyGtk_Get(layer))));
+}
+
+static PyObject *_wrap_gv_layer_set_visible(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int visible;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_layer_set_visible", &PyGtk_Type, &layer, &visible))
+        return NULL;
+    gv_layer_set_visible(GV_LAYER(PyGtk_Get(layer)), visible);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_layer_reproject(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    char *projection;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_layer_reproject", &PyGtk_Type, &layer, &projection))
+        return NULL;
+    return PyInt_FromLong(gv_layer_reproject(GV_LAYER(PyGtk_Get(layer)), projection));
+}
+
+static PyObject *_wrap_gv_layer_get_view(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_layer_get_view", &PyGtk_Type, &layer))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_layer_get_view(GV_LAYER(PyGtk_Get(layer))));
+}
+
+static PyObject *_wrap_gv_shape_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_shape_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_shape_layer_get_type());
+}
+
+static PyObject *_wrap_gv_shape_layer_clear_selection(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shape_layer_clear_selection", &PyGtk_Type, &layer))
+        return NULL;
+    gv_shape_layer_clear_selection(GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shape_layer_select_all(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shape_layer_select_all", &PyGtk_Type, &layer))
+        return NULL;
+    gv_shape_layer_select_all(GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shape_layer_select_shape(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int shape_id;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_shape_layer_select_shape", &PyGtk_Type, &layer, &shape_id))
+        return NULL;
+    gv_shape_layer_select_shape(GV_SHAPE_LAYER(PyGtk_Get(layer)), shape_id);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shape_layer_deselect_shape(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int shape_id;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_shape_layer_deselect_shape", &PyGtk_Type, &layer, &shape_id))
+        return NULL;
+    gv_shape_layer_deselect_shape(GV_SHAPE_LAYER(PyGtk_Get(layer)), shape_id);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shape_layer_subselect_shape(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int shape_id;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_shape_layer_subselect_shape", &PyGtk_Type, &layer, &shape_id))
+        return NULL;
+    gv_shape_layer_subselect_shape(GV_SHAPE_LAYER(PyGtk_Get(layer)), shape_id);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_shape_layer_get_subselection(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_shape_layer_get_subselection", &PyGtk_Type, &layer))
+        return NULL;
+    return PyInt_FromLong(gv_shape_layer_get_subselection(GV_SHAPE_LAYER(PyGtk_Get(layer))));
+}
+
+static PyObject *_wrap_gv_shapes_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_shapes_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_shapes_layer_get_type());
+}
+
+static PyObject *_wrap_gv_shapes_layer_new(PyObject *self, PyObject *args) {
+    PyObject *py_shapes = Py_None;
+    GvShapes *shapes = NULL;
+
+    if (!PyArg_ParseTuple(args, "|O:gv_shapes_layer_new", &py_shapes))
+        return NULL;
+    if (PyGtk_Check(py_shapes))
+        shapes = GV_SHAPES(PyGtk_Get(py_shapes));
+    else if (py_shapes != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "shapes argument must be a GvShapes or None");
+        return NULL;
+    }
+    return PyGtk_New((GtkObject *)gv_shapes_layer_new(shapes));
+}
+
+static PyObject *_wrap_gv_shapes_layer_get_symbol_manager(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int ok_to_create;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_shapes_layer_get_symbol_manager", &PyGtk_Type, &layer, &ok_to_create))
+        return NULL;
+    retval = (GtkObject *)gv_shapes_layer_get_symbol_manager(GV_SHAPES_LAYER(PyGtk_Get(layer)), ok_to_create);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_point_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_point_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_point_layer_get_type());
+}
+
+static PyObject *_wrap_gv_point_layer_new(PyObject *self, PyObject *args) {
+    PyObject *py_points = Py_None;
+    GvPoints *points = NULL;
+
+    if (!PyArg_ParseTuple(args, "|O:gv_point_layer_new", &py_points))
+        return NULL;
+    if (PyGtk_Check(py_points))
+        points = GV_POINTS(PyGtk_Get(py_points));
+    else if (py_points != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "points argument must be a GvPoints or None");
+        return NULL;
+    }
+    return PyGtk_New((GtkObject *)gv_point_layer_new(points));
+}
+
+static PyObject *_wrap_gv_line_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_line_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_line_layer_get_type());
+}
+
+static PyObject *_wrap_gv_line_layer_new(PyObject *self, PyObject *args) {
+    PyObject *py_lines = Py_None;
+    GvPolylines *lines = NULL;
+
+    if (!PyArg_ParseTuple(args, "|O:gv_line_layer_new", &py_lines))
+        return NULL;
+    if (PyGtk_Check(py_lines))
+        lines = GV_POLYLINES(PyGtk_Get(py_lines));
+    else if (py_lines != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "lines argument must be a GvPolylines or None");
+        return NULL;
+    }
+    return PyGtk_New((GtkObject *)gv_line_layer_new(lines));
+}
+
+static PyObject *_wrap_gv_area_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_area_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_area_layer_get_type());
+}
+
+static PyObject *_wrap_gv_area_layer_new(PyObject *self, PyObject *args) {
+    PyObject *py_areas = Py_None;
+    GvAreas *areas = NULL;
+
+    if (!PyArg_ParseTuple(args, "|O:gv_area_layer_new", &py_areas))
+        return NULL;
+    if (PyGtk_Check(py_areas))
+        areas = GV_AREAS(PyGtk_Get(py_areas));
+    else if (py_areas != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "areas argument must be a GvAreas or None");
+        return NULL;
+    }
+    return PyGtk_New((GtkObject *)gv_area_layer_new(areas));
+}
+
+static PyObject *_wrap_gv_pquery_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_pquery_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_pquery_layer_get_type());
+}
+
+static PyObject *_wrap_gv_pquery_layer_new(PyObject *self, PyObject *args) {
+    PyObject *py_shapes = Py_None;
+    GvShapes *shapes = NULL;
+
+    if (!PyArg_ParseTuple(args, "|O:gv_pquery_layer_new", &py_shapes))
+        return NULL;
+    if (PyGtk_Check(py_shapes))
+        shapes = GV_SHAPES(PyGtk_Get(py_shapes));
+    else if (py_shapes != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "shapes argument must be a GvShapes or None");
+        return NULL;
+    }
+    return PyGtk_New((GtkObject *)gv_pquery_layer_new(shapes));
+}
+
+static PyObject *_wrap_ip_gcp_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":ip_gcp_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(ip_gcp_layer_get_type());
+}
+
+static PyObject *_wrap_ip_gcp_layer_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":ip_gcp_layer_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)ip_gcp_layer_new());
+}
+
+static PyObject *_wrap_app_cur_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":app_cur_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(app_cur_layer_get_type());
+}
+
+static PyObject *_wrap_app_cur_layer_new(PyObject *self, PyObject *args) {
+    PyObject *py_shapes = Py_None;
+    GvShapes *shapes = NULL;
+
+    if (!PyArg_ParseTuple(args, "O:app_cur_layer_new", &py_shapes))
+        return NULL;
+    if (PyGtk_Check(py_shapes))
+        shapes = GV_SHAPES(PyGtk_Get(py_shapes));
+    else if (py_shapes != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "shapes argument must be a GvShapes or None");
+        return NULL;
+    }
+    return PyGtk_New((GtkObject *)app_cur_layer_new(shapes));
+}
+
+static PyObject *_wrap_gv_symbol_manager_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_symbol_manager_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_symbol_manager_get_type());
+}
+
+static PyObject *_wrap_gv_get_symbol_manager(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_get_symbol_manager"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_get_symbol_manager());
+}
+
+static PyObject *_wrap_gv_symbol_manager_eject_symbol(PyObject *self, PyObject *args) {
+    PyObject *manager;
+    const char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_symbol_manager_eject_symbol", &PyGtk_Type, &manager, &name))
+        return NULL;
+    gv_symbol_manager_eject_symbol(GV_SYMBOL_MANAGER(PyGtk_Get(manager)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_symbol_manager_has_symbol(PyObject *self, PyObject *args) {
+    PyObject *manager;
+    const char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_symbol_manager_has_symbol", &PyGtk_Type, &manager, &name))
+        return NULL;
+    return PyInt_FromLong(gv_symbol_manager_has_symbol(GV_SYMBOL_MANAGER(PyGtk_Get(manager)), name));
+}
+
+static PyObject *_wrap_gv_manager_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_manager_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_manager_get_type());
+}
+
+static PyObject *_wrap_gv_get_manager(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_get_manager"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_get_manager());
+}
+
+static PyObject *_wrap_gv_manager_get_preference(PyObject *self, PyObject *args) {
+    PyObject *manager;
+    const char *ret, *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_manager_get_preference", &PyGtk_Type, &manager, &name))
+        return NULL;
+    ret = gv_manager_get_preference(GV_MANAGER(PyGtk_Get(manager)), name);
+    if( ret != NULL)
+        return PyString_FromString(ret);
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject *_wrap_gv_manager_set_preference(PyObject *self, PyObject *args) {
+    PyObject *manager;
+    const char *name, *value;
+
+    if (!PyArg_ParseTuple(args, "O!ss:gv_manager_set_preference", &PyGtk_Type, &manager, &name, &value))
+        return NULL;
+    gv_manager_set_preference(GV_MANAGER(PyGtk_Get(manager)), name, value);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_manager_get_busy(PyObject *self, PyObject *args) {
+    PyObject *manager;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_manager_get_busy", &PyGtk_Type, &manager))
+        return NULL;
+    return PyInt_FromLong(gv_manager_get_busy(GV_MANAGER(PyGtk_Get(manager))));
+}
+
+static PyObject *_wrap_gv_manager_set_busy(PyObject *self, PyObject *args) {
+    PyObject *manager;
+    int busy;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_manager_set_busy", &PyGtk_Type, &manager, &busy))
+        return NULL;
+    gv_manager_set_busy(GV_MANAGER(PyGtk_Get(manager)), busy);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_manager_dump(PyObject *self, PyObject *args) {
+    PyObject *manager;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_manager_dump", &PyGtk_Type, &manager))
+        return NULL;
+    gv_manager_dump(GV_MANAGER(PyGtk_Get(manager)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_tool_get_type());
+}
+
+static PyObject *_wrap_gv_tool_activate(PyObject *self, PyObject *args) {
+    PyObject *tool, *view;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_tool_activate", &PyGtk_Type, &tool, &PyGtk_Type, &view))
+        return NULL;
+    gv_tool_activate(GV_TOOL(PyGtk_Get(tool)), GV_VIEW_AREA(PyGtk_Get(view)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_tool_deactivate(PyObject *self, PyObject *args) {
+    PyObject *tool, *view;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_tool_deactivate", &PyGtk_Type, &tool, &PyGtk_Type, &view))
+        return NULL;
+    gv_tool_deactivate(GV_TOOL(PyGtk_Get(tool)), GV_VIEW_AREA(PyGtk_Get(view)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_tool_get_view(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_tool_get_view", &PyGtk_Type, &tool))
+        return NULL;
+    retval = (GtkObject *)gv_tool_get_view(GV_TOOL(PyGtk_Get(tool)));
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_tool_set_cursor(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    int cursor_type;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_tool_set_cursor", &PyGtk_Type, &tool, &cursor_type))
+        return NULL;
+    gv_tool_set_cursor(GV_TOOL(PyGtk_Get(tool)), cursor_type);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_selection_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_selection_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_selection_tool_get_type());
+}
+
+static PyObject *_wrap_gv_selection_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_selection_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_selection_tool_new());
+}
+
+static PyObject *_wrap_gv_selection_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_selection_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_selection_tool_set_layer(GV_SELECTION_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_zoompan_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_zoompan_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_zoompan_tool_get_type());
+}
+
+static PyObject *_wrap_gv_zoompan_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_zoompan_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_zoompan_tool_new());
+}
+
+static PyObject *_wrap_gv_point_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_point_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_point_tool_get_type());
+}
+
+static PyObject *_wrap_gv_point_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_point_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_point_tool_new());
+}
+
+static PyObject *_wrap_gv_point_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_point_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_point_tool_set_layer(GV_POINT_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_point_tool_set_named_layer(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_point_tool_set_named_layer", &PyGtk_Type, &tool, &name))
+        return NULL;
+    gv_point_tool_set_named_layer(GV_POINT_TOOL(PyGtk_Get(tool)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_line_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_line_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_line_tool_get_type());
+}
+
+static PyObject *_wrap_gv_line_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_line_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_line_tool_new());
+}
+
+static PyObject *_wrap_gv_line_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_line_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_line_tool_set_layer(GV_LINE_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_line_tool_set_named_layer(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_line_tool_set_named_layer", &PyGtk_Type, &tool, &name))
+        return NULL;
+    gv_line_tool_set_named_layer(GV_LINE_TOOL(PyGtk_Get(tool)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_rect_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_rect_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_rect_tool_get_type());
+}
+
+static PyObject *_wrap_gv_rect_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_rect_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_rect_tool_new());
+}
+
+static PyObject *_wrap_gv_rect_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_rect_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_rect_tool_set_layer(GV_RECT_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_rect_tool_set_named_layer(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_rect_tool_set_named_layer", &PyGtk_Type, &tool, &name))
+        return NULL;
+    gv_rect_tool_set_named_layer(GV_RECT_TOOL(PyGtk_Get(tool)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_rotate_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_rotate_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_rotate_tool_get_type());
+}
+
+static PyObject *_wrap_gv_rotate_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_rotate_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_rotate_tool_new());
+}
+
+static PyObject *_wrap_gv_rotate_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_rotate_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_rotate_tool_set_layer(GV_ROTATE_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_rotate_tool_set_named_layer(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_rotate_tool_set_named_layer", &PyGtk_Type, &tool, &name))
+        return NULL;
+    gv_rotate_tool_set_named_layer(GV_ROTATE_TOOL(PyGtk_Get(tool)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_area_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_area_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_area_tool_get_type());
+}
+
+static PyObject *_wrap_gv_area_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_area_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_area_tool_new());
+}
+
+static PyObject *_wrap_gv_area_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_area_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_area_tool_set_layer(GV_AREA_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_area_tool_set_named_layer(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_area_tool_set_named_layer", &PyGtk_Type, &tool, &name))
+        return NULL;
+    gv_area_tool_set_named_layer(GV_AREA_TOOL(PyGtk_Get(tool)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_node_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_node_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_node_tool_get_type());
+}
+
+static PyObject *_wrap_gv_node_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_node_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_node_tool_new());
+}
+
+static PyObject *_wrap_gv_node_tool_set_layer(PyObject *self, PyObject *args) {
+    PyObject *tool, *layer;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_node_tool_set_layer", &PyGtk_Type, &tool, &PyGtk_Type, &layer))
+        return NULL;
+    gv_node_tool_set_layer(GV_NODE_TOOL(PyGtk_Get(tool)), GV_SHAPE_LAYER(PyGtk_Get(layer)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_roi_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_roi_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_roi_tool_get_type());
+}
+
+static PyObject *_wrap_gv_roi_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_roi_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_roi_tool_new());
+}
+
+static PyObject *_wrap_gv_poi_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_poi_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_poi_tool_get_type());
+}
+
+static PyObject *_wrap_gv_poi_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_poi_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_poi_tool_new());
+}
+
+static PyObject *_wrap_gv_track_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_track_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_track_tool_get_type());
+}
+
+static PyObject *_wrap_gv_track_tool_new(PyObject *self, PyObject *args) {
+    PyObject *label;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_track_tool_new", &PyGtk_Type, &label))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_track_tool_new(GTK_OBJECT(PyGtk_Get(label))));
+}
+
+static PyObject *_wrap_gv_toolbox_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_toolbox_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_toolbox_get_type());
+}
+
+static PyObject *_wrap_gv_toolbox_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_toolbox_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_toolbox_new());
+}
+
+static PyObject *_wrap_gv_toolbox_add_tool(PyObject *self, PyObject *args) {
+    PyObject *toolbox, *tool;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!sO!:gv_toolbox_add_tool", &PyGtk_Type, &toolbox, &name, &PyGtk_Type, &tool))
+        return NULL;
+    gv_toolbox_add_tool(GV_TOOLBOX(PyGtk_Get(toolbox)), name, GV_TOOL(PyGtk_Get(tool)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_toolbox_activate_tool(PyObject *self, PyObject *args) {
+    PyObject *toolbox;
+    char *name;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_toolbox_activate_tool", &PyGtk_Type, &toolbox, &name))
+        return NULL;
+    gv_toolbox_activate_tool(GV_TOOLBOX(PyGtk_Get(toolbox)), name);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_undo_register_data(PyObject *self, PyObject *args) {
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_undo_register_data", &PyGtk_Type, &data))
+        return NULL;
+    gv_undo_register_data(GV_DATA(PyGtk_Get(data)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_undo_pop(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_undo_pop"))
+        return NULL;
+    gv_undo_pop();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_undo_clear(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_undo_clear"))
+        return NULL;
+    gv_undo_clear();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_undo_can_undo(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_undo_can_undo"))
+        return NULL;
+    return PyInt_FromLong(gv_undo_can_undo());
+}
+
+static PyObject *_wrap_gv_undo_close(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_undo_close"))
+        return NULL;
+    gv_undo_close();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_undo_open(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_undo_open"))
+        return NULL;
+    gv_undo_open();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_undo_start_group(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_undo_start_group"))
+        return NULL;
+    return PyInt_FromLong(gv_undo_start_group());
+}
+
+static PyObject *_wrap_gv_undo_end_group(PyObject *self, PyObject *args) {
+    int group;
+
+    if (!PyArg_ParseTuple(args, "i:gv_undo_end_group", &group))
+        return NULL;
+    gv_undo_end_group(group);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_link_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_view_link_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_view_link_get_type());
+}
+
+static PyObject *_wrap_gv_view_link_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_view_link_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_view_link_new());
+}
+
+static PyObject *_wrap_gv_view_link_register_view(PyObject *self, PyObject *args) {
+    PyObject *link, *view;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_link_register_view", &PyGtk_Type, &link, &PyGtk_Type, &view))
+        return NULL;
+    gv_view_link_register_view(GV_VIEW_LINK(PyGtk_Get(link)), GV_VIEW_AREA(PyGtk_Get(view)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_link_remove_view(PyObject *self, PyObject *args) {
+    PyObject *link, *view;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_view_link_remove_view", &PyGtk_Type, &link, &PyGtk_Type, &view))
+        return NULL;
+    gv_view_link_remove_view(GV_VIEW_LINK(PyGtk_Get(link)), GV_VIEW_AREA(PyGtk_Get(view)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_link_enable(PyObject *self, PyObject *args) {
+    PyObject *link;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_link_enable", &PyGtk_Type, &link))
+        return NULL;
+    gv_view_link_enable(GV_VIEW_LINK(PyGtk_Get(link)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_link_disable(PyObject *self, PyObject *args) {
+    PyObject *link;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_view_link_disable", &PyGtk_Type, &link))
+        return NULL;
+    gv_view_link_disable(GV_VIEW_LINK(PyGtk_Get(link)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_view_link_set_cursor_mode(PyObject *self, PyObject *args) {
+    PyObject *link;
+    int cursor_mode;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_view_link_set_cursor_mode", &PyGtk_Type, &link, &cursor_mode))
+        return NULL;
+    gv_view_link_set_cursor_mode(GV_VIEW_LINK(PyGtk_Get(link)), cursor_mode);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_raster_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_raster_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_raster_get_type());
+}
+
+static PyObject *_wrap_gv_raster_flush_cache(PyObject *self, PyObject *args) {
+    PyObject *raster;
+    int x_off, y_off, width, height;
+
+    if (!PyArg_ParseTuple(args, "O!iiii:gv_raster_flush_cache", &PyGtk_Type, &raster, &x_off, &y_off, &width, &height))
+        return NULL;
+    gv_raster_flush_cache(GV_RASTER(PyGtk_Get(raster)), x_off, y_off, width, height);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_raster_get_min(PyObject *self, PyObject *args) {
+    PyObject *raster;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_get_min", &PyGtk_Type, &raster))
+        return NULL;
+    return PyFloat_FromDouble(gv_raster_get_min(GV_RASTER(PyGtk_Get(raster))));
+}
+
+static PyObject *_wrap_gv_raster_get_max(PyObject *self, PyObject *args) {
+    PyObject *raster;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_get_max", &PyGtk_Type, &raster))
+        return NULL;
+    return PyFloat_FromDouble(gv_raster_get_max(GV_RASTER(PyGtk_Get(raster))));
+}
+
+static PyObject *_wrap_gv_raster_cache_get_max(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_raster_cache_get_max"))
+        return NULL;
+    return PyInt_FromLong(gv_raster_cache_get_max());
+}
+
+static PyObject *_wrap_gv_raster_cache_get_used(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_raster_cache_get_used"))
+        return NULL;
+    return PyInt_FromLong(gv_raster_cache_get_used());
+}
+
+static PyObject *_wrap_gv_raster_cache_set_max(PyObject *self, PyObject *args) {
+    int new_max;
+
+    if (!PyArg_ParseTuple(args, "i:gv_raster_cache_set_max", &new_max))
+        return NULL;
+    gv_raster_cache_set_max(new_max);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_raster_set_poly_order_preference(PyObject *self, PyObject *args) {
+    PyObject *raster;
+    int poly_order;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_raster_set_poly_order_preference", &PyGtk_Type, &raster, &poly_order))
+        return NULL;
+    gv_raster_set_poly_order_preference(GV_RASTER(PyGtk_Get(raster)), poly_order);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_raster_layer_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_raster_layer_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_get_type());
+}
+
+static PyObject *_wrap_gv_raster_layer_lut_color_wheel_new(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int h_type, s_type, v_type;
+    double h_param, s_param, v_param;
+
+    if (!PyArg_ParseTuple(args, "O!ididid:gv_raster_layer_lut_color_wheel_new", &PyGtk_Type, &layer, &h_type, &h_param, &s_type, &s_param, &v_type, &v_param))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_lut_color_wheel_new(GV_RASTER_LAYER(PyGtk_Get(layer)), h_type, h_param, s_type, s_param, v_type, v_param));
+}
+
+static PyObject *_wrap_gv_raster_layer_lut_color_wheel_new_ev(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int set_phase, set_magnitude;
+
+    if (!PyArg_ParseTuple(args, "O!ii:gv_raster_layer_lut_color_wheel_new_ev", &PyGtk_Type, &layer, &set_phase, &set_magnitude))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_lut_color_wheel_new_ev(GV_RASTER_LAYER(PyGtk_Get(layer)), set_phase, set_magnitude));
+}
+
+static PyObject *_wrap_gv_raster_layer_lut_color_wheel_1d_new(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    double s, v, offset;
+
+    if (!PyArg_ParseTuple(args, "O!ddd:gv_raster_layer_lut_color_wheel_1d_new", &PyGtk_Type, &layer, &s, &v, &offset))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_lut_color_wheel_1d_new(GV_RASTER_LAYER(PyGtk_Get(layer)), s, v, offset));
+}
+
+static PyObject *_wrap_gv_raster_layer_alpha_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int alpha_mode;
+    double alpha_check_val;
+
+    if (!PyArg_ParseTuple(args, "O!id:gv_raster_layer_alpha_set", &PyGtk_Type, &layer, &alpha_mode, &alpha_check_val))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_alpha_set(GV_RASTER_LAYER(PyGtk_Get(layer)), alpha_mode, alpha_check_val));
+}
+
+static PyObject *_wrap_gv_raster_layer_min_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+    double min;
+
+    if (!PyArg_ParseTuple(args, "O!id:gv_raster_layer_min_set", &PyGtk_Type, &layer, &isource, &min))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_min_set(GV_RASTER_LAYER(PyGtk_Get(layer)), isource, min));
+}
+
+static PyObject *_wrap_gv_raster_layer_min_get(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_raster_layer_min_get", &PyGtk_Type, &layer, &isource))
+        return NULL;
+    return PyFloat_FromDouble(gv_raster_layer_min_get(GV_RASTER_LAYER(PyGtk_Get(layer)), isource));
+}
+
+static PyObject *_wrap_gv_raster_layer_max_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+    double max;
+
+    if (!PyArg_ParseTuple(args, "O!id:gv_raster_layer_max_set", &PyGtk_Type, &layer, &isource, &max))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_max_set(GV_RASTER_LAYER(PyGtk_Get(layer)), isource, max));
+}
+
+static PyObject *_wrap_gv_raster_layer_max_get(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_raster_layer_max_get", &PyGtk_Type, &layer, &isource))
+        return NULL;
+    return PyFloat_FromDouble(gv_raster_layer_max_get(GV_RASTER_LAYER(PyGtk_Get(layer)), isource));
+}
+
+static PyObject *_wrap_gv_raster_layer_nodata_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+    double nodata_real, nodata_imaginary;
+
+    if (!PyArg_ParseTuple(args, "O!idd:gv_raster_layer_nodata_set", &PyGtk_Type, &layer, &isource, &nodata_real, &nodata_imaginary))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_nodata_set(GV_RASTER_LAYER(PyGtk_Get(layer)), isource, nodata_real, nodata_imaginary));
+}
+
+static PyObject *_wrap_gv_raster_layer_type_get(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_raster_layer_type_get", &PyGtk_Type, &layer, &isource))
+        return NULL;
+    return PyFloat_FromDouble(gv_raster_layer_type_get(GV_RASTER_LAYER(PyGtk_Get(layer)), isource));
+}
+
+static PyObject *_wrap_gv_raster_layer_get_const_value(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_raster_layer_get_const_value", &PyGtk_Type, &layer, &isource))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_get_const_value(GV_RASTER_LAYER(PyGtk_Get(layer)), isource));
+}
+
+static PyObject *_wrap_gv_raster_layer_get_data(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int isource;
+    GtkObject *retval;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_raster_layer_get_data", &PyGtk_Type, &layer, &isource))
+        return NULL;
+    retval = (GtkObject *)gv_raster_layer_get_data(GV_RASTER_LAYER(PyGtk_Get(layer)), isource);
+    if (retval) return PyGtk_New(retval);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_raster_layer_get_mode(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_layer_get_mode", &PyGtk_Type, &layer))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_get_mode(GV_RASTER_LAYER(PyGtk_Get(layer))));
+}
+
+static PyObject *_wrap_gv_raster_layer_texture_clamp_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int s_clamp, t_clamp;
+
+    if (!PyArg_ParseTuple(args, "O!ii:gv_raster_layer_texture_clamp_set", &PyGtk_Type, &layer, &s_clamp, &t_clamp))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_texture_clamp_set(GV_RASTER_LAYER(PyGtk_Get(layer)), s_clamp, t_clamp));
+}
+
+static PyObject *_wrap_gv_raster_layer_zoom_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int max_mode, min_mode;
+
+    if (!PyArg_ParseTuple(args, "O!ii:gv_raster_layer_zoom_set", &PyGtk_Type, &layer, &max_mode, &min_mode))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_zoom_set(GV_RASTER_LAYER(PyGtk_Get(layer)), max_mode, min_mode));
+}
+
+static PyObject *_wrap_gv_raster_layer_blend_mode_set(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int mode, sfactor, dfactor;
+
+    if (!PyArg_ParseTuple(args, "O!iii:gv_raster_layer_blend_mode_set", &PyGtk_Type, &layer, &mode, &sfactor, &dfactor))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_blend_mode_set(GV_RASTER_LAYER(PyGtk_Get(layer)), mode, sfactor, dfactor));
+}
+
+static PyObject *_wrap_gv_raster_layer_lut_type_get(PyObject *self, PyObject *args) {
+    PyObject *layer;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_raster_layer_lut_type_get", &PyGtk_Type, &layer))
+        return NULL;
+    return PyInt_FromLong(gv_raster_layer_lut_type_get(GV_RASTER_LAYER(PyGtk_Get(layer))));
+}
+
+static PyObject *_wrap_gv_raster_layer_add_height(PyObject *self, PyObject *args) {
+    PyObject *layer, *height_raster;
+    double default_height;
+
+    if (!PyArg_ParseTuple(args, "O!O!d:gv_raster_layer_add_height", &PyGtk_Type, &layer, &PyGtk_Type, &height_raster, &default_height))
+        return NULL;
+    gv_raster_layer_add_height(GV_RASTER_LAYER(PyGtk_Get(layer)), GV_RASTER(PyGtk_Get(height_raster)), default_height);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_raster_layer_clamp_height(PyObject *self, PyObject *args) {
+    PyObject *layer;
+    int bclamp_min, bclamp_max;
+    double min_height, max_height;
+
+    if (!PyArg_ParseTuple(args, "O!iidd:gv_raster_layer_clamp_height", &PyGtk_Type, &layer, &bclamp_min, &bclamp_max, &min_height, &max_height))
+        return NULL;
+    gv_raster_layer_clamp_height(GV_RASTER_LAYER(PyGtk_Get(layer)), bclamp_min, bclamp_max, min_height, max_height);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_texture_cache_dump(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_texture_cache_dump"))
+        return NULL;
+    gv_texture_cache_dump();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_texture_cache_get_max(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_texture_cache_get_max"))
+        return NULL;
+    return PyInt_FromLong(gv_texture_cache_get_max());
+}
+
+static PyObject *_wrap_gv_texture_cache_get_used(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_texture_cache_get_used"))
+        return NULL;
+    return PyInt_FromLong(gv_texture_cache_get_used());
+}
+
+static PyObject *_wrap_gv_texture_cache_set_max(PyObject *self, PyObject *args) {
+    int new_max;
+
+    if (!PyArg_ParseTuple(args, "i:gv_texture_cache_set_max", &new_max))
+        return NULL;
+    gv_texture_cache_set_max(new_max);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_build_skirt(PyObject *self, PyObject *args) {
+    PyObject *raster;
+    double base_z;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_build_skirt", &PyGtk_Type, &raster, &base_z))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_build_skirt(GV_RASTER_LAYER(PyGtk_Get(raster)), base_z));
+}
+
+static PyObject *_wrap_gtk_color_well_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gtk_color_well_get_type"))
+        return NULL;
+    return PyInt_FromLong(gtk_color_well_get_type());
+}
+
+static PyObject *_wrap_gtk_color_well_new(PyObject *self, PyObject *args) {
+    char *title;
+
+    if (!PyArg_ParseTuple(args, "s:gtk_color_well_new", &title))
+        return NULL;
+    return PyGtk_New((GtkObject *)gtk_color_well_new(title));
+}
+
+static PyObject *_wrap_gtk_color_well_set_d(PyObject *self, PyObject *args) {
+    PyObject *cwell;
+    double r, g, b, a;
+
+    if (!PyArg_ParseTuple(args, "O!dddd:gtk_color_well_set_d", &PyGtk_Type, &cwell, &r, &g, &b, &a))
+        return NULL;
+    gtk_color_well_set_d(GTK_COLOR_WELL(PyGtk_Get(cwell)), r, g, b, a);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gtk_color_well_set_i8(PyObject *self, PyObject *args) {
+    PyObject *cwell;
+    int r, g, b, a;
+
+    if (!PyArg_ParseTuple(args, "O!iiii:gtk_color_well_set_i8", &PyGtk_Type, &cwell, &r, &g, &b, &a))
+        return NULL;
+    gtk_color_well_set_i8(GTK_COLOR_WELL(PyGtk_Get(cwell)), r, g, b, a);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gtk_color_well_set_i16(PyObject *self, PyObject *args) {
+    PyObject *cwell;
+    int r, g, b, a;
+
+    if (!PyArg_ParseTuple(args, "O!iiii:gtk_color_well_set_i16", &PyGtk_Type, &cwell, &r, &g, &b, &a))
+        return NULL;
+    gtk_color_well_set_i16(GTK_COLOR_WELL(PyGtk_Get(cwell)), r, g, b, a);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gtk_color_well_set_use_alpha(PyObject *self, PyObject *args) {
+    PyObject *cwell;
+    int use_alpha;
+
+    if (!PyArg_ParseTuple(args, "O!i:gtk_color_well_set_use_alpha", &PyGtk_Type, &cwell, &use_alpha))
+        return NULL;
+    gtk_color_well_set_use_alpha(GTK_COLOR_WELL(PyGtk_Get(cwell)), use_alpha);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gtk_color_well_set_continuous(PyObject *self, PyObject *args) {
+    PyObject *cwell;
+    int update_continuous;
+
+    if (!PyArg_ParseTuple(args, "O!i:gtk_color_well_set_continuous", &PyGtk_Type, &cwell, &update_continuous))
+        return NULL;
+    gtk_color_well_set_continuous(GTK_COLOR_WELL(PyGtk_Get(cwell)), update_continuous);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gtk_color_well_set_title(PyObject *self, PyObject *args) {
+    PyObject *cwell;
+    char *title;
+
+    if (!PyArg_ParseTuple(args, "O!s:gtk_color_well_set_title", &PyGtk_Type, &cwell, &title))
+        return NULL;
+    gtk_color_well_set_title(GTK_COLOR_WELL(PyGtk_Get(cwell)), title);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_autopan_tool_get_type(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_autopan_tool_get_type"))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_get_type());
+}
+
+static PyObject *_wrap_gv_autopan_tool_new(PyObject *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ":gv_autopan_tool_new"))
+        return NULL;
+    return PyGtk_New((GtkObject *)gv_autopan_tool_new());
+}
+
+static PyObject *_wrap_gv_autopan_tool_play(PyObject *self, PyObject *args) {
+    PyObject *tool;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_play", &PyGtk_Type, &tool))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_play(GV_AUTOPAN_TOOL(PyGtk_Get(tool))));
+}
+
+static PyObject *_wrap_gv_autopan_tool_pause(PyObject *self, PyObject *args) {
+    PyObject *tool;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_pause", &PyGtk_Type, &tool))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_pause(GV_AUTOPAN_TOOL(PyGtk_Get(tool))));
+}
+
+static PyObject *_wrap_gv_autopan_tool_stop(PyObject *self, PyObject *args) {
+    PyObject *tool;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_stop", &PyGtk_Type, &tool))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_stop(GV_AUTOPAN_TOOL(PyGtk_Get(tool))));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_speed(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    double speed;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_autopan_tool_set_speed", &PyGtk_Type, &tool, &speed))
+        return NULL;
+    return PyFloat_FromDouble(gv_autopan_tool_set_speed(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), speed));
+}
+
+static PyObject *_wrap_gv_autopan_tool_get_speed(PyObject *self, PyObject *args) {
+    PyObject *tool;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_get_speed", &PyGtk_Type, &tool))
+        return NULL;
+    return PyFloat_FromDouble(gv_autopan_tool_get_speed(GV_AUTOPAN_TOOL(PyGtk_Get(tool))));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_location(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    double x, y, z;
+
+    if (!PyArg_ParseTuple(args, "O!ddd:gv_autopan_tool_set_location", &PyGtk_Type, &tool, &x, &y, &z))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_location(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), x, y, z));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_overlap(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    double overlap;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_autopan_tool_set_overlap", &PyGtk_Type, &tool, &overlap))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_overlap(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), overlap));
+}
+
+static PyObject *_wrap_gv_autopan_tool_get_overlap(PyObject *self, PyObject *args) {
+    PyObject *tool;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_get_overlap", &PyGtk_Type, &tool))
+        return NULL;
+    return PyFloat_FromDouble(gv_autopan_tool_get_overlap(GV_AUTOPAN_TOOL(PyGtk_Get(tool))));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_block_x_size(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    double block_x_size;
+    int mode;
+
+    if (!PyArg_ParseTuple(args, "O!di:gv_autopan_tool_set_block_x_size", &PyGtk_Type, &tool, &block_x_size, &mode))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_block_x_size(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), block_x_size, mode));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_x_resolution(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    double resolution;
+
+    if (!PyArg_ParseTuple(args, "O!d:gv_autopan_tool_set_x_resolution", &PyGtk_Type, &tool, &resolution))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_x_resolution(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), resolution));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_standard_path(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    int path_type;
+
+    if (!PyArg_ParseTuple(args, "O!i:gv_autopan_tool_set_standard_path", &PyGtk_Type, &tool, &path_type))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_standard_path(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), path_type));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_lines_path(PyObject *self, PyObject *args) {
+    PyObject *tool, *lines;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_autopan_tool_set_lines_path", &PyGtk_Type, &tool, &PyGtk_Type, &lines))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_lines_path(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), GV_SHAPES(PyGtk_Get(lines))));
+}
+
+static PyObject *_wrap_gv_autopan_tool_clear_trail(PyObject *self, PyObject *args) {
+    PyObject *tool;
+
+    if (!PyArg_ParseTuple(args, "O!:gv_autopan_tool_clear_trail", &PyGtk_Type, &tool))
+        return NULL;
+    gv_autopan_tool_clear_trail(GV_AUTOPAN_TOOL(PyGtk_Get(tool)));
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_trail_color(PyObject *self, PyObject *args) {
+    PyObject *tool, *view;
+    double red, green, blue, alpha;
+
+    if (!PyArg_ParseTuple(args, "O!O!dddd:gv_autopan_tool_set_trail_color", &PyGtk_Type, &tool, &PyGtk_Type, &view, &red, &green, &blue, &alpha))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_trail_color(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), GV_VIEW_AREA(PyGtk_Get(view)), red, green, blue, alpha));
+}
+
+static PyObject *_wrap_gv_autopan_tool_set_trail_mode(PyObject *self, PyObject *args) {
+    PyObject *tool, *view;
+    int trail_mode;
+
+    if (!PyArg_ParseTuple(args, "O!O!i:gv_autopan_tool_set_trail_mode", &PyGtk_Type, &tool, &PyGtk_Type, &view, &trail_mode))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_set_trail_mode(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), GV_VIEW_AREA(PyGtk_Get(view)), trail_mode));
+}
+
+static PyObject *_wrap_gv_autopan_tool_save_trail_tiles(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    const char *basename;
+
+    if (!PyArg_ParseTuple(args, "O!s:gv_autopan_tool_save_trail_tiles", &PyGtk_Type, &tool, &basename))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_save_trail_tiles(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), basename));
+}
+
+static PyObject *_wrap_gv_autopan_tool_load_trail_tiles(PyObject *self, PyObject *args) {
+    PyObject *tool;
+    const char *basename;
+    int num_trail_tiles;
+
+    if (!PyArg_ParseTuple(args, "O!si:gv_autopan_tool_load_trail_tiles", &PyGtk_Type, &tool, &basename, &num_trail_tiles))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_load_trail_tiles(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), basename, num_trail_tiles));
+}
+
+static PyObject *_wrap_gv_autopan_tool_register_view(PyObject *self, PyObject *args) {
+    PyObject *tool, *view;
+    int can_resize, can_reposition, trail_mode;
+
+    if (!PyArg_ParseTuple(args, "O!O!iii:gv_autopan_tool_register_view", &PyGtk_Type, &tool, &PyGtk_Type, &view, &can_resize, &can_reposition, &trail_mode))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_register_view(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), GV_VIEW_AREA(PyGtk_Get(view)), can_resize, can_reposition, trail_mode));
+}
+
+static PyObject *_wrap_gv_autopan_tool_remove_view(PyObject *self, PyObject *args) {
+    PyObject *tool, *view;
+
+    if (!PyArg_ParseTuple(args, "O!O!:gv_autopan_tool_remove_view", &PyGtk_Type, &tool, &PyGtk_Type, &view))
+        return NULL;
+    return PyInt_FromLong(gv_autopan_tool_remove_view(GV_AUTOPAN_TOOL(PyGtk_Get(tool)), GV_VIEW_AREA(PyGtk_Get(view))));
+}
+

Added: packages/openev/branches/upstream/current/pymod/gvogrdlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvogrdlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvogrdlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,228 @@
+##############################################################################
+# $Id: gvogrdlg.py,v 1.5 2003/09/09 15:18:46 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  OGR Layer Selection and Loading Dialog.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2003, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvogrdlg.py,v $
+#  Revision 1.5  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.4  2003/01/07 14:56:02  warmerda
+#  Removed print statement.
+#
+#  Revision 1.3  2003/01/07 03:37:53  warmerda
+#  lots of upgrades
+#
+#  Revision 1.2  2003/01/06 22:36:47  warmerda
+#  Added prototype ExecuteSQL() support
+#
+#  Revision 1.1  2003/01/06 21:40:19  warmerda
+#  New
+#
+#
+
+from gtk import *
+import gview
+import os.path
+import gvhtml
+import ogr
+
+
+class GvOGRDlg(GtkWindow):
+    def __init__(self, ds, viewwindow):
+        GtkWindow.__init__(self)
+        self.set_title('Vector Layer Selection')
+        self.set_usize(500, 500)
+        self.set_border_width(3)
+        self.set_policy(TRUE,TRUE,FALSE)
+        self.connect('delete-event',self.close)
+        shell = GtkVBox(homogeneous=FALSE,spacing=3)
+        self.add(shell)
+        gvhtml.set_help_topic(self, "veclayerselect.html" );
+
+        # Layer list
+        layerbox = GtkScrolledWindow()
+        shell.pack_start(layerbox)
+        layerlist = GtkCList(cols=2)
+            
+        layerbox.add_with_viewport(layerlist)
+        layerlist.set_shadow_type(SHADOW_NONE)
+        layerlist.set_selection_mode(SELECTION_SINGLE)
+        layerlist.set_row_height(30)
+        layerlist.set_column_width(0, 24)
+        #layerlist.connect('select-row', self.layer_selected)
+        layerlist.connect('button-press-event', self.list_clicked)
+
+        # Clip to view?
+
+        hbox = GtkHBox(homogeneous=FALSE)
+        shell.pack_start( hbox, expand=FALSE )
+
+        self.clip_to_view_btn = GtkCheckButton()
+        hbox.pack_start( self.clip_to_view_btn, expand=FALSE )
+
+        hbox.pack_start( GtkLabel('Clip To View' ), expand=FALSE )
+
+        # SQL Box.
+
+        hbox = GtkHBox(homogeneous=FALSE, spacing=3)
+        shell.pack_start( hbox,expand=FALSE )
+        
+        sql_button = GtkButton('Execute SQL:')
+        sql_button.connect('clicked', self.execute_sql)
+        hbox.pack_start(sql_button, expand=FALSE)
+        
+        self.sql_cmd = GtkEntry()
+        hbox.pack_start(self.sql_cmd,expand=TRUE)
+
+        # buttons
+        button_box = GtkHButtonBox()
+        button_box.set_layout_default(BUTTONBOX_START)
+        ok_button = GtkButton('Accept')
+        ok_button.connect('clicked', self.accept)
+        loadall_button = GtkButton('Load All')
+        loadall_button.connect('clicked', self.load_all)
+        cancel_button = GtkButton('Cancel')
+        cancel_button.connect('clicked', self.close)
+        help_button = GtkButton('Help')
+        help_button.connect('clicked', self.help_cb)
+        button_box.pack_start(ok_button, expand=FALSE)
+        button_box.pack_start(loadall_button, expand=FALSE)
+        button_box.pack_start(cancel_button, expand=FALSE)
+        button_box.pack_start(help_button, expand=FALSE)
+        shell.pack_start(button_box,expand=FALSE)
+
+        self.connect('realize', self.realize)
+
+        self.sel_pixmap = \
+            GtkPixmap(self,os.path.join(gview.home_dir,'pics',
+                                        'ck_on_l.xpm'))
+        self.not_sel_pixmap = \
+            GtkPixmap(self,os.path.join(gview.home_dir,'pics',
+                                        'ck_off_l.xpm'))
+        
+        shell.show_all()
+
+        self.ds = ds
+        self.viewwindow = viewwindow
+        self.layerlist = layerlist
+
+        layer_count = ds.GetLayerCount()
+        self.layer_names = []
+        self.layer_sel = []
+        for i in range(layer_count):
+            layer = ds.GetLayer( i )
+            self.layer_names.append( layer.GetName() )
+            self.layer_sel.append( 0 )
+
+        self.show_all()
+
+    def help_cb(self,*args):
+        gvhtml.LaunchHTML( "veclayerselect.html" );
+    
+    def close(self,*args):
+        self.ds.Destroy()
+        self.hide()
+        return TRUE
+
+    def load_all(self,*args):
+        for i in range(len(self.layer_sel)):
+            self.layer_sel[i] = 1
+        self.accept()
+
+    def accept(self,*args):
+
+        if self.clip_to_view_btn.get_active():
+            xmin, ymin, xmax, ymax = self.viewwindow.viewarea.get_extents()
+
+            wkt = 'POLYGON((%g %g,%g %g,%g %g,%g %g,%g %g))' % \
+                   (xmin,ymax,xmax,ymax,xmax,ymin,xmin,ymin,xmin,ymax)
+            rect = ogr.CreateGeometryFromWkt( wkt )
+        else:
+            rect = None
+            
+        for i in range(len(self.layer_sel)):
+            if self.layer_sel[i]:
+                layer = self.ds.GetLayer( i )
+
+                if rect is not None:
+                    layer.SetSpatialFilter( rect )
+                    
+                self.viewwindow.file_open_ogr_by_layer( layer )
+                
+                if rect is not None:
+                    layer.SetSpatialFilter( None )
+
+        if rect is not None:
+            rect.Destroy()
+            
+        self.close()
+
+    def realize(self, widget):
+        lst = self.layerlist
+
+        lst.freeze()
+        lst.clear()
+
+        i = 0
+        for entry in self.layer_names:
+            lst.append(('', entry))
+                
+            lst.set_pixmap(i, 0, self.not_sel_pixmap)
+
+            i = i + 1
+
+        lst.thaw()        
+
+    def list_clicked(self, lst, event):
+        row, col = lst.get_selection_info(int(event.x), int(event.y))
+        lst.emit_stop_by_name('button-press-event')
+
+        if event.type is GDK._2BUTTON_PRESS:
+            for i in range(len(self.layer_sel)):
+                self.layer_sel[i] = 0
+                
+            self.layer_sel[row] = 1
+            self.accept()
+        else:
+            self.layer_sel[row] = not self.layer_sel[row]
+        
+        if self.layer_sel[row]:
+            lst.set_pixmap(row, 0, self.sel_pixmap)
+        else:
+            lst.set_pixmap(row, 0, self.not_sel_pixmap)
+        
+    def execute_sql(self, *args):
+
+        statement = self.sql_cmd.get_text()
+
+        layer = self.ds.ExecuteSQL( statement )
+
+        if layer is not None:
+            self.viewwindow.file_open_ogr_by_layer( layer )
+            
+            self.ds.ReleaseResultsSet( layer )

Added: packages/openev/branches/upstream/current/pymod/gvogrfs.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvogrfs.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvogrfs.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,349 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: gvogrfs.py,v 1.7 2003/05/08 16:19:39 pgs Exp $
+#
+# Project:  OpenEV
+# Purpose:  Classes for building, and parsing OGR Feature Style Specifications
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvogrfs.py,v $
+#  Revision 1.7  2003/05/08 16:19:39  pgs
+#  made params safe if value missing
+#
+#  Revision 1.6  2001/04/29 16:27:33  pgs
+#  added has_part() to OGRFeatureStyle class
+#
+#  Revision 1.5  2001/04/23 21:21:28  warmerda
+#  added OGRFeatureStyleParam.set() and OGRFeatureStyleParm.set_parm
+#
+#  Revision 1.4  2001/04/19 22:07:53  warmerda
+#  avoid use of +=
+#
+#  Revision 1.3  2001/04/16 13:50:35  warmerda
+#  Paul added OGRFeatureStyle class
+#
+#  Revision 1.2  2001/04/09 18:37:46  warmerda
+#  improved color handling
+#
+#  Revision 1.1  2001/03/21 22:40:33  warmerda
+#  New
+#
+#
+
+import gview
+import string
+
+def gv_to_ogr_color( rgba ):
+    if len(rgba) == 3:
+        rgba = (rgba[0], rgba[1], rgba[2], 1.0)
+
+    red = min(255,max(0,int(rgba[0] * 255 + 0.5)))
+    green = min(255,max(0,int(rgba[1] * 255 + 0.5)))
+    blue = min(255,max(0,int(rgba[2] * 255 + 0.5)))
+    alpha = min(255,max(0,int(rgba[3] * 255 + 0.5)))
+
+    color = '#%02X%02X%02X' % (red, green, blue)
+    if alpha != 255:
+        color = color + '%02X' % alpha
+
+    return color
+
+def ogr_to_gv_color( ogr_color ):
+    if len(ogr_color) == 9:
+        return (int(ogr_color[1:3],16) / 255.0,
+                int(ogr_color[3:5],16) / 255.0,
+                int(ogr_color[5:7],16) / 255.0,
+                int(ogr_color[7:9],16) / 255.0)
+    elif len(ogr_color) == 7:
+        return (int(ogr_color[1:3],16) / 255.0,
+                int(ogr_color[3:5],16) / 255.0,
+                int(ogr_color[5:7],16) / 255.0,
+                1.0)
+    else:
+        return (0,0,0,1.0)
+
+class OGRFeatureStyleParam:
+
+    def __init__(self, parm=None):
+        if parm is not None:
+            self.parse(parm)
+
+    def set(self, name, value, role='string_value', units=''):
+        self.param_name = name
+        self.units = units
+        self.value = value
+        self.role = role
+
+    def parse(self, parm):
+        (key,value) = string.split(parm,':',1)
+        self.param_name = key
+        
+        #trap params that have no value
+        if len(value) == 0:
+            self.role='numeric_value'
+            self.value = ''
+            self.units = ''
+            return
+
+        # Handle units
+        self.units = ''
+        if value[-2:] in ['px','pt','mm','cm','in']:
+            self.units = value[-2:]
+            value = value[:-2]
+        elif value[-1:] == 'g':
+            self.units = value[-1:]
+            value = value[:-1]
+
+        if value[0] == '"':
+            if value[-1:] != '"':
+                raise ValueError, 'unterminated literal - ' + parm
+
+            self.role = 'string_value'
+            self.value = value[1:-1]
+
+        elif value[0] == '{':
+            if value[-1:] != '}':
+                raise ValueError, 'unterminated fieldname - ' + parm
+
+            self.role = 'field_name'
+            self.value = value[1:-1]
+
+        else:
+            self.role = 'numeric_value'
+            self.value = value
+
+    def unparse(self):
+        result = self.param_name + ':'
+        if self.role == 'numeric_value':
+            result = result + self.value
+        elif self.role == 'field_name':
+            result = result + '{'+self.value+'}'
+        else:
+            result = result + '"'+self.value+'"'
+
+        result = result + self.units
+
+        return result
+
+    def __str__(self):
+        result = '  parm=%s  role=%12s  value=%-20s' \
+                 % (self.param_name, self.role, self.value)
+        if len(self.units) > 0:
+            result = result + ' units:'+self.units
+
+        return result
+
+class OGRFeatureStylePart:
+    def __init__(self):
+        pass
+
+    def parse(self, style_part):
+
+        style_part = string.strip(style_part)
+        i = string.find(style_part, '(')
+        if i == -1:
+            raise ValueError, 'no args to tool name - ' + style_part
+
+        self.tool_name = string.upper(style_part[:i])
+        if self.tool_name not in [ 'PEN', 'BRUSH', 'SYMBOL', 'LABEL' ]:
+            raise ValueError, 'unrecognised tool name - ' + style_part
+
+        if style_part[-1:] != ')':
+            raise ValueError, 'missing end bracket - ' + style_part
+
+        tool_parms = style_part[i+1:-1]
+
+        parms_list = []
+        i = 0
+        last_i = 0
+        in_literal = 0
+        while i < len(tool_parms):
+
+            if tool_parms[i] == '"':
+                if not in_literal or i == 0 or tool_parms[i-1] != '\\':
+                    in_literal = not in_literal
+
+            if not in_literal and tool_parms[i] == ',':
+                parms_list.append( string.strip(tool_parms[last_i:i]) )
+                i = i + 1
+                last_i = i
+
+            i = i + 1
+
+        if in_literal:
+            raise ValueError, 'unterminated string literal - ' + style_part
+
+        parms_list.append(string.strip(tool_parms[last_i:]))
+        self.parms = {}
+        for parm_literal in parms_list:
+            parm = OGRFeatureStyleParam(parm_literal)
+            self.parms[parm.param_name] = parm
+
+    def unparse(self):
+        result = self.tool_name + '('
+        first = 1
+        for key in self.parms.keys():
+            if first:
+                first = 0
+            else:
+                result = result + ','
+
+            result = result + self.parms[key].unparse()
+        result = result + ')'
+
+        return result
+
+    def set_parm(self, parm_obj ):
+        self.parms[parm_obj.param_name] = parm_obj
+
+    def get_parm(self, parm_name, default_value=None):
+        if self.parms.has_key(parm_name):
+            return self.parms[parm_name].value
+        else:
+            return default_value
+
+    def get_color(self, default_value=None):
+        color = self.get_parm('c', None)
+        if color is None or color[0] != '#':
+            return default_value
+        else:
+            return ogr_to_gv_color( color )
+
+    def __str__(self):
+        result = 'Tool:%s\n' % self.tool_name
+        for key in self.parms.keys():
+            parm = self.parms[key]
+            result = result + '  %s\n' % str(parm)
+
+        return result
+
+class OGRFeatureStyle:
+    """
+    Encapulation of an OGR Feature Style
+
+    This object keeps one tool of each type in a dictionary and allows parsing
+    and unparsing of the ogrfs property that would be stored on a vector
+    layer.  The semi-colon separator is used for parts. 
+    """
+    def __init__(self, style=None):
+        self.parts = {}
+
+        if style is not None:
+            self.parse(style)
+
+    def parse(self, style):
+        """
+        parse a style into style parts by breaking it apart at any
+        ';' not within '"'
+        """
+        #TODO: check to see if it is of type string 
+        #      or can be turned into a string as well
+        if style is None:
+            return
+            
+        style = string.strip(style)
+        if style == '':
+            print 'empty style'
+            return
+        in_quote = 0
+        part_start = 0
+        for i in range(len(style)):
+            char = style[i:i+1]
+            if char == chr(34):
+                in_quote = not in_quote
+            if not in_quote and char == ";":
+                part = style[part_start:i]
+                self.parse_part(part)
+                part_start = i + 1
+        #check for the last one ...
+        if part_start != 0 or len(self.parts) == 0:
+            part = style[part_start:]
+            self.parse_part(part)
+
+    def parse_part(self, part):
+        """
+        parse a single part
+        """
+        ogr_part = OGRFeatureStylePart()
+        try:
+            ogr_part.parse(part)
+            self.add_part(ogr_part)
+        except:
+            print 'Invalid part in feature sytle definition'
+
+    def unparse(self):
+        """
+        compose the feature style into a string
+        """
+        result = ''
+        sep = ''
+        for key in self.parts.keys():
+            part = self.parts[key]
+            result = result + sep + part.unparse()
+            sep = ";"
+        return result
+
+    def add_part(self, ogr_part):
+        """
+        add an OGRFeatureStylePart
+        """
+        if issubclass(ogr_part.__class__, OGRFeatureStylePart):
+            self.parts[ogr_part.tool_name] = ogr_part
+        else:
+            raise TypeError, 'ogr_part must be an OGRFeatureStylePart'
+
+    def get_part(self, part_name, default = None):
+        if self.parts.has_key(part_name):
+            return self.parts[part_name]
+        else:
+            return default
+
+    def remove_part(self, part_name):
+        if self.parts.has_key(part_name):
+            del self.parts[part_name]
+            
+    def has_part(self, part_name):
+        return self.parts.has_key(part_name)
+
+    def __str__(self):
+        result = 'Feature Style Definition:\n'
+        for key in self.parts.keys():
+            part = self.parts[key]
+            result = result + '  %s\n' % str(part)
+
+        return result
+
+
+if __name__ == '__main__':
+
+    fsp = OGRFeatureStylePart()
+
+    while 1:
+        line = raw_input('OGR FS:')
+        if len(line) == 0:
+            sys.exit(0)
+
+        fsp.parse( line )
+        print fsp
+        print fsp.unparse()
+
+


Property changes on: packages/openev/branches/upstream/current/pymod/gvogrfs.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/gvogrfsgui.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvogrfsgui.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvogrfsgui.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,554 @@
+###############################################################################
+# $Id: gvogrfsgui.py,v 1.6 2001/09/17 15:27:03 pgs Exp $
+#
+# Project:  OpenEV
+# Purpose:  GUI Widgets for displaying and manipulating OGRFS rendering
+#           descriptions.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvogrfsgui.py,v $
+#  Revision 1.6  2001/09/17 15:27:03  pgs
+#  updated to work with new ColorButton
+#
+#  Revision 1.5  2001/07/01 03:06:51  pgs
+#  hacked to get manual change of offsets working
+#
+#  Revision 1.4  2001/05/05 18:28:14  pgs
+#  added interactive updates plus reverse dy offset
+#
+#  Revision 1.3  2001/05/04 02:56:56  pgs
+#  a couple of bug fixes plus added label offsets
+#
+#  Revision 1.2  2001/04/24 14:24:02  warmerda
+#  added support for applying text indirectly
+#
+#  Revision 1.1  2001/04/23 21:20:19  warmerda
+#  New
+#
+#
+
+from gtk import *
+from string import *
+from gvsignaler import *
+import gvutils
+import gview
+import pgu
+import sys
+import pgucolorsel
+import pgufont
+import pgucombo
+import pgucolor
+import pgumenu
+import gvogrfs
+
+#map display names to ogr symbol names and an icon (for an image menu)
+ogrfs_symbols = {
+    'cross'             : ('"ogr-sym-0"', 'sym_cross.xpm'),
+    'x'                 : ('"ogr-sym-1"', 'sym_x.xpm'),
+    'unfilled circle'   : ('"ogr-sym-2"', 'sym_circle.xpm'),
+    'filled circle'     : ('"ogr-sym-3"', 'sym_filled_circle.xpm'),
+    'unfilled square'   : ('"ogr-sym-4"', 'sym_square.xpm'),
+    'filled square'     : ('"ogr-sym-5"', 'sym_filled_square.xpm'),
+    'unfilled triangle' : ('"ogr-sym-6"', 'sym_triangle.xpm'),
+    'filled triangle'   : ('"ogr-sym-7"', 'sym_filled_triangle.xpm'),
+    'unfilled star'     : ('"ogr-sym-8"', 'sym_star.xpm'),
+    'filled star'       : ('"ogr-sym-9"', 'sym_filled_star.xpm'),
+    'vertical bar'      : ('"ogr-sym-10"','sym_vertical.xpm')
+         }
+
+ogrfs_symbol_names = ['cross', 'x',
+             'unfilled circle', 'filled circle',
+             'unfilled square', 'filled square',
+             'unfilled triangle', 'filled triangle',
+             'unfilled star', 'filled star',
+             'vertical bar']
+
+
+###############################################################################
+class GvLabelStyle(GtkVBox, Signaler):
+
+    ###########################################################################
+    def __init__(self, spacing=10, text_entry=FALSE, layer=None,
+                 label_field = TRUE, enable_offsets=FALSE, interactive=FALSE):
+        GtkVBox.__init__(self,spacing=spacing)
+
+        self.ogrfs_obj = None
+        self.layer = layer
+        self.shape_obj = None
+        self.text_entry = text_entry
+        self.enable_offsets = enable_offsets
+        self.interactive = interactive
+        self.updating = 0
+        self.old_list = []
+
+        self.set_border_width(10)
+        self.create_gui()
+
+        self.connect('unrealize', self.close)
+
+        self.publish('ogrfs-changed')
+        self.publish('apply-text-to-field')
+
+        self.gui_update()
+        self.show_all()
+
+        if not label_field:
+            self.field_label.hide()
+            self.label_field.hide()
+
+    ###########################################################################
+    def close( self, *args ):
+        pass
+
+    ###########################################################################
+    def set_ogrfs( self, ogrfs_obj, layer = None, fontlist = None,
+                   shape_obj = None ):
+
+        if layer is not None:
+            self.layer = layer
+
+        if ogrfs_obj is None:
+            ogrfs_obj = gvogrfs.OGRFeatureStylePart()
+            font = pgufont.XLFDFontSpec()
+            default_font = gview.get_preference('default-font')
+            if default_font is None:
+                font.set_font_part('Family', 'fixed')
+            else:
+                font.parse_font_spec(default_font)
+            if self.enable_offsets:
+                dx = self.x_offset.get_value_as_float()
+                dy = self.y_offset.get_value_as_float()
+            ogrfs_obj.parse('LABEL(t:"",f:"%s",c:#88FF88)' % font)
+
+        self.ogrfs_obj = ogrfs_obj
+        self.shape_obj = shape_obj
+
+        self.gui_update()
+
+    ###########################################################################
+    def create_gui(self):
+
+        table = GtkTable()
+        table.set_row_spacings(3)
+        table.set_col_spacings(3)
+        self.pack_start(table)
+
+        # collect candidate field names from the schema.
+        fnlist = [ 'disabled' ]
+
+        # Field Name
+        self.field_label = GtkLabel('Label Field:')
+        table.attach(self.field_label, 0, 1, 0, 1,
+                xoptions=SHRINK, yoptions=SHRINK)
+        self.label_field = pgucombo.pguCombo()
+        self.label_field.set_popdown_strings( fnlist )
+        self.label_field.entry.connect('changed', self.label_change_cb)
+        table.attach(self.label_field, 1, 3, 0, 1,
+                xoptions=SHRINK, yoptions=SHRINK)
+
+        # Create Color control.
+        table.attach(GtkLabel('Color:'), 0, 1, 1, 2,
+                xoptions=SHRINK, yoptions=SHRINK)
+        self.label_color = pgucolorsel.ColorControl('Label Color',
+                                                    self.label_change_cb)
+        table.attach(self.label_color, 1, 3, 1, 2,
+                yoptions=SHRINK)
+
+        # Font
+        table.attach(GtkLabel('Font:'), 0, 1, 2, 3,
+                xoptions=SHRINK, yoptions=SHRINK)
+        self.label_font = pgufont.pguFontControl()
+        self.label_font.subscribe('font-changed', self.label_change_cb)
+        table.attach(self.label_font, 1, 2, 2, 3,
+                xoptions=SHRINK)
+
+        #######################################################################
+        # Add Text entry/edit
+        if self.text_entry:
+            table.attach(GtkLabel('Text:'), 0, 1, 3, 4,
+                    xoptions=SHRINK, yoptions=SHRINK)
+
+            self.text_entry = GtkEntry()
+            self.text_entry.connect('activate',self.text_change_cb)
+            if self.interactive:
+                self.text_entry.connect('changed', self.text_change_cb)
+            else:
+                self.text_entry.connect('focus-out-event',self.text_change_cb)
+            table.attach(self.text_entry, 1, 3, 3, 4)
+        else:
+            self.text_entry = None
+
+        if self.enable_offsets:
+            #Label offsets
+            table.attach(GtkLabel('Label X Offset:'), 0, 1, 4, 5,
+                    xoptions=SHRINK, yoptions=SHRINK)
+            spin_adjust = GtkAdjustment(value=0.0, lower=-20.0, upper=20.0, step_incr=1.0)
+            self.x_offset = GtkSpinButton(spin_adjust)
+            self.x_offset.set_editable(TRUE)
+            self.x_offset.set_digits(1)
+            self.x_offset.set_usize(75, 0)
+            self.x_offset.connect('changed', self.label_change_cb)
+            table.attach(self.x_offset, 1, 3, 4, 5,
+                            xoptions=SHRINK, yoptions=SHRINK)
+
+            #Label offsets
+            table.attach(GtkLabel('Label Y Offset:'), 0, 1, 5, 6,
+                    xoptions=SHRINK, yoptions=SHRINK)
+            spin_adjust = GtkAdjustment(value=0.0, lower=-20.0, upper=20.0, step_incr=1.0)
+            self.y_offset = GtkSpinButton(spin_adjust)
+            self.y_offset.set_editable(TRUE)
+            self.y_offset.set_digits(1)
+            self.y_offset.set_usize(75, 0)
+            self.y_offset.connect('changed', self.label_change_cb)
+            table.attach(self.y_offset, 1, 3, 5, 6,
+                            xoptions=SHRINK, yoptions=SHRINK)
+
+
+    ###########################################################################
+    def gui_update(self, *args):
+
+        self.updating = TRUE
+
+        # Update the field list.
+        fnlist = [ 'disabled' ]
+        if self.layer is not None:
+            schema = self.layer.get_parent().get_schema()
+            for item in schema:
+                fnlist.append( item[0] )
+
+        if fnlist != self.old_list:
+            self.label_field.set_popdown_strings( fnlist )
+            self.old_list = fnlist
+
+        self.label_field.entry.delete_text(0,-1)
+
+        if self.ogrfs_obj is None:
+            self.label_field.entry.insert_text('disabled')
+            self.label_color.set_color( (0.5, 1.0, 0.5, 1.0) )
+            if self.enable_offsets:
+                self.x_offset.set_value(0.0)
+                self.y_offset.set_value(0.0)
+        else:
+            font = pgufont.XLFDFontSpec()
+            font_spec = self.ogrfs_obj.parms['f'].value
+            if font_spec is not None:
+                font.parse_font_spec(font_spec)
+            else:
+                default_font = gview.get_preference('default-font')
+                if default_font is None:
+                    font.set_font_part('Family', 'fixed')
+                else:
+                    font.parse_font_spec(default_font)
+            self.label_font.set_font(str(font))
+
+            tparm = self.ogrfs_obj.parms['t']
+            if tparm.role == 'field_name':
+                self.label_field.entry.insert_text(tparm.value)
+                if self.shape_obj is not None:
+                    text_value = self.shape_obj.get_property(tparm.value,'')
+                else:
+                    text_value = ''
+            else:
+                self.label_field.entry.insert_text('disabled')
+                text_value = tparm.value
+
+            color = self.ogrfs_obj.get_color((0.5, 1.0, 0.5, 1.0))
+            self.label_color.set_color( color )
+
+            if self.text_entry is not None:
+                self.text_entry.set_text(text_value)
+                #self.text_entry.set_sensitive(tparm.role != 'field_name')
+
+            if self.enable_offsets:
+                try:
+                    dx = float(self.ogrfs_obj.get_parm('dx'))
+                    self.x_offset.set_value(dx)
+                except:
+                    dx = None
+
+                if dx is None:
+                    dx = 0.0
+
+                try:
+                    dy = float(self.ogrfs_obj.get_parm('dy'))
+                    self.y_offset.set_value(-dy)
+                except:
+                    dy = None
+
+                if dy is None:
+                    dy = 0.0
+
+                #update the widgets
+
+
+        self.updating = FALSE
+
+    ###########################################################################
+    # Handle updates to the raw text.  Normally we just turn this over to
+    # the generic label_change_cb(), but if we have field indirection in
+    # operation, then we instead emit a signal offering the text to the
+    # application to apply to the shape.
+
+    def text_change_cb(self, *args):
+        field_name = self.label_field.entry.get_chars(0,-1)
+        if field_name == 'disabled' or len(field_name) == 0:
+            self.label_change_cb()
+            return
+
+        #this shouldn't happen :)
+        val = ''
+        if self.text_entry is not None:
+            val = self.text_entry.get_text()
+
+        self.notify( 'apply-text-to-field',
+                     field_name, val )
+        self.gui_update()
+
+    ###########################################################################
+    # Handle updates to the label font.
+
+    def label_change_cb(self, *args):
+        if self.layer is None or self.updating:
+            return
+
+        font = self.label_font.get_font()
+        field_name = self.label_field.entry.get_chars(0,-1)
+        text_value = ''
+        if self.text_entry is not None:
+            text_value = self.text_entry.get_text()
+
+        color = self.label_color.current_color
+        color = gvogrfs.gv_to_ogr_color( color )
+
+        import string
+        x_off = ''
+        y_off = ''
+
+
+
+        if self.enable_offsets:
+            #handle user editing of the values in the x and y offsets.
+            #something odd happens when listening to the 'changed' signal
+            #of the spin box, the text value is difference from the float
+            #value, even if the values should match.  This hack forces the
+            #spin buttons to update the value and exits because this
+            #will be called again right away
+            x = self.x_offset.get_value_as_float()
+            y = self.y_offset.get_value_as_float()
+            sx = self.x_offset.get_text()
+            sy = self.y_offset.get_text()
+            try:
+                if float(sx) != x:
+                    self.x_offset.set_value(float(sx))
+                    return
+                if float(sy) != y:
+                    self.y_offset.set_value(float(sy))
+                    return
+            except:
+                return
+            if 0.0 != x:
+                x_off = 'dx:%s,' % x
+
+            if 0.0 != y:
+                y_off = 'dy:%s,' % -y
+
+        if field_name == 'disabled' or len(field_name) == 0:
+            if text_value is not None:
+                ogrfs = 'LABEL(%s%st:\"%s\",f:"%s",c:%s)' % \
+                        (x_off, y_off, text_value,font,color)
+            else:
+                ogrfs = None
+        else:
+            ogrfs = 'LABEL(%s%st:{%s},f:"%s",c:%s)' % \
+                        (x_off, y_off, field_name, font, color)
+
+        if ogrfs is None:
+            self.ogrfs_obj = None
+        else:
+            self.ogrfs_obj = gvogrfs.OGRFeatureStylePart()
+            self.ogrfs_obj.parse( ogrfs )
+
+        self.gui_update()
+
+        self.notify('ogrfs-changed')
+
+    #
+    def set_sensitive(self, sensitive):
+        self.text_entry.set_sensitive(sensitive)
+
+    ###########################################################################
+    # Handle updates to the label font.
+    def text_input(self, keyval):
+        if keyval > 31 and keyval < 256 and self.text_entry is not None:
+            new_string = self.text_entry.get_text()
+            new_string = new_string + chr(keyval)
+            self.text_entry.set_text( new_string )
+
+        elif keyval == GDK.Return:
+            self.label_change_cb()
+
+        # add support for delete, etc, later.
+
+
+
+class GvSymbolStyle(GtkVBox, Signaler):
+    """
+    A generic embeddable widget for controlling the SYMBOL ogr feature style
+    on an arbitrary object (layer or shape).
+    """
+
+    def __init__(self, spacing=10, ogrfs_obj=None, layer=None):
+        """
+        Initialize the widget optionally setting the ogr object for which this
+        represents the SYMBOL feature style.
+        """
+        GtkVBox.__init__(self, spacing=spacing)
+        self.create_gui()
+
+        self.updating = FALSE
+        self.publish('ogrfs-changed')
+        self.set_ogrfs(ogrfs_obj, layer)
+
+    def create_gui(self):
+        """
+        create the widgets for this symbol
+        """
+
+        table = GtkTable()
+        table.set_row_spacings(3)
+        table.set_col_spacings(3)
+        self.pack_start(table)
+        #symbol color
+        table.attach(GtkLabel('Color: '), 0, 1, 0, 1,
+                xoptions=SHRINK, yoptions=SHRINK)
+        self.symbol_color = pgucolor.ColorButton((0.5, 1.0, 0.5, 1.0))
+        self.symbol_color.connect('color-set', self.color_change)
+        table.attach(self.symbol_color, 1, 2, 0, 1,
+                xoptions=SHRINK, yoptions=SHRINK)
+
+        # Point symbol
+        table.attach(GtkLabel('Symbol:'), 0, 1, 1, 2,
+                xoptions=SHRINK, yoptions=SHRINK)
+        self.symbol_type = pgumenu.pguMenuFactory(MENU_FACTORY_OPTION_MENU)
+        entries = []
+        for i in range(len(ogrfs_symbol_names)):
+            sym_name = ogrfs_symbol_names[i]
+            sym_img = ogrfs_symbols[sym_name][1]
+            a = '<image:' + sym_img + '>' + sym_name
+            entries.append((a, None, self.symbol_change, ogrfs_symbols[sym_name][0]))
+
+        self.symbol_type.add_entries(entries)
+
+        self.symbol_type.set_usize(150, 30)
+        table.attach(self.symbol_type, 1, 4, 1, 2,
+                xoptions=SHRINK, yoptions=SHRINK)
+
+        # Point size
+        table.attach(GtkLabel('Scale: '), 0, 1, 2, 3,
+                xoptions=SHRINK, yoptions=SHRINK)
+        spin_adjust = GtkAdjustment(value=1.0, lower=0.2,
+                        upper=4.0, step_incr=0.1)
+        self.symbol_size = GtkSpinButton(spin_adjust)
+        self.symbol_size.set_editable(TRUE)
+        self.symbol_size.set_digits(1)
+        self.symbol_size.set_usize(75, 0)
+        self.symbol_size.connect('changed', self.scale_change)
+        table.attach(self.symbol_size, 1, 3, 2, 3,
+                xoptions=SHRINK, yoptions=SHRINK)
+
+
+
+    def gui_update(self, *args):
+        """
+        refresh the screen
+        """
+        #get the current values
+        if self.updating:
+            return
+
+        self.updating = TRUE
+
+        sym = int(self.ogrfs_obj.parms['id'].value[8:9])
+        color = gvogrfs.ogr_to_gv_color(self.ogrfs_obj.parms['c'].value)
+        try:
+            scale = float(self.ogrfs_obj.get_parm('s'))
+        except:
+            scale = None
+
+        if scale is None:
+            scale = 1.0
+
+        #update the widgets
+        self.symbol_type.set_history(sym)
+        self.symbol_color.set_color(color)
+        self.symbol_size.set_value(scale)
+
+        self.updating = FALSE
+
+    def set_ogrfs(self, ogrfs_obj, layer = None):
+        """
+        set the ogr feature specification from the shape object passed,
+        or from the layer if the shape has none, or provide a default
+        """
+
+        if ogrfs_obj is None:
+            ogrfs_obj = gvogrfs.OGRFeatureStylePart()
+            ogrfs_obj.parse('SYMBOL(id:"ogr-sym-0",c:#88FF88)')
+
+        self.ogrfs_obj = ogrfs_obj
+        self.layer = layer
+        self.gui_update()
+
+    def color_change(self, widget, *args):
+        """
+        """
+        print 'color_change(', widget, args, ')'
+        val = 'c:%s' % gvogrfs.gv_to_ogr_color(widget.get_color())
+        parm = gvogrfs.OGRFeatureStyleParam()
+        parm.parse(val)
+        self.ogrfs_obj.set_parm(parm)
+        self.notify('ogrfs-changed')
+
+    def scale_change(self, widget):
+        """
+        change the scale ... be careful as it may not exist beforehand
+        """
+        val = 's:%s' % widget.get_value_as_float()
+        parm = gvogrfs.OGRFeatureStyleParam()
+        parm.parse(val)
+        self.ogrfs_obj.set_parm(parm)
+        self.notify('ogrfs-changed')
+
+    def symbol_change(self, widget, symbol):
+        """
+        change the symbol
+        """
+        val = 'id:%s' % symbol
+        parm = gvogrfs.OGRFeatureStyleParam()
+        parm.parse(val)
+        self.ogrfs_obj.set_parm(parm)
+        self.notify('ogrfs-changed')
+
+
+
+pgu.gtk_register('GvLabelStyle',GvLabelStyle)
+pgu.gtk_register('GvSymbolStyle', GvSymbolStyle)

Added: packages/openev/branches/upstream/current/pymod/gvplot.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvplot.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvplot.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,752 @@
+#! /usr/bin/env python
+###############################################################################
+#
+# Project:  OpenEV
+# Purpose:  Implement the generic Plot function for simple internal plotting.
+# Author:   Frank Warmerdam <warmerdam at pobox.com>
+#           Gillian Walter <gwalter at atlsci.com>
+#
+###############################################################################
+# Copyright (c) 2001, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvplot.py,v $
+#  Revision 1.21  2005/08/18 19:56:15  warmerda
+#  Make dependence on pygtk optional.
+#
+#  Revision 1.20  2005/08/01 09:31:27  andrey_kiselev
+#  Use 'png' terminal type instead of 'png color' as of recent gnuplot.
+#
+#  Revision 1.19  2004/11/01 16:18:44  gmwalter
+#  Add option to return a layer.
+#
+#  Revision 1.18  2004/09/26 01:36:52  warmerda
+#  Raise runtime error if we can't find gnuplot.
+#
+#  Revision 1.17  2004/08/31 21:02:13  gmwalter
+#  Added ability to return a raster layer in plot3d.
+#
+#  Revision 1.16  2004/06/15 20:30:15  gmwalter
+#  Check in William's plot additions.
+#
+#  Revision 1.15  2003/09/30 17:56:12  gmwalter
+#  Update so that window title of plots can be set.
+#
+#  Revision 1.14  2003/06/17 14:58:38  gmwalter
+#  gvplot: changed so that graphs don't immediately disappear
+#  in the gnuplot terminal case.
+#  gvrasterpropdlg: fix so that user can go back to opaque white modulation
+#  colour.
+#
+#  Revision 1.13  2002/07/12 12:46:06  warmerda
+#  expanded tabs
+#
+#  Revision 1.12  2002/04/18 18:16:16  gmwalter
+#  Windows bug fixes.
+#
+#  Revision 1.11  2002/04/18 16:52:05  warmerda
+#  ensure file closed before unlink
+#
+#  Revision 1.10  2002/04/17 18:31:46  gmwalter
+#  Fixed typos.
+#
+#  Revision 1.9  2002/04/17 14:43:46  warmerda
+#  improved logic for finding gnuplot executable
+#
+#  Revision 1.8  2002/04/17 13:41:39  warmerda
+#  rewritten to avoid use of Gnuplot.py
+#
+#  Revision 1.7  2002/03/25 20:57:04  gmwalter
+#  Add print capability to the plot window.
+#
+#  Revision 1.5  2002/03/22 15:17:09  gmwalter
+#  Added contour plot option to plot3d.
+#
+#  Revision 1.4  2001/12/06 14:22:52  gmwalter
+#  Added loop to plot3d to check for temporary file creation before
+#  trying to open it (plot3d is much slower than plot).
+#
+#  Revision 1.3  2001/11/29 02:46:48  warmerda
+#  Gillian provided plot3d() extention
+#
+#  Revision 1.2  2001/11/23 20:07:44  warmerda
+#  Use os.tempnam() to prepare a name for the temporary file.
+#
+#  Revision 1.1  2001/11/14 22:34:27  warmerda
+#  New
+#
+#
+
+import Numeric
+
+try:
+    import pygtk
+    pygtk.require("1.2")
+except:
+    pass
+
+import gtk
+import os
+import string
+import GtkExtra
+import gvutils
+
+###############################################################################
+# plot()
+
+def plot( data=None, xaxis=None, yaxis=None, xmin=None, xmax=None,
+          ymin=None, ymax=None, title=None, cmds=None, 
+          terminal = 'openev', output = None, wintitle=None,
+          datastyle = None, multiplot = False , multilabels = (),
+          multiopts = ()):
+    """plot(data [, xaxis=text] [,yaxis=text] [,title=text] 
+                  [, xmin=n] [, xmax=n] [, ymin=n] [, ymax=n] 
+                  [, cmds=cmd_list]   [,terminal={"openev","gnuplot"}]
+                  [, wintitle=text] [, datastyle=text]
+                  [, multiplot = {True,False}] [, multilabels = list]
+                  [, multiopts = list]
+        data -- data array to plot, should be 1-D set of Y data
+                or 2-D array with pairs of (x,y) values.
+                or 3-D array with pairs of (x,y,z) values '
+                or 2-D array with tuples of (x,y1,y2,...,yN) values.'
+    """
+
+    ###########################################################################
+    # Print usage() message if no options given.
+
+    if data is None:
+        print 'Usage: plot(data [, xaxis=text] [,yaxis=text] [,title=text] '
+        print '                 [, xmin=n] [, xmax=n] [, ymin=n] [, ymax=n] '
+        print '                 [, cmds=cmd_list] '
+        print '                 [,terminal={"openev","gnuplot"}]'
+        print '                 [,wintitle=text] [, datastyle=text] '
+        print '                 [, multiplot = {True,False}]'
+        print '                 [, multilabels = list]  [, multiopts = list] )'
+        print ''
+        print ' data -- data array to plot, should be 1-D set of Y data'
+        print '         or 2-D array with pairs of (x,y) values.'
+        print '         or 3-D array with pairs of (x,y,z) values '
+        print '         or 2-D array with tuples of (x,y1,y2,...,yN) values.'
+        print '         for the last multiplot must be true, multilables is'
+        print '         a list of text labels for the graphs, and multiopts'
+        print '         is a list of gnuplot options to be added to the'
+        print '         individual graphs'
+        print ''
+        return
+    
+    ###########################################################################
+    # Work out the shape of the data.  A 1-D array is assumed to be Y
+    # values.  An Nx2 array is assumed to be (X,Y) values.  A 3-D array is
+    # assumed to be (X,Y,Z) values.  If multiplot is True we need
+    # a Nxk array, with K at least 2
+
+    try:
+        dshape = Numeric.shape( data )
+    except:
+        raise ValueError, "data argument to plot() does not appear to be a NumPy array"
+
+    dim = len(dshape)
+
+    ###########################################################################
+    # Reformat the list into a uniform Nx2 format.
+
+    if multiplot == False:        
+        if dim == 1:
+            dim=2
+            list = []
+            for i in range(len(data)):
+                list.append( (i,data[i]) )
+            data = list
+        elif dim == 2 and dshape[1] == 2 and dshape[0] > 1:
+            pass
+        else:
+            raise ValueError, "data argument dimension or shape is not supported."
+    else:
+        #error checking for multiplot needs work
+        if dim > 1:
+            pass
+        else:
+            raise ValueError, "multiplot dimension too small"
+    ###########################################################################
+    # Setup Plot Options.
+
+    g = llplot()
+           
+    if datastyle is not None:
+        cmd = 'set data style ' + str(datastyle)
+        g.add_cmd(cmd)
+    else:    
+        g.add_cmd('set data style linespoints')
+
+    if xaxis is not None:
+        g.add_cmd( 'set xlabel "%s"' % xaxis )
+
+    if yaxis is not None:
+        g.add_cmd( 'set ylabel "%s"' % yaxis )
+
+    if title is not None:
+        g.add_cmd( 'set title "%s"' % title )
+
+    if xmin is not None and xmax is not None:
+        g.add_cmd( 'set xrange [%s:%s]' % (str(xmin),str(xmax)) )
+
+    if ymin is not None and ymax is not None:
+        g.add_cmd( 'set yrange [%s:%s]' % (str(ymin),str(ymax)) )
+
+    if cmds is not None:
+        for cmd in cmds:
+            g.add_cmd(cmd)
+
+    g.set_data( data,'',dim,1,multiplot,multilabels,multiopts)
+    
+    ###########################################################################
+    # Generate output.
+
+    if terminal == 'gnuplot':
+        g.batch = 0
+        g.plot_current()
+        raw_input('Please press return to continue...\n')
+        return
+
+    elif terminal == 'postscript':
+
+        g.batch = 1
+        
+        #if (os.name == 'nt'):
+        #    output = string.join(string.split(output,'\\'),'/')
+
+        g.add_cmd( 'set terminal postscript color 10' )
+        g.add_cmd( "set output '%s'" % output )
+ 
+        g.plot_current()
+
+        return
+
+    elif terminal == 'pbm':
+
+        g.batch = 1
+        
+        g.add_cmd( 'set terminal pbm color' )
+        g.add_cmd( "set output '%s'" % output )
+
+        g.plot_current()
+
+        return
+
+    elif terminal == 'xpm':
+
+        import gdal
+        import time
+        
+        g.batch = 1
+        
+        out_temp = gvutils.tempnam(extension='png')
+
+        g.add_cmd( 'set terminal png' )
+        g.add_cmd( "set output '%s'" % out_temp )
+
+        g.plot_current()
+
+        pngDS = gdal.Open( out_temp )
+        if pngDS is None:
+            return None
+
+        xpmDriver = gdal.GetDriverByName( 'XPM' )
+        if xpmDriver is None:
+            return None
+
+        xpmDriver.CreateCopy( output, pngDS, 0 )
+
+        pngDS = None
+        os.unlink( out_temp )
+
+        return
+    
+    else:
+        import gdal
+        import gdalnumeric
+        import time
+        import gview
+
+        g.batch = 1
+        
+        temp_file = gvutils.tempnam()
+         
+        # make sure the file has been created
+        create_temp = open(temp_file,'w')
+        create_temp.close()   
+              
+        g.add_cmd( 'set terminal pbm color' )
+        g.add_cmd( "set output '%s'" % temp_file )
+
+        g.plot_current()
+        
+        time.sleep( 1 )
+
+        image = gdalnumeric.LoadFile( temp_file )
+        image_ds = gdalnumeric.OpenArray( image )
+        
+        try:
+            os.unlink( temp_file )
+        except:
+            pass
+        rlayer = gview.GvRasterLayer( gview.GvRaster(dataset=image_ds, real=1),
+                                      rl_mode = gview.RLM_RGBA )
+
+        rlayer.set_source(1, gview.GvRaster(dataset=image_ds, real=2) )
+        rlayer.set_source(2, gview.GvRaster(dataset=image_ds, real=3) )
+
+        if terminal == 'rasterlayer':
+            return rlayer
+
+        graphwin = GvGraphWindow( rlayer )
+        if wintitle is not None:
+            graphwin.set_title(wintitle)
+
+###############################################################################
+# plot3d()
+
+def plot3d( data=None, xvec=None, yvec=None, xaxis=None, yaxis=None, zaxis=None,
+            xmin=None, xmax=None, ymin=None, ymax=None, zmin=None, zmax = None,
+            title=None, cmds=None, terminal = 'openev', output = None,
+            plottype="parametric", wintitle=None):
+
+    """plot3d(data [,xvec=xaxis_values] [,yvec=yaxis_values] 
+                   [, xaxis=text] [,yaxis=text] [,zaxis=text] [,title=text] 
+                   [, xmin=n, xmax=n] [, ymin=n, ymax=n] [, zmin=n, zmax=n] 
+                   [, cmds=cmd_list] [,terminal={"openev","gnuplot","rasterlayer"}]
+                   [, output=ps_filename]
+                   [,plottype = {"parametric","contour"}]
+                   [, wintitle=text])
+                   
+        data -- data array to plot, should be 2-D set of Z values.
+                   Size of data should be length(x) x length(y), if x
+                   and y are present.'
+        xvec -- 1-D Vector of values for axis of first dimension of data.
+        yvec -- 1-D Vector of values for axis of second dimension of data.
+    """
+
+    ###########################################################################
+    # Print usage() message if no options given.
+
+    if data is None:
+        print 'Usage: plot3d(data [,xvec=xaxis_values] [,yvec=yaxis_values] '
+        print '    [, xaxis=text] [,yaxis=text] [,zaxis=text] [,title=text] '
+        print '    [, xmin=n, xmax=n] [, ymin=n, ymax=n] [, zmin=n, zmax=n] '
+        print '    [, cmds=cmd_list] [,terminal={"openev","gnuplot,"rasterlayer"}]'
+        print '    [, output=ps_filename] [,plottype = {"parametric","contour"}]'
+        print ''
+        print ' data -- data array to plot, should be 2-D set of Z values.'
+        print '         Size of data should be length(x) x length(y), if x'
+        print '         and y are present.'
+        print ' xvec -- 1-D Vector of values for axis of first dimension of data.'
+        print ' yvec -- 1-D Vector of values for axis of second dimension of data.'
+        print ''
+        return
+    
+    ###########################################################################
+    # Work out the shape of the data.  A 1-D array is assumed to be Y
+    # values.  An Nx2 array is assumed to be (X,Y) values.  All others are
+    # currently invalid.
+
+    try:
+        dshape = Numeric.shape( data )
+    except:
+        raise ValueError, "data argument to plot() does not appear to be a NumPy array"
+
+    ##########################################################################
+    # Make sure xvec, yvec are valid indices for x/y axis and revert to
+    # default if not.
+
+    if xvec is None:
+        xvec = Numeric.arange(dshape[0])
+    else:
+        try:
+            xshape = Numeric.shape( xvec )
+            if (len(xvec) != dshape[0]):
+                print 'Incorrect length for xvec- reverting to default.'
+                xvec = Numeric.arange(dshape[0])
+            elif (len(xshape) > 1):
+                print 'xvec should be 1-D- reverting to default.'
+                xvec = Numeric.arange(dshape[0])
+        except:
+            print 'xvec appears not to be a NumPy array- reverting to default.'
+            xvec = Numeric.arange(dshape[0])
+
+    if yvec is None:
+        yvec = Numeric.arange(dshape[1])
+    else:
+        try:
+            yshape = Numeric.shape( yvec )
+            if (len(yvec) != dshape[1]):
+                print 'Incorrect length for yvec- reverting to default.'
+                yvec = Numeric.arange(dshape[1])
+            elif (len(yshape) > 1):
+                print 'yvec should be 1-D- reverting to default.'
+                yvec = Numeric.arange(dshape[1])
+        except:
+            print 'yvec appears not to be a NumPy array- reverting to default.'
+            yvec = Numeric.arange(dshape[1])
+
+
+    ###########################################################################
+    # Setup Plot Options.
+    
+    g = llplot()
+
+    g.batch = 1
+    
+    if plottype == "contour":
+        g.add_cmd('set nosurface')
+        g.add_cmd('set contour')
+        g.add_cmd('set view 0,0')
+        g.add_cmd('set data style lines')
+        g.add_cmd('set cntrparam levels 15')
+    else:
+#        g.add_cmd('set parametric')
+        g.add_cmd('set data style lines')
+        g.add_cmd('set hidden3d')
+
+    if xaxis is not None:
+        g.add_cmd( 'set xlabel "%s"' % xaxis )
+
+    if yaxis is not None:
+        g.add_cmd( 'set ylabel "%s"' % yaxis )
+
+    if title is not None:
+        g.add_cmd( 'set title "%s"' % title )
+
+    if xmin is not None and xmax is not None:
+        g.add_cmd( 'set xrange [%s:%s]' % (str(xmin),str(xmax)) )
+
+    if ymin is not None and ymax is not None:
+        g.add_cmd( 'set yrange [%s:%s]' % (str(ymin),str(ymax)) )
+
+    if plottype != "contour":
+        if zaxis is not None:
+            g.add_cmd( 'set zlabel "%s"' % zaxis )
+
+    if plottype != "contour":
+        if zmin is not None and zmax is not None:
+            g.add_cmd( 'set zrange [%s:%s]' % (str(zmin),str(zmax)) )
+
+    if cmds is not None:
+        for cmd in cmds:
+            g.add_cmd(cmd)
+
+    ###########################################################################
+    # Attach the data.
+    #
+    # Note that we emit the x and y values with each data point.  It would
+    # be nice to rewrite this to use binary format eventually.
+
+    tup_data = []
+    for x_i in range(len(xvec)):
+        for y_i in range(len(yvec)):
+            tup_data.append( (xvec[x_i], yvec[y_i], data[x_i][y_i]) )
+
+    g.set_data( tup_data, dimension = 3, xlen = len(xvec) )
+
+    ###########################################################################
+    # Generate output.
+
+    if terminal == 'gnuplot':
+        g.batch = 0
+        
+        g.plot_current()
+        raw_input('Please press return to continue...\n')
+
+    elif terminal == 'postscript':
+        if (os.name == 'nt'):
+            output = string.join(string.split(output,'\\'),'/')
+   
+        g.add_cmd( 'set terminal postscript color 10' )
+        g.add_cmd( "set output '%s'" % output )
+
+        g.plot_current()
+        
+    else:
+        import gdal
+        import gdalnumeric
+        import time
+        import gview
+
+        temp_file = gvutils.tempnam()
+        
+        g.add_cmd( 'set terminal pbm color' )
+        g.add_cmd( "set output '%s'" % temp_file )
+
+        g.plot_current()
+
+        image = gdalnumeric.LoadFile( temp_file )
+        image_ds = gdalnumeric.OpenArray( image )
+        
+        try:
+            os.unlink( temp_file )
+        except:
+            pass
+
+        rlayer = gview.GvRasterLayer( gview.GvRaster(dataset=image_ds, real=1),
+                                      rl_mode = gview.RLM_RGBA )
+
+        rlayer.set_source(1, gview.GvRaster(dataset=image_ds, real=2) )
+        rlayer.set_source(2, gview.GvRaster(dataset=image_ds, real=3) )
+
+        if terminal == 'rasterlayer':
+            return rlayer
+	    
+        graphwin = GvGraphWindow( rlayer )
+        if wintitle is not None:
+            graphwin.set_title(wintitle)
+
+
+
+
+###############################################################################
+# llplot - low level plot object used to manage plot state, and pipe handling.
+#
+# Only utilized from within gvplot.py.
+
+class llplot:
+    
+    def __init__(self):
+        self.cmds = []
+        self.batch = 1
+        self.data = None
+        self.data_title = ''
+        self.pipe = None
+        self.tmpnam = None
+        self.base_command = self.find_gnuplot()
+        if self.base_command is None:
+            raise RuntimeError, "Unable to find gnuplot executable."
+        self.dimension = 2
+
+    def find_gnuplot( self ):
+        import gview
+        exe = gview.get_preference('gnuplot')
+        if exe is not None:
+            if os.path.isfile(exe):
+                return exe
+            else:
+                gvutils.warning( 'Disregarding gnuplot preference "%s", executable not found.' % exe )
+        
+        exe = gvutils.FindExecutable( 'gnuplot' )
+        if exe is None:
+            exe = gvutils.FindExecutable( 'pgnuplot.exe' )
+        if exe is None:
+            exe = gvutils.FindExecutable( 'wgnupl32.exe' )
+        if exe is None:
+            exe = gvutils.FindExecutable( 'wgnuplot.exe' )
+
+        return exe
+
+    def add_cmd( self, command ):
+        self.cmds.append( command )
+
+    def set_data( self, data, data_title = '', dimension = 2, xlen = 1,
+                  multiplot = False, multilabels = (),multiopts =()):
+        self.data = data
+        self.data_title = data_title
+        self.dimension = dimension
+        self.xlen = xlen
+        self.multiplot = multiplot
+        self.multilabels = multilabels
+        self.multiopts = multiopts
+
+        if (multiplot == True) and (len(multilabels) == 0):
+            self.multilabels = range(self.data.shape[1]-1)
+            
+        if (multiplot == True) and (len(multiopts) == 0):
+            self.multiopts=[]
+            for i in range(self.data.shape[1]-1):
+                self.multiopts.append("")
+
+        
+    def plot_current( self ):
+        """Data is a list of (x,y) pairs"""
+
+        if self.batch:
+            self.open_tmpfile()
+        else:
+            self.open_pipe()
+        
+        for cmd in self.cmds:
+            self.write(cmd + '\n')
+
+        if self.multiplot == False:
+            if self.dimension == 2:
+                self.write( 'plot "-" title "%s"\n' % self.data_title )
+            else:
+                self.write( 'splot "-" title "%s"\n' % self.data_title )
+                
+        else:
+            cmd = '"-" title "' + str(self.multilabels[0]) + '" ' + self.multiopts[0]
+            for i in range(self.data.shape[1]-2):
+                cmd = cmd + ',"-" title "' + str(self.multilabels[i+1]) + '" ' + self.multiopts[i+1]
+            #print "cmd", cmd    
+            self.write( 'plot ' + cmd  +'\n')
+ 
+        self.write_data()
+
+        self.complete_command()
+            
+        self.cleanup()
+
+    def write_data( self ):
+        if self.multiplot == False:
+            if self.dimension == 2:
+                for pnt in self.data:
+                    self.write( '%s %s\n' % (pnt[0], pnt[1]) )
+                self.write('e\n')
+            else:
+                i = 0
+                for pnt in self.data:
+                    self.write( '%s %s %s\n' % (pnt[0], pnt[1], pnt[2]) )
+                    i = i + 1
+                    if i % self.xlen == 0:
+                        self.write( '\n' )
+                    
+                self.write('e\n')
+        else:
+            for i in range(self.data.shape[1]-1):
+                for pnt in self.data:
+                    self.write( '%s %s\n' % (pnt[0], pnt[1+i]) )
+                self.write('e\n')    
+                
+            
+    def open_pipe( self ):
+        import os
+        
+        self.pipe = os.popen(self.base_command, 'w')
+        
+        # forward write and flush methods:
+        self.write = self.pipe.write
+        self.flush = self.pipe.flush
+
+    def open_tmpfile( self ):
+        import os
+
+        self.tmpnam = gvutils.tempnam()
+        self.pipe = open(self.tmpnam, 'w')
+        
+        # forward write and flush methods:
+        self.write = self.pipe.write
+        self.flush = self.pipe.flush
+
+    def complete_command( self ):
+        if self.pipe is not None:
+            self.pipe.flush()
+            
+        if self.tmpnam is not None:
+            self.pipe.close()
+            self.pipe = None
+
+            # command = self.base_command + ' < ' + self.tmpnam
+            command = self.base_command + ' ' + self.tmpnam
+
+            os.system( command )
+
+    def cleanup( self ):
+        # Closing the pipe makes the graphs
+        # disappear immediately, so leave out
+        # pipe cleanup in gnuplot case.
+        if ((self.batch == 1) and (self.pipe is not None)):
+            self.pipe.close()
+            self.pipe = None
+
+        if self.tmpnam is not None:
+            os.unlink( self.tmpnam )
+
+###############################################################################
+# GvGraphWindow -- a very simple window for display graph images in.
+
+class GvGraphWindow(gtk.GtkWindow):
+
+    def __init__(self,rlayer):
+
+        gtk.GtkWindow.__init__(self)
+
+        import gview
+
+        self.rlayer = rlayer
+        raster = rlayer.get_data()
+        self.xsize = raster.get_dataset().RasterXSize
+        self.ysize = raster.get_dataset().RasterYSize
+
+        self.set_policy(gtk.TRUE,gtk.TRUE,gtk.FALSE)
+        # self.set_usize(self.xsize, self.ysize)
+        self.viewarea = gview.GvViewArea()
+        self.viewarea.add_layer( self.rlayer )
+        self.viewarea.size(self.xsize, self.ysize)
+        shell = gtk.GtkVBox(spacing=0)
+        self.add(shell)
+
+        # Print menu
+        menuf = GtkExtra.MenuFactory()
+        self.menuf = menuf
+        menuf.add_entries([
+                 ('File/Print', None, self.print_cb)])
+
+        shell.pack_start(menuf, expand=gtk.FALSE)
+
+        shell.pack_start( self.viewarea )
+
+        self.connect( 'delete-event', self.close )
+        self.show_all()
+
+        self.viewarea.fit_extents(0, self.ysize, self.xsize, -self.ysize )
+
+    def print_cb(self, *args):
+        import gvprint
+        pd = gvprint.GvPrintDialog( self.viewarea )
+ 
+    def close( self, *args ):
+        self.rlayer = None
+        self.destroy()
+    
+
+###############################################################################
+# main()
+
+if __name__ == '__main__':
+    data1 = [[1,2], [1.1,3], [1.2,4]]
+    plot( data1, xmin = -1, xmax = 5, terminal='gnuplot' )
+
+    data2 = [1,2,5,4,3,3]
+    plot( data2, xaxis = 'X', yaxis = 'power', title='Power Cycle',
+          ymin = 0, ymax = 6, terminal='gnuplot' )
+
+    data3 = [[1,2,3,4,5],[2,2.5,3,3.5,4],[3,3,3,3,3],[4,3.5,3,2.5,2],[5,4,3,2,1]]
+    plot3d( data3, xaxis = 'X', yaxis = 'Y', zaxis = 'F(X,Y)', title='A Plot',
+            terminal = 'gnuplot' )
+
+    xvals=[4,5,6,7,8]
+    yvals=[12,13,14,15,16]
+    plot3d( data3, xaxis = 'X', yaxis = 'Y', zaxis = 'F(X,Y)', title='A Plot',
+            terminal = 'gnuplot' , zmin=-1, zmax=5, xvec=xvals, yvec=yvals,
+            cmds = ('set view 70,40','set grid','set contour base') )
+
+    xvals=[4,5,6,7]
+    yvals=[12,11,14,13,16]
+    plot3d( data3, xaxis = 'X', yaxis = 'Y', zaxis = 'F(X,Y)', title='A Plot',
+            terminal = 'gnuplot' , zmin=-1, zmax=5, xvec=xvals, yvec=yvals,
+            cmds = ('set view 70,40','set grid','set contour base') )
+    
+
+
+


Property changes on: packages/openev/branches/upstream/current/pymod/gvplot.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/gvpquerypropdlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvpquerypropdlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvpquerypropdlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,216 @@
+###############################################################################
+# $Id: gvpquerypropdlg.py,v 1.11 2000/08/23 14:32:10 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  GvPqueryLayer Properties Dialog
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvpquerypropdlg.py,v $
+#  Revision 1.11  2000/08/23 14:32:10  warmerda
+#  fixed logic of pixel_mode callback
+#
+#  Revision 1.10  2000/08/22 16:13:33  warmerda
+#  fixed layer: prefixing of layer name
+#
+#  Revision 1.9  2000/08/10 15:59:29  warmerda
+#  added help topic
+#
+#  Revision 1.8  2000/07/21 01:34:56  warmerda
+#  added read_only flag for GvData, and utilize for vector layers
+#
+#  Revision 1.7  2000/07/20 02:45:04  warmerda
+#  avoid doing gui updates in response to signals if dialog destroyed
+#
+#  Revision 1.6  2000/06/14 15:13:21  warmerda
+#  updated pgu stuff
+#
+#  Revision 1.5  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+from gtk import *
+from string import *
+
+from gvconst import *
+import gview
+import gvvectorpropdlg
+import gvutils
+import pgucolorsel
+import gvhtml
+
+pq_prop_dialog_list = []
+
+def LaunchPQueryPropDialog(layer):
+    # Check list to see if dialog exists - make it visible
+    for test_dialog in pq_prop_dialog_list:
+        if test_dialog.layer._o == layer._o:
+            test_dialog.update_gui()
+            test_dialog.show()
+            test_dialog.get_window()._raise()
+            return test_dialog
+
+    # Create new dialog if one doesn't exist already
+    new_dialog = GvPQueryPropDialog(layer)
+    pq_prop_dialog_list.append( new_dialog )
+    return new_dialog
+
+class GvPQueryPropDialog(gvvectorpropdlg.GvVectorPropDialog):
+
+    def __init__(self, layer):
+        GtkWindow.__init__(self)
+        self.set_title('GView')
+        self.layer = layer
+        self.updating = FALSE
+
+        gvhtml.set_help_topic( self, "gvpquerypropdlg.html" )
+        
+        # create the general layer properties dialog
+        self.create_notebook()
+        self.create_pane1()
+        
+        if self.layer is not None:
+            self.layer.connect('display-change', self.refresh_cb)
+        
+        # Setup Object Drawing Properties Tab
+        self.pane2 = GtkVBox(spacing=10)
+        self.pane2.set_border_width(10)
+        self.notebook.append_page( self.pane2, GtkLabel('Draw Styles'))
+
+        vbox = GtkVBox(spacing=10)
+        self.pane2.add(vbox)
+
+        # Create Color control.
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Color:'),expand=FALSE)
+        self.point_color = \
+                 pgucolorsel.ColorControl('Point Color',
+                                          self.color_cb,'_point_color')
+        box.pack_start(self.point_color)
+
+        # Point size
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Point Size:'),expand=FALSE)
+        self.point_size = GtkCombo()
+        self.point_size.set_popdown_strings(
+            ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '15', '20') )
+        self.point_size.entry.connect('changed', self.point_size_cb)
+        box.pack_start(self.point_size,expand=FALSE)
+
+        # Coordinate
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Coordinate:'),expand=FALSE)
+
+        self.coord_om = gvutils.GvOptionMenu(
+            ('Off','Raster Pixel/Line','Georeferenced','Geodetic (lat/long)'),
+            self.set_coordinate_mode)
+        box.pack_start(self.coord_om,expand=FALSE)
+
+        # Raster Value
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Pixel Value:'),expand=FALSE)
+
+        self.pixel_mode_om = \
+            gvutils.GvOptionMenu(('On','Off'), self.set_pixel_mode)
+        box.pack_start(self.pixel_mode_om,expand=FALSE)
+
+        self.update_gui()
+        
+        self.show_all()
+
+    # Initialize GUI state from underlying object state.
+    def update_gui(self):
+        if self.flags( DESTROYED ) > 0:
+            return
+        
+        if self.layer is None or self.updating == TRUE:
+            return
+
+        self.updating = TRUE
+        
+        # Layer name.
+        self.layer_name.set_text( self.layer.get_name() )
+        
+        # Visibility radio buttons
+        self.vis_yes.set_active( self.layer.is_visible() )
+        self.vis_no.set_active( not self.layer.is_visible() )
+
+        # Editability radio buttons
+        self.edit_yes.set_active( not self.layer.is_read_only() )
+        self.edit_no.set_active( self.layer.is_read_only() )
+
+        self.set_color_or_default('_point_color', self.point_color)
+        # point size
+        self.point_size.entry.delete_text(0,-1)
+        if self.layer.get_property('_point_size') is None:
+            self.point_size.entry.insert_text('6')
+        else:
+            self.point_size.entry.insert_text(
+                self.layer.get_property('_point_size'))
+
+        # coordinate mode
+        mode = self.layer.get_property( '_coordinate_mode' )
+        if mode is None:
+            self.coord_om.set_history(2)
+        elif mode == 'off':
+            self.coord_om.set_history(0)
+        elif mode == 'raster':
+            self.coord_om.set_history(1)
+        elif mode == 'latlong':
+            self.coord_om.set_history(3)
+        else:
+            self.coord_om.set_history(2)
+
+        # pixel mode
+        mode = self.layer.get_property( '_pixel_mode' )
+        if mode is None or mode != 'off':
+            self.pixel_mode_om.set_history(0)
+        else:
+            self.pixel_mode_om.set_history(1)
+
+        self.updating = FALSE
+
+    # Dialog closed, remove references to python object
+    def close( self, widget, args ):
+        pq_prop_dialog_list.remove(self)
+
+    def set_coordinate_mode(self, om):
+        if self.coord_om.get_history() == 0:
+            self.layer.set_property( '_coordinate_mode', 'off')
+        elif  self.coord_om.get_history() == 1:
+            self.layer.set_property( '_coordinate_mode', 'raster')
+        elif  self.coord_om.get_history() == 2:
+            self.layer.set_property( '_coordinate_mode', 'georef')
+        elif  self.coord_om.get_history() == 3:
+            self.layer.set_property( '_coordinate_mode', 'latlong')
+        self.layer.display_change()
+
+    def set_pixel_mode(self, om):
+        if om.get_history() == 0:
+            self.layer.set_property( '_pixel_mode', 'on')
+        else:
+            self.layer.set_property( '_pixel_mode', 'off')
+        self.layer.display_change()
+

Added: packages/openev/branches/upstream/current/pymod/gvprint.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvprint.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvprint.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,442 @@
+###############################################################################
+# $Id: gvprint.py,v 1.16 2004/07/24 14:35:04 andrey_kiselev Exp $
+#
+# Project:  OpenEV
+# Purpose:  Print Dialog
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvprint.py,v $
+#  Revision 1.16  2004/07/24 14:35:04  andrey_kiselev
+#  Added possibility to explicitly specify printed image size;
+#  several GUI tweaks.
+#
+#  Revision 1.15  2004/01/15 19:21:20  gmwalter
+#  Avoid deprecation warnings in python 2.3 related
+#  to float being input instead of integer.
+#
+#  Revision 1.14  2002/07/12 12:46:06  warmerda
+#  expanded tabs
+#
+#  Revision 1.13  2002/03/21 21:00:26  warmerda
+#  add better error testing when printing
+#
+#  Revision 1.12  2001/03/28 15:04:05  warmerda
+#  avoid rounding oddness for stuff that should be 1:1
+#
+#  Revision 1.11  2001/03/19 21:57:14  warmerda
+#  expand tabs
+#
+#  Revision 1.10  2001/01/23 16:18:12  warmerda
+#  Removed debug statement.
+#
+#  Revision 1.9  2001/01/23 15:47:41  warmerda
+#  added GIF output support
+#
+#  Revision 1.8  2000/08/10 15:59:29  warmerda
+#  added help topic
+#
+#  Revision 1.7  2000/08/08 18:15:22  warmerda
+#  fixed handling of output label
+#
+#  Revision 1.6  2000/08/07 17:18:13  warmerda
+#  added windows printing support
+#
+#  Revision 1.5  2000/08/03 18:20:41  warmerda
+#  implemented print scaling and paper sizes properly
+#
+#  Revision 1.4  2000/07/26 21:04:36  warmerda
+#  fixed _temp.tif name
+#
+#  Revision 1.3  2000/07/20 03:22:16  warmerda
+#  added greyscale support for TIFF, and got PNG working
+#
+#  Revision 1.2  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+from gtk import *
+
+from gvconst import *
+import gview
+import gvutils
+import gdal
+import os
+import gvhtml
+
+paper_sizes = ( ("US Letter",       8.500, 11.000 ),
+                ("US Legal",        8.500, 14.000 ),
+                ("A4",              8.268, 11.693 ),
+                ("B5",              7.205, 10.118 ),
+                ("A3",             11.693, 16.535 ) )
+
+DR_POSTSCRIPT = 0
+DR_TIFF = 1
+DR_PNG = 2
+DR_WINPRINT = 3
+DR_GIF = 4
+
+DV_FILE = 0
+DV_PRINTER = 1
+
+class GvPrintDialog(GtkWindow):
+
+    def __init__(self, view):
+        GtkWindow.__init__(self)
+        self.set_title('Print')
+        self.connect('delete-event',self.close)
+        self.view = view
+
+        gvhtml.set_help_topic( self, "gvprint.html" );
+
+        self.command = gview.get_preference('print_command')
+        if self.command is None:
+            self.command = 'lpr'
+            
+        self.filename = 'openev.ps'
+
+        cgroup = GtkVBox(spacing=6)
+        cgroup.set_border_width(10)
+        self.add( cgroup )
+
+        table = GtkTable()
+        table.n_columns = 2
+        table.n_rows = 4
+        cgroup.add(table)
+
+        # Setup Driver Option Menu
+	driver_label = GtkLabel('Driver:')
+	driver_label.set_alignment(0, 0.5)
+        table.attach(driver_label,0,1,0,1)
+        if os.name == "nt":
+            self.driver = gvutils.GvOptionMenu( ('PostScript', 'TIFF', 'PNG',
+                                                 'Windows Print Driver',
+                                                 'GIF' ),
+                                                self.update_cb )
+        else:
+            self.driver = gvutils.GvOptionMenu( ('PostScript', 'TIFF', 'PNG',
+                                                 '', 'GIF' ),
+                                                self.update_cb )
+        table.attach(self.driver,1,2,0,1)
+
+        # Setup Device Option Menu
+	device_label = GtkLabel('Device:')
+	device_label.set_alignment(0, 0.5)
+        table.attach(device_label,0,1,1,2)
+        self.device = gvutils.GvOptionMenu( ('File', 'Spool to Printer'),
+                                            self.device_cb )
+        table.attach(self.device,1,2,1,2)
+
+        # Setup File/Command entry.
+        self.file_label = GtkLabel('File:')
+	self.file_label.set_alignment(0, 0.5)
+        table.attach(self.file_label,0,1,2,3)
+        self.file = GtkEntry(maxlen=40)
+        table.attach(self.file,1,2,2,3)
+
+        # Setup Output Type
+        self.output_label = GtkLabel('Output Type:')
+	self.output_label.set_alignment(0, 0.5)
+        table.attach(self.output_label,0,1,3,4)
+        self.output = gvutils.GvOptionMenu( ('Greyscale', 'Color' ), None )
+        table.attach(self.output,1,2,3,4)
+
+        # Setup Paper Type
+        self.paper_label = GtkLabel('Paper:')
+	self.paper_label.set_alignment(0, 0.5)
+        table.attach(self.paper_label,0,1,4,5)
+        sizes = []
+        for entry in paper_sizes:
+            sizes.append( entry[0] )
+        self.paper = gvutils.GvOptionMenu( sizes, self.update_cb )
+        table.attach(self.paper,1,2,4,5)
+
+        # Setup Scale slider
+        self.scale_label = GtkLabel('Scale:')
+	self.scale_label.set_alignment(0, 0.5)
+        table.attach(self.scale_label,0,1,5,6)
+        self.scale_adjustment = GtkAdjustment(1, 0, 1.25, 0.05, 0.05, 0.05)
+        self.scale_slider = GtkHScale(self.scale_adjustment)
+        table.attach(self.scale_slider,1,2,5,6)
+
+        # Setup Resolution spinner
+	resolution_label = GtkLabel('Resolution:')
+	resolution_label.set_alignment(0, 0.5)
+        table.attach(resolution_label,0,1,6,7)
+        self.resolution_adjustment = GtkAdjustment(1, 0, 10, 0.1, 0.1, 0.1)
+	self.resolution_spinner = \
+	    GtkSpinButton(self.resolution_adjustment,climb_rate=0.1,digits=1)
+	self.resolution_spinner.connect("changed", self.resolution_cb)
+	table.attach(self.resolution_spinner,1,2,6,7)
+
+        # Setup Size entries
+	size_label = GtkLabel('Image size:')
+	size_label.set_alignment(0, 0.5)
+        table.attach(size_label,0,1,7,8)
+	size_box = GtkHBox(spacing=5)
+	self.xsize_entry = GtkEntry()
+	self.xsize_entry.connect('activate', self.resolution_cb)
+	self.xsize_entry.connect('leave-notify-event', self.resolution_cb)
+	size_box.pack_start(self.xsize_entry)
+	size_box.pack_start(GtkLabel('x'))
+	self.ysize_entry = GtkEntry()
+	self.ysize_entry.connect('activate', self.resolution_cb)
+	self.ysize_entry.connect('leave-notify-event', self.resolution_cb)
+	size_box.pack_start(self.ysize_entry)
+        table.attach(size_box,1,2,7,8)
+
+        # Add Print, and Close button(s)
+	btn_box = GtkHBox(spacing=10)
+
+        but = GtkButton('Print')
+        but.connect('clicked',self.print_cb)
+	btn_box.pack_start(but)
+
+        but = GtkButton('Close')
+        but.connect('clicked',self.close)
+	btn_box.pack_start(but)
+
+        table.attach(btn_box,0,2,8,9)
+
+        # Initialize values.
+        if gview.get_preference('print_driver') is not None:
+            self.driver.set_history(int(gview.get_preference('print_driver')))
+        elif os.name == 'nt':
+            self.driver.set_history(DR_WINPRINT)
+
+        if gview.get_preference('print_device') is not None:
+            self.device.set_history(int(gview.get_preference('print_device')))
+
+        if self.device.get_history() == 0:
+            self.set_default_filename()
+        else:
+            self.file.set_text( self.command )
+
+        if gview.get_preference('print_paper') is not None:
+            self.paper.set_history(int(gview.get_preference('print_paper')))
+
+        if gview.get_preference('print_output') is not None:
+            self.output.set_history(int(gview.get_preference('print_output')))
+
+        if gview.get_preference('print_resolution') is not None:
+	    resolution = float(gview.get_preference('print_resolution'))
+            self.resolution_adjustment.set_value(resolution)
+	    width = int(self.view.get_width() * resolution + 0.5)
+	    height = int(self.view.get_height() * resolution + 0.5)
+	    self.xsize_entry.set_text(str(width))
+	    self.ysize_entry.set_text(str(height))
+
+        self.set_paper_size()
+        self.scale_adjustment.set_value(1.0)
+
+        # Show
+        table.set_row_spacings(6)
+        table.show_all()
+        self.update_cb()
+        cgroup.show()
+        self.show()
+
+    def resolution_cb(self,entry,*args):
+        try:
+            value = float(entry.get_text())
+	except:
+	    return
+
+	if entry == self.resolution_spinner:
+	    resolution = self.resolution_adjustment.value
+	    width = int(self.view.get_width() * resolution + 0.5)
+	    self.xsize_entry.set_text(str(width))
+	    height = int(self.view.get_height() * resolution + 0.5)
+	    self.ysize_entry.set_text(str(height))
+	elif entry == self.xsize_entry:
+	    resolution = value / self.view.get_width()
+	    height = int(self.view.get_height() * resolution + 0.5)
+	    self.ysize_entry.set_text(str(height))
+	elif entry == self.ysize_entry:
+	    resolution = value / self.view.get_height()
+	    width = int(self.view.get_width() * resolution + 0.5)
+	    self.xsize_entry.set_text(str(width))
+	self.resolution_adjustment.set_value(resolution)
+	
+
+    def device_cb(self, *args):
+        if self.device.get_history() == 0:
+            self.command = self.file.get_text()
+            self.set_default_filename()
+        else:
+            self.file.set_text(self.command)
+        self.update_cb( args )
+
+    def set_default_filename(self):
+        if self.driver.get_history() == DR_TIFF:
+            self.file.set_text('openev.tif')
+        elif self.driver.get_history() == DR_PNG:
+            self.file.set_text('openev.png')
+        elif self.driver.get_history() == DR_GIF:
+            self.file.set_text('openev.gif')
+        else:
+            self.file.set_text('openev.ps')
+
+    def set_paper_size(self):
+        # Setup paper size.
+        self.paper_x = 8.5
+        self.paper_y = 11
+        try:
+            entry = paper_sizes[self.paper.get_history()]
+            self.paper_x = entry[1]
+            self.paper_y = entry[2]
+        except:
+            pass
+
+    def update_cb(self, *args):
+        
+        driver = self.driver.get_history()
+
+        # Set FILE/PRINTER Device based on driver.
+        if driver == DR_TIFF or driver == DR_PNG or driver == DR_GIF:
+            self.device.set_history(DV_FILE)
+        if driver == DR_WINPRINT:
+            self.device.set_history(DV_PRINTER)
+        if driver == DR_POSTSCRIPT and os.name == 'nt':
+            self.device.set_history(DV_FILE)
+
+        self.set_paper_size()
+
+        # Hide the file/command tool for WINDRIVER
+        if driver == DR_WINPRINT:
+            self.file_label.hide()
+            self.file.hide()
+            self.output_label.hide()
+            self.output.hide()
+        else:
+            self.file_label.show()
+            self.file.show()
+            self.output_label.show()
+            self.output.show()
+
+        if self.device.get_history() == DV_PRINTER:
+            self.file_label.set_text('Command:')
+        else:
+            self.file_label.set_text('File:')
+
+        # Make Positioning controls visible only for PostScript
+        if driver == DR_POSTSCRIPT:
+            self.scale_label.show()
+            self.scale_slider.show()
+            self.paper_label.show()
+            self.paper.show()
+        else:
+            self.scale_label.hide()
+            self.scale_slider.hide()
+            self.paper_label.hide()
+            self.paper.hide()
+                    
+    def print_cb(self, *args):
+        if self.resolution_adjustment.value >= 0.99 \
+           and self.resolution_adjustment.value <= 1.01:
+            width = self.view.get_width()
+            height = self.view.get_height()
+        else:
+            width = self.view.get_width() * self.resolution_adjustment.value
+            height = self.view.get_height() * self.resolution_adjustment.value
+            width=int(width+0.5)
+            height=int(height+0.5)
+
+        if width / self.paper_x > height / self.paper_y:
+            pixels_per_inch = width / (self.paper_x*0.9)
+        else:
+            pixels_per_inch = height / (self.paper_y*0.9)
+
+        pixels_per_inch = pixels_per_inch * self.scale_adjustment.value
+        ulx = (self.paper_x - width/pixels_per_inch)/2.0
+        uly = (self.paper_y - height/pixels_per_inch)/2.0
+        lrx = self.paper_x - ulx
+        lry = self.paper_y - uly
+        
+        try:
+            os.unlink( self.file.get_text() )
+        except:
+            pass
+
+        err = 0            
+        if self.driver.get_history() == DR_POSTSCRIPT:
+            filename = self.file.get_text()
+            if self.device.get_history() == 1:
+                filename = '|' + filename
+                
+            err = self.view.print_postscript_to_file(width,height,
+                                               ulx,uly,lrx,lry,
+                                               self.output.get_history(),
+                                               filename )
+        elif self.driver.get_history() == DR_TIFF:
+            err = self.view.print_to_file(width,height,self.file.get_text(),
+                                    'GTiff',self.output.get_history())
+        elif self.driver.get_history() == DR_PNG:
+            err = self.view.print_to_file(width,height,'_temp.tif','GTiff',
+                                          self.output.get_history())
+            if err == 0:
+                gdal.GetDriverByName('PNG').CreateCopy(self.file.get_text(),
+                                                   gdal.Open('_temp.tif'),TRUE)
+            os.unlink( '_temp.tif' )
+        elif self.driver.get_history() == DR_WINPRINT:
+            self.view.print_to_windriver( width, height, ulx, uly, lrx, lry,
+                                          self.output.get_history() )
+        elif self.driver.get_history() == DR_GIF:
+            err = self.view.print_to_file(width,height,'_temp.tif','GTiff',
+                                    self.output.get_history())
+            if err == 0:
+                if self.output.get_history() == 1:
+                    gdal.RGBFile2PCTFile( '_temp.tif', '_temp2.tif' )
+                    os.unlink('_temp.tif')
+                    os.rename('_temp2.tif','_temp.tif')
+                
+                gdal.GetDriverByName('GIF').CreateCopy(self.file.get_text(),
+                                                  gdal.Open('_temp.tif'),TRUE)
+            os.unlink( '_temp.tif' )
+
+        if err != 0:
+            gvutils.error('The request to print appears to have failed.')
+
+        self.close()
+            
+    def close(self, *args):
+        if self.device.get_history() == 1:
+            gview.set_preference('print_command',self.file.get_text())
+        gview.set_preference('print_driver', str(self.driver.get_history()))
+        gview.set_preference('print_device', str(self.device.get_history()))
+        gview.set_preference('print_paper', str(self.paper.get_history()))
+        gview.set_preference('print_output', str(self.output.get_history()))
+        gview.set_preference('print_resolution',
+                             str(self.resolution_adjustment.value))
+        gview.set_preference('print_scale',
+                             str(self.scale_adjustment.value))
+            
+        self.destroy()
+        
+        return TRUE
+        
+
+if __name__ == '__main__':
+    dialog = GvPrintDialog(None)
+
+    dialog.connect('delete-event', mainquit)
+
+    mainloop()

Added: packages/openev/branches/upstream/current/pymod/gvrasterpropdlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvrasterpropdlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvrasterpropdlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1240 @@
+###############################################################################
+# $Id: gvrasterpropdlg.py,v 1.57 2005/06/27 19:37:24 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  GvRasterLayer Properties Dialog
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvrasterpropdlg.py,v $
+#  Revision 1.57  2005/06/27 19:37:24  gmwalter
+#  Bring in Vincent's slider fix.
+#
+#  Revision 1.56  2004/12/03 00:24:10  gmwalter
+#  Fix scaling slider page increments so that
+#  they work for small floating-point valued
+#  images.
+#
+#  Revision 1.55  2004/10/30 18:25:44  warmerda
+#  Don't barf out of projection is None.
+#
+#  Revision 1.54  2004/08/31 21:26:46  warmerda
+#  trap failed efforts to fetch datum
+#
+#  Revision 1.53  2004/06/23 14:35:17  gmwalter
+#  Added support for multi-band complex imagery.
+#
+#  Revision 1.52  2004/06/03 20:13:07  andrey_kiselev
+#  Use reasonable values when projections and datums are not set.
+#
+#  Revision 1.51  2004/05/12 19:26:06  andrey_kiselev
+#  Preliminary support for changing projection on the fly.
+#
+#  Revision 1.50  2004/04/02 17:28:45  gmwalter
+#  Fix initialization bug from last changes.
+#
+#  Revision 1.49  2004/04/02 17:01:02  gmwalter
+#  Updated nodata support for complex and
+#  rgb data.
+#
+#  Revision 1.48  2004/02/20 12:34:19  andrey_kiselev
+#  Generate different band names to avoid problems with equally named keys.
+#
+#  Revision 1.47  2004/01/22 21:28:42  andrey_kiselev
+#  New control to display and change NODATA value for viewed RasterSource.
+#
+#  Revision 1.46  2003/11/05 20:27:23  gmwalter
+#  Fix to allow dialog to recognize GCPProjection string if Projection
+#  string isn't present.
+#
+#  Revision 1.45  2003/06/17 14:58:39  gmwalter
+#  gvplot: changed so that graphs don't immediately disappear
+#  in the gnuplot terminal case.
+#  gvrasterpropdlg: fix so that user can go back to opaque white modulation
+#  colour.
+#
+#  Revision 1.44  2003/04/30 15:05:24  gmwalter
+#  Fixed a typo that was preventing a tuple from being formed and causing
+#  bands to wrap around.
+#
+#  Revision 1.43  2003/02/20 19:27:22  gmwalter
+#  Updated link tool to include Diana's ghost cursor code, and added functions
+#  to allow the cursor and link mechanism to use different gcps
+#  than the display for georeferencing.  Updated raster properties
+#  dialog for multi-band case.  Added some signals to layerdlg.py and
+#  oeattedit.py to make it easier for tools to interact with them.
+#  A few random bug fixes.
+#
+#  Revision 1.42  2002/12/17 16:11:47  gmwalter
+#  Clamp lower bound of Scale_Max to 0.0 in RLM_COMPLEX case in gui_refresh
+#  to avoid confusing phase flips at the 0 crossing when phase display is used.
+#
+#  Revision 1.41  2002/03/04 21:52:26  warmerda
+#  add support for band names
+#
+#  Revision 1.40  2002/03/04 16:26:42  warmerda
+#  added greyscale lock option for RGB layers
+#
+#  Revision 1.39  2001/10/17 16:24:55  warmerda
+#  move complex luts to gview.py
+#
+#  Revision 1.38  2001/10/12 19:25:15  warmerda
+#  improved logic for setting the initial scaling min/max of the scaling controls
+#
+#  Revision 1.37  2001/08/22 02:16:28  warmerda
+#  ensure that gvraster reference not kept from GvRasterSource
+#
+#  Revision 1.36  2001/07/24 21:21:45  warmerda
+#  added EV style phase colormap
+#
+#  Revision 1.35  2001/07/16 15:20:40  warmerda
+#  switched around so magnitude is default for complex layers
+#
+#  Revision 1.34  2001/04/03 02:29:30  warmerda
+#  improved some pane geometry
+#
+#  Revision 1.33  2001/03/19 21:57:14  warmerda
+#  expand tabs
+#
+#  Revision 1.32  2000/08/25 20:20:03  warmerda
+#  added limited nodata support, upped max band count to 30
+#
+#  Revision 1.31  2000/08/11 20:17:15  warmerda
+#  added metadata to image info, made scrollable
+#
+#  Revision 1.30  2000/08/11 19:19:31  warmerda
+#  don't show scaling min for complex layers
+#
+
+from gtk import *
+from string import *
+import gvutils
+import pgucolorsel
+import sys
+import gdal
+import osr
+import gvhtml
+import Numeric
+
+from gvconst import *
+import gview
+
+prop_dialog_list = []
+
+def LaunchRasterPropDialog(layer):
+    # Check list to see if dialog exists - make it visible
+    for test_dialog in prop_dialog_list:
+        if test_dialog.layer._o == layer._o:
+            test_dialog.update_gui()
+            test_dialog.show()
+            test_dialog.get_window()._raise()
+            return test_dialog
+
+    # Create new dialog if one doesn't exist already
+    new_dialog = GvRasterPropDialog(layer)
+    prop_dialog_list.append( new_dialog )
+    return new_dialog
+
+class GvRasterSource(GtkFrame):
+    def __init__(self,name,layer,src_index,master_dialog):
+        GtkFrame.__init__(self,name)
+        self.master_dialog = master_dialog
+        self.updating = FALSE
+        self.src_index = src_index
+
+        # Eventually the following will have to be more sophisticated.
+        if layer is not None:
+            self.layer = layer
+            self.gvraster = layer.get_parent()
+            self.display_change_id = layer.connect('display-change',
+                                                   self.gui_refresh)
+
+        vbox = GtkVBox(spacing=5)
+        vbox.set_border_width(5)
+        self.add(vbox)
+        self.updating = TRUE
+        
+        # ------ Band Selection -------
+        hbox = GtkHBox(spacing=5)
+        vbox.pack_start(hbox,expand=FALSE)
+        hbox.pack_start(GtkLabel('Band:'))
+        self.band_combo = GtkCombo()
+        hbox.pack_start(self.band_combo)
+        self.band_combo.entry.connect('changed', self.set_band_cb)
+        self.band_combo.entry.connect('key_press_event', \
+	    self.combo_entry_key_press_cb)
+        prototype_data = self.layer.get_parent()
+        band_list = ['constant']
+        ds = prototype_data.get_dataset()
+        band_count = ds.RasterCount
+
+        # Fill in the dictionary
+        #
+        self.__bandDic = {}
+        self.__allBands = []
+        self.__bandNums = []
+        for band in range(band_count) :
+            bandKey = self.band_desc(band+1)
+            self.__allBands.append( bandKey )
+            self.__bandNums.append( str(band+1) )
+            self.__bandDic[bandKey] = str(band+1)
+
+        for band in range(min(30,band_count)):
+            band_list.append( self.band_desc(band+1) )
+
+        if band_count > 30:
+            band_list.append( '...'+self.__allBands[-1] )
+
+        self.band_combo.set_popdown_strings( band_list )
+
+        # ------ Establish scaling range ------
+
+        smin = layer.min_get(src_index)
+        smax = layer.max_get(src_index)
+        delta = smax - smin
+        smax = smax + delta * 0.25
+        smin = smin - delta * 0.25
+        
+        if self.layer.get_mode() == gview.RLM_COMPLEX:
+            smin = 0.0
+        elif self.layer.get_parent().get_band().DataType == gdal.GDT_Byte:
+            smin = 0
+            smax = 255
+        elif self.layer.get_parent().get_band().DataType == gdal.GDT_UInt16:
+            smin = 0
+        elif self.layer.get_parent().get_band().DataType == gdal.GDT_UInt32:
+            smin = 0
+
+        # Make sure slider still has reasonable step sizes
+        # for cases where image has small, floating point
+        # values.
+        if delta > 10:
+            new_inc = 1
+        else:
+            new_inc = delta/100.0
+            
+        # calculate #digits for slider. If datatype is integer, #digits will
+        # be 0.  if datatype is floating point, #digits will be set depending
+        # on order of magnitude of delta:
+	if self.layer.get_parent().get_band().DataType == gdal.GDT_Byte \
+	or self.layer.get_parent().get_band().DataType == gdal.GDT_UInt16 \
+	or self.layer.get_parent().get_band().DataType == gdal.GDT_Int16 \
+	or self.layer.get_parent().get_band().DataType == gdal.GDT_UInt32 \
+	or self.layer.get_parent().get_band().DataType == gdal.GDT_Int32:
+		sliderDigits = 0
+	else:
+		sliderDigits = max(0, 2 - int(Numeric.log10(delta)))       
+
+                    
+        # ------ Scale Min -------
+        hbox = GtkHBox(spacing=5)
+        self.min_hbox = hbox
+        vbox.pack_start(hbox)
+        hbox.pack_start(GtkLabel('Scale Min:'),expand=FALSE)
+        self.min_adjustment = GtkAdjustment(layer.min_get(src_index),
+                                smin, smax, new_inc, new_inc, new_inc)
+        self.min_adjustment.connect('value-changed',self.adjustment_cb)
+        self.min_slider = GtkHScale(self.min_adjustment)
+        self.min_slider.set_digits(sliderDigits)
+        hbox.pack_start(self.min_slider)
+        self.min_entry = GtkEntry(maxlen=8)
+        self.min_entry.connect('activate',self.entry_cb)
+        self.min_entry.connect('leave-notify-event',self.entry_cb)
+        hbox.pack_start(self.min_entry,expand=FALSE)
+
+        # ------ Scale Max -------
+        hbox = GtkHBox(spacing=5)
+        self.max_hbox = hbox
+        vbox.pack_start(hbox)
+        hbox.pack_start(GtkLabel('Scale Max:'),expand=FALSE)
+        self.max_adjustment = GtkAdjustment(layer.max_get(src_index),
+                                smin, smax, new_inc, new_inc, new_inc)
+        self.max_adjustment.connect('value-changed',self.adjustment_cb)
+        self.max_slider = GtkHScale(self.max_adjustment)
+        self.max_slider.set_digits(sliderDigits)
+        hbox.pack_start(self.max_slider)
+        self.max_entry = GtkEntry(maxlen=8)
+        self.max_entry.connect('activate',self.entry_cb)
+        self.max_entry.connect('leave-notify-event',self.entry_cb)
+        hbox.pack_start(self.max_entry,expand=FALSE)
+
+        # ------ NODATA -------
+        hbox = GtkHBox(spacing=5)
+        self.nodata_hbox = hbox
+        vbox.pack_start(hbox)
+        hbox.pack_start(GtkLabel('NODATA value:'), expand=FALSE)
+        self.nodata_entry = GtkEntry(maxlen=19)
+        self.nodata_entry.connect('activate', self.entry_cb)
+        self.nodata_entry.connect('leave-notify-event', self.entry_cb)
+        hbox.pack_start(self.nodata_entry, expand=FALSE)
+        if (src_index < 3) and (ds.RasterCount > src_index):
+            nodata=ds.GetRasterBand(src_index+1).GetNoDataValue()
+            if nodata is not None:
+                if (type(nodata) != type(complex(1,0))):
+                    nodata=complex(nodata,0)
+                self.layer.nodata_set(src_index,nodata.real,nodata.imag)
+            
+        # ------- Constant Value -----
+        self.const_entry = GtkEntry(maxlen=8)
+        self.const_entry.connect('activate',self.const_cb)
+        self.const_entry.connect('leave-notify-event',self.const_cb)
+        vbox.pack_start(self.const_entry)
+
+        self.updating = FALSE
+            
+        self.gui_refresh()
+        
+        self.connect( 'destroy', self.cleanup)
+
+    def combo_entry_key_press_cb( self, entryBox, event, *args ) :
+        import GDK
+
+        if self.updating:
+            return
+
+        if( event.keyval == GDK.Right ) :
+            try :
+                currentIndex = self.__allBands.index( entryBox.get_text() )
+                nextIndex = min( len(self.__allBands)-1, currentIndex+1 )
+                entryBox.set_text( self.__allBands[nextIndex] )
+            except ValueError : 
+                return
+
+        elif( event.keyval == GDK.Left ) : 
+            try :
+                currentIndex = self.__allBands.index( entryBox.get_text() )
+                prevIndex = max( 0, currentIndex - 1 )
+                entryBox.set_text( self.__allBands[prevIndex] )
+            except ValueError : 
+                return
+
+        elif( event.keyval == GDK.Return ) : 
+            entryText = entryBox.get_text()
+            if( entryText in self.__bandDic.keys() ) : 
+                pass
+            else : 
+                try :
+                    bandIndex = self.__bandNums.index(entryText)
+                    entryBox.set_text( self.__allBands[bandIndex] )
+                except ValueError : 
+                    entryBox.set_text("")
+                except KeyError :
+                    entryBox.set_text("")
+        
+    def __del__(self):
+        print 'Destroying GvRasterSource'
+
+    def cleanup(self, *args):
+        self.layer = None
+        self.gvraster = None
+
+    def band_desc(self,iband):
+        band = self.layer.get_parent().get_dataset().GetRasterBand(iband)
+        if len(band.GetDescription()) > 0:
+	    # XXX: band descriptions must be different, because we will use
+	    # them as keys in dictionary. That's why we print band number
+	    # here.
+            return '%d: %s' % (iband,band.GetDescription())
+        else:
+            return '%d' % iband
+        
+    def gui_refresh(self, *args):
+
+        if self.layer is None:
+            return
+        
+        if self.flags( DESTROYED ) > 0:
+            self.layer.disconnect( self.display_change_id )
+            return
+
+        if self.updating:
+            return
+
+        self.updating = TRUE
+        if self.layer.get_mode() == gview.RLM_COMPLEX:
+            new_min = max(0.0,self.layer.min_get(self.src_index))
+        else:
+            new_min = self.layer.min_get(self.src_index)
+
+        if self.layer.min_get(self.src_index) < self.min_adjustment.lower:               
+            self.min_adjustment.set_all( new_min,
+                                         new_min,
+                                         self.min_adjustment.upper,
+                                         self.min_adjustment.step_increment,
+                                         self.min_adjustment.page_increment,
+                                         self.min_adjustment.page_size)
+            self.min_adjustment.changed()                
+            self.max_adjustment.set_all( new_min,
+                                         new_min,
+                                         self.max_adjustment.upper,
+                                         self.max_adjustment.step_increment,
+                                         self.max_adjustment.page_increment,
+                                         self.max_adjustment.page_size)
+            self.max_adjustment.changed()
+            
+        if self.layer.max_get(self.src_index) > self.max_adjustment.upper:
+            self.min_adjustment.set_all( new_min,
+                                         self.min_adjustment.lower,
+                                         self.layer.max_get(self.src_index),
+                                         self.min_adjustment.step_increment,
+                                         self.min_adjustment.page_increment,
+                                         self.min_adjustment.page_size)
+            self.min_adjustment.changed()
+            self.max_adjustment.set_all( new_min,
+                                         self.max_adjustment.lower,
+                                         self.layer.max_get(self.src_index),
+                                         self.max_adjustment.step_increment,
+                                         self.max_adjustment.page_increment,
+                                         self.max_adjustment.page_size)
+            self.max_adjustment.changed()
+
+        self.min_adjustment.set_value(new_min)
+        self.min_entry.set_text(str(new_min))
+            
+        self.max_adjustment.set_value(self.layer.max_get(self.src_index))
+        self.max_entry.set_text(str(self.layer.max_get(self.src_index)))
+        nodata=self.layer.nodata_get(self.src_index)
+        if type(nodata) == type((1,)):
+  	    self.nodata_entry.set_text(str(nodata[0])+'+'+str(nodata[1])+
+                                       'j')
+        else:
+  	    self.nodata_entry.set_text(str(nodata))
+            
+        self.const_entry.set_text( \
+                str(self.layer.get_const_value(self.src_index)))
+        
+        if self.layer.get_data(self.src_index) is None:
+            self.const_entry.show()
+            self.min_hbox.hide()
+            self.max_hbox.hide()
+            self.nodata_hbox.hide()
+            self.band_combo.entry.set_text('constant')
+        else:
+            self.const_entry.hide()
+            self.max_hbox.show()
+            if ((self.layer.get_mode() != gview.RLM_COMPLEX) and
+                (band_is_complex(self.layer,self.src_index) == 0)):
+                self.min_hbox.show()
+            else:
+                self.min_hbox.hide()
+            self.nodata_hbox.show()
+
+            # Set the band selector.
+            band = self.layer.get_data(self.src_index).get_band()
+            dataset = self.layer.get_data(self.src_index).get_dataset()
+            for iband in range(dataset.RasterCount):
+                test_band = dataset.GetRasterBand(iband+1)
+                if test_band._o == band._o:
+                    self.band_combo.entry.set_text(self.band_desc(iband+1))
+                    break
+
+        self.updating = FALSE
+
+    def set_band_cb(self,*args):
+        if self.updating:
+            return
+
+        if self.band_combo.entry.get_text() == 'constant':
+            self.layer.set_source(self.src_index, None,
+                                  self.layer.min_get(self.src_index),
+                                  self.layer.max_get(self.src_index),
+                                  self.layer.get_const_value(self.src_index),
+                                  self.layer.source_get_lut(self.src_index),
+                                  None)
+        else:
+            try:            
+                tokens = self.__bandDic[self.band_combo.entry.get_text()],
+            except KeyError :
+                tokens = ""
+                
+            try:
+                band_number = int(tokens[0])
+            except:
+                return
+
+            dataset = self.layer.get_parent().get_dataset()
+            raster = gview.manager.get_dataset_raster( dataset, band_number )
+            if raster is not None:
+                if( self.layer.get_property('_scale_lock') is not None and 
+                    self.layer.get_property('_scale_lock') == 'locked' ) : 
+
+                    if( self.layer.get_property("_scale_limits") ) :
+                        rasterMin, rasterMax = \
+                            map(atof, split(self.layer.get_property("_scale_limits")))
+                else :
+                    rasterMin = raster.get_min()
+                    rasterMax = raster.get_max()
+
+                self.layer.set_source(self.src_index, raster,
+                                    rasterMin, rasterMax,
+                                    self.layer.get_const_value(self.src_index),
+                                    self.layer.source_get_lut(self.src_index),
+                          dataset.GetRasterBand(band_number).GetNoDataValue())
+                
+        if self.src_index < 3 and self.master_dialog.greyscale_is_set():
+            self.master_dialog.enforce_greyscale(self.src_index)
+
+        # enable alpha support if user modifies alpha band.
+        if self.src_index == 3:
+            self.layer.blend_mode_set( RL_BLEND_FILTER )
+
+        self.gui_refresh()
+        
+    def adjustment_cb(self,adjustment,*args):
+        if self.updating:
+            return
+
+        value = adjustment.value
+        if value < -1 or value > 1:
+            value = int(value*10) / 10.0
+
+        if adjustment == self.min_adjustment:
+            self.layer.min_set( self.src_index, value )
+        else:
+            self.layer.max_set( self.src_index, value )
+        
+        if self.src_index < 3 and self.master_dialog.greyscale_is_set():
+            self.master_dialog.enforce_greyscale(self.src_index)
+
+    
+    def entry_cb(self,entry,*args):
+        if self.updating:
+            return
+
+        try:
+            value = complex(entry.get_text())
+	except:
+	    return
+
+	if entry == self.min_entry:
+	    self.layer.min_set( self.src_index, value.real )
+	elif entry == self.max_entry:
+	    self.layer.max_set( self.src_index, value.real )
+	else:
+            self.layer.nodata_set( self.src_index, value.real, value.imag )
+
+        if self.src_index < 3 and self.master_dialog.greyscale_is_set():
+            self.master_dialog.enforce_greyscale(self.src_index)
+
+    def const_cb(self,entry,*args):
+        if self.updating:
+            return
+
+        try:
+            self.layer.set_source(self.src_index,
+                                  self.layer.get_data(self.src_index),
+                                  self.layer.min_get(self.src_index),
+                                  self.layer.max_get(self.src_index),
+                                  int(entry.get_text()),
+                                  self.layer.source_get_lut(self.src_index),
+                                  self.layer.nodata_get(self.src_index))
+        except:
+            self.const_entry.set_text( \
+                str(self.layer.get_const_value(self.src_index)))
+
+        # enable alpha support if user modifies alpha band.
+        if self.src_index == 3:
+            self.layer.blend_mode_set( RL_BLEND_FILTER )
+    
+        if self.src_index < 3 and self.master_dialog.greyscale_is_set():
+            self.master_dialog.enforce_greyscale(self.src_index)
+
+class GvRasterPropDialog(GtkWindow):
+
+    def __init__(self, layer):
+        GtkWindow.__init__(self)
+        self.set_border_width(3)
+        if layer is not None:
+            self.set_title(layer.get_name()+' Properties')
+        else:
+            self.set_title('Raster Properties')
+
+        gvhtml.set_help_topic( self, "gvrasterpropdlg.html" )
+        self.layer = layer
+        self.updating = FALSE
+
+        if self.layer is not None:
+            self.display_change_id = self.layer.connect('display-change',
+                                                        self.refresh_cb)
+            self.teardown_id = layer.connect('teardown',self.close)
+
+        
+        # create the general layer properties dialog
+        self.create_notebook()
+
+        self.create_pane1()
+        
+        self.updating = TRUE
+        self.create_sourcepane()
+        self.updating = FALSE
+        
+        self.create_openglprop()
+        self.create_lutprop()
+	self.create_projprop()
+        self.create_imageinfo()
+
+        self.update_gui()
+        self.show_all()
+        self.update_gui()
+        
+        for source in self.sources:
+            source.gui_refresh()
+
+    def __del__(self):
+        print 'disconnect:', self.display_change_id
+        self.layer.disconnect(self.display_change_id)
+
+    def create_notebook(self):
+        self.notebook = GtkNotebook()
+        self.add( self.notebook )
+        self.connect('delete-event', self.close)
+
+    def create_lutprop(self):
+        
+        self.lut_pane = GtkVBox(spacing=10)
+        self.lut_pane.set_border_width(10)
+        self.notebook.append_page( self.lut_pane, GtkLabel('LUT'))
+
+        self.lut_preview = GtkPreview()
+        self.lut_preview.size(256,32)
+        self.lut_pane.pack_start(self.lut_preview)
+
+        self.complex_lut_om = \
+            gvutils.GvOptionMenu(('Magnitude', 'Phase',
+                                  'Magnitude & Phase', 'Real','Imaginary'),
+                                 self.complex_lut_cb)
+        self.lut_pane.pack_start(self.complex_lut_om, expand=FALSE)
+
+    def create_sourcepane(self):
+        self.sources = []
+        self.source_pane = GtkVBox(spacing=10)
+        self.source_pane.set_border_width(10)
+        self.notebook.append_page( self.source_pane, GtkLabel('Raster Source'))
+
+        if self.layer.get_mode() == gview.RLM_RGBA:
+
+            source = GvRasterSource('Red',self.layer,0,self)
+            self.source_pane.pack_start(source, expand=FALSE)
+            self.sources.append(source)
+            
+            source = GvRasterSource('Green',self.layer,1,self)
+            self.source_pane.pack_start(source, expand=FALSE)
+            self.sources.append(source)
+            
+            source = GvRasterSource('Blue',self.layer,2,self)
+            self.source_pane.pack_start(source, expand=FALSE)
+            self.sources.append(source)
+            
+            source = GvRasterSource('Alpha',self.layer,3,self)
+            self.source_pane.pack_start(source, expand=FALSE)
+            self.sources.append(source)
+
+            self.grey_toggle = GtkCheckButton(label='Greyscale Lock')
+            self.grey_toggle.connect('toggled', self.greyscale_cb)
+            self.source_pane.pack_start(self.grey_toggle, expand=FALSE)
+            self.grey_toggle.set_active( self.greyscale_is_set() )
+
+            scaleHBox = GtkHBox(spacing=10)
+            self.scale_toggle = GtkCheckButton(label="Scale Lock")
+            self.scale_min_entry = GtkEntry()
+            self.scale_max_entry = GtkEntry()
+            self.scale_min_entry.connect( "activate", self.activateLockEntry_cb )
+            self.scale_max_entry.connect( "activate", self.activateLockEntry_cb )
+            w, h = self.scale_min_entry.size_request()
+            self.scale_min_entry.set_usize(50, h)
+            self.scale_max_entry.set_usize(50, h)
+
+
+            scaleHBox.pack_start( self.scale_toggle    ) 
+            scaleHBox.pack_start( self.scale_min_entry )
+            scaleHBox.pack_start( self.scale_max_entry )
+            
+            self.scale_toggle.connect( "toggled", self.scalelock_cb)
+            self.source_pane.pack_start( scaleHBox, expand=FALSE )
+            self.scale_toggle.set_active( self.scalelock_is_set() )
+            
+        else:
+            source = GvRasterSource('Raster',self.layer,0,self)
+            self.source_pane.pack_start(source, expand=FALSE)
+            self.sources.append(source)
+        
+    def create_pane1(self):
+        # Setup General Properties Tab
+        self.pane1 = GtkVBox(spacing=10)
+        self.pane1.set_border_width(10)
+        self.notebook.append_page( self.pane1, GtkLabel('General'))
+
+        # Setup layer name entry box.
+        box = GtkHBox(spacing=5)
+        self.pane1.pack_start(box, expand=FALSE)
+        label = GtkLabel('Layer:' )
+        box.pack_start(label,expand=FALSE)
+        self.layer_name = GtkEntry()
+        self.layer_name.connect('changed', self.name_cb)
+        box.pack_start(self.layer_name)
+
+        # Setup Visibility radio buttons.
+        vis_box = GtkHBox(spacing=5)
+        self.pane1.pack_start(vis_box, expand=FALSE)
+        vis_box.pack_start(GtkLabel('Visibility:'),expand=FALSE)
+        self.vis_yes = GtkRadioButton(label='yes')
+        self.vis_yes.connect('toggled', self.visibility_cb)
+        vis_box.pack_start(self.vis_yes,expand=FALSE)
+        self.vis_no = GtkRadioButton(label='no',group=self.vis_yes)
+        self.vis_no.connect('toggled', self.visibility_cb)
+        vis_box.pack_start(self.vis_no,expand=FALSE)
+
+        # Setup Editability radio buttons.
+        edit_box = GtkHBox(spacing=5)
+        self.pane1.pack_start(edit_box, expand=FALSE)
+        edit_box.pack_start(GtkLabel('Editable:'),expand=FALSE)
+        self.edit_yes = GtkRadioButton(label='yes')
+        self.edit_yes.connect('toggled', self.edit_cb)
+        edit_box.pack_start(self.edit_yes,expand=FALSE)
+        self.edit_no = GtkRadioButton(label='no',group=self.edit_yes)
+        self.edit_no.connect('toggled', self.edit_cb)
+        edit_box.pack_start(self.edit_no,expand=FALSE)
+
+    def create_openglprop(self):
+        oglpane = GtkVBox(spacing=10)
+        oglpane.set_border_width(10)
+        self.notebook.append_page(oglpane, GtkLabel('Draw Style'))
+
+        # Create Modulation Color
+        box = GtkHBox(spacing=5)
+        oglpane.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Modulation Color:'),expand=FALSE)
+        self.mod_color = pgucolorsel.ColorControl('Modulation Color',
+                                                  self.color_cb,None)
+        box.pack_start(self.mod_color)
+
+        # Create Interpolation Control
+        box = GtkHBox(spacing=5)
+        oglpane.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Subpixel Interpolation:'),expand=FALSE)
+        self.interp_om = gvutils.GvOptionMenu(('Linear','Off (Nearest)'), \
+                                              self.set_interp_cb)
+        box.pack_start(self.interp_om,expand=FALSE)
+
+    def create_projparms(self):
+	"""Create projection parameters controls"""
+
+	self.parm_dict = {}
+	if self.proj_table is not None:
+	    self.proj_table.destroy()
+	self.proj_table = GtkTable(2, len(self.proj_parms[self.proj_index]))
+	self.proj_table.set_border_width(5)
+	self.proj_table.set_row_spacings(5)
+	self.proj_table.set_col_spacings(5)
+        self.proj_table.show()
+	row = 0
+	for i in self.proj_parms[self.proj_index]:
+	    parm_label = GtkLabel(i[1])
+	    parm_label.set_alignment(0, 0.5)
+	    self.proj_table.attach(parm_label, 0, 1, row, row + 1)
+	    parm_label.show()
+	    parm_value = self.sr.GetProjParm(i[0])
+	    if parm_value is None:
+		parm_value = str(i[3])
+	    parm_entry = GtkEntry()
+	    parm_entry.set_text(str(parm_value))
+	    self.parm_dict[i[0]] = parm_value
+	    parm_entry.set_editable(TRUE)
+	    parm_entry.connect('changed', self.parm_entry_cb, i[0])
+	    self.proj_table.attach(parm_entry, 1, 2, row, row + 1)
+	    parm_entry.show()
+	    row += 1
+
+	self.proj_vbox.pack_end(self.proj_table, expand=FALSE)
+	
+    def create_projprop(self):
+        projpane = GtkVBox(spacing=10)
+        projpane.set_border_width(10)
+        self.notebook.append_page(projpane, GtkLabel('Coordinate System'))
+
+        self.projprop_vbox = GtkVBox(spacing=5)
+
+	# Projection frame
+	proj_frame = GtkFrame('Projection')
+	proj_frame.show()
+        projpane.pack_start(proj_frame, expand=FALSE)
+	self.proj_vbox = GtkVBox(spacing=5)
+
+	# Fetch projection record
+	self.proj_full = ''
+	proj_name = ''
+	projection = self.layer.get_projection()
+
+        self.sr = None
+        if projection is not None and len(projection) > 0:
+            self.sr = osr.SpatialReference()
+            if self.sr.ImportFromWkt( projection ) == 0:
+                self.proj_full = self.sr.ExportToPrettyWkt( simplify = 1 )
+		if self.proj_full is None:
+		    self.proj_full = ''
+		proj_name = self.sr.GetAttrValue("PROJECTION")
+		if proj_name is None:
+		    proj_name = ''
+
+        # Create projection switch
+	proj_hbox = GtkHBox(spacing=5)
+	proj_hbox.pack_start(GtkLabel('Projection Name:'), \
+	    expand=FALSE, padding=5)
+	proj_methods = osr.GetProjectionMethods()
+	self.projs = map(lambda x: x.__getitem__(0), proj_methods)
+	self.projs.insert(0, '')
+	proj_names = map(lambda x: x.__getitem__(1), proj_methods)
+	proj_names.insert(0, 'None')
+	self.proj_parms = map(lambda x: x.__getitem__(2), proj_methods)
+	self.proj_parms.insert(0, [])
+	self.proj_index = self.projs.index(proj_name)
+
+	self.proj_table = None
+	self.proj_om = gvutils.GvOptionMenu(proj_names, self.set_proj_cb)
+	self.create_projparms()
+	self.proj_om.set_history(self.proj_index)
+	proj_hbox.pack_start(self.proj_om, padding=5)
+	self.proj_vbox.pack_start(proj_hbox, expand=FALSE)
+
+	proj_frame.add(self.proj_vbox)
+
+	# Datum frame
+	datum_frame = GtkFrame('Datum')
+	datum_frame.show()
+        projpane.pack_start(datum_frame, expand=FALSE)
+	datum_hbox = GtkHBox(spacing=5)
+	datum_hbox.pack_start(GtkLabel('Datum Name:'), expand=FALSE, padding=5)
+
+        try:
+            self.datum_name = self.sr.GetAttrValue("DATUM")
+        except:
+            self.datum_name = None
+            
+	self.datum_names = {None:"None", osr.SRS_DN_NAD27:"NAD27", \
+	    osr.SRS_DN_NAD83:"NAD83", osr.SRS_DN_WGS72:"WGS72", \
+	    osr.SRS_DN_WGS84:"WGS84"}
+	try:
+	    self.datum_index = self.datum_names.keys().index(self.datum_name)
+	except ValueError:
+	    self.datum_index = self.datum_names.keys().index(None)
+	self.datum_om = gvutils.GvOptionMenu(self.datum_names.values(), \
+	    self.set_datum_cb)
+	self.datum_om.set_history(self.datum_index)
+	datum_hbox.pack_start(self.datum_om, expand=FALSE, padding=5)
+
+	datum_frame.add(datum_hbox)
+
+	# Units frame
+	units_frame = GtkFrame('Units')
+	#units_frame.show()
+        #projpane.pack_start(units_frame, expand=FALSE)
+	units_hbox = GtkHBox(spacing=5)
+	units_hbox.pack_start(GtkLabel('Units:'), expand=FALSE, padding=5)
+
+	units_frame.add(units_hbox)
+
+	# WKT frame
+	proj_text_frame = GtkFrame('Well Known Text')
+	proj_text_frame.show()
+	projpane.pack_end(proj_text_frame, expand=TRUE)
+
+	self.proj_text = GtkText()
+	self.proj_text.set_line_wrap(TRUE)
+	self.proj_text.set_word_wrap(FALSE)
+	self.proj_text.set_editable(FALSE)
+	self.proj_text.show()
+	self.proj_text.insert_defaults(self.proj_full)
+
+	proj_scrollwin = GtkScrolledWindow()
+	proj_scrollwin.set_usize(0, 300)
+        proj_scrollwin.add(self.proj_text)
+	proj_text_frame.add(proj_scrollwin)
+
+    def create_imageinfo(self):
+        iipane = GtkVBox(spacing=10)
+        iipane.set_border_width(10)
+        self.notebook.append_page( iipane, GtkLabel('Image Info') )
+
+        self.ii_text = GtkText()
+        self.ii_text.set_line_wrap(FALSE)
+        self.ii_text.set_word_wrap(FALSE)
+        self.ii_text.set_editable(FALSE)
+        self.ii_text.show()
+
+        self.ii_scrollwin = GtkScrolledWindow()
+        self.ii_scrollwin.add( self.ii_text)
+        iipane.pack_start(self.ii_scrollwin,expand=TRUE)
+
+        # Now create and assign the text contents.
+        gdal_ds = self.layer.get_parent().get_dataset()
+
+        text = ''
+
+        text = text + 'Filename: ' + gdal_ds.GetDescription() + '\n'
+
+        text = text + 'Size: ' + str(gdal_ds.RasterXSize) + 'P x '
+        text = text +            str(gdal_ds.RasterYSize) + 'L x '
+        text = text +            str(gdal_ds.RasterCount) + 'Bands\n'
+
+        driver = gdal_ds.GetDriver()
+        text = text + 'Driver: ' + driver.LongName + '\n'
+
+        transform = gdal_ds.GetGeoTransform()
+        if transform[2] == 0.0 and transform[4] == 0.0:
+            text = text + 'Origin: ' + str(transform[0])               \
+                               + ' ' + str(transform[3]) + '\n'
+            text = text + 'Pixel Size: ' + str(transform[1])           \
+                                 + ' x ' + str(transform[5]) + '\n'
+                                               
+        projection = gdal_ds.GetProjection()
+        if ((projection is None) or (len(projection) == 0)):
+            projection = gdal_ds.GetGCPProjection()
+
+        if projection is None:
+            projection=""
+            
+        if len(projection) > 0:
+            sr = osr.SpatialReference()
+            if sr.ImportFromWkt( projection ) == 0:
+                projection = sr.ExportToPrettyWkt( simplify = 1 )
+
+        text = text + 'Projection:\n'
+        text = text + projection + '\n'
+
+        metadata = gdal_ds.GetMetadata()
+        if len(metadata) > 0:
+            text = text + 'Metadata:\n'
+            for item in metadata.items():
+                text = text + '    '+item[0]+': '+item[1]+'\n'
+
+        for band_index in range(gdal_ds.RasterCount):
+            band = gdal_ds.GetRasterBand(band_index+1)
+
+            text = text + 'Band %2d: Type=' % (band_index+1)
+            text = text + gdal.GetDataTypeName(band.DataType)
+            if len(band.GetDescription()) > 0:
+                text = text + ' - ' + band.GetDescription()
+            text = text + '\n'
+
+        self.ii_text.insert_defaults(text)
+
+    # Initialize GUI state from underlying object state.
+    def update_gui(self):
+        if self.layer is None or self.updating == TRUE:
+            return
+
+        self.updating = TRUE
+        
+        # Layer name.
+        self.layer_name.set_text( self.layer.get_name() )
+        
+        # Visibility radio buttons
+        self.vis_yes.set_active( self.layer.is_visible() )
+        self.vis_no.set_active( not self.layer.is_visible() )
+
+        # Editability radio buttons
+        self.edit_yes.set_active( not self.layer.is_read_only() )
+        self.edit_no.set_active( self.layer.is_read_only() )
+
+        # modulation color
+        tflag, tcolor = self.layer.texture_mode_get()
+        if tflag == 0:
+            tcolor = (1,1,1,1)
+        self.mod_color.set_color(tcolor)
+
+        # Interpolation Mode
+        zmin, zmax = self.layer.zoom_get()
+        if zmax == RL_FILTER_BILINEAR:
+            self.interp_om.set_history(0)
+        else:
+            self.interp_om.set_history(1)
+
+        self.updating = FALSE
+
+        # LUT
+        if self.layer.get_mode() != gview.RLM_RGBA:
+            lut_tuple = self.layer.lut_get()
+        else:
+            if self.greyscale_is_set() and band_is_complex(self.layer,0):
+                # Only show complex lut frame when bands are locked,
+                # because that's the only time it makes sense
+                # to apply non-magnitude luts.
+                lut_tuple = self.layer.lut_get(rgba_complex=1)
+                # Don't let the user modulate alpha by a band, because
+                # results won't make sense unless the LUT is magnitude
+                # (when alpha is not constant, it uses the red component
+                # of the lookup table for the modulating band- this will
+                # give the expected behaviour if the LUT is magnitude,
+                # but will give non-sensible results if the LUT
+                # is phase). 
+                self.sources[3].hide()
+            else:
+                lut_tuple = self.layer.lut_get()
+                self.sources[3].show()
+            
+        if lut_tuple is None:
+            self.lut_pane.hide()
+        elif lut_tuple[2] == 1:
+            lut_rgba = lut_tuple[0]
+            lut_rgb = gview.rgba_to_rgb(lut_rgba)
+            
+            self.lut_pane.show()
+            self.lut_preview.size(256,32)
+
+            for i in range(32):
+                self.lut_preview.draw_row( lut_rgb, 0, i, 256)
+
+            self.complex_lut_om.hide()
+            self.lut_preview.queue_draw()
+        else:
+            lut_rgba = lut_tuple[0]
+            lut_rgb = gview.rgba_to_rgb(lut_rgba)
+            
+            self.lut_pane.show()
+            self.lut_preview.size(256,256)
+            for row in range(256):
+                row_data = lut_rgb[row*768:(row+1)*768]
+                self.lut_preview.draw_row( row_data, 0, row, 256)
+
+            self.complex_lut_om.show()
+            self.lut_preview.queue_draw()
+
+    def name_cb(self, *args):
+        if self.layer_name.get_text() != self.layer.get_name():
+            self.layer.set_name( self.layer_name.get_text() )
+
+    # Visibility changing
+    def visibility_cb( self, widget ):
+        self.layer.set_visible( self.vis_yes.active )
+
+    # Readonly changing
+    def edit_cb( self, widget ):
+        self.layer.set_read_only( self.edit_no.active )
+
+    def scalelock_is_set( self ) : 
+        if( self.layer.get_property('_scale_lock') is not None and
+            self.layer.get_property('_scale_lock') == 'locked' ) :
+                return 1
+        else :
+            return 0
+
+    def activateLockEntry_cb( self, *args ) : 
+        if( self.scale_toggle.get_active() ) :
+            self.scalelock_cb()
+
+    def scalelock_cb( self, *args ) : 
+        if( self.scalelock_is_set() ) : 
+            self.layer.set_property('_scale_lock', 'unlocked')
+        else :
+            self.layer.set_property('_scale_lock', 'locked'  )
+
+        if( self.scalelock_is_set() ):
+            try :
+                min_scale = atof( self.scale_min_entry.get_text() )
+                max_scale = atof( self.scale_max_entry.get_text() )
+            except ValueError : 
+                min_scale, max_scale = self.layer.min_get(0), self.layer.max_get(0)
+                self.scale_min_entry.set_text( str(min_scale) )
+                self.scale_max_entry.set_text( str(max_scale) )
+
+            self.layer.set_property \
+                ('_scale_limits', str(min_scale) + ' ' + str(max_scale) )
+
+            for iSource in [0,1,2] :
+                self.layer.min_set(iSource, min_scale)
+                self.layer.max_set(iSource, max_scale)
+            
+    def greyscale_is_set(self):
+        if self.layer.get_property('_greyscale_lock') is not None \
+           and self.layer.get_property('_greyscale_lock') == 'locked':
+            return 1
+        else:
+            return 0
+    
+    def greyscale_cb(self, *args):
+        if self.greyscale_is_set():
+            self.layer.set_property('_greyscale_lock','unlocked')
+
+            if self.layer.get_mode() == gview.RLM_RGBA:
+                self.complex_lut_om.set_history(0)
+                self.layer.complex_lut('magnitude')
+        else:
+            self.layer.set_property('_greyscale_lock','locked')
+
+        self.grey_toggle.set_active( self.greyscale_is_set() )
+
+        if self.greyscale_is_set():
+            self.enforce_greyscale(0)
+
+        # show/hide lut as necessary    
+        self.update_gui()
+
+    def enforce_greyscale(self, isrc):
+        if isrc != 0:
+            self.layer.set_source(0, self.layer.get_data(isrc),
+                                  self.layer.min_get(isrc),
+                                  self.layer.max_get(isrc),
+                                  self.layer.get_const_value(isrc),
+                                  self.layer.source_get_lut(isrc),
+                                  self.layer.nodata_get(isrc))
+
+        if isrc != 1:
+            self.layer.set_source(1, self.layer.get_data(isrc),
+                                  self.layer.min_get(isrc),
+                                  self.layer.max_get(isrc),
+                                  self.layer.get_const_value(isrc),
+                                  self.layer.source_get_lut(isrc),
+                                  self.layer.nodata_get(isrc))
+
+        if isrc != 2:
+            self.layer.set_source(2, self.layer.get_data(isrc),
+                                  self.layer.min_get(isrc),
+                                  self.layer.max_get(isrc),
+                                  self.layer.get_const_value(isrc),
+                                  self.layer.source_get_lut(isrc),
+                                  self.layer.nodata_get(isrc))
+
+        # In multi-band complex case, reset alpha band to a constant
+        if band_is_complex(self.layer,0):
+            self.layer.set_source(3, None,
+                                  self.layer.min_get(3),
+                                  self.layer.max_get(3),
+                                  self.layer.get_const_value(3),
+                                  self.layer.source_get_lut(3),
+                                  None)
+    
+    def complex_lut_cb(self, *args):
+        # Magnitude
+        if self.complex_lut_om.get_history() == 0:
+            method = 'magnitude'
+            
+        # Phase
+        elif self.complex_lut_om.get_history() == 1:
+            method = 'phase'
+
+        # Magnitude and Phase
+        elif self.complex_lut_om.get_history() == 2:
+            method = 'magphase'
+
+        # Real
+        elif self.complex_lut_om.get_history() == 3:
+            method = 'real'
+
+        # Imaginary
+        elif self.complex_lut_om.get_history() == 4:
+            method = 'imaginary'
+
+        self.layer.complex_lut( method )
+        
+        self.update_gui()
+
+    # Set modulation color
+    def color_cb( self, color, type ):
+        #if color[0] == 1.0 and color[1] == 1.0 \
+        #   and color[2] == 1.0 and color[3] == 1.0:
+        #    pass
+        #else:
+        if color[3] != 1.0:
+            self.layer.blend_mode_set( RL_BLEND_FILTER )
+        self.layer.texture_mode_set( 1, color )
+            
+
+    def set_interp_cb(self,*args):
+        if self.interp_om.get_history() == 0:
+            self.layer.zoom_set(RL_FILTER_BILINEAR,RL_FILTER_BILINEAR)
+        else:
+            self.layer.zoom_set(RL_FILTER_NEAREST,RL_FILTER_NEAREST)
+
+    def update_proj_text(self):
+	"""Update text control showing projection information"""
+	self.proj_full = self.sr.ExportToPrettyWkt( simplify = 1 )
+	if self.proj_full is None:
+		self.proj_full = ''
+	self.proj_text.delete_text(0, self.proj_text.get_length())
+	self.proj_text.insert_defaults(self.proj_full)
+    
+    def parm_entry_cb(self, entry, parm):
+	"""Set projection parameters"""
+	self.parm_dict[parm] = float(entry.get_text())
+	self.sr.SetNormProjParm(parm, self.parm_dict[parm])
+
+	self.update_proj_text()
+
+    def set_proj_cb(self,*args):
+	"""Set projection"""
+	if self.proj_index != self.proj_om.get_history():
+	    self.proj_index = self.proj_om.get_history()
+	    self.sr = osr.SpatialReference()
+	    self.sr.SetWellKnownGeogCS(self.datum_names[self.datum_name])
+	    self.sr.SetProjection(self.projs[self.proj_index])
+	    for i in self.proj_parms[self.proj_index]:
+		try:
+		    self.sr.SetNormProjParm(i[0], self.parm_dict[i[0]])
+		except KeyError:
+		    self.sr.SetNormProjParm(i[0], i[3])
+	    self.layer.set_projection(self.sr.ExportToWkt())
+	    self.create_projparms()
+
+	    self.update_proj_text()
+
+    def set_datum_cb(self, *args):
+	"""Set datum"""
+	if self.datum_index != self.datum_om.get_history():
+	    self.datum_index = self.datum_om.get_history()
+	    self.datum_name = self.datum_names.keys()[self.datum_index]
+	    self.sr.SetWellKnownGeogCS(self.datum_names[self.datum_name])
+	    self.update_proj_text()
+
+    # Dialog closed, remove references to python object
+    def close( self, *args ):
+        prop_dialog_list.remove(self)
+        self.layer.disconnect(self.display_change_id)
+        self.layer.disconnect(self.teardown_id)
+
+        self.sources = None
+        self.layer = None
+        self.destroy()
+        
+    # Force GUI Refresh
+    def refresh_cb( self, widget, args ):
+        self.update_gui()
+
+def band_is_complex(layer,src_index):
+    """ Returns 1 if src_index'th band of layer is present
+        and complex; 0 otherwise.
+    """
+    
+    try:
+        srctype=layer.get_data(src_index).get_band().DataType
+        ctypes=[gdal.GDT_CInt16, gdal.GDT_CInt32, gdal.GDT_CFloat32,
+                        gdal.GDT_CFloat64]
+        if srctype in ctypes:
+            return 1
+
+        return 0
+    except:
+        return 0
+        
+if __name__ == '__main__':
+    dialog = GvRasterPropDialog(None)
+    dialog.connect('delete-event', mainquit)
+
+    mainloop()

Added: packages/openev/branches/upstream/current/pymod/gvsdsdlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvsdsdlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvsdsdlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,162 @@
+##############################################################################
+# $Id: gvsdsdlg.py,v 1.4 2003/09/09 15:18:46 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Subdataset Selection Dialog
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvsdsdlg.py,v $
+#  Revision 1.4  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.3  2003/01/08 03:25:18  warmerda
+#  avoid having any lines selected
+#
+#  Revision 1.2  2003/01/07 03:38:09  warmerda
+#  added better on/off icons
+#
+#  Revision 1.1  2001/06/27 14:33:17  warmerda
+#  New
+#
+#
+
+from gtk import *
+import gview
+import os.path
+import gvhtml
+
+
+class GvSDSDlg(GtkWindow):
+    def __init__(self, dataset, viewwindow):
+        GtkWindow.__init__(self)
+        self.set_title('SubDataset Selection')
+        self.set_usize(400, 300)
+        self.set_border_width(3)
+        self.set_policy(TRUE,TRUE,FALSE)
+        self.connect('delete-event',self.close)
+        shell = GtkVBox(spacing=3)
+        self.add(shell)
+        #gvhtml.set_help_topic(self, "layerdlg.html" );
+
+        # Layer list
+        layerbox = GtkScrolledWindow()
+        shell.pack_start(layerbox)
+        layerlist = GtkCList(cols=2)
+            
+        layerbox.add_with_viewport(layerlist)
+        layerlist.set_shadow_type(SHADOW_NONE)
+        layerlist.set_selection_mode(SELECTION_SINGLE)
+        layerlist.set_row_height(30)
+        layerlist.set_column_width(0, 24)
+        #layerlist.connect('select-row', self.layer_selected)
+        layerlist.connect('button-press-event', self.list_clicked)
+
+        # buttons
+        button_box = GtkHButtonBox()
+        button_box.set_layout_default(BUTTONBOX_START)
+        ok_button = GtkButton('Accept')
+        ok_button.connect('clicked', self.accept)
+        apply_button = GtkButton('Cancel')
+        apply_button.connect('clicked', self.close)
+        cancel_button = GtkButton('Help')
+        cancel_button.connect('clicked', self.help_cb)
+        button_box.pack_start(ok_button, expand=FALSE)
+        button_box.pack_start(apply_button, expand=FALSE)
+        button_box.pack_start(cancel_button, expand=FALSE)
+        shell.pack_start(button_box,expand=FALSE)
+
+        self.connect('realize', self.realize)
+
+        self.sel_pixmap = \
+            GtkPixmap(self,os.path.join(gview.home_dir,'pics',
+                                        'ck_on_l.xpm'))
+        self.not_sel_pixmap = \
+            GtkPixmap(self,os.path.join(gview.home_dir,'pics',
+                                        'ck_off_l.xpm'))
+        
+        shell.show_all()
+
+        self.dataset = dataset
+        self.viewwindow = viewwindow
+        self.layerlist = layerlist
+
+        self.sds = dataset.GetSubDatasets()
+        self.sds_sel = []
+        for entry in self.sds:
+            self.sds_sel.append( 0 )
+            
+        self.show_all()
+
+    def help_cb(self,*args):
+        pass
+    
+    def close(self,*args):
+        self.hide()
+        return TRUE
+
+    def accept(self,*args):
+        for i in range(len(self.sds_sel)):
+            if self.sds_sel[i]:
+                self.viewwindow.file_open_by_name( self.sds[i][0] )
+        self.close()
+
+    def realize(self, widget):
+        lst = self.layerlist
+        sds = self.sds
+
+        lst.freeze()
+        lst.clear()
+
+        i = 0
+        for entry in sds:
+            lst.append(('', entry[1]))
+                
+            lst.set_pixmap(i, 0, self.not_sel_pixmap)
+
+            i = i + 1
+
+        lst.thaw()        
+
+    def list_clicked(self, lst, event):
+        #print event.type
+        
+        row, col = lst.get_selection_info(int(event.x), int(event.y))        
+	lst.emit_stop_by_name('button-press-event')
+
+        if event.type is GDK._2BUTTON_PRESS:
+            for i in range(len(self.sds_sel)):
+                self.sds_sel[i] = 0
+                
+            self.sds_sel[row] = 1
+            self.accept()
+        else:
+            self.sds_sel[row] = not self.sds_sel[row]
+        
+        if self.sds_sel[row]:
+            lst.set_pixmap(row, 0, self.sel_pixmap)
+        else:
+            lst.set_pixmap(row, 0, self.not_sel_pixmap)
+        
+

Added: packages/openev/branches/upstream/current/pymod/gvselbrowser.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvselbrowser.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvselbrowser.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,336 @@
+###############################################################################
+# $Id: gvselbrowser.py,v 1.4 2001/11/09 15:41:49 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  GUI component to show the current list of selected objects, and
+#           to control a single sub-selection out of that set. 
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2001, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvselbrowser.py,v $
+#  Revision 1.4  2001/11/09 15:41:49  warmerda
+#  avoid using negative oids
+#
+#  Revision 1.3  2001/04/23 15:22:47  warmerda
+#  added callback on oid text field
+#
+#  Revision 1.2  2001/04/19 22:07:53  warmerda
+#  avoid use of +=
+#
+#  Revision 1.1  2001/04/09 18:26:46  warmerda
+#  New
+#
+#
+
+from gtk import *
+from string import *
+from gvsignaler import *
+import os.path
+import pgu
+import gview
+import gvutils
+
+class GvSelBrowser(GtkVBox):
+
+    def __init__(self, spacing=10):
+        GtkVBox.__init__(self, spacing=spacing)
+
+        self.updating = 0
+        
+        self.sel_manager = gview.app.sel_manager
+
+        self.sel_manager.subscribe('active-layer-changed', self.update_gui)
+        self.sel_manager.subscribe('selection-changed', self.update_gui)
+        self.sel_manager.subscribe('subselection-changed', self.update_gui)
+
+        self.tooltips = GtkTooltips()
+
+        hbox = GtkHBox(spacing=3)
+        self.pack_start(hbox,expand=FALSE)
+        self.hbox = hbox
+
+        hbox.pack_start(GtkLabel('Shape:'),expand=FALSE)
+
+        self.oid_tb = GtkEntry(maxlen=7)
+        self.oid_tb.connect('activate', self.oid_cb)
+        self.oid_tb.connect('focus-out-event', self.oid_cb)
+        hbox.pack_start(self.oid_tb)
+
+        left_button = GtkButton()
+        left_button.add(GtkPixmap(self,os.path.join(gview.home_dir,'pics',
+                                                    'pan_left.xpm')))
+        self.tooltips.set_tip(left_button,'Cycle Selection Down')
+        left_button.connect('clicked', self.cycle_down)
+        hbox.pack_start(left_button,expand=FALSE)
+
+        self.n_of_n_label = GtkLabel('XXXX of XXXX')
+        hbox.pack_start(self.n_of_n_label)
+        
+        right_button = GtkButton()
+        right_button.add(GtkPixmap(self,os.path.join(gview.home_dir,'pics',
+                                                    'pan_rght.xpm')))
+        self.tooltips.set_tip(left_button,'Cycle Selection Up')
+        right_button.connect('clicked', self.cycle_up)
+        hbox.pack_start(right_button, expand=FALSE)
+
+        hbox = GtkHBox(spacing=3)
+        self.pack_start(hbox)
+        self.layer_label = GtkLabel('XXXXXXXXXXXXXXXXXXXXXXXXXXX')
+        self.layer_label.set_justify( JUSTIFY_LEFT )
+        hbox.pack_start(self.layer_label, expand=FALSE)
+
+        self.connect('unrealize', self.close)
+        
+        self.update_gui()
+        self.show_all()
+
+    def close(self, *args):
+        self.sel_manager.unsubscribe('active-layer-changed', self.update_gui)
+        self.sel_manager.unsubscribe('selection-changed', self.update_gui)
+        self.sel_manager.unsubscribe('subselection-changed', self.update_gui)
+        
+    def update_gui(self, *args):
+        self.updating = 1
+        layer = self.sel_manager.get_active_layer()
+        if layer is None:
+            self.layer_label.set_text('Layer: <none selected>')
+        else:
+            self.layer_label.set_text('Layer: '+layer.get_name())
+            
+        try:
+            layer = self.sel_manager.get_active_layer()
+            selected = layer.get_selected()
+            subsel = layer.get_subselected()
+        except:
+            self.n_of_n_label.set_text('0 of 0')
+            self.oid_tb.set_text('')
+            self.updating = 0
+            return
+        
+        self.oid_tb.set_text(str(subsel))
+
+        index_of = self.get_sel_index(subsel,selected)
+        
+        label = '%d of %d' % (index_of+1, len(selected))
+        self.n_of_n_label.set_text(label)
+        
+        self.updating = 0
+
+    def oid_cb(self, *args):
+        if self.updating:
+            return
+        
+        try:
+            new_oid = int(self.oid_tb.get_text())
+            layer = self.sel_manager.get_active_layer()
+            selected = layer.get_selected()
+            if new_oid in selected:
+                layer.subselect_shape( new_oid )
+            else:
+                layer.clear_selection()
+                if new_oid >= 0:
+                    layer.select_shape( new_oid )
+
+            layer.display_change()
+        except:
+            pass
+
+    def cycle_down(self, *args):
+        try:
+            layer = self.sel_manager.get_active_layer()
+            selected = layer.get_selected()
+        except:
+            return
+        
+        index_of = self.get_sel_index( layer.get_subselected(), selected )
+        if index_of > 0:
+            layer.subselect_shape( selected[index_of-1] )
+
+    def cycle_up(self, *args):
+        try:
+            layer = self.sel_manager.get_active_layer()
+            selected = layer.get_selected()
+        except:
+            return
+            
+        index_of = self.get_sel_index( layer.get_subselected(), selected )
+        if index_of < len(selected)-1:
+            layer.subselect_shape( selected[index_of+1] )
+
+    def get_sel_index(self, subsel, selected):
+        index_of = 0
+        while index_of < len(selected) \
+              and selected[index_of] != subsel:
+            index_of = index_of + 1
+
+        if index_of >= len(selected):
+            return -1
+        else:
+            return index_of
+
+class GvSelectionManager(Signaler):
+
+    """
+    Convenient manager for view, layer, and shape selection tracking.
+
+    The GvSelectionManager provides a single object which can be easily
+    used to track changes in current view, layer, shape selection and shape
+    sub-selection.  This is mainly useful because adding and removing
+    callbacks to individual layers and views is a hassle.
+
+    This class is normally accessed as "gview.app.sel_manager", and the
+    object instance is normally created by the openev.py startup.  The
+    object publishes the following "gvsignaler.py" style signals.  Use
+    the "subscribe" method to add a callback.
+
+    active-view-changed -- The current application view has changed (as
+                           understood by openev.ViewManager).
+
+    active-layer-changed -- The current layer of the active view (as returned
+                            by GvViewArea.active_layer()) has changed, possibly
+                            as a result of the current view changing.
+
+    selection-changed -- The shape selection on the current layer (as
+                         GvShapeLayer.get_selected()) has changed, possibly
+                         as a result of a change of active layer or view.
+                         Clearing selection, and selecting non-GvShapeLayers
+                         can result in a selection-changed.
+
+    subselection-changed -- The item within the current selection has
+                            changed, possible as the result of a change in
+                            selection, active layer or active view.
+
+    """
+
+    def __init__(self, view_manager):
+
+        self.view_manager = view_manager
+        self.view_manager.subscribe('active-view-changed',self.view_change)
+
+        self.view = self.view_manager.get_active_view()
+        if self.view is not None:
+            self.view_cb_id \
+                = self.view.connect('active-changed', self.layer_change)
+        else:
+            self.view_cb_id = None
+
+        self.layer = None
+        self.layer_selcb_id = None
+        self.layer_sselcb_id = None
+
+        self.sel_len = 0
+        self.ssel = -1
+        self.ssel_layer = None
+        
+        self.publish('active-view-changed')
+        self.publish('active-layer-changed')
+        self.publish('selection-changed')
+        self.publish('subselection-changed')
+
+        self.layer_change()
+        
+    def view_change(self, *args):
+        if self.view == self.view_manager.get_active_view():
+            return
+
+        if self.view_cb_id is not None:
+            self.view.disconnect(self.view_cb_id)
+            self.view_cb_id = None
+            
+        self.view = self.view_manager.get_active_view()
+        
+        if self.view is not None:
+            self.view_cb_id \
+                = self.view.connect('active-changed', self.layer_change)
+        
+        self.notify('active-view-changed')
+        self.layer_change()
+
+    def get_active_view_window(self):
+        """Fetch active GvViewWindow."""
+        return self.view_manager.get_active_view_window()
+
+    def get_active_view(self):
+        """Fetch active GvViewArea."""
+        return self.view_manager.get_active_view()
+
+    def layer_change(self, *args):
+        if self.view is None:
+            new_layer = None
+        else:
+            new_layer = self.view.active_layer()
+
+        if new_layer == self.layer:
+            return
+        
+        if self.layer_selcb_id is not None:
+            self.layer.disconnect(self.layer_selcb_id)
+            self.layer.disconnect(self.layer_sselcb_id)
+            self.layer_selcb_id = None
+
+        self.layer = new_layer
+
+        if self.layer is not None \
+           and gvutils.is_of_class(self.layer.__class__, 'GvShapeLayer'):
+            self.layer_selcb_id = \
+                 self.layer.connect('selection-changed',self.sel_change)
+            self.layer_sselcb_id = \
+                 self.layer.connect('subselection-changed',self.ssel_change)
+
+        self.notify('active-layer-changed')
+        self.sel_change()
+        self.ssel_change()
+
+    def get_active_layer(self):
+        return self.layer
+
+    def sel_change(self,*args):
+        try:
+            new_len = len(self.layer.get_selected())
+        except:
+            new_len = 0
+
+        if new_len == 0 and self.sel_len == 0:
+            return
+
+        self.sel_len = new_len
+        self.notify('selection-changed')
+
+    def ssel_change(self,*args):
+
+        try:
+            new_ssel = self.layer.get_subselected()
+        except:
+            new_ssel = -1
+
+        if new_ssel == -1 and self.ssel == -1:
+            return
+
+        if self.ssel_layer == self.layer and new_ssel == self.ssel:
+            return
+
+        self.ssel_layer = self.layer
+        self.ssel = new_ssel
+
+        self.notify('subselection-changed')
+    
+
+pgu.gtk_register('GvSelBrowser',GvSelBrowser)

Added: packages/openev/branches/upstream/current/pymod/gvshell.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvshell.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvshell.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,153 @@
+###############################################################################
+# $Id: gvshell.py,v 1.5 2003/07/30 15:31:12 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Extra intrinsics for OpenEV PyShell environment.  Used from
+#           gviewapp.pyshell().
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvshell.py,v $
+#  Revision 1.5  2003/07/30 15:31:12  gmwalter
+#  Update display() function so it can display 1-D arrays.
+#
+#  Revision 1.4  2003/07/28 19:42:34  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.3  2002/09/12 15:02:54  warmerda
+#  removed problematic import
+#
+#  Revision 1.2  2001/11/12 18:47:47  warmerda
+#  Fixed formatting of shape info in local_vars_list.
+#
+#  Revision 1.1  2001/11/12 18:44:35  warmerda
+#  New
+#
+
+import gview
+import gdalnumeric
+import gdal
+
+###############################################################################
+# define easy file view command
+def display(array, prototype_name = None):
+    import Numeric
+    if len(Numeric.shape(array)) == 1:
+        array=Numeric.reshape(array,(1,Numeric.shape(array)[0]))
+        
+    array_name = gdalnumeric.GetArrayFilename(array)
+    ds = gview.manager.get_dataset( array_name )
+    if prototype_name is not None:
+        prototype_ds = gdal.Open( prototype_name )
+        gdalnumeric.CopyDatasetInfo( prototype_ds, ds )
+            
+    gview.app.file_open_by_name( array_name )
+
+###############################################################################
+# Utility to get ROI marked with tool from array
+def get_roi(num_array):
+    roi = gview.app.toolbar.get_roi()
+    
+    x1 = int(roi[0])
+    y1 = int(roi[1])
+    x2 = int(roi[0] + roi[2]) + 1
+    y2 = int(roi[1] + roi[3]) + 1
+    
+    return num_array[...,y1:y2,x1:x2]
+
+###############################################################################
+def roi():
+    return gview.app.toolbar.get_roi()
+
+
+###############################################################################
+#                        local_vars_list()
+#
+# This function is invoked on locals() by entering "locals" in the
+# pyshell.py command window.  The MyInteractiveConsole.push() method
+# intercepts the input line and replaces 'locals' with the call.
+#
+# This function attempts to print out a list of all local variables in the
+# command environment that have been defined since the shell was instantiated.
+# This is accomplished by keeping a list of variables (and methods) at the
+# startup time in the gview.shell_base_vars variable.  This is done by
+# the GViewApp.pyshell() function.
+#
+# All objects existing at startup time are ignored, all others are listed for
+# the user.
+#
+# An attempt is made to distinguish between NumPy arrays, and other variables.
+# Numpy arrays will have their shape and type printed in a user friendly
+# (hopefully) manner, while other locals are printed with just their name,
+# and type.
+# 
+def local_vars_list( var_list = None, typestrings=None ):
+    """local_vars_list( var_list = None, typestrings=None)
+       var_list: list of variables to search through
+       typestrings: list of types to include (strings
+                    corresponding to type(var).__name__,
+                    where var is a variable of the type
+                    to search for).
+    """
+    
+    import Numeric
+    img_type = type(Numeric.array((1,2)))
+
+    type_dict = {'b': 'UnsignedInt8', 'D': 'CFloat64', 'F':'CFloat32',
+                 'd': 'Float64', 'f': 'Float32', 'l': 'Int32',
+                 '1': 'Int8', 's': 'Int16', 'i': 'Int32' }
+
+    list = var_list.keys()
+    txtlist=[]
+    for varname in list:
+        if varname in gview.shell_base_vars:
+            continue
+
+        var = var_list[varname]
+        t = type(var)
+
+        # Only show variables of type typestring
+        if ((typestrings is not None) and
+            (t.__name__ not in typestrings)):
+            continue
+
+        if t == img_type:
+            shape = Numeric.shape(var)
+            shape_str = None
+            for dim in shape:
+                if shape_str == None:
+                    shape_str = ') '
+                else:
+                    shape_str = 'x' + shape_str
+                    
+                shape_str = str(dim) + shape_str
+            shape_str = ' (' + shape_str
+                
+            try:
+                txtlist.append(varname+ shape_str+ type_dict[var.typecode()])
+            except:
+                txtlist.append(varname+ shape_str+ var.typecode())
+        else:
+            txtlist.append('%s (%s)' % (varname, t.__name__))
+
+    return txtlist
+

Added: packages/openev/branches/upstream/current/pymod/gvsignaler.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvsignaler.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvsignaler.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,125 @@
+###############################################################################
+# $Id: gvsignaler.py,v 1.6 2003/01/07 18:44:32 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Signaler - implements subscription/notification system a bit like
+#           Gtk signaling.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvsignaler.py,v $
+#  Revision 1.6  2003/01/07 18:44:32  gmwalter
+#  Changed a tuple to a list to fix a blocking bug.
+#
+#  Revision 1.5  2001/04/03 02:30:38  warmerda
+#  fixed signal name typo
+#
+#  Revision 1.4  2001/03/19 21:57:14  warmerda
+#  expand tabs
+#
+#  Revision 1.3  2000/08/09 01:04:13  warmerda
+#  fixed bug in unsubscribe
+#
+#  Revision 1.2  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+
+"""
+MODULE
+   gvsignaler
+   
+DESCRIPTION
+   Provides an event subscription/notification mechanism a bit like
+   Gtk signal handling.  Classes which derive from Signaler can
+   publish a list of named signals.  These signals can then be
+   attached to arbitrary callback methods/functions using subscribe().
+   More than one callback function per signal can be attached.
+   The Signaler executes the callback functions with the notify()
+   method.
+
+   Arguments to the callback functions are (in order):
+     1. The Signaler instance.
+     2. Any signal specific arguments provided to notify().
+     3. Subscriber baggage arguments provided to subscribe().
+   The baggage arguments act like the 'data' argument of Gtk signals.
+"""
+
+class UnpublishedSignalError(Exception): pass
+class SignalExistsError(Exception): pass
+
+class Signaler:
+    "Base class for objects with published signals"
+    signal = {}  # Prevents AttributeErrors
+
+    def publish(self, *sigs):
+        "Publish one or more named signals"
+        if not self.__dict__.has_key('signal'):
+            self.signal = {}
+        for s in sigs:
+            if self.signal.has_key(s):
+                raise SignalExistsError
+            self.signal[s] = [0, []]  # Blocked flag, handlers list
+
+    def subscribe(self, name, meth, *args):
+        "Attach a callback function/method to a signal"
+        try:
+            self.signal[name][1].append((meth, args))
+        except KeyError:
+            raise UnpublishedSignalError
+
+    def unsubscribe(self, name, meth):
+        "Remove a callback function/method for a named signal"
+        try:
+            l = len(self.signal[name][1])
+            for si in range(l):
+                if self.signal[name][1][si][0] == meth:
+                    del self.signal[name][1][si]
+                    break
+        except KeyError:
+            raise UnpublishedSignalError
+
+    def notify(self, name, *args):
+        "Execute callbacks attached to the named signal"
+        try:
+            sig = self.signal[name]
+        except KeyError:
+            raise UnpublishedSignalError
+        # Check for blocked signal
+        if sig[0] == 0:
+            for s in sig[1]:
+                apply(s[0], (self,) + args + s[1])
+
+    def block(self, name):
+        "Prevent a signal from being emitted"
+        try:
+            self.signal[name][0] = 1
+        except KeyError:
+            raise UnpublishedSignalError
+
+    def unblock(self, name):
+        "Allows a blocked signal to be emitted"
+        try:
+            self.signal[name][0] = 0
+        except KeyError:
+            raise UnpublishedSignalError
+
+    

Added: packages/openev/branches/upstream/current/pymod/gvutils.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvutils.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvutils.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1147 @@
+###############################################################################
+# $Id: gvutils.py,v 1.29 2004/10/15 16:52:20 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Convenience widgets, and services built on Gtk widgets.
+#           Note that these will eventually be moved into an Atlantis wide
+#           set of utility classes in python.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvutils.py,v $
+#  Revision 1.29  2004/10/15 16:52:20  gmwalter
+#  Bug fix in GvEntryFrame default value setting.
+#
+#  Revision 1.28  2004/10/12 14:25:21  gmwalter
+#  Added forgotten import.
+#
+#  Revision 1.27  2004/10/07 19:02:34  gmwalter
+#  Extract generic code into gvutils.py.
+#
+#  Revision 1.26  2004/07/05 08:47:02  dem
+#  don't raise a top level exception when a deserialization
+#  of a layer fails in projects reloading
+#
+#  Revision 1.25  2004/07/02 16:37:09  dem
+#  - Implement project files portability
+#  - Change to don't crash OpenEV when a deserialization
+#    of a layer fails in projects reloading.
+#    Popup a gvutils.warning instead.
+#
+#  Revision 1.24  2003/08/28 19:50:18  warmerda
+#  added XMLSerializeSimpleObjAttributes (and deserialize)
+#
+#  Revision 1.23  2003/05/22 18:38:28  desch-mosher
+#
+#  return from parse_accerator incorrectly shifted
+#
+#  Revision 1.22  2003/05/09 13:48:34  warmerda
+#  don't read more than 20 bytes in is_project_file()
+#
+#  Revision 1.21  2003/01/18 22:50:01  gmwalter
+#  Change XMLInsert to allow top-level insertion.
+#
+#  Revision 1.20  2002/11/26 16:16:41  gmwalter
+#  Added a few more xml functions, extended XMLFind (default behaviour should
+#  be unchanged).  These functions may be rewritten/replaced later.
+#
+#  Revision 1.19  2002/10/21 01:25:48  warmerda
+#  avoid use of system tempnam() to avoid annoyying warning
+#
+#  Revision 1.18  2002/07/15 18:43:06  pgs
+#  modified XMLFindValue to take an empty path (operate on current node)
+#
+#  Revision 1.17  2002/07/12 12:46:06  warmerda
+#  expanded tabs
+#
+#  Revision 1.16  2002/07/08 19:46:03  warmerda
+#  added project save/load capability
+#
+#  Revision 1.15  2002/07/07 21:06:15  warmerda
+#  preliminary addition of project saving
+#
+#  Revision 1.14  2002/04/25 15:19:58  gmwalter
+#  Updated the menu extension mechanism to get rid of the extend_menu function and to allow the help menu to remain on the right side of the menubar.  Got rid of some extra print statements and the gnuplot 0 y-range warning in the histogram tool initialization.
+#
+#  Revision 1.13  2002/04/18 16:47:17  warmerda
+#  Python2.0 doesn't have os.tempnam(), provide alternative
+#
+#  Revision 1.12  2002/04/17 14:43:20  warmerda
+#  added FindExecutable()
+#
+#  Revision 1.11  2002/04/17 13:42:20  warmerda
+#  added tempnam() method
+#
+#  Revision 1.10  2002/02/28 18:52:22  gmwalter
+#  Added a point-of-interest tool similar to the region-of-interest
+#  tool (allows a user to select a temporary point without having to add a
+#  new layer).  Added a mechanism to allow some customization of openev
+#  via a textfile defining external modules.
+#
+#  Revision 1.9  2001/05/16 00:46:05  pgs
+#  change tolower to lower in is_shapefile
+#
+#  Revision 1.8  2001/05/15 14:14:49  warmerda
+#  added is_shapefile() function
+#
+#  Revision 1.7  2001/03/19 21:57:14  warmerda
+#  expand tabs
+#
+#  Revision 1.6  2000/08/24 17:52:12  warmerda
+#  added error and warning calls
+#
+#  Revision 1.5  2000/06/14 15:13:38  warmerda
+#  moved registering and color selector to pygtkutils
+#
+#  Revision 1.4  2000/06/12 20:16:31  warmerda
+#  added is_of_class test
+#
+#  Revision 1.3  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+import gtk; _gtk = gtk; del gtk
+import GtkExtra
+import string
+import pgu
+import os
+import sys
+import pgufilesel
+
+def is_of_class(class_obj,class_name):
+    if class_obj.__name__ == class_name:
+        return 1
+    for c in class_obj.__bases__:
+        if is_of_class(c,class_name) == 1:
+            return 1
+    return 0
+    
+class GvOptionMenu(_gtk.GtkOptionMenu):
+
+    def __init__(self, contents, callback = None):
+        _gtk.GtkOptionMenu.__init__(self)
+
+        menu = _gtk.GtkMenu()
+        self.callback = callback
+
+        item_widget = None
+        counter = 0
+        for item in contents:
+            item_widget = _gtk.GtkRadioMenuItem( item_widget, item )
+            item_widget.show()
+            item_widget.connect('activate', self.set_om_selection,
+                                counter )
+            menu.append(item_widget)
+            counter = counter + 1
+
+        self.cur_selection = 0
+        menu.show()
+        self.set_menu(menu)
+
+    def set_history(self, item):
+        if item == self.cur_selection:
+            return
+        
+        self.cur_selection = item
+        _gtk.GtkOptionMenu.set_history( self, item )
+
+        if self.callback is not None:
+            self.callback( self )
+
+    def get_history(self):
+        return self.cur_selection
+        
+    def set_om_selection(self, widget, data ):
+        if widget.active:
+            self.set_history( data )
+
+pgu.gtk_register('GvOptionMenu',GvOptionMenu)
+
+#
+# Copied from GtkExtra
+#
+class _MessageBox(_gtk.GtkDialog):
+        def __init__(self, message="", buttons=(), pixmap=None,
+                     modal=_gtk.TRUE):
+                _gtk.GtkDialog.__init__(self)
+                self.connect("destroy", self.quit)
+                self.connect("delete_event", self.quit)
+                self.modal = modal
+                if modal:
+                        _gtk.grab_add(self)
+                hbox = _gtk.GtkHBox(spacing=5)
+                hbox.set_border_width(5)
+                self.vbox.pack_start(hbox)
+                hbox.show()
+                if pixmap:
+                        self.realize()
+                        pixmap = _gtk.GtkPixmap(self, pixmap)
+                        hbox.pack_start(pixmap, expand=_gtk.FALSE)
+                        pixmap.show()
+                label = _gtk.GtkLabel(message)
+                label.set_justify( _gtk.JUSTIFY_LEFT )
+                hbox.pack_start(label)
+                label.show()
+
+                for text in buttons:
+                        b = _gtk.GtkButton(text)
+                        b.set_flags(_gtk.CAN_DEFAULT)
+                        b.set_data("user_data", text)
+                        b.connect("clicked", self.click)
+                        self.action_area.pack_start(b)
+                        b.show()
+                self.ret = None
+
+        def quit(self, *args):
+                self.hide()
+                self.destroy()
+                if self.modal:
+                    _gtk.mainquit()
+                    
+        def click(self, button):
+                self.ret = button.get_data("user_data")
+                self.quit()
+
+def warning( text ):
+    import gview
+    import os.path
+    
+    warning_pixmap = os.path.join(gview.home_dir,'pics','warning.xpm')
+    win = _MessageBox(text, ('OK',), pixmap=warning_pixmap, modal=_gtk.FALSE )
+    win.set_title('Warning')
+    win.show()
+    return
+
+def error( text ):
+    import gview
+    import os.path
+    
+    warning_pixmap = os.path.join(gview.home_dir,'pics','warning.xpm')
+    win = _MessageBox(text, ('OK',), pixmap=warning_pixmap, modal=_gtk.TRUE )
+    win.set_title('ERROR')
+    win.show()
+    _gtk.mainloop()
+    
+    return
+
+def is_shapefile( filename ):
+    try:
+        ext = string.lower(filename[len(filename)-4:])
+        if ext == '.shp' or ext == '.shx' or ext == '.dbf':
+            return 1
+        else:
+            return 0
+    except:
+        return 0
+
+def is_project_file( filename ):
+    try:
+        ext = string.lower(filename[len(filename)-4:])
+        if ext == '.opf':
+            return 1
+
+        first_line = open(filename).read(20)
+        if first_line[:10] == '<GViewApp>':
+            return 1
+        else:
+            return 0
+    except:
+        return 0
+
+
+# GvMenuFactory is just GtkExtra.MenuFactory, with the
+# addition of a function to allow you to insert entries
+# after the fact...
+
+class GvMenuFactory:
+    def __init__(self, type=GtkExtra.MENU_FACTORY_MENU_BAR):
+        self.accelerator = _gtk.GtkAccelGroup()
+        if type == GtkExtra.MENU_FACTORY_MENU_BAR:
+            self.__w = _gtk.GtkMenuBar()
+            self.__ret = self.__w
+        elif type == GtkExtra.MENU_FACTORY_MENU:
+            self.__w = _gtk.GtkMenu()
+            self.__w.set_accel_group(self.accelerator)
+            self.__ret = self.__w
+        elif type == GtkExtra.MENU_FACTORY_OPTION_MENU:
+            self.__w = _gtk.GtkMenu()
+            self.__w.set_accel_group(self.accelerator)
+            self.__ret = GtkOptionMenu()
+            self.__ret.set_menu(self.__w)
+        self.__menus = {}
+        self.__items = {}
+    def __getattr__(self, key):
+        return getattr(self.__ret, key)
+    def add_entries(self, entries):
+        for entry in entries:
+            apply(self.create, tuple(entry))
+    def create(self, path, accelerator=None, callback=None, *args):
+        last_slash = string.rfind(path, '/')
+        if last_slash < 0:
+            parentmenu = self.__w
+        else:
+            parentmenu = self.get_menu(path[:last_slash])
+        label = path[last_slash+1:]
+        if label == '<separator>':
+            item = _gtk.GtkMenuItem()
+        elif label[:7] == '<check>':
+            item = _gtk.GtkCheckMenuItem(label[7:])
+        else:
+            item = _gtk.GtkMenuItem(label)
+        if label != '<nothing>':
+            item.show()
+        if accelerator:
+            key, mods = self.parse_accelerator(accelerator)
+            item.add_accelerator("activate", self.accelerator,
+                                     key, mods, 'visible')
+        if callback:
+            apply(item.connect, ("activate", callback) + args)
+        # right justify the help menu automatically
+        if string.lower(label) == 'help' and parentmenu == self.__w:
+            item.right_justify()
+        parentmenu.append(item)
+        self.__items[path] = item
+        return item
+    def get_menu(self, path):
+        if path == '':
+            return self.__w
+        if self.__menus.has_key(path):
+            return self.__menus[path]
+        wid = self.create(path)
+        menu = _gtk.GtkMenu()
+        menu.set_accel_group(self.accelerator)
+        wid.set_submenu(menu)
+        self.__menus[path] = menu
+        return menu
+    def parse_accelerator(self, accelerator):
+        key = 0
+        mods = 0
+        done = _gtk.FALSE
+        while not done:
+            if accelerator[:7] == '<shift>':
+                mods = mods | _gtk.GDK.SHIFT_MASK
+                accelerator = accelerator[7:]
+            elif accelerator[:5] == '<alt>':
+                mods = mods | _gtk.GDK.MOD1_MASK
+                accelerator = accelerator[5:]
+            elif accelerator[:6] == '<meta>':
+                mods = mods | _gtk.GDK.MOD1_MASK
+                accelerator = accelerator[6:]
+            elif accelerator[:9] == '<control>':
+                mods = mods | _gtk.GDK.CONTROL_MASK
+                accelerator = accelerator[9:]
+            else:
+                done = _gtk.TRUE
+                key = ord(accelerator[0])
+        return key, mods
+    def remove_entry(self, path):
+        if path not in self.__items.keys():
+            return
+        item = self.__items[path]
+        item.destroy()
+        length = len(path)
+        # clean up internal hashes
+        for i in self.__items.keys():
+            if i[:length] == path:
+                del self.__items[i]
+        for i in self.__menus.keys():
+            if i[:length] == path:
+                del self.__menus[i]
+
+    def get_entry(self, path):
+        result = []
+        if path not in self.__items.keys():
+            return result
+        item = self.__items[path]
+        result.append(item)
+        length = len(path)
+        # clean up internal hashes
+        for i in self.__items.keys():
+            if i[:length] == path:
+                result.append(self.__items[i])
+        for i in self.__menus.keys():
+            if i[:length] == path:
+                result.append(self.__menus[i])
+		
+	return result
+    def remove_entries(self, paths):
+        for path in paths:
+            self.remove_entry(path)
+    def find(self, path):
+        return self.__items[path]
+
+    def insert_entry(self, pos, path, accelerator=None, callback=None, *args):
+        # like create, but lets you specify position in menu
+        last_slash = string.rfind(path, '/')
+        if last_slash < 0:
+            parentmenu = self.__w
+        else:
+            parentmenu = self.insert_get_menu(path[:last_slash])
+        label = path[last_slash+1:]
+        if label == '<separator>':
+            item = _gtk.GtkMenuItem()
+        elif label[:7] == '<check>':
+            item = _gtk.GtkCheckMenuItem(label[7:])
+        else:
+            item = _gtk.GtkMenuItem(label)
+        if label != '<nothing>':
+            item.show()
+        if accelerator:
+            key, mods = self.parse_accelerator(accelerator)
+            item.add_accelerator("activate", self.accelerator,
+                             key, mods, 'visible')
+        if callback:
+            apply(item.connect, ("activate", callback) + args)
+        # right justify the help menu automatically
+        if string.lower(label) == 'help' and parentmenu == self.__w:
+            item.right_justify()
+        # all this copying for just the next few line...
+        if pos is not None:
+            parentmenu.insert(item, pos)
+        elif parentmenu == self.__w:
+            # Make sure Help retains far-right position
+            if self.__menus.has_key('Help'):
+                num_main_menus = 0
+                for current_path in self.__menus.keys():
+                    # Check that it isn't a sub-menu...
+                    temp_slash = string.rfind(current_path,'/')
+                    if temp_slash < 0:
+                        num_main_menus = num_main_menus + 1
+                parentmenu.insert(item,max(num_main_menus - 1,1))
+            else:
+                parentmenu.append(item)
+        else:
+            parentmenu.append(item)
+
+        self.__items[path] = item
+        return item
+
+    def insert_get_menu(self, path):
+        # Allows new menus to be placed before help on toolbar
+        # by using insert_entry to create parents instead of 
+        # create.
+        if path == '':
+            return self.__w
+        if self.__menus.has_key(path):
+            return self.__menus[path]
+        wid = self.insert_entry(None,path)
+        menu = _gtk.GtkMenu()
+        menu.set_accel_group(self.accelerator)
+        wid.set_submenu(menu)
+        self.__menus[path] = menu
+        return menu
+
+
+def read_keyval( line ) :
+    import re
+    import string
+
+    # skip comments & lines that don't contain a '='
+    if line[0] == '#' : return [None,None]
+    if '=' not in line : return [None,None]
+
+    # Grab the key, val
+    [ key, val ] = re.compile( r"\s*=\s*" ).split( line )
+
+    # Strip excess characters from the key string
+    key_re = re.compile( r"\b\w+\b" )
+    key = key[key_re.search(key).start():]
+    key = key[:key_re.search(key).end()]
+
+    # Strip excess characters from the value string
+    val = string.strip( val )
+    i = string.find( val, ' ' )
+    if i > 0 : val = val[0:i]
+
+    return [ key, val ]
+
+
+def get_tempdir():
+    if os.environ.has_key('TMPDIR'):
+        tmpdir = os.environ['TMPDIR']
+    elif os.environ.has_key('TEMPDIR'):
+        tmpdir = os.environ['TEMPDIR']
+    elif os.environ.has_key('TEMP'):
+        tmpdir = os.environ['TEMP']
+    else:
+        if os.name == 'nt':
+            tmpdir = 'C:'
+        else:
+            tmpdir = '/tmp'
+
+    return tmpdir
+
+def tempnam( tdir = None, basename = None, extension = None ):
+    import os.path
+    import gview
+
+    if tdir is None:
+        plotfile = gview.get_preference('gvplot_tempfile')
+        if plotfile is not None and len(plotfile) > 0:
+            if os.path.isdir(plotfile):
+                tdir = plotfile
+            elif os.path.isdir(os.path.dirname(plotfile)):
+                tdir = os.path.dirname(plotfile)
+            else:
+                tdir = get_tempdir()
+        else:
+            tdir = get_tempdir()
+
+    if basename is None:
+        try:
+            pgu.pnm = pgu.pnm + 1
+        except:
+            pgu.pnm = 1
+        basename = 'OBJ_' + str(pgu.pnm)
+
+    if extension is None:
+        extension = 'tmp'
+
+    return os.path.join(tdir,basename + '.' + extension)        
+
+def FindExecutable( exe_name ):
+    """Try to return full path to requested executable.
+
+    First checks directly, then searches $OPENEV_HOME/bin and the PATH.
+    Will add .exe on NT.  Returns None on failure.
+    """
+
+    import os.path
+    import gview
+    import string
+
+    if os.name == 'nt':
+        (root, ext) = os.path.splitext(exe_name)
+        if ext != '.exe':
+            exe_name = exe_name + '.exe'
+
+    if os.path.isfile(exe_name):
+        return exe_name
+
+    if os.path.isfile(os.path.join(gview.home_dir,'bin',exe_name)):
+        return os.path.join(gview.home_dir,'bin',exe_name)
+
+    exe_path = os.environ['PATH']
+    if (os.name == 'nt'):
+        path_items = string.split(exe_path,';')
+    else:
+        path_items = string.split(exe_path,':')
+
+    for item in path_items:
+        exe_path = os.path.join(item,exe_name)
+        if os.path.isfile(exe_path):
+            return exe_path
+
+    return None
+
+def XMLFindValue( node, path, default = None ):
+    import gdal
+    if path == '' or path == None:
+        tnode = node
+    else:
+        tnode = XMLFind( node, path )
+        if tnode is None:
+            return default
+
+    for child in tnode[2:]:
+        if child[0] == gdal.CXT_Text:
+            return child[1]
+
+    return default
+
+def XMLFind( node, path, maxfind=1, attr=None,value=None ):
+    import gdal
+    broken_up = string.split( path, '.', 1 )
+    found_list=[]
+    if len(broken_up) == 2:
+        component, rest_of_path = broken_up
+    else:
+        component, rest_of_path = broken_up[0], None
+
+    for subnode in node[2:]:
+        if subnode[1] == component and \
+          (subnode[0] == gdal.CXT_Element or subnode[0] == gdal.CXT_Attribute):
+            if rest_of_path is None:
+                if ((attr is None) and (value is None)):
+                    found_list.append(subnode)
+                    if ((maxfind is not None) and (len(found_list) >= maxfind)):
+                        break
+                else:
+                    if XMLFindValue(subnode,attr) == value:
+                        found_list.append(subnode)
+                    if ((maxfind is not None) and (len(found_list) >= maxfind)):
+                        break                        
+            else:
+                if maxfind is None:
+                    submaxfind=maxfind
+                else:
+                    submaxfind=maxfind-len(found_list)
+                    
+                sub_list = XMLFind( subnode, rest_of_path, submaxfind,attr, value )
+                if sub_list is not None:
+                    if submaxfind > 1:
+                        # If maxfind > 1, a list of lists is returned...
+                        found_list.extend(sub_list)
+                    else:
+                        found_list.append(sub_list)
+                        
+    if len(found_list) == 0:
+        return None
+    elif maxfind == 1:
+        return found_list[0]
+    else:
+        return found_list
+
+def XMLInstantiate( node, parent, filename=None ):
+    import gdal
+
+    if len(node) < 2 or node[0] != gdal.CXT_Element:
+        raise AttributeError,'corrupt value passed to XMLInstantiate.'
+        return None
+
+    classname = node[1]
+    module = XMLFindValue( node, 'module', 'gview' )
+
+    try:
+        exec "import " + module
+        exec "func = %s.%sFromXML" % (module, classname)
+        instance = func( node, parent, filename=filename )
+        return instance
+    except:
+        warning( 'Failed to instantiate a %s:%s' % (module, classname) )
+        #raise
+        return None
+
+def XMLSerializeSimpleObjAttributes( obj, attrib_list, xml_list = [] ):
+    """
+    This method is used to serlialize a list of simple object attributes
+    as elements in a gdal compatible "pseudo-xml-list-tree".  Each attribute
+    found on the source attribute will be converted to string type using the
+    str() function, and added to the XML tree as an element with the
+    element name being the attribute name, and the value being the
+    contents of the element.
+
+    This serialization approach (along with XMLDeserializeSimpleObjAttributes
+    is intended to make saving and restoring objects with lots of simple
+    attributes to and from a project file fairly easy.
+
+    obj -- the object instance from which attributes will be extracted.
+    attrib_list -- a list of attribute tuples.  Each tuple contains the
+    attribute name and a function for converting a string into the
+    appropriate type (normally one of str, int or float).
+    xml_list -- the existing tree to which the new elements will be added.
+
+    Returns the modified xml_list.
+
+    Example attribute list:
+
+    attrib_list = [ (filename, str), (xsize, int), (ysize, int) ]
+
+    
+    """ 
+    import gdal
+    
+    for item in attrib_list:
+        if obj.__dict__.has_key( item[0] ):
+            text_value = str(obj.__dict__[item[0]])
+            xml_list.append( [gdal.CXT_Element, item[0],
+                              [gdal.CXT_Text, text_value] ] )
+
+    return xml_list
+
+def XMLDeserializeSimpleObjAttributes( obj, attrib_list, xml_tree ):
+    failures = 0
+    
+    for item in attrib_list:
+        text_value = XMLFindValue( xml_tree, item[0], None )
+        if text_value is not None:
+            try:
+                func = item[1]
+                typed_value = func( text_value )
+                obj.__dict__[item[0]] = typed_value
+            except:
+                failures = failures + 1
+                print 'Failed to decode %s attribute with text value (%s).' \
+                      % ( item[0], text_value )
+        
+    return failures
+
+# XMLPop, XMLInsert, XMLReplaceAttr: tools for manipulating xml files-
+# Might be changed or removed later.
+
+def XMLPop(node,path,maxpop=1,attr=None,value=None,overwrite='n'):
+    # Pop path from node if path has attr=value.
+    # pop up to maxpop instances, where maxpop is
+    # 1 by default.  Set maxpop to None to return all
+    # instances.
+    # Returns (cnode,list of popped nodes), where cnode
+    # is a copy of node with the excess stuff removed
+    # if overwrite is set to 'y', node is altered and returned
+    
+    import gdal
+
+
+    # avoid overwriting the contents of node
+    if overwrite=='n':
+        import copy
+        cnode=copy.deepcopy(node)
+    else:
+        cnode=node
+        
+    broken_up = string.split( path, '.', 1 )
+    popped_list=[]
+    subpopped=[]
+    if len(broken_up) == 2:
+        component, rest_of_path = broken_up
+    else:
+        component, rest_of_path = broken_up[0], None
+
+    if ((maxpop is not None) and (maxpop < 1)):
+        return (cnode,[])
+    
+    indx=1
+    indxlist=[]
+    count=0
+    
+    for subnode in cnode[2:]:
+        indx=indx+1
+        if subnode[1] == component and \
+          (subnode[0] == gdal.CXT_Element or subnode[0] == gdal.CXT_Attribute):
+            if rest_of_path is None:
+                if ((attr is None) and (value is None)):
+                    # Store index for later popping
+                    indxlist.append(indx)
+                    count=count+1
+                    if ((maxpop is not None) and (count >= maxpop)):
+                        break
+                else:
+                    if XMLFindValue(subnode,attr) == value:
+                        indxlist.append(indx)
+                        count=count+1
+                        if ((maxpop is not None) and (count >= maxpop)):
+                            break
+            else:
+                if maxpop is None:
+                    submaxpop=None
+                else:
+                    submaxpop=maxpop-count
+                junk,sub_list = XMLPop(subnode,rest_of_path,submaxpop,attr,value,overwrite='y')
+                if len(sub_list) > 0:
+                    count=count+len(sub_list)
+                    subpopped.extend(sub_list)
+                    
+                if count >= maxpop:
+                    break
+                
+    # pop the top-level values now
+    pcount=0
+    for indx in indxlist:
+        popped_list.append(cnode.pop(indx-pcount))
+        # index should decrease with each pop...
+        pcount=pcount+1
+
+        
+    return (cnode,popped_list)
+
+
+def XMLInsert(node,path,newnode,maxinsert=1,attr=None,value=None,overwrite='n'):
+    # Append newnode to all instances of path found within node
+    # that have attr=value, up to a maximum of maxinsert instances.
+    # Set maxinsert to None to insert in all path instances.
+    # Return the number of items inserted.
+    import gdal
+
+    # avoid overwriting the contents of node
+    if overwrite=='n':
+        import copy
+        cnode=copy.deepcopy(node)
+    else:
+        cnode=node
+     
+    broken_up = string.split( path, '.', 1 )
+    if ((maxinsert is not None) and (maxinsert < 1)):
+        return (cnode,0)
+    
+    insert_num=0
+    if len(broken_up) == 2:
+        component, rest_of_path = broken_up
+    else:
+        component, rest_of_path = broken_up[0], None
+
+    indx=1
+    
+    if path == '' and attr is None:
+        # Insert at top level and return
+        cnode.append(newnode)
+        return (cnode,1)
+    
+    for subnode in cnode[2:]:
+        indx=indx+1
+        if subnode[1] == component and \
+          (subnode[0] == gdal.CXT_Element or subnode[0] == gdal.CXT_Attribute):
+            if rest_of_path is None:
+                if ((attr is None) and (value is None)):
+                    subnode.append(newnode)
+                    insert_num=insert_num+1
+                    if ((maxinsert is not None) and (insert_num >= maxinsert)):
+                        return (cnode,insert_num)
+                else:
+                    if XMLFindValue(subnode,attr) == value:
+                        subnode.append(newnode)
+                        insert_num=insert_num+1
+                        if ((maxinsert is not None) and (insert_num >= maxinsert)):
+                            return (cnode,insert_num) 
+            else:
+                if maxinsert is None:
+                    submaxinsert=None
+                else:
+                    submaxinsert=maxinsert-insert_num
+                junk,subinsert=XMLInsert( subnode, rest_of_path,newnode,submaxinsert,attr,value,overwrite='y' )
+                insert_num=insert_num+subinsert
+                if ((maxinsert is not None) and (insert_num >= maxinsert)):
+                    return (cnode,insert_num)
+    return (cnode,insert_num)
+
+def XMLReplaceAttr( node, path, pathvalue, maxreplace=1, attr=None, value=None, overwrite='n' ):
+    # path should end with the attribute to be replaced.  attr and value, if entered, should be
+    # at the same level as the attribute to be replaced.
+    import gdal
+    import os.path
+    
+    if overwrite == 'n':
+        import copy
+        cnode=copy.deepcopy(node)
+    else:
+        cnode=node
+    replaced=0
+    if ((maxreplace is not None) and (maxreplace < 1)):
+        return (cnode,replaced)
+    
+    if path == '' or path == None:
+        print 'Error- No attribute to replace was entered...'
+        return
+    elif ((attr is None) and (value is None)):
+        tnode = XMLFind( cnode, path, maxreplace)
+        if tnode is None:
+            return (cnode,replaced)
+    else:
+        top_path,replace_attr=os.path.splitext(path)
+        if replace_attr == '':
+            replace_attr = top_path
+            top_path=''
+        else:
+            replace_attr=replace_attr[1:] # Get rid of .
+        inode = XMLFind( cnode, top_path, None,attr, value )
+        if inode is None:
+            return (cnode,replaced)
+        # Of the paths that have attr=value, see which ones also
+        # contain the replace_attr to be replaced.
+        tnode=[]
+        for item in inode:
+            temp=XMLFind( item, replace_attr)
+            if temp is not None:
+                if maxreplace is None:
+                    tnode.append(temp)
+                elif (len(tnode)<maxreplace):
+                    tnode.append(temp)
+
+        if len(tnode) < 1:
+            return (cnode, replaced)
+        
+        if maxreplace == 1:
+            tnode=tnode[0]
+
+    if maxreplace == 1:
+        for child in tnode[2:]:
+            if child[0] == gdal.CXT_Text:
+                child[1] = pathvalue
+                replaced=replaced+1
+                return (cnode,replaced)
+    else:
+        for item in tnode:
+            for child in item[2:]:
+                if child[0] == gdal.CXT_Text:
+                    child[1] = pathvalue
+                    replaced=replaced+1
+                    if ((maxreplace is not None) and (replaced >= maxreplace)):
+                        return (cnode,replaced)
+        
+    return (cnode,replaced)
+
+
+
+#-----------------------------------------------------------------
+# GvDataFilesFrame- function to create data file frame and entries
+#-----------------------------------------------------------------
+class GvDataFilesFrame(_gtk.GtkFrame):
+    def __init__(self,title='',sel_list=('Input','Output'),editable=_gtk.TRUE):
+        _gtk.GtkFrame.__init__(self)
+        self.set_label(title)
+        self.channels=sel_list
+
+        self.show_list = []
+        self.file_dict = {}
+        self.button_dict = {}
+        self.entry_dict = {}
+
+        #  File options
+        file_table = _gtk.GtkTable(len(self.channels),5,_gtk.FALSE)
+        file_table.set_row_spacings(3)
+        file_table.set_col_spacings(3)
+        self.table = file_table
+        self.add(file_table)
+        self.show_list.append(file_table)
+
+        for idx in range(len(self.channels)):
+            ch = self.channels[idx]
+            self.button_dict[ch] = _gtk.GtkButton(ch)
+            self.button_dict[ch].set_usize(100,25)
+            self.show_list.append(self.button_dict[ch])
+            file_table.attach(self.button_dict[ch], 0,1, idx,idx+1)
+            self.entry_dict[ch] = _gtk.GtkEntry()
+            self.entry_dict[ch].set_editable(editable)
+            self.entry_dict[ch].set_usize(400, 25)
+            self.entry_dict[ch].set_text('')
+            self.show_list.append(self.entry_dict[ch])
+            self.set_dsfile('',ch)
+            file_table.attach(self.entry_dict[ch], 1,5, idx,idx+1)
+            if editable == _gtk.TRUE:
+                self.entry_dict[ch].connect('leave-notify-event',self.update_ds)
+                
+
+        for bkey in self.button_dict.keys():
+            self.button_dict[bkey].connect('clicked',self.set_dsfile_cb,bkey)
+
+    def set_border_width(self,width):
+        self.table.set_border_width(width)
+
+    def set_spacings(self, rowspc, colspc):
+        self.table.set_row_spacings(rowspc)
+        self.table.set_col_spacings(colspc)
+
+    def update_ds(self,*args):
+        for ch in self.channels:
+            self.set_dsfile(self.entry_dict[ch].get_text(),ch)
+
+    def show(self,*args):
+        for item in self.show_list:
+            item.show()
+
+
+    def set_dsfile_cb(self,*args):
+        fkey = args[1]
+        file_str = 'Select ' + fkey + ' File'
+        pgufilesel.SimpleFileSelect(self.set_dsfile,
+                                    fkey,
+                                    file_str)
+
+    def set_dsfile(self,fname,fkey):
+        self.file_dict[fkey] = fname
+        
+        # Save selected file directory
+        head = os.path.dirname(fname)
+        if len(head) > 0:
+            if os.access(head,os.R_OK):
+                pgufilesel.simple_file_sel_dir = head+os.sep
+                
+        if self.entry_dict.has_key(fkey):            
+            if self.file_dict[fkey] is None:
+                self.entry_dict[fkey].set_text('')
+            else:
+                self.entry_dict[fkey].set_text(
+                     self.file_dict[fkey])
+
+    def get(self,fkey):
+        if self.file_dict.has_key(fkey):
+            return self.file_dict[fkey]
+        else:
+            return None
+
+#-----------------------------------------------------------------
+# GvEntryFrame- function to create a frame with a table of entries.
+# Input: a list, or list of list, of strings.  initializing with
+# [['e1','e2'],['e3'],['e4','e5']] would create a table like this:
+#
+# e1: <entry>   e2:<entry>
+# e3: <entry>
+# e4: <entry>   e5:<entry>
+#
+# The strings in the list are used to index for returning entry
+# values, and must be unique.
+#
+# If some of the entries must have only certain values, a second
+# list of the same size may be supplied.  This should contain
+# None where entries should be used, but a tuple of strings
+# where an option menu should be used.
+#-----------------------------------------------------------------
+class GvEntryFrame(_gtk.GtkFrame):
+    def __init__(self,title,entry_list,widget_list=None):
+        _gtk.GtkFrame.__init__(self)
+        self.set_label(title)
+        table_rows = len(entry_list)
+        cols = 1
+        for item in entry_list:
+            if type(item) in [type((1,)),type([1])]:
+                cols = max(cols,len(item))
+
+        # Note: the extra one column is because on windows,
+        # creating a gtk table with N columns sometimes only shows
+        # N-1 columns???
+        self.table = _gtk.GtkTable(table_rows,cols*2+1,_gtk.FALSE)
+        self.table.set_col_spacings(3)
+        self.table.set_row_spacings(3)
+        self.add(self.table)
+        self.entries = {}
+        if widget_list is None:
+            ridx=0
+            for item in entry_list:
+                if type(item) in [type((1,)),type([1])]:
+                    cidx=0
+                    for item2 in item:
+                        label = _gtk.GtkLabel(item2)
+                        label.set_alignment(0,0.5)
+                        self.table.attach(label,cidx,cidx+1,ridx,ridx+1)
+                        self.entries[item2] = _gtk.GtkEntry(maxlen=30)
+                        self.entries[item2].set_editable(_gtk.TRUE)
+                        cidx = cidx+1
+                        self.table.attach(self.entries[item2],
+                                          cidx,cidx+1,ridx,ridx+1)
+                        cidx = cidx+1
+                else:
+                    label = _gtk.GtkLabel(item)
+                    label.set_alignment(0,0.5)
+                    self.table.attach(label,0,1,ridx,ridx+1)
+                    self.entries[item] = _gtk.GtkEntry(maxlen=30)
+                    self.entries[item].set_editable(_gtk.TRUE)
+                    self.table.attach(self.entries[item],
+                                      1,2,ridx,ridx+1)
+                ridx=ridx+1
+            
+        else:
+            ridx=0
+            for item in entry_list:
+                wtype=widget_list[ridx]
+                if type(item) in [type((1,)),type([1])]:
+                    cidx=0
+                    widx=0
+                    for item2 in item:
+                        wtype2=wtype[widx]
+                        label = _gtk.GtkLabel(item2)
+                        label.set_alignment(0,0.5)
+                        self.table.attach(label,cidx,cidx+1,ridx,ridx+1)
+                        if wtype2 is None:
+                            self.entries[item2] = _gtk.GtkEntry(maxlen=30)
+                            self.entries[item2].set_editable(_gtk.TRUE)
+                        else:
+                            self.entries[item2] = GvOptionMenu(wtype2)
+                            self.entries[item2].set_history(0)
+                            self.entries[item2].contents = wtype2
+                        cidx = cidx+1
+                        self.table.attach(self.entries[item2],
+                                          cidx,cidx+1,ridx,ridx+1)
+                        cidx = cidx+1
+                        widx = widx+1
+                else:
+                    label = _gtk.GtkLabel(item)
+                    label.set_alignment(0,0.5)
+                    self.table.attach(label,0,1,ridx,ridx+1)
+                    if wtype is None:
+                        self.entries[item] = _gtk.GtkEntry(maxlen=30)
+                        self.entries[item].set_editable(_gtk.TRUE)
+                    else:
+                        self.entries[item] = GvOptionMenu(wtype)
+                        self.entries[item].set_history(0)
+                        self.entries[item].contents = wtype
+                    self.table.attach(self.entries[item],
+                                      1,2,ridx,ridx+1)
+                ridx=ridx+1
+            
+
+    def get(self,fkey):
+        if self.entries.has_key(fkey):
+            if hasattr(self.entries[fkey],'get_text'):
+                return self.entries[fkey].get_text()
+            else:
+                hist=self.entries[fkey].get_history()
+                return self.entries[fkey].contents[hist]   
+        else:
+            return None
+
+    def set_default_values(self,default_dict):
+        """Set default entry values.  Input is default_dict,
+           a dictionary with keys corresponding to entries
+           (strings) and values corresponding to default
+           text or menu setting (also strings).
+        """
+        for ckey in default_dict.keys():
+            cval = default_dict[ckey]
+            if self.entries.has_key(ckey):
+                if hasattr(self.entries[ckey],'set_text'):
+                    self.entries[ckey].set_text(cval)
+                else:
+                    useidx=None
+                    for idx in range(len(self.entries[ckey].contents)):
+                        if self.entries[ckey].contents[idx] == cval:
+                            useidx=idx
+                    if useidx is not None:
+                        self.entries[ckey].set_history(useidx)
+                    else:
+                        print cval+' not a valid entry for '+ckey
+                    
+            else:
+                print 'No entry '+ckey+'- skipping'
+
+    def set_default_lengths(self,default_dict):
+        """ Set the maximum entry lengths for non-menu entries.
+            Input is defalut_dict, a dictionary with keys
+            corresponding to entries (strings) and values
+            corresponding to entry lengths (integers)
+        """
+        for ckey in default_dict.keys():
+            cval = default_dict[ckey]
+            if self.entries.has_key(ckey):
+                if hasattr(self.entries[ckey],'set_text'):
+                    self.entries[ckey].set_max_length(cval)
+                else:
+                    print 'Length cannot be set for a menu ('+ckey+')'
+                    
+            else:
+                print 'No entry '+ckey+'- skipping'
+
+    def set_border_width(self,width):
+        self.table.set_border_width(width)
+
+    def set_spacings(self, rowspc, colspc):
+        self.table.set_row_spacings(rowspc)
+        self.table.set_col_spacings(colspc)
+
+                
+if __name__ == '__main__':
+    dialog = _gtk.GtkWindow()
+
+    om = GvOptionMenu( ('Option 1', 'Option 2') )
+    om.show()
+    dialog.add( om )
+    
+    dialog.connect('delete-event', _gtk.mainquit)
+    dialog.show()
+
+    _gtk.mainloop()

Added: packages/openev/branches/upstream/current/pymod/gvvectorpropdlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvvectorpropdlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvvectorpropdlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,602 @@
+###############################################################################
+# $Id: gvvectorpropdlg.py,v 1.23 2003/02/14 23:03:35 pgs Exp $
+#
+# Project:  OpenEV
+# Purpose:  GvShapesLayer Properties Dialog
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: gvvectorpropdlg.py,v $
+#  Revision 1.23  2003/02/14 23:03:35  pgs
+#  fixed typo in area_edge_width signal callback
+#
+#  Revision 1.21  2002/03/21 15:48:51  warmerda
+#  added support for geometry specific _ogr_ogrfs_... layer properties
+#
+#  Revision 1.20  2001/04/16 17:06:49  warmerda
+#  paul added symbol support to vector prop dialog
+#
+#  Revision 1.19  2001/04/03 02:29:30  warmerda
+#  improved some pane geometry
+#
+#  Revision 1.18  2001/03/26 19:25:42  warmerda
+#  fixed typo with font list
+#
+#  Revision 1.17  2001/03/21 22:41:27  warmerda
+#  added labels support under drawing style
+#
+#  Revision 1.16  2000/08/10 15:59:29  warmerda
+#  added help topic
+#
+#  Revision 1.15  2000/08/08 20:12:21  warmerda
+#  close on layer teardown
+#
+#  Revision 1.14  2000/07/21 01:31:12  warmerda
+#  added read_only flag for GvData, and utilize for vector layers
+#
+#  Revision 1.13  2000/07/20 02:45:04  warmerda
+#  avoid doing gui updates in response to signals if dialog destroyed
+#
+#  Revision 1.12  2000/06/14 15:13:21  warmerda
+#  updated pgu stuff
+#
+#  Revision 1.11  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+from gtk import *
+from string import *
+import gvutils
+import pgucolorsel
+
+from gvconst import *
+import gview
+import gvhtml
+import gvogrfs
+import pgutogglebutton
+
+prop_dialog_list = []
+
+def LaunchVectorPropDialog(layer):
+    # Check list to see if dialog exists - make it visible
+    for test_dialog in prop_dialog_list:
+        if test_dialog.layer._o == layer._o:
+            test_dialog.update_gui()
+            test_dialog.show()
+            test_dialog.get_window()._raise()
+            return test_dialog
+
+    # Create new dialog if one doesn't exist already
+    new_dialog = GvVectorPropDialog(layer)
+    prop_dialog_list.append( new_dialog )
+    return new_dialog
+
+symbols = [ 'cross', 'x',
+             'unfilled circle', 'filled circle',
+             'unfilled square', 'filled square',
+             'unfilled triangle', 'filled triangle',
+             'unfilled star', 'filled star',
+             'vertical bar' ]
+
+class GvVectorPropDialog(GtkWindow):
+
+    def __init__(self, layer):
+        GtkWindow.__init__(self)
+        self.set_title(layer.get_name()+' Properties')
+        self.layer = layer
+        self.updating = FALSE
+
+        if self.layer is not None:
+            self.display_change_id = layer.connect('display-change',
+                                                   self.refresh_cb)
+            self.teardown_id = layer.connect('teardown',self.close)
+
+        # create the general layer properties dialog
+        self.create_notebook()
+        self.create_pane1()
+
+        # Setup Object Drawing Properties Tab
+        self.pane2 = GtkVBox(spacing=10)
+        self.pane2.set_border_width(10)
+        self.notebook.append_page( self.pane2, GtkLabel('Draw Styles'))
+
+        gvhtml.set_help_topic( self, "gvvectorpropdlg.html" )
+
+
+        # ANTIALIASING
+        box = GtkHBox(spacing=3)
+        self.pane2.pack_start(box)
+        box.pack_start( GtkLabel("Anti-alias:"), expand=FALSE )
+        self.antialias = pgutogglebutton.pguToggleButton()
+        self.antialias.connect('toggled', self.antialias_cb )
+        box.pack_start( self.antialias, expand=FALSE )
+
+        # POINT CONTROLS -----------------------------------------------------
+        frame = GtkFrame('Points')
+        self.pane2.pack_start(frame)
+
+        vbox = GtkVBox(spacing=10)
+        vbox.set_border_width(10)
+        frame.add(vbox)
+
+        #create a symbol control
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Symbol:'), expand=FALSE)
+        self.point_symbol = GtkCombo()
+        #add a default to symbol names
+        self.point_symbol.set_popdown_strings( tuple(symbols) )
+        self.point_symbol.entry.set_editable(FALSE)
+        #self.point_symbol.set_value_in_list(TRUE, FALSE)
+        self.point_symbol.entry.connect('changed', self.symbol_cb)
+        box.pack_start(self.point_symbol)
+
+        # Create Color control.
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Color:'),expand=FALSE)
+        self.point_color = pgucolorsel.ColorControl('Point Color',
+                                                  self.symbol_cb)
+        box.pack_start(self.point_color)
+
+        # Point size
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Point Size:'),expand=FALSE)
+        self.point_size = GtkCombo()
+        self.point_size.set_popdown_strings(
+            ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '15', '20') )
+        self.point_size.entry.connect('changed', self.point_size_cb)
+        box.pack_start(self.point_size,expand=FALSE)
+
+        # LINE CONTROLS ------------------------------------------------------
+        frame = GtkFrame('Lines')
+        self.pane2.pack_start(frame)
+
+        vbox = GtkVBox()
+        vbox.set_border_width(10)
+        frame.add(vbox)
+
+        # Create Color control.
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Color:'),expand=FALSE)
+        self.line_color = pgucolorsel.ColorControl('Line Color',
+                                         self.color_cb,'_line_color')
+        box.pack_start(self.line_color)
+        
+        #Line Width
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Width:'), expand=FALSE)
+        self.line_width = GtkEntry()
+        self.line_width.connect('changed', self.line_width_cb)
+        box.pack_start( self.line_width, expand=FALSE)
+        
+
+        # AREA CONTROLS ------------------------------------------------------
+        frame = GtkFrame('Areas')
+        self.pane2.pack_start(frame)
+
+        vbox = GtkVBox(spacing=10)
+        vbox.set_border_width(10)
+        frame.add(vbox)
+
+        # Create Color controls
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Edge Color:'),expand=FALSE)
+        self.area_edge_color = pgucolorsel.ColorControl('Area Edge Color',
+                                              self.color_cb,'_area_edge_color')
+        box.pack_start(self.area_edge_color)
+
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Fill Color:'),expand=FALSE)
+        self.area_fill_color = pgucolorsel.ColorControl('Area Fill Color',
+                                              self.color_cb,'_area_fill_color')
+        box.pack_start(self.area_fill_color)
+
+        #Area Edge Width
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Edge Width:'), expand=FALSE)
+        self.area_edge_width = GtkEntry()
+        self.area_edge_width.connect('changed', self.area_edge_width_cb)
+        box.pack_start( self.area_edge_width, expand=FALSE)
+        
+
+        # LABEL CONTROLS -----------------------------------------------------
+        frame = GtkFrame('Labels')
+        self.pane2.pack_start(frame)
+
+        vbox = GtkVBox(spacing=10)
+        vbox.set_border_width(10)
+        frame.add(vbox)
+
+        # collect candidate field names from the schema.
+        fnlist = [ 'disabled' ]
+        schema = self.layer.get_parent().get_schema()
+        for item in schema:
+            fnlist.append( item[0] )
+
+        # Field Name
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Label Field:'),expand=FALSE)
+        self.label_field = GtkCombo()
+        self.label_field.set_popdown_strings( fnlist )
+        self.label_field.entry.connect('changed', self.label_change_cb)
+        box.pack_start(self.label_field,expand=FALSE)
+
+        # Create Color control.
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Color:'),expand=FALSE)
+        self.label_color = pgucolorsel.ColorControl('Label Color',
+                                                    self.label_change_cb)
+        box.pack_start(self.label_color)
+
+        # Font
+        font_list = self.layer.get_view().get_fontnames()
+        box = GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=FALSE)
+        box.pack_start(GtkLabel('Font:'),expand=FALSE)
+        self.label_font = GtkCombo()
+        self.label_font.set_popdown_strings(font_list)
+        self.label_font.entry.connect('changed', self.label_change_cb)
+        box.pack_start(self.label_font,expand=FALSE)
+
+        self.update_gui()
+
+        self.show_all()
+
+    def create_notebook(self):
+        self.set_border_width(3)
+        self.notebook = GtkNotebook()
+        self.add( self.notebook )
+        self.connect('delete-event', self.close)
+
+    def create_pane1(self):
+        # Setup General Properties Tab
+        self.pane1 = GtkVBox(spacing=10)
+        self.pane1.set_border_width(10)
+        self.notebook.append_page( self.pane1, GtkLabel('General'))
+
+        # Setup layer name entry box.
+        box = GtkHBox(spacing=5)
+        self.pane1.pack_start(box, expand=FALSE)
+        label = GtkLabel('Layer:' )
+        box.pack_start(label,expand=FALSE)
+        self.layer_name = GtkEntry()
+        self.layer_name.connect('changed', self.name_cb)
+        box.pack_start(self.layer_name)
+
+        # Setup Visibility radio buttons.
+        vis_box = GtkHBox(spacing=5)
+        self.pane1.pack_start(vis_box, expand=FALSE)
+        vis_box.pack_start(GtkLabel('Visibility:'),expand=FALSE)
+        self.vis_yes = GtkRadioButton(label='yes')
+        self.vis_yes.connect('toggled', self.visibility_cb)
+        vis_box.pack_start(self.vis_yes,expand=FALSE)
+        self.vis_no = GtkRadioButton(label='no',group=self.vis_yes)
+        self.vis_no.connect('toggled', self.visibility_cb)
+        vis_box.pack_start(self.vis_no,expand=FALSE)
+
+        # Setup Editability radio buttons.
+        edit_box = GtkHBox(spacing=5)
+        self.pane1.pack_start(edit_box, expand=FALSE)
+        edit_box.pack_start(GtkLabel('Editable:'),expand=FALSE)
+        self.edit_yes = GtkRadioButton(label='yes')
+        self.edit_yes.connect('toggled', self.edit_cb)
+        edit_box.pack_start(self.edit_yes,expand=FALSE)
+        self.edit_no = GtkRadioButton(label='no',group=self.edit_yes)
+        self.edit_no.connect('toggled', self.edit_cb)
+        edit_box.pack_start(self.edit_no,expand=FALSE)
+
+    # Initialize GUI state from underlying object state.
+    def update_gui(self):
+
+        if self.flags( DESTROYED ) > 0:
+            return
+
+        if self.layer is None or self.updating == TRUE:
+            return
+
+        self.updating = TRUE
+
+        # Layer name.
+        self.layer_name.set_text( self.layer.get_name() )
+
+        # Visibility radio buttons
+        self.vis_yes.set_active( self.layer.is_visible() )
+        self.vis_no.set_active( not self.layer.is_visible() )
+
+        # Editability radio buttons
+        self.edit_yes.set_active( not self.layer.is_read_only() )
+        self.edit_no.set_active( self.layer.is_read_only() )
+
+        # colors
+        self.set_color_or_default('_point_color', self.point_color)
+        self.set_color_or_default('_line_color', self.line_color)
+        self.set_color_or_default('_area_edge_color', self.area_edge_color)
+        self.set_color_or_default('_area_fill_color', self.area_fill_color)
+
+        # point size
+        self.point_size.entry.delete_text(0,-1)
+        if self.layer.get_property('_point_size') is None:
+            self.point_size.entry.insert_text('6')
+        else:
+            self.point_size.entry.insert_text(
+                self.layer.get_property('_point_size'))
+                
+        #line and area edge width
+        self.line_width.delete_text( 0, -1 )
+        if self.layer.get_property('_line_width') is None:
+            self.line_width.insert_text('1.0')
+        else:
+            self.line_width.insert_text(
+                 self.layer.get_property( '_line_width' ))
+
+        self.area_edge_width.delete_text( 0, -1 )
+        if self.layer.get_property('_area_edge_width') is None:
+            self.area_edge_width.insert_text('1.0')
+        else:
+            self.area_edge_width.insert_text(
+                 self.layer.get_property( '_area_edge_width' ))
+                 
+        # antialiasing
+        if self.layer.get_property('_gl_antialias') is None:
+            self.antialias.set_active( FALSE )
+        elif self.layer.get_property('_gl_antialias') == "0":
+            self.antialias.set_active( FALSE )
+        else:
+            self.antialias.set_active( TRUE )
+
+        # font and symbol information
+
+        self.label_font.entry.delete_text(0,-1)
+        self.label_field.entry.delete_text(0,-1)
+
+        ogrfs = self.layer.get_property('_gv_ogrfs_point')
+        ogrfs_obj = gvogrfs.OGRFeatureStyle()
+        ogrfs_label = None
+        try:
+            ogrfs_obj.parse( ogrfs )
+        except:
+            print 'update_gui: error parsing ogrfs:'
+            print ogrfs
+
+        ogrfs_label = ogrfs_obj.get_part('LABEL')
+        if ogrfs_label is None:
+            self.label_font.entry.insert_text('Fixed')
+            self.label_field.entry.insert_text('disabled')
+            self.label_color.set_color( (0.5, 1.0, 0.5, 1.0) )
+        else:
+            self.label_font.entry.insert_text(ogrfs_label.parms['f'].value)
+            self.label_field.entry.insert_text(ogrfs_label.parms['t'].value)
+            ogr_color = ogrfs_label.parms['c'].value
+            gv_color = gvogrfs.ogr_to_gv_color(ogr_color)
+            self.label_color.set_color(gv_color)
+
+        ogrfs_symbol = ogrfs_obj.get_part('SYMBOL')
+        if ogrfs_symbol is None:
+            self.point_symbol.entry.set_text('cross')
+        else:
+            ogr_sym = ogrfs_symbol.parms['id'].value
+            try:
+                sym_num = int(ogr_sym[8:9])
+                sym_name = symbols[sym_num]
+            except:
+                sym_name = 'cross'
+            self.point_symbol.entry.set_text( sym_name )
+
+        self.updating = FALSE
+
+    def name_cb(self, *args):
+        if self.layer_name.get_text() != self.layer.get_name():
+            self.layer.set_name( self.layer_name.get_text() )
+
+    # Set color from property, or use default color.
+    def set_color_or_default(self, property_name, widget):
+        if self.layer.get_property( property_name ) is None:
+            widget.set_color( (0.5, 1.0, 0.5, 1.0) )
+        else:
+            widget.set_color_from_string(
+                self.layer.get_property( property_name ))
+
+    # Color of a feature type changed
+    def color_cb( self, color, type ):
+        if self.layer is None:
+            print 'set ' + type + ' to ', color
+            return
+
+        prop = str(color[0]) + ' ' + str(color[1]) + ' ' \
+               + str(color[2]) + ' ' + str(color[3])
+
+        old_prop = self.layer.get_property( type )
+        if old_prop is None or old_prop != prop:
+            self.layer.set_property( type, prop )
+            self.layer.display_change()
+
+    # Handle updates to the point size.
+    def point_size_cb(self, args):
+        if self.layer is None:
+            return
+
+        new_text = self.point_size.entry.get_chars(0,-1)
+        if len(new_text) == 0:
+            return
+
+        if self.layer.get_property('_point_size') is not None \
+           and self.layer.get_property('_point_size') == new_text:
+            return
+
+        self.layer.set_property( '_point_size', new_text)
+        self.layer.display_change()
+        
+    #Handle changes to the line width
+    def line_width_cb( self, *args ):
+        if self.layer is None:
+            return
+            
+        new_text = self.line_width.get_chars(0, -1)
+        if (len(new_text)) == 0:
+            return
+            
+        if self.layer.get_property('_line_width') is not None \
+           and self.layer.get_property('_line_width') == new_text:
+            return
+        
+        self.layer.set_property('_line_width', new_text)
+        self.layer.display_change()
+        
+    #Handle changes to the area edge width
+    def area_edge_width_cb( self, *args ):
+        if self.layer is None:
+            return
+            
+        new_text = self.area_edge_width.get_chars(0, -1)
+        if (len(new_text)) == 0:
+            return
+            
+        if self.layer.get_property('_area_edge_width') is not None \
+           and self.layer.get_property('_area_edge_width') == new_text:
+            return
+        
+        self.layer.set_property('_area_edge_width', new_text)
+        self.layer.display_change()
+        
+    # Handle changes to antialiasing
+    def antialias_cb( self, *args ):
+        if self.layer is None:
+            return
+            
+        antialias = self.layer.get_property( "_gl_antialias" )
+        if antialias is None:
+            antialias = "0"
+        elif antialias != "0":
+            antialias = "1"
+            
+        if self.antialias.get_active() and antialias != "1":
+            self.layer.set_property( "_gl_antialias", "1" )
+            self.layer.display_change()
+        elif antialias != "0":
+            self.layer.set_property( "_gl_antialias", "0" )
+            self.layer.display_change()
+            
+
+    # Handle updates to the label font.
+    def label_change_cb(self, *args):
+        if self.layer is None or self.updating:
+            return
+
+        font = self.label_font.entry.get_chars(0,-1)
+        field_name = self.label_field.entry.get_chars(0,-1)
+
+        color = self.label_color.current_color
+        color = gvogrfs.gv_to_ogr_color( color )
+
+        ogrfs = self.layer.get_property('_gv_ogrfs_point')
+        ogrfs_obj = gvogrfs.OGRFeatureStyle()
+        try:
+            ogrfs_obj.parse( ogrfs )
+        except:
+            print 'an error occurred parsing the ogrfs property:\n', ogrfs
+
+        #remove the old label
+        ogrfs_obj.remove_part('LABEL')
+
+        if field_name != 'disabled' and len(field_name) != 0:
+            ogrfs_label = gvogrfs.OGRFeatureStylePart()
+            ogrfs_label.parse(
+                 'LABEL(t:{%s},f:"%s",c:%s)' % (field_name, font, color) )
+            ogrfs_obj.add_part( ogrfs_label )
+
+        self.layer.set_property( '_gv_ogrfs_point', ogrfs_obj.unparse() )
+        self.layer.display_change()
+
+    # Visibility changing
+    def visibility_cb( self, widget ):
+        self.layer.set_visible( self.vis_yes.active )
+
+    # Visibility changing
+    def edit_cb( self, widget ):
+        self.layer.set_read_only( self.edit_no.active )
+
+    #symbol changing
+    def symbol_cb( self, widget, *args ):
+        """
+        update the symbol.  This might have been called because the color
+        changed also, so update the _point_color property too.
+        """
+
+        if self.layer is None or self.updating:
+            return
+
+        symbol = self.point_symbol.entry.get_text()
+
+        ogrfs = self.layer.get_property('_gv_ogrfs_point')
+        ogrfs_obj = gvogrfs.OGRFeatureStyle()
+        try:
+            ogrfs_obj.parse( ogrfs )
+        except:
+            print 'an error occurred parsing the ogrfs property:\n', ogrfs
+
+        #remove the old symbol
+        ogrfs_obj.remove_part('SYMBOL')
+
+        point_sym_text = '"ogr-sym-%s"' % symbols.index(symbol)
+        color = self.point_color.current_color
+        #should this only be done on point layers?
+        point_ogr_color = gvogrfs.gv_to_ogr_color(color)
+        point_size = self.point_size.entry.get_text()
+        ogr_part = 'SYMBOL(c:' + point_ogr_color + ',id:' + \
+            point_sym_text + ')'
+        ogrfs_sym = gvogrfs.OGRFeatureStylePart()
+        ogrfs_sym.parse( ogr_part )
+        ogrfs_obj.add_part( ogrfs_sym )
+
+        self.layer.set_property('_gv_ogrfs_point', ogrfs_obj.unparse())
+
+        prop = str(color[0]) + ' ' + str(color[1]) + \
+              ' ' + str(color[2]) + ' ' + str(color[3])
+        self.layer.set_property( '_point_color', prop )
+        self.layer.display_change()
+
+    # Dialog closed, remove references to python object
+    def close( self, widget, args ):
+        self.layer.disconnect(self.teardown_id)
+        self.layer.disconnect(self.display_change_id)
+        prop_dialog_list.remove(self)
+        self.layer = None
+        self.destroy()
+
+    # Force GUI Refresh
+    def refresh_cb( self, widget, args ):
+        self.update_gui()
+
+if __name__ == '__main__':
+    dialog = GvVectorPropDialog(None)
+    dialog.connect('delete-event', mainquit)
+
+    mainloop()
+

Added: packages/openev/branches/upstream/current/pymod/gvviewwindow.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/gvviewwindow.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/gvviewwindow.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1726 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: gvviewwindow.py,v 1.96 2005/10/13 21:21:51 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  OpenEV General Purpose GvViewWindow class.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvviewwindow.py,v $
+#  Revision 1.96  2005/10/13 21:21:51  gmwalter
+#  Update for 3D RGB drapes.
+#
+#  Revision 1.95  2005/08/07 18:23:19  warmerda
+#  moved about box into oe_about.py
+#
+#  Revision 1.94  2005/06/28 21:40:17  gmwalter
+#  Avoid view title conflicts (saved projects)
+#
+#  Revision 1.93  2005/06/28 18:14:25  gmwalter
+#  Update Atlantis->Vexcel Canada; fix sizing for no scrollbar case
+#
+#  Revision 1.92  2005/02/10 21:14:53  warmerda
+#  Suppress terminal reporting of gdal errors while test-opening a file
+#  as a raster.  Otherwise we get a bunch of error noise from any OGR
+#  files.
+#
+#  Revision 1.91  2005/01/14 17:18:34  warmerda
+#  save and restore menufile and iconfile
+#
+#  Revision 1.90  2004/11/03 20:38:13  warmerda
+#  updated to version 1.8 in about box
+#
+#  Revision 1.89  2004/10/07 20:14:03  warmerda
+#  added support to treat greyscale+alpha as RGBA
+#
+#  Revision 1.88  2004/07/02 16:40:51  dem
+#  - Implement project files portability
+#  - last_strech restored in projects reloading
+#  - add a "File/Save Project as..." menu
+#
+#  Revision 1.87  2004/06/23 14:35:17  gmwalter
+#  Added support for multi-band complex imagery.
+#
+#  Revision 1.86  2004/04/21 14:35:27  andrey_kiselev
+#  New option: save last visited directory.
+#
+#  Revision 1.85  2004/04/05 05:32:25  warmerda
+#  Updated about box version to 1.7.
+#
+#  Revision 1.84  2004/02/23 16:09:54  warmerda
+#  For some reason gview.manager.get_dataset() was return None for the
+#  dataset, and 0 for GetLastErrorNo().  I changed to treat 0 as a non-fatal
+#  error in file_open_by_name().  I'm not sure if this indicates a wider
+#  problem.
+#
+#  Revision 1.83  2004/02/10 15:47:56  andrey_kiselev
+#  Added open_gdal_dataset method.
+#
+#  Revision 1.82  2004/01/23 15:08:30  gmwalter
+#  Add a few hooks for other applications to
+#  use to alter closing behaviour of windows.
+#
+#  Revision 1.81  2003/12/28 15:56:07  warmerda
+#  removed unneeded openev import
+#
+#  Revision 1.80  2003/09/30 16:40:50  gmwalter
+#  Avoid errors when zoom icon is not present.
+#
+#  Revision 1.79  2003/07/28 19:42:34  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.78  2003/06/25 17:08:58  warmerda
+#  added rotate tool
+#
+#  Revision 1.77  2003/04/15 15:03:25  gmwalter
+#  Update Atlantis web address in Help/About dialog.
+#
+#  Revision 1.76  2003/03/13 19:52:01  gmwalter
+#  Make sure pop-up dialog appears rather than command line error message when
+#  a file fails to open.
+#
+#  Revision 1.75  2003/03/07 19:56:19  gmwalter
+#  Fix goto dialog for raw layer case with row/col coordinates.
+#
+
+import gtk
+from gtk import FALSE, TRUE
+from GDK import *
+import gtkmissing
+import sys
+# import GtkExtra
+
+import gview
+import gvconst
+import layerdlg
+import gdal
+from gdalconst import *
+import gvutils
+import os
+import pgufilesel
+import pguprogress
+import math
+import gvhtml
+import string
+import glob
+import gviewapp
+
+ratio_list = ['250:1', '200:1', '150:1', '100:1', '80:1', '60:1', '45:1',
+              '35:1', '25:1', '18:1', '10:1', '8:1', '4:1', '2:1', '1:1',
+              '1:2', '1:4', '1:6', '1:8', '1:10', '1:12', '1:14','1:16',
+              '1:20', '1:25', '1:30', '1:40', '1:60', '1:80', '1:100',]
+
+def GvViewWindowFromXML( node, parent, filename=None ):
+    import gdal
+    instance = \
+      GvViewWindow( app = parent, 
+        title = gvutils.XMLFindValue( node, "title", '' ),
+        show_menu = int(gvutils.XMLFindValue( node, "show_menu", "1")),
+        show_icons = int(gvutils.XMLFindValue( node, "show_icons", "1")),
+        show_tracker = int(gvutils.XMLFindValue( node, "show_tracker", "1")),
+        show_scrollbars = int(gvutils.XMLFindValue( node, "show_scrollbars", "1")),
+        menufile = gvutils.XMLFindValue( node, 'menufile', None),
+        iconfile = gvutils.XMLFindValue( node, 'iconfile', None))
+
+    if gvutils.XMLFindValue( node, "width", None ) is not None and \
+       gvutils.XMLFindValue( node, "height", None ) is not None:
+        instance.set_default_size(
+            int(gvutils.XMLFindValue( node, "width", "600")),
+            int(gvutils.XMLFindValue( node, "height", "600")) )
+
+    viewarea_tree = gvutils.XMLFind( node, "GvViewArea" )   
+    if viewarea_tree != None:
+        instance.viewarea.initialize_from_xml( viewarea_tree,
+                                               filename=filename )
+
+    # Avoid multiple views with the same title when user opens
+    # new views after opening a project- same title causes
+    # problems in view management.  Need to consider possibitity
+    # that user may have shut several views before saving the
+    # project, so next_viewnum may need to be > # views.
+    GvViewWindow.next_viewnum = GvViewWindow.next_viewnum + 1
+    title = gvutils.XMLFindValue( node, "title", '')
+    if len(title) > 5:
+        try:
+            vn = int(title[5:])
+            GvViewWindow.next_viewnum = max( GvViewWindow.next_viewnum,
+                                             vn + 1 )
+        except:
+            pass
+        
+    instance.show()
+
+    # We can't move the window till after it is shown.
+    if gvutils.XMLFindValue( node, "x", None ) is not None and \
+       gvutils.XMLFindValue( node, "y", None ) is not None:
+        instance.window_move(
+            int(gvutils.XMLFindValue( node, "x", "0")),
+            int(gvutils.XMLFindValue( node, "y", "0")) )
+
+    return instance
+
+class GvViewWindow(gtk.GtkWindow):
+    next_viewnum = 1
+
+    def __init__(self, app=None, title=None, show_menu=1, show_icons=1, 
+                 show_tracker=1, show_scrollbars=1, menufile='DefaultMenuFile.xml',iconfile='DefaultIconFile.xml'):
+        
+        gtk.GtkWindow.__init__(self)
+
+        if title is None:
+            title = 'View %d' % GvViewWindow.next_viewnum
+            GvViewWindow.next_viewnum = GvViewWindow.next_viewnum + 1
+
+        self.app = app
+        self.set_title('OpenEV: '+title)
+        gvhtml.set_help_topic(self, 'mainwindow.html')
+        self.title = title
+        self.file_sel = None
+        self.drape_file_sel = None
+        self.DEM_file_sel = None
+        shell = gtk.GtkVBox(spacing=0)
+        self.add(shell)
+        self.pref_dialog = None
+        self.position3D_dialog = None
+        self.set_policy(TRUE,TRUE,FALSE)
+        self.zoom = 0.0
+        self.zoom_flag = 'yes' # see set_zoom_factor_cb()
+        self.zoom_factor = None
+        self.position3D_dialog = None
+        self.menufile = menufile
+        self.iconfile = iconfile
+
+        # Menu bar
+        if show_menu > 0:
+            self.create_menubar(menufile)
+            shell.pack_start(self.menuf, expand=FALSE)
+        else:
+            self.menuf = None
+
+
+        if show_icons > 0:
+            self.create_iconbar(iconfile)
+            shell.pack_start(self.iconbar,expand=FALSE)
+        else:
+            self.iconbar = None        
+
+        # Add the actual GvViewArea for drawing in
+        self.viewarea = gview.GvViewArea()
+
+        if gview.get_preference('view_background_color') is not None:
+            tokens = string.split(gview.get_preference('view_background_color'))
+            self.viewarea.set_background_color( [ float(tokens[0]),
+                                                  float(tokens[1]),
+                                                  float(tokens[2]),
+                                                  float(tokens[3])] )
+
+        # Update Zoom ratio box in toolbar whenever view changes
+	# (actually, only when zoom changes)
+        self.view_state_changed_id = \
+	    self.viewarea.connect("view-state-changed", self.update_zoom_cb)
+        self.viewarea.connect("active-changed", self.update_zoom_cb)
+        
+	size = (620, 620)
+	if show_scrollbars:
+            self.scrolled_window = gtk.GtkScrolledWindow()
+            self.set_usize(size[0], size[1] + 60)
+            self.scrolled_window.add(self.viewarea)
+            shell.pack_start(self.scrolled_window, expand=TRUE)
+        else:
+            self.viewarea.size(size[0],size[1])
+            self.scrolled_window = None
+            shell.pack_start(self.viewarea, expand=TRUE)
+
+        if show_tracker:
+            statusbar = gtk.GtkHBox()
+            shell.pack_start(statusbar, expand=FALSE)
+            label = gtk.GtkLabel()
+            statusbar.pack_start(label, expand=FALSE, padding=3)
+            tracker = gview.GvTrackTool(label)
+            tracker.activate(self.viewarea)
+            self.tracker = tracker
+        else:
+            self.tracker = None
+
+        # End of widgets
+        self.viewarea.grab_focus()
+        shell.show_all()
+
+        self.show_rfl()
+
+        self.app.view_manager.add_view( self )
+
+        self.rawgeo_update()
+        
+        self.viewarea.connect('key-press-event', self.key_press_cb)
+
+        # The close flag is meant to be used
+        # by other applications that use OpenEV's windows,
+        # but may want other closing behaviour for that
+        # particular window (eg. user
+        # unable to close, hiding instead of destroying,
+        # etc.).  0 is for default, 1 for not doing anything,
+        # 2 for hiding.
+        self.close_flag=0
+        
+        # Trap window close event
+        self.connect('delete-event', self.close)
+       
+
+    def serialize(self,base=None, filename=None ):
+        if base is None:
+            base = [gdal.CXT_Element, 'GvViewWindow']
+            base.append( [gdal.CXT_Attribute, 'module',
+                         [gdal.CXT_Text, 'gvviewwindow']] )
+
+        if self.menuf is None:
+            base.append( [gdal.CXT_Attribute, 'show_menu',
+                         [gdal.CXT_Text, '0']] )
+        if self.iconbar is None:
+            base.append( [gdal.CXT_Attribute, 'show_icons',
+                         [gdal.CXT_Text, '0']] )
+        if self.tracker is None:
+            base.append( [gdal.CXT_Attribute, 'show_tracker',
+                         [gdal.CXT_Text, '0']] )
+        if self.scrolled_window is None:
+            base.append( [gdal.CXT_Attribute, 'show_scrollbars',
+                         [gdal.CXT_Text, '0']] )
+
+        geometry = self.get_allocation()
+        
+        base.append( [gdal.CXT_Attribute, 'width',
+                      [gdal.CXT_Text, str(geometry[2])]] )
+        base.append( [gdal.CXT_Attribute, 'height',
+                      [gdal.CXT_Text, str(geometry[3])]] )
+
+        x_pos, y_pos = self.get_position()
+        base.append( [gdal.CXT_Attribute, 'x',
+                      [gdal.CXT_Text, str(x_pos)]] )
+        base.append( [gdal.CXT_Attribute, 'y',
+                      [gdal.CXT_Text, str(y_pos)]] )
+
+        base.append( [gdal.CXT_Element, 'title',
+                      [gdal.CXT_Text, self.title]] )
+
+        if self.menufile is not None:
+            base.append( [gdal.CXT_Element, 'menufile',
+                      [gdal.CXT_Text, self.menufile]] )
+
+        if self.iconfile is not None:
+            base.append( [gdal.CXT_Element, 'iconfile',
+                      [gdal.CXT_Text, self.iconfile]] )
+
+        base.append( self.viewarea.serialize( filename=filename ) )
+
+        return base
+
+    def show_rfl(self, *args):
+        if self.menuf is None:
+            return
+
+        if ((self.app is None) or (hasattr(self.app,'get_rfl') == 0)):
+            return
+
+        try:
+            list = self.app.get_rfl()
+            for i in range(5):
+                menuitem = self.menuf.find('File/rfl'+str(i+1))
+                if i < len(list):
+                    menuitem.children()[0].set_text(list[i])
+                    menuitem.show()
+                else:
+                    menuitem.children()[0].set_text('')
+                    menuitem.hide()
+        except:
+            # Some menus don't have rfl
+            pass
+
+    def rfl_cb(self, menuitem, rfl_index, *args):
+        self.file_open_by_name(menuitem.children()[0].get(), sds_check=0)
+    
+    def make_active(self, *args):
+        self.app.view_manager.set_active_view( self )
+
+    def key_press_cb( self, viewarea, event, *args ):
+        if event.keyval == F9:
+            gview.texture_cache_dump()
+
+    def busy_changed_cb(self,*args):
+        if gview.manager.get_busy():
+            self.idlebusy_pixmap.set( self.busy_icon[0], self.busy_icon[1] )
+        else:
+            self.idlebusy_pixmap.set( self.idle_icon[0], self.idle_icon[1] )
+
+    def print_cb(self, *args):
+        import gvprint
+        pd = gvprint.GvPrintDialog( self.viewarea )
+        
+    def helpcb(self, item, topic='openevmain.html'):
+        gvhtml.LaunchHTML( topic )
+
+    def aboutcb(self, *args):
+        import oe_about
+
+        oe_about.ShowAboutBox( self )
+
+    def set_close_function(self,ctype=0):
+        """ Set the close behaviour for the view:
+            Input parameters:
+                ctype- 0 for default behaviour (close view,
+                      exit when last one is shut); 1
+                      so that window can't be closed
+                      at all; 2 so that window will be
+                      hidden rather than closed.  Note
+                      that the application will have to
+                      reset the close function back again
+                      for cases 1 and 2 if it wants to
+                      close the window using the close function.
+                      
+        """
+        self.close_flag=ctype
+    
+        
+    def close(self, *args):
+        # first check for non-standard close settings
+        if self.close_flag == 1:
+            return TRUE
+        elif self.close_flag == 2:
+            self.hide()
+            return TRUE
+        
+        # what else do we need to do?
+        if len(self.app.view_manager.get_views()) == 1:
+            if self.app.request_quit() > 0:
+                if self.menuf is not None:
+                    self.app.unsubscribe('rfl-change',self.show_rfl)
+                # request_quit() sends out the order for the view
+                # to be shut.  The destroy() and return FALSE are
+                # redundant and sometimes result in errors (project
+                # files).
+                #self.destroy()
+                #return FALSE
+                return TRUE
+            else:
+                return TRUE
+        else:
+            if self.menuf is not None:
+                self.app.unsubscribe('rfl-change',self.show_rfl)
+            self.destroy()
+            return TRUE
+
+    def hide_entry(self,entrystr='File/Exit'):
+        """Hide a menu entry (can be temporary)
+           Input: entrystr- eg. 'File/Exit', 'File/Close',...
+           Returns 1 for success, 0 for failure.
+        """
+        try:
+            item=self.menuf.find(entrystr)
+            if item is not None:
+                item.hide()
+                return 1
+            return 0
+        except:
+            return 0
+        
+    def show_entry(self,entrystr='File/Exit'):
+        """Re-show a hidden menu entry
+           Input: entrystr- eg. 'File/Exit', 'File/Close',...
+           Returns 1 for success, 0 for failure.
+        """
+        try:
+            item=self.menuf.find(entrystr)
+            if item is not None:
+                item.show()
+                return 1
+            return 0
+        except:
+            return 0
+   
+    def exit(self, *args ):
+        # should ask for confirmation at this point.
+        self.app.request_quit()
+
+    def undo(self, *args):
+        self.make_active()
+        gview.undo_pop()
+
+    def show_oeattedit(self, *args):
+        self.make_active()
+        
+        import oeattedit
+
+        oeattedit.launch()
+        
+    def show_layerdlg(self, *args):
+        self.layerdlg.show()
+        self.layerdlg.get_window()._raise()
+
+    def show_toolbardlg(self, *args):
+        self.make_active()
+        self.toolbar.show()
+        self.toolbar.get_window()._raise()
+
+    def goto_dlg(self, *args):
+        """ Create the GoTo Dialog box with coordinate system option menu and
+        text entry fields """
+        window = gtk.GtkWindow()
+        window.set_title('Go To...')
+        window.set_border_width(10)
+        vbox = gtk.GtkVBox(homogeneous=FALSE,spacing=15)
+        window.add(vbox)
+
+        # Make this a selection menu - doesn't work, yet!
+        box = gtk.GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=gtk.FALSE)
+        box.pack_start(gtk.GtkLabel('Coordinate System:'),expand=FALSE)
+        self.coord_system_om = gvutils.GvOptionMenu(('Row/Col','Native'),
+                                                    self.set_coord_system)
+        box.pack_start(self.coord_system_om,expand=FALSE)
+        
+        # Get current position in view native projection
+        #   - changing Option Menu updates this in entry fields
+
+        current_pos = self.viewarea.get_translation()
+        
+        # X Position
+        box = gtk.GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=gtk.FALSE)
+        box.pack_start(gtk.GtkLabel('X Position:'),expand=FALSE)
+        x_pos_entry = gtk.GtkEntry(maxlen=14)
+        x_pos_entry.set_text(str(-current_pos[0]))
+        box.pack_start(x_pos_entry,expand=FALSE)
+
+        # Y Position
+        box = gtk.GtkHBox(spacing=3)
+        vbox.pack_start(box, expand=gtk.FALSE)
+        box.pack_start(gtk.GtkLabel('Y Position:'),expand=FALSE)
+        y_pos_entry = gtk.GtkEntry(maxlen=14)
+        y_pos_entry.set_text(str(-current_pos[1]))
+        box.pack_start(y_pos_entry,expand=FALSE)
+
+        # Button to move
+        goto_button = gtk.GtkButton('Go To...')
+        goto_button.connect('clicked', self.goto_location )
+        vbox.pack_start(goto_button,expand=FALSE)
+
+        self.x_pos_entry = x_pos_entry
+        self.y_pos_entry = y_pos_entry
+
+        # set default to be native system - must be after x/y_pos_entry are setup
+        self.coord_system_om.set_history(1)
+        
+        window.show_all()
+
+    def set_coord_system(self, om, *args):
+        """ Set coordinate system goto coordinates entered in GoTo dialog from
+        option menu. """
+        current_pos = self.viewarea.get_translation()
+        
+        if om.get_history() == 0:
+            self.goto_coord_system = 'pixel'
+        # Lat/Long Not Working Yet!
+        elif om.get_history() == 9:
+            self.goto_coord_system = 'lat-long'
+        elif  om.get_history() == 1:
+            self.goto_coord_system = 'native'
+        else:
+            self.goto_coord_system = 'native'
+
+    def goto_location(self, Button, *args):
+        """ Translate view to location specified in GoTo Dialog, using projection """
+        self.make_active()
+
+        coord_system = self.goto_coord_system
+        str_x = self.x_pos_entry.get_text()
+        str_y = self.y_pos_entry.get_text()
+
+        x = string.atof(str_x)
+        y = string.atof(str_y)
+
+        if coord_system == 'pixel':
+            # Get current raster
+            layer = self.viewarea.active_layer()
+
+            if (layer is None) or (gvutils.is_of_class( layer.__class__, 'GvRasterLayer' ) == 0):
+                gvutils.warning('Please select a raster layer using the layer dialog.\n')
+                return
+
+            raster = layer.get_parent()
+            if ((raster is not None) and (self.viewarea.get_raw(layer) == 0)):
+                # layer is georeferenced, coordinates are pixel/line
+                position = raster.pixel_to_georef(x,y)
+            elif (raster is not None):
+                # layer is in pixel/line coordinates, coordinates are pixel/line
+                position = (x,y)
+            else:
+                return
+            
+        # Doesn't work Yet!
+        elif coord_system == 'lat-long':
+            position = self.viewarea.map_location((x,y))
+
+        elif coord_system == 'native':
+            # native - do nothing
+            position = (x,y)
+
+        else:
+            print 'Error in gvviewwindow.py function goto_location() passed invalid coordinate system'
+            # native - do nothing
+            position = (x,y)
+
+        self.viewarea.set_translation(-position[0],-position[1])
+
+    def menu_save_project(self, *args):
+        self.app.save_project()
+
+    def menu_save_project_as(self, *args):
+        self.app.save_project_as()
+
+    def menu_new_view(self, *args):
+        self.app.new_view()
+
+    def save_vector_layer_request( self, *args ):
+        self.make_active()
+
+        layer = self.viewarea.active_layer()
+        if layer is None or \
+           gvutils.is_of_class( layer.__class__, 'GvShapesLayer' ) == 0:
+            gvutils.warning('Please select a vector layer using the layer\n'+\
+                            'dialog before attempting to save.' )
+            return
+
+        pgufilesel.SimpleFileSelect( self.save_vector_layer_with_file,
+                                     cb_data = layer.get_parent(),
+                                     title = 'Shapefile To Save to',
+                                     default_filename = layer.get_name() )
+
+    def save_vector_layer_with_file( self, filename, shapes_data ):
+        if shapes_data.save_to( filename ) == 0:
+            gvutils.error('Unable to save vectors to:'+filename)
+
+    def destroy_preferences(self,*args):
+        self.pref_dialog = None
+        
+    def file_open_shape_by_name(self, filename):
+        self.make_active()
+        shape_data = gview.GvShapes(shapefilename=filename)
+        if shape_data is None or shape_data._o is None:
+            gvutils.error('Unable to open '+filename+' for loading.')
+            return
+        
+        self.app.add_to_rfl(filename)
+        gview.undo_register(shape_data)
+        
+        layer = gview.GvShapesLayer( shape_data )
+        layer.set_name(filename)
+        self.viewarea.add_layer(layer)
+        self.viewarea.set_active_layer(layer)
+
+    def file_open_ogr_by_name(self, filename):
+        import ogr
+        import gvogrdlg
+
+        self.make_active()
+
+        hDS = ogr.Open( filename )
+        if hDS is None:
+            return FALSE
+
+        self.app.add_to_rfl(filename)
+        
+        dlg = gvogrdlg.GvOGRDlg(hDS, self )
+
+        return TRUE
+    
+    def file_open_ogr_by_layer(self, layer):
+
+        import _gv
+
+        raw_data = _gv.gv_shapes_from_ogr_layer( layer._o )
+        if raw_data is None:
+            return FALSE
+
+        shape_data = gview.GvShapes(_obj=raw_data)
+        if shape_data is None:
+            return
+
+        if len(shape_data) > 0:
+            gview.undo_register(shape_data)
+        
+            layer = gview.GvShapesLayer( shape_data )
+            self.viewarea.add_layer(layer)
+            self.viewarea.set_active_layer(layer)
+        else:
+            # I am not sure how to blow away the GvShapes properly.
+            pass
+                
+        return TRUE
+
+    def file_import_cb(self, *args):
+        self.make_active()
+        pgufilesel.SimpleFileSelect( self.file_import_by_name, None,
+                                     'File To Import',
+                                     help_topic = 'files.html' )
+
+    def file_import_by_name( self, filename, *args ):
+        self.make_active()
+        dataset = gdal.Open( filename )
+        if dataset is None:
+            gvutils.error('Unable to open '+filename+' for import.')
+            return
+
+        geotiff = gdal.GetDriverByName("GTiff")
+        if geotiff is None:
+            gvutils.error("Yikes!  Can't find GeoTIFF driver!")
+            return
+
+        newbase, ext = os.path.splitext(filename)
+        newfile = newbase + ".tif"
+        i = 0
+        while os.path.isfile(newfile):
+            i = i+1
+            newfile = newbase+"_"+str(i)+".tif"
+
+        progress = pguprogress.PGUProgressDialog( 'Import to '+newfile,
+                                                  cancel = TRUE )
+        progress.SetDefaultMessage( "translated" )
+
+        old_cache_max = gdal.GetCacheMax()
+        if old_cache_max < 20000000:
+            gdal.SetCacheMax( 20000000 )
+        
+        new_dataset = geotiff.CreateCopy( newfile, dataset, FALSE,
+                                          ['TILED=YES',],
+                                          callback = progress.ProgressCB )
+        dataset = None
+
+        if progress.cancelled:
+            progress.destroy()
+            if os.path.isfile(newfile):
+                os.unlink(newfile)
+            gdal.SetCacheMax( old_cache_max );
+            return
+            
+        if new_dataset == None:
+            progress.destroy()
+            gvutils.error('Unable to translate '+filename+' to '+newfile)
+            if os.path.isfile(newfile):
+                os.unlink(newfile)
+            gdal.SetCacheMax( old_cache_max );
+            return
+
+        progress.SetDefaultMessage( "overviews built" )
+        new_dataset.BuildOverviews( "average", callback = progress.ProgressCB )
+        new_dataset = None
+
+        progress.destroy()
+
+        gdal.SetCacheMax( old_cache_max );
+
+        # open normally
+        self.file_open_by_name( newfile, sds_check=0 )
+
+
+    def file_open_cb(self, *args):
+        self.make_active()
+
+	if gview.get_preference('save_recent_directory') == 'on':
+	    recent_dir = gview.get_preference('recent_directory')
+	else:
+	    recent_dir = None
+
+	pgufilesel.SimpleFileSelect( ok_cb = self.file_open_name_check,
+                                     title = 'File Open',
+				     default_filename = recent_dir,
+                                     help_topic = 'files.html' )
+
+# buffer function to check for wild cards in filename and then expand them
+    def file_open_name_check(self, filename, lut=None,*args):
+        if ('*' in filename)or('?' in filename):
+            for file in glob.glob(filename):
+               self.file_open_by_name(file)
+        else:
+            self.file_open_by_name(filename)
+
+    def open_subdataset_check( self, dataset ):
+        import gvsdsdlg
+        dlg = gvsdsdlg.GvSDSDlg(dataset, self)
+
+    def file_open_ap_envisat(self, dataset ):
+        options = []
+        if gview.get_preference('gcp_warp_mode') is not None \
+           and gview.get_preference('gcp_warp_mode') == 'no':
+            options.append(('raw','yes'))
+
+        md = dataset.GetMetadata()
+        try:
+            md1 = md['SPH_MDS1_TX_RX_POLAR']
+            md2 = md['SPH_MDS2_TX_RX_POLAR']
+        except:
+            md1 = ''
+            md2 = ''
+
+        raster1 = gview.manager.get_dataset_raster(dataset,1)
+        raster2 = gview.manager.get_dataset_raster(dataset,2)
+
+        rl1 = gview.GvRasterLayer(raster1, options, rl_mode=gview.RLM_AUTO )
+        rl1.set_name( 'MDS1: ' + md1 )
+        rl2 = gview.GvRasterLayer(raster2, options, rl_mode=gview.RLM_AUTO )
+        rl2.set_name( 'MDS2: ' + md2 )
+
+        # Add MDS1 to the current view window. 
+
+        self.viewarea.add_layer(rl1)
+        self.viewarea.set_active_layer(rl1)
+        self.rawgeo_update()
+        self.set_title( self.title + ': MDS1- ' + md1 )
+
+        # Create a new view window and add MDS2 to it.
+
+        view2 = self.app.new_view()
+        view2.viewarea.add_layer(rl2)
+        view2.viewarea.set_active_layer(rl2)
+        view2.rawgeo_update()
+        view2.set_title( view2.title + ': MDS2- ' + md2 )
+
+        # Setup link between views.
+        link = gview.GvViewLink()
+        link.register_view( self.viewarea )
+        link.register_view( view2.viewarea )
+        link.enable()
+
+    def open_gdal_dataset(self, dataset, lut=None, sds_check=1, \
+			  add_to_rfl=0, *args):
+	"""Opens existing GDAL dataset."""
+
+        self.make_active()
+
+        dataset = gview.manager.add_dataset(dataset)
+	if dataset is None:
+	    return
+
+        if sds_check and len(dataset.GetSubDatasets()) > 0:
+            self.open_subdataset_check( dataset )
+            return
+
+	if add_to_rfl:
+	    self.app.add_to_rfl(dataset.GetDescription())
+
+        md = dataset.GetMetadata()
+        # special hack for displaying AP envisat specially.
+        if md.has_key('MPH_PHASE') and dataset.RasterCount == 2:
+            self.file_open_ap_envisat( dataset )
+            return
+
+        raster = gview.manager.get_dataset_raster(dataset,1)
+        options = []
+        if gview.get_preference('gcp_warp_mode') is not None \
+           and gview.get_preference('gcp_warp_mode') == 'no':
+            options.append(('raw','yes'))
+
+        if lut:
+            raster_layer = gview.GvRasterLayer(raster, options,
+                                               rl_mode = gview.RLM_SINGLE )
+            raster_layer.lut_put(lut)
+
+        elif dataset.RasterCount > 2:
+            raster_layer = gview.GvRasterLayer(raster, options,
+                                               rl_mode = gview.RLM_RGBA )
+        elif dataset.RasterCount == 2 and \
+             dataset.GetRasterBand(2).GetRasterColorInterpretation() == gdal.GCI_AlphaBand:
+            raster_layer = gview.GvRasterLayer(raster, options,
+                                               rl_mode = gview.RLM_RGBA )
+        else:
+            raster_layer = gview.GvRasterLayer(raster, options,
+                                               rl_mode = gview.RLM_AUTO )
+        raster_layer.set_name(dataset.GetDescription())
+
+        # Lots of logic to handle RGB and RGBA Layers
+        if raster_layer.get_mode() == gview.RLM_RGBA \
+           and dataset.RasterCount == 2:
+            
+            alpha_band = gview.manager.get_dataset_raster(dataset,2)
+            raster_layer.set_source(1,raster)
+            raster_layer.set_source(2,raster)
+            raster_layer.set_source(3,alpha_band)
+
+            raster_layer.blend_mode_set( gview.RL_BLEND_FILTER )
+
+        if raster_layer.get_mode() == gview.RLM_RGBA \
+           and dataset.RasterCount > 2:
+            
+            green_raster = gview.manager.get_dataset_raster(dataset,2)
+            blue_raster = gview.manager.get_dataset_raster(dataset,3)
+
+            raster_layer.set_source(1,green_raster)
+            raster_layer.set_source(2,blue_raster)
+
+            if dataset.RasterCount > 3:
+                band = dataset.GetRasterBand(4)
+                if band.GetRasterColorInterpretation() == gdal.GCI_AlphaBand:
+                    raster_layer.blend_mode_set( gview.RL_BLEND_FILTER )
+                    alpha_raster = \
+                        gview.manager.get_dataset_raster(dataset, 4)
+                    raster_layer.set_source(3,alpha_raster)
+
+        self.viewarea.add_layer(raster_layer)
+        self.viewarea.set_active_layer(raster_layer)
+        self.rawgeo_update()
+
+    def file_open_by_name(self, filename, lut=None, sds_check=1, *args):
+        head = os.path.dirname(filename)
+        if len(head) > 0:
+            if os.access(head,os.R_OK):
+                pgufilesel.simple_file_sel_dir = head+os.sep
+
+	if gview.get_preference('save_recent_directory') == 'on':
+	    gview.set_preference('recent_directory', head+os.sep)
+
+        if gvutils.is_shapefile(filename):
+            self.file_open_shape_by_name(filename)
+            return
+
+        if gvutils.is_project_file(filename):
+            self.app.load_project(filename)
+            return
+
+        gdal.PushErrorHandler( 'CPLQuietErrorHandler' )
+        dataset = gview.manager.get_dataset(filename)
+        gdal.PopErrorHandler()
+        if dataset is None \
+               and gdal.GetLastErrorNo() != 4 \
+               and gdal.GetLastErrorNo() != 0:
+            gvutils.error( 'Unable to open '+filename+'\n\n' \
+                           + gdal.GetLastErrorMsg() )
+            return
+
+        # catch ogr file open failure and pop up
+        # a warning rather than dumping to screen.
+        try:
+            if dataset is None and self.file_open_ogr_by_name(filename):
+                return
+
+            if dataset is None:
+                gvutils.error('Unable to open '+filename+'\n\n' \
+                              + gdal.GetLastErrorMsg() )
+                return
+        except:
+            if dataset is None:
+                gvutils.error('Unable to open '+filename+'\n\n' \
+                              + gdal.GetLastErrorMsg() )
+                return
+
+	self.open_gdal_dataset(dataset, lut, sds_check, add_to_rfl=1)
+
+    def init_custom_icons(self):
+        pass
+    
+    def init_default_icons(self):
+        # Zoom ratio selection box
+        zoom_factor = gtk.GtkCombo()
+        zoom_factor.set_popdown_strings(ratio_list)
+        zoom_factor.entry.set_text('1:1')
+        zoom_factor.set_usize(70,20)
+        self.zoom_entry_changed_id = zoom_factor.entry.connect('changed', self.set_zoom_factor_cb)
+        zoom_factor.list.connect('selection-changed', self.set_zoom_factor_focus_cb)
+        self.zoom_factor = zoom_factor
+        
+        # raw / georeferenced pixmap
+        self.raw_icon = gtk.create_pixmap_from_xpm(self,None,
+                   os.path.join(gview.home_dir,'pics', 'worldg.xpm'))
+        self.geo_icon = gtk.create_pixmap_from_xpm(self,None,
+                   os.path.join(gview.home_dir,'pics', 'worldrgb.xpm'))
+        self.rawgeo_pixmap = gtk.GtkPixmap(self.raw_icon[0],
+                                           self.raw_icon[1])
+        
+        # idle / busy pixmap
+        self.idle_icon = gtk.create_pixmap_from_xpm(self,None,
+                   os.path.join(gview.home_dir,'pics', 'idle.xpm'))
+        self.busy_icon = gtk.create_pixmap_from_xpm(self,None,
+                   os.path.join(gview.home_dir,'pics', 'busy.xpm'))
+        self.idlebusy_pixmap = gtk.GtkPixmap(self.busy_icon[0],
+                                             self.busy_icon[1])
+        gview.manager.connect('busy-changed', self.busy_changed_cb)
+
+    def create_iconbar(self, iconfile='DefaultIconFile.xml'):
+        self.iconbar = gtk.GtkToolbar(gtk.ORIENTATION_HORIZONTAL,
+                                      gtk.TOOLBAR_ICONS)
+        
+        self.init_default_icons()
+        self.init_custom_icons()
+
+        self.icon_cmds = None
+        if iconfile is not None:
+            # check that icon file exists.  If not, use old values for
+            # backwards compatibility
+            fulliconfile = os.path.join(gview.home_dir,'xmlconfig',iconfile)
+            if os.path.isfile(fulliconfile) == 1:
+                if (self.app is not None) and hasattr(self.app,'load_icons_file_from_xml'):
+                    icon_cmds=self.app.load_icons_file_from_xml( iconfile )
+                else:
+                    icon_cmds = self.load_icons_file_from_xml( iconfile )
+                self.icon_cmds = icon_cmds
+            else:
+                raise AttributeError,'Unable to find view icon configuration file '+iconfile
+        else:
+            self.icon_cmds=self.old_icon_cmds()
+            
+        for cmd in self.icon_cmds:
+            exec cmd
+            
+        gview.manager.set_busy(TRUE)
+ 
+    def old_icon_cmds(self):
+        icon_cmds=[]
+        icon_cmds.append("self.add_icon_to_bar( 'openfile.xpm', None,'Open and Display Raster/Vector File',self.file_open_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'print.xpm', None,'Print Current View',self.print_cb, 'gvprint.html' )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'nonelut.xpm', None,'Revert to no Enhancement',self.nonelut_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'linear.xpm', None,'Linear Stretch/Enhancement',self.linear_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'equalize.xpm', None,'Apply Equalization Enhancement to Raster',self.equalize_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'log.xpm', None,'Logarithmic Enhancement to Raster',self.log_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'windowed.xpm', None,'Windowed Raster Re-enhancement',self.restretch_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'classify.xpm', None,'Classify Raster',self.classify_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'legend.xpm', None,'Show Legend',self.show_legend_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'seeall.xpm', None,'Fit All Layers',self.seeall_cb )")
+
+        # Zoom ratio selection box
+        icon_cmds.append("self.iconbar.append_widget(self.zoom_factor, 'Zoom Ratio', 'Zoom Ratio')")
+
+        icon_cmds.append("self.add_icon_to_bar( 'zoomin.xpm', None,'Zoom in x2',self.zoomin_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'zoomout.xpm', None,'Zoom out x2',self.zoomout_cb )")
+
+        icon_cmds.append("self.add_icon_to_bar( 'refresh.xpm', None,'Refresh Rasters From Disk',self.refresh_cb )")
+
+        # raw / georeferenced pixmap
+        icon_cmds.append("self.iconbar.append_item(None, 'Georeferenced','Georeferenced', self.rawgeo_pixmap,self.rawgeo_cb )")
+
+        # Help
+        icon_cmds.append("self.add_icon_to_bar( 'help.xpm', None,'Launch Online Help',self.helpcb )")
+
+        # idle / busy pixmap
+        icon_cmds.append("self.iconbar.append_item(None, 'Busy Indicator','Busy Indicator', self.idlebusy_pixmap,self.do_nothing )")
+
+        return icon_cmds
+       
+    def create_menubar(self, menufile='DefaultMenuFile.xml'):
+        self.menuf = gvutils.GvMenuFactory()
+
+        self.menu_cmd = None        
+        if menufile is not None:
+            # Check that menu file exists.  If not, load old menu
+            # for backwards comaptibility.
+            fullmenufile = os.path.join(gview.home_dir,'xmlconfig',menufile)
+            if os.path.isfile(fullmenufile) == 1:
+                if ((self.app is not None) and hasattr(self.app,'load_menus_file_from_xml')):
+                    # Application parses the xml file itself
+                    menu_cmd=self.app.load_menus_file_from_xml( menufile, self.title )
+                else:
+                    menu_cmd = self.load_menus_file_from_xml( menufile )
+                self.menu_cmd = menu_cmd
+                #print menu_cmd
+            else:
+                raise AttributeError,'Unable to find view menu configuration file '+menufile
+        else:
+            self.menu_cmd = self.old_menu_cmd()
+
+        exec self.menu_cmd
+        self.add_accel_group(self.menuf.accelerator)
+
+        if self.app is not None:
+            self.app.subscribe('rfl-change',self.show_rfl)
+
+
+    def old_menu_cmd(self):
+        menu_cmd = "self.menuf.add_entries([" + \
+                "('File/Import', None, self.file_import_cb )," + \
+                "('File/Open', '<control>O', self.file_open_cb )," + \
+                "('File/Open 3D', None, self.open_3D_request)," + \
+                "('File/Save Vector Layer', None, self.save_vector_layer_request)," + \
+                "('File/Save Project', None, self.menu_save_project)," + \
+                "('File/New View', None, self.menu_new_view)," + \
+                "('File/Print', None, self.print_cb)," + \
+                "('File/<separator>', None, None)," + \
+                "('File/rfl1', None, self.rfl_cb, 1)," + \
+                "('File/rfl2', None, self.rfl_cb, 2)," + \
+                "('File/rfl3', None, self.rfl_cb, 3)," + \
+                "('File/rfl4', None, self.rfl_cb, 4)," + \
+                "('File/rfl5', None, self.rfl_cb, 5)," + \
+                "('File/<separator>', None, None)," + \
+                "('File/Close', None, self.close)," + \
+                "('File/Exit', '<control>Q', self.exit)," + \
+                "('Edit/Undo', None, self.undo)," + \
+                "('Edit/Layers...', None, self.app.show_layerdlg)," + \
+                "('Edit/Vector Layer Attributes...', None, self.show_oeattedit)," + \
+                "('Edit/Edit Toolbar...', None, self.app.show_toolbardlg)," + \
+                "('Edit/Go To...', None, self.goto_dlg)," + \
+                "('Edit/Python Shell...', None, self.pyshell)," + \
+                "('Edit/3D Position...', None, self.position_3d)," + \
+                "('Edit/Preferences...', None, self.app.launch_preferences)," + \
+                "('Help/Help...', None, self.helpcb, 'openevmain.html')," + \
+                "('Help/<separator>', None, None)," + \
+                "('Help/Web Page...', None, self.helpcb,'http://OpenEV.Sourceforge.net/')," + \
+                "('Help/About...', None, self.aboutcb)])"
+
+        return menu_cmd
+
+    def load_menus_file_from_xml(self, menufile='DefaultMenuFile.xml'):
+        # load in menu file and populate menu entries
+        menufile = os.path.join(gview.home_dir,'xmlconfig',menufile)
+        menu_list = []
+        try:
+            raw_xml = open(menufile).read()
+        except:
+            raise AttributeError,"Unable to load " + menufile
+            return
+
+        tree = gdal.ParseXMLString( raw_xml )
+        if tree is None:
+            raise AttributeError,"Problem occured parsing menu file " + menufile
+            return
+
+        if tree[1] != 'GViewAppMenu':
+            raise AttributeError,"Root of %s is not GViewAppMenu node " % menufile
+            return
+
+        # loop over entries getting path,accelerator,callback and arguments
+        menu_trees = gvutils.XMLFind( tree, 'Menu')
+        if menu_trees is None:
+            raise AttributeError,"Invalid menu file format"
+      
+        for node in menu_trees[2:]:
+            if node[1] == 'entry':
+                node_path  = gvutils.XMLFind( node, 'path')
+                if node_path is None:
+                    raise AttributeError,"Invalid menu file format - missing path"
+                 
+                entry_type = gvutils.XMLFindValue( node_path, 'type', '')
+                entry_path = gvutils.XMLFindValue( node, 'path','')
+                
+                if (string.find(entry_path,"/") == -1):
+                    raise AttributeError,"Invalid menu file format - bad path:%s" % entry_path
+                    
+                if (entry_type != ''):
+                    entry_type = "<" + entry_type + ">"
+                path_split=string.split(entry_path,"/")
+                path_split[-1] = entry_type + path_split[-1]
+                entry_path=string.join(path_split,"/")
+
+                entry_accelerator = gvutils.XMLFindValue( node, 'accelerator', 'None')
+                if (entry_accelerator != 'None'):
+                    (key,mod) = string.split(entry_accelerator,'+')
+                    entry_accelerator = "'<" + key + ">" + mod + "'"
+
+                entry_callback = gvutils.XMLFindValue( node, 'callback', 'None')
+                entry= "("                                             \
+                        + string.join((entry_path,entry_accelerator,   \
+                                       entry_callback),",")
+
+                arguments = gvutils.XMLFind( node, 'arguments')
+                if arguments is not None:
+                    args_list = []
+                    args =  gvutils.XMLFind( arguments, 'arg','')
+                    if args is not None:
+                        for arg in args:
+                            args_list.append(gvutils.XMLFindValue( arg, '',''))
+                        entry = entry + "," + string.join(args_list,",")
+
+                entry = entry + ")"
+
+                menu_list.append(entry)
+            else:
+                raise AttributeError,"Invalid menu file format"
+            
+        # create the menu command to populate the entries
+        menu_cmd =  "self.menuf.add_entries([" + string.join(menu_list,',') + "])"
+        return menu_cmd
+
+
+    def load_icons_file_from_xml(self, iconfile='DefaultIconFile.xml'):
+        #print "LOADING ICONS"
+        # load in icon file and create icon commands
+        iconfile = os.path.join(gview.home_dir,'xmlconfig',iconfile)
+        icon_list = []
+        try:
+            raw_xml = open(iconfile).read()
+        except:
+            raise AttributeError,"Unable to load " + iconfile
+            return
+
+        tree = gdal.ParseXMLString( raw_xml )
+        if tree is None:
+            raise AttributeError,"Problem occured parsing icon file " + iconfile
+            return
+
+        if tree[1] != 'GViewAppIconBar':
+            raise AttributeError,"Root of %s is not GViewAppIconBar node " % iconfile
+            return
+
+        # loop over entries getting icon,label,hint,callback and help
+        icon_trees = gvutils.XMLFind( tree, 'Iconbar')
+        if icon_trees is None:
+            raise AttributeError,"Invalid icon file format"
+        
+        for node in icon_trees[2:]:
+            if node[1] == 'icon':
+                type = None
+                icon_label = gvutils.XMLFindValue( node, 'label','None')
+                icon_hint = gvutils.XMLFindValue( node, 'hint','None')
+                icon_callback = gvutils.XMLFindValue( node, 'callback','None')
+                icon_help = gvutils.XMLFindValue( node, 'help','None')
+                icon_file = gvutils.XMLFindValue( node, 'xpm','None')
+                # xpm files - need to add path and possible help
+                if (icon_file != 'None'):
+                    type = 'xpm'
+                    icon = "self.add_icon_to_bar("                           \
+                            + string.join((icon_file,icon_label,icon_hint,   \
+                                           icon_callback,icon_help),",")     \
+                            + ")" 
+
+                # pixmap files - not adding path or help 
+                icon_file = gvutils.XMLFindValue( node, 'pixmap','None')
+                if (icon_file!= 'None'):
+                    type = 'pixmap'
+                    icon = "self.iconbar.append_item("                        \
+                            + string.join((icon_label,icon_hint,icon_hint,    \
+                                              icon_file,icon_callback),",")   \
+                            + ")" 
+
+                # widget  
+                icon_file = gvutils.XMLFindValue( node, 'widget','None')
+                if (icon_file!= 'None'):
+                    type = 'widget'
+                    icon_file = gvutils.XMLFindValue( node, 'widget','None')
+                    icon = "self.iconbar.append_widget("                       \
+                            + string.join((icon_file,icon_hint,icon_hint),",") \
+                            + ")" 
+                # none of the above
+                if type is None:
+                    raise AttributeError,"Invalid icon file format - unknown type"
+
+                icon_list.append(icon)
+            else:
+                raise AttributeError,"Invalid icon file format"
+
+
+        return icon_list
+
+    def do_nothing(self, *args):
+        pass
+    
+    def add_icon_to_bar(self, filename, text, hint_text, cb, help_topic=None):
+        full_filename = os.path.join(gview.home_dir,'pics',filename)
+        pix, mask = gtk.create_pixmap_from_xpm(self,None,full_filename)
+        item = self.iconbar.append_item(text,hint_text, hint_text,
+                                        gtk.GtkPixmap(pix,mask), cb)
+        if help_topic is not None:
+            gvhtml.set_help_topic(item, help_topic)
+
+    def insert_tool_icon(self, filename, text, hint_text, cb, help_topic=None, pos=0):
+        # Tool specifies full filename (file may not be in pics directory)
+        
+        pix, mask = gtk.create_pixmap_from_xpm(self,None,filename)       
+        item = self.iconbar.insert_item(text,hint_text, hint_text,
+                                        gtk.GtkPixmap(pix,mask), cb, pos )
+        if help_topic is not None:
+            gvhtml.set_help_topic(item, help_topic)
+
+    def classify_cb(self, *args):
+        self.make_active()
+        if hasattr(self.viewarea.active_layer(),'get_mode'):
+            if self.viewarea.active_layer().get_mode() == gview.RLM_COMPLEX:
+                gvutils.warning('Complex rasters cannot be classified!')
+            elif self.viewarea.active_layer().get_mode() == gview.RLM_RGBA:
+                gvutils.warning('Multiband rasters cannot be classified!')
+            else:
+                self.viewarea.active_layer().classify()
+        else:
+            self.viewarea.active_layer().classify()
+        
+    def show_legend_cb(self, *args):
+        self.make_active()
+        self.viewarea.active_layer().show_legend()
+        
+    def restretch_cb(self, *args):
+        self.make_active()
+        try:
+            self.viewarea.active_layer().window_restretch()
+        except:
+            gvutils.warning('This can only be applied to a raster layer.\n' \
+                          + 'Select a raster layer for this view in the \nlayers dialog.' )
+        
+    def equalize_cb(self, *args):
+        self.make_active()
+        try:
+            self.viewarea.active_layer().equalize()
+        except:
+            gvutils.warning('This can only be applied to a raster layer.\n' \
+                          + 'Select a raster layer for this view in the \nlayers dialog.' )
+            #import traceback
+            #traceback.print_exc()
+        
+    def linear_cb(self, *args):
+        self.make_active()
+        try:
+            self.viewarea.active_layer().linear()
+        except:
+            gvutils.warning('This can only be applied to a raster layer.\n' \
+                          + 'Select a raster layer for this view in the \nlayers dialog.' )
+        
+    def log_cb(self, *args):
+        self.make_active()
+        try:
+            self.viewarea.active_layer().log()
+        except:
+            gvutils.warning('This can only be applied to a raster layer.\n' \
+                          + 'Select a raster layer for this view in the \nlayers dialog.' )
+        
+    def nonelut_cb(self, *args):
+        self.make_active()
+        try:
+            self.viewarea.active_layer().none_lut()
+        except:
+            gvutils.warning('This can only be applied to a raster layer.\n' \
+                          + 'Select a raster layer for this view in the \nlayers dialog.' )
+
+    def seeall_cb(self,*args):
+        self.make_active()
+        try:
+            self.viewarea.fit_all_layers()
+        except:
+            pass
+        
+    def onetoone_cb(self,*args):
+        self.make_active()
+        try:
+            view = self.viewarea
+            layer = view.active_layer()
+            point1 = view.inverse_map_pointer(layer.pixel_to_view( 0, 0 ))
+            point2 = view.inverse_map_pointer(layer.pixel_to_view( 1, 1 ))
+            dist = math.sqrt(math.pow((point1[0]-point2[0]),2)
+                             + math.pow((point1[1]-point2[1]),2))
+            factor = dist / math.sqrt(2)
+            view.zoom(-1 * (math.log(factor) / math.log(2)) )
+        except:
+            gvutils.warning('This operation can only be done if a raster layer is the\nactive layer.  Please select a raster layer for this view in the layers dialog.')
+
+    def set_zoom_factor_focus_cb(self,*args):
+        """ Keep the focus in the view window when we selected a new zooming ratio from
+        the combo box.  To ensure key events still recieved such as Home, and arrows """
+        self.viewarea.grab_focus()
+    
+    def set_zoom_factor_cb(self,*args):
+        self.make_active()
+        
+        if self.zoom_factor is None:
+            # if zoom factor icon doesn't exist (no iconbar)
+            return
+        
+        try:
+            ratio_text = string.split(self.zoom_factor.entry.get_text(), ':')
+            ratio = [string.atof(ratio_text[0]), string.atof(ratio_text[1])]
+        except:
+            # if invalid text entered do nothing
+            return
+
+        # Make sure both values are positive
+        if (ratio[0] <= 0.0) or (ratio[1] <= 0.0):
+            return
+
+        try:
+            view = self.viewarea
+            layer = view.active_layer()
+            point1 = view.inverse_map_pointer(layer.pixel_to_view( 0, 0 ))
+            point2 = view.inverse_map_pointer(layer.pixel_to_view( 1, 1 ))
+            dist = math.sqrt(math.pow((point1[0]-point2[0]),2)
+                             + math.pow((point1[1]-point2[1]),2))
+            factor = dist / math.sqrt(2)
+            self.zoom = factor
+        
+            # Block view-state-changed signal while we update zoom factor
+            view.signal_handler_block(self.view_state_changed_id)
+            view.zoom(-1 * (math.log((ratio[1]/ratio[0])*factor) / math.log(2)) )
+            view.signal_handler_unblock(self.view_state_changed_id)
+
+            self.zoom_flag = 'yes'
+        except:
+            # To prevent multiple error messages use the zoom_flag to keep track if we
+            # have successfully zoomed since last error message, if not then we don't
+            # generate another one
+            if self.zoom_flag == 'yes':
+                self.zoom_flag = 'no'
+                gvutils.warning('This operation can only be done if a raster layer is the\nactive layer.  Please select a raster layer for this view\nin the layers dialog.')
+
+        #self.viewarea.grab_focus()
+          
+    def refresh_cb(self, *args):
+        self.make_active()
+        try:
+            layer = self.viewarea.active_layer()
+            for isource in range(4):
+                raster = layer.get_data(isource)
+                if raster is not None:
+                    raster.changed()
+        except:
+            gvutils.warning('The refresh from disk operation can only be\n'+\
+                            'applied to raster layers.  Select a raster\n'+\
+                            'layer for this view in the layers dialog.')
+            pass
+        
+    def zoomin_cb(self,*args):
+        self.make_active()
+        try:
+            self.viewarea.zoom(1)
+        except:
+            pass
+        
+    def zoomout_cb(self,*args):
+        self.make_active()
+        try:
+            self.viewarea.zoom(-1)
+        except:
+            pass
+
+    def update_zoom_cb(self, *args):
+        # Note: we do NOT use temp_zoom to get the zooming factor (doesn't work for
+        #       geo-referenced images) we just use it as a way to check if the zoom
+        #       has changed and therefore if we have to do the calculation
+        temp_zoom = self.viewarea.get_zoom()
+        layer = self.viewarea.active_layer()
+
+        if self.zoom_factor is None:
+            return gtk.FALSE 
+
+        try:
+            # Check if zoom factor changed
+            if (temp_zoom != self.zoom) and (layer is not None):
+                self.zoom = temp_zoom
+                self.zoom_flag = 'yes'
+
+                point1 = self.viewarea.inverse_map_pointer(layer.pixel_to_view( 0, 0 ))
+                point2 = self.viewarea.inverse_map_pointer(layer.pixel_to_view( 1, 1 ))
+                dist = math.sqrt(math.pow((point1[0]-point2[0]),2)
+                                 + math.pow((point1[1]-point2[1]),2))
+                factor = dist / math.sqrt(2)
+            
+                if (factor > 1):
+                    ratio = str(round((factor/1.0),1)) + ':1'
+                else:
+                    ratio = '1:' + str(round(1.0/factor,1))
+
+                # Block combo box changed signal while we update the text entry
+                self.zoom_factor.entry.signal_handler_block(self.zoom_entry_changed_id)
+                self.zoom_factor.entry.set_text(ratio)
+                self.zoom_factor.entry.signal_handler_unblock(self.zoom_entry_changed_id)
+        except:
+            self.zoom_factor.entry.signal_handler_block(self.zoom_entry_changed_id)
+            self.zoom_factor.entry.set_text('?:?')
+            self.zoom_factor.entry.signal_handler_unblock(self.zoom_entry_changed_id)
+
+        # Put focus back into view window
+        #self.viewarea.grab_focus()
+
+        # Return false to continue propogation of the view-state-changed signal
+        return gtk.FALSE   
+
+        
+    def pyshell(self, *args):
+        self.make_active()
+        self.app.pyshell()
+        
+    # -------- 3D File Open and Setup --------
+    def open_3D_request(self, *args):
+        """ 3D File Open Dialog for selecting drape and height data """
+        self.make_active()
+        self.drape_dataset = None
+        self.DEM_dataset = None
+
+        # Create Dialog Window
+        dialog = gtk.GtkWindow()
+        dialog.set_title('Open 3D')
+        dialog.set_border_width(10)
+        # dialog.set_usize(500, 850)
+        dialog.set_policy(FALSE, FALSE, TRUE)
+        gvhtml.set_help_topic( dialog, 'open3d.html' )
+        
+        box = gtk.GtkVBox(homogeneous=FALSE, spacing=5)
+        dialog.add(box)
+        self.file_dialog_3D = dialog
+        
+        # Drape File Selector
+        drape_label = gtk.GtkLabel('Select Drape')
+        box.pack_start(drape_label)
+
+        self.drape_fileSelectWin = gtk.GtkFileSelection()
+        zsChildren = self.drape_fileSelectWin.children()[0].children() 
+        for zsChild in zsChildren : zsChild.reparent(box)
+
+        # DEM File Selector
+        ruler1 = gtk.GtkHSeparator()
+        box.pack_start(ruler1)
+        DEM_label = gtk.GtkLabel('Select DEM')
+        box.pack_start(DEM_label)
+
+        self.DEM_fileSelectWin = gtk.GtkFileSelection()
+        zsChildren = self.DEM_fileSelectWin.children()[0].children() 
+        for zsChild in zsChildren : zsChild.reparent(box)
+
+        # Mesh LOD and Height Scale
+        mesh_opts = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        lod_label =  gtk.GtkLabel('Mesh Level of Detail')
+        spin_adjust = gtk.GtkAdjustment(value=3, lower=0, upper=8, step_incr=1)
+        self.lod_spin_button = gtk.GtkSpinButton(spin_adjust, climb_rate=1, digits=0)
+
+        hscale_label = gtk.GtkLabel('Height Scaling Factor:')
+        self.scale_value = gtk.GtkEntry(maxlen=7)
+        self.scale_value.set_text('1.0')
+        
+        mesh_opts.pack_start(lod_label)
+        mesh_opts.pack_start(self.lod_spin_button)
+        mesh_opts.pack_start(hscale_label)
+        mesh_opts.pack_start(self.scale_value)
+        box.pack_start(mesh_opts)
+
+        # DEM height clamping options
+        min_clamp_opts = gtk.GtkHBox(homogeneous=TRUE,spacing=5)
+        self.min_heightclamp_entry = gtk.GtkEntry(maxlen=10)
+        self.min_heightclamp_entry.set_text('0.0')
+        min_clamp_label = gtk.GtkLabel('Minimum Height:')
+        self.min_heightclamp_toggle = gtk.GtkCheckButton('Clamp Minimum Height')
+        self.min_heightclamp_toggle.set_active(gtk.FALSE)
+        min_clamp_opts.pack_start(self.min_heightclamp_toggle)
+        min_clamp_opts.pack_start(min_clamp_label)
+        min_clamp_opts.pack_start(self.min_heightclamp_entry)
+        box.pack_start(min_clamp_opts)
+
+        max_clamp_opts = gtk.GtkHBox(homogeneous=TRUE,spacing=5)
+        self.max_heightclamp_entry = gtk.GtkEntry(maxlen=10)
+        self.max_heightclamp_entry.set_text('100000.0')
+        max_clamp_label = gtk.GtkLabel('Maximum Height:')
+        self.max_heightclamp_toggle = gtk.GtkCheckButton('Clamp Maximum Height')
+        self.max_heightclamp_toggle.set_active(gtk.FALSE)
+        max_clamp_opts.pack_start(self.max_heightclamp_toggle)
+        max_clamp_opts.pack_start(max_clamp_label)
+        max_clamp_opts.pack_start(self.max_heightclamp_entry)
+        box.pack_start(max_clamp_opts)
+
+        # Okay/Cancel Buttons
+        buttons = gtk.GtkHBox(homogeneous=FALSE, spacing=5)
+        okay = gtk.GtkButton('OK')
+        okay.set_usize(64, 32)
+        okay.connect('clicked', self.perform_3D_request)
+        
+        cancel = gtk.GtkButton('Cancel')
+        cancel.set_usize(64, 32)
+        cancel.connect('clicked', dialog.destroy)
+
+        help = gtk.GtkButton('Help')
+        help.set_usize(64, 32)
+        help.connect('clicked', self.helpcb, 'open3d.html')
+
+        buttons.pack_end(help, expand=FALSE)
+        buttons.pack_end(cancel, expand=FALSE)
+        buttons.pack_end(okay, expand=FALSE)
+        box.pack_start(buttons, expand=FALSE)
+
+        # Show everything but unused fileselection buttons
+        dialog.show_all()
+        box.children()[1].hide()  # Remove Drape Create/Delete/Rename 
+        box.children()[6].hide()  # Remove Drape Ok/Cancel
+        box.children()[9].hide()  # Remove DEM Create/Delete
+        box.children()[14].hide() # Remove DEM Ok/Cancel
+        
+
+    def perform_3D_request(self, *args):
+        """Tries to open selected files, then creates 3D Layer and switches to 3D mode"""
+        drape_filename = self.drape_fileSelectWin.get_filename()
+        dem_filename = self.DEM_fileSelectWin.get_filename()
+        mesh_lod = self.lod_spin_button.get_value_as_int()
+        hscale = float(self.scale_value.get_text())
+
+        if (self.min_heightclamp_toggle.get_active() == gtk.TRUE):
+            min_clamp = float(self.min_heightclamp_entry.get_text())
+        else:
+            min_clamp = None
+
+        if (self.max_heightclamp_toggle.get_active() == gtk.TRUE):
+            max_clamp = float(self.max_heightclamp_entry.get_text())
+        else:
+            max_clamp = None
+
+        # Do real work.
+        self.view3d_action( dem_filename, drape_filename, mesh_lod, hscale,
+                            min_clamp, max_clamp )
+
+        # Clean up File Dialog Window
+        self.file_dialog_3D.destroy()
+
+    def view3d_action( self, dem_filename, drape_filename = None,
+                       mesh_lod = None, hscale = None,
+                       min_clamp = None, max_clamp = None ):
+
+        self.make_active()
+        gview.manager.set_busy(TRUE)
+
+        # Fill default parameters.
+        if drape_filename is None:
+            drape_filename = dem_filename
+        if mesh_lod is None:
+            mesh_lod = 3
+        if hscale is None:
+            hscale = 1.0
+        
+        # Get Data
+        drape_dataset = gview.manager.get_dataset(drape_filename)
+        if drape_dataset is None or drape_dataset._o is None:
+            gvutils.error( 'Unable to open drape dataset: '+drape_filename)
+            return
+
+        DEM_dataset = self.raster_open_by_name(dem_filename)
+        if DEM_dataset is None or DEM_dataset._o is None:
+            return
+            
+        if (drape_dataset is not None) and (DEM_dataset is not None):
+            # Get Current View & Prefs
+            view = self.viewarea
+
+            options = []
+            if gview.get_preference('_gcp_warp_mode') is not None \
+               and gview.get_preference('_gcp_warp_mode') == 'no':
+                options.append(('raw','yes'))
+
+            # Set Current View to 3D Mode
+            view.set_mode(gvconst.MODE_3D)
+            # view.height_scale(hscale)
+            options.append(('mesh_lod',str(mesh_lod)))
+
+            band = drape_dataset.GetRasterBand(1)
+            interp = band.GetRasterColorInterpretation()
+            
+            # Create Drape Raster
+            drape_raster = gview.manager.get_dataset_raster(drape_dataset,1)
+            gview.undo_register(drape_raster)
+
+            # Create Drape Raster Layer
+            if drape_dataset.RasterCount > 2:
+                drape_raster_layer = gview.GvRasterLayer(drape_raster, options,
+                                                  rl_mode = gview.RLM_RGBA )
+            else:
+                drape_raster_layer = gview.GvRasterLayer(drape_raster, options,
+                                                  rl_mode = gview.RLM_AUTO )
+
+            # Logic to handle RGB and RGBA Layers
+            if drape_raster_layer.get_mode() == gview.RLM_RGBA:
+            
+                green_raster= gview.manager.get_dataset_raster(drape_dataset,2)
+                blue_raster = gview.manager.get_dataset_raster(drape_dataset,3)
+
+                drape_raster_layer.set_source(1,green_raster)
+                drape_raster_layer.set_source(2,blue_raster)
+
+                if drape_dataset.RasterCount > 3:
+                    band = drape_dataset.GetRasterBand(4)
+                    if band.GetRasterColorInterpretation() == \
+                                                gdal.GCI_AlphaBand:
+                        drape_raster_layer.blend_mode_set(
+                            gview.RL_BLEND_FILTER )
+                        drape_raster_layer.set_source(3,
+                             gview.manager.get_dataset_raster(drape_dataset,4))
+
+            # Add to view
+            drape_raster_layer.set_name(drape_dataset.GetDescription() )
+            view.add_layer(drape_raster_layer)
+            view.set_active_layer(drape_raster_layer)
+
+            # Create DEM Raster and Add as Height
+            DEM_raster = gview.GvRaster(dataset=DEM_dataset)
+            DEM_raster.set_name(str(dem_filename))
+            drape_raster_layer.add_height(DEM_raster)
+
+            # perform clamping, if requested
+            if min_clamp is not None:
+                drape_raster_layer.clamp_height(1,0,min_clamp)
+
+            if max_clamp is not None:
+                drape_raster_layer.clamp_height(0,1,0,max_clamp)
+
+            # Modify hscale to be more reasonable in some geo-referenced cases 
+            #[hscalex1,dummy] = DEM_raster.pixel_to_georef(0,0)
+            #[hscalex2,dummy] = DEM_raster.pixel_to_georef(DEM_dataset.RasterXSize - 1,0)
+            #hscale_georef = hscale*abs(hscalex2-hscalex1)/DEM_dataset.RasterXSize
+            #view.height_scale(hscale_georef)
+            view.height_scale(hscale) 
+           
+            # Try to make sure everything is visible.
+            self.seeall_cb()
+
+    def position_3d(self, *args):
+        self.make_active()
+        if self.position3D_dialog is None:
+            self.position3D_dialog = \
+                         gviewapp.Position_3D_Dialog(self.app.view_manager)
+            self.position3D_dialog.connect('destroy', self.destroy_position_3d)
+        self.position3D_dialog.show()
+        self.position3D_dialog.get_window()._raise()
+
+        view = self.viewarea
+        self.position3D_dialog.update_cb(view)
+        view.connect('view-state-changed', self.position3D_dialog.update_cb)
+
+    def destroy_position_3d(self,*args):
+        self.position3D_dialog = None
+                
+    def raster_open_by_name(self,filename):
+        self.make_active()
+        gdal.ErrorReset()
+        dataset = gdal.Open(filename)
+        if dataset is None:
+            gvutils.error('Unable to open: '+filename+'\n\n'+ \
+                          gdal.GetLastErrorMsg())
+            return None
+        
+        return dataset
+
+    def rawgeo_cb( self, *args ):
+        ref_layer = self.viewarea.active_layer()
+        self.viewarea.set_raw(ref_layer, not self.viewarea.get_raw(ref_layer))
+        self.rawgeo_update()
+
+    def rawgeo_update( self, *args ):
+        if self.iconbar is None:
+            return
+
+        ref_layer = self.viewarea.active_layer()
+        if self.viewarea.get_raw( ref_layer ):
+            self.rawgeo_pixmap.set( self.raw_icon[0], self.raw_icon[1] )
+        else:
+            self.rawgeo_pixmap.set( self.geo_icon[0], self.geo_icon[1] )

Added: packages/openev/branches/upstream/current/pymod/ibrowse.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/ibrowse.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/ibrowse.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+
+import gviewapp
+import gview
+import gtk
+from gtk import FALSE, TRUE
+import GDK
+import sys
+import random
+import os
+
+
+def force_load( filename ):
+    ds = gview.manager.get_dataset( filename )
+    if ds is None:
+        return ds
+    
+    band_list = []
+    for band in range(0,ds.RasterCount):
+        band_list.append( ds.GetRasterBand(band+1) )
+
+    for line in range(0,ds.RasterYSize):
+        for band in range(0,ds.RasterCount):
+            band_list[band].ReadRaster( 0, line, ds.RasterXSize, 1 )
+
+    if ds.RasterCount >= 3:
+        rasters = (gview.manager.get_dataset_raster( ds, 1 ),
+                   gview.manager.get_dataset_raster( ds, 2 ),
+                   gview.manager.get_dataset_raster( ds, 3 ))
+    else:
+        rasters = [gview.manager.get_dataset_raster( ds, 1 )]
+
+    for rast in rasters:
+        rast.force_load()
+        
+    gview.file_list_ds.append(rasters)
+    del gview.file_list_ds[0]
+
+    return ds
+
+def clean_old_layer():
+    view = gview.app.view_manager.get_active_view()
+
+    layers = view.list_layers()
+    if len(layers) > 1:
+        view.remove_layer( layers[0] )
+
+    gview.request_clean = 0
+
+    try:
+        ds = force_load( gview.file_list[gview.file_cur+1] )
+    except:
+        pass
+        
+def request_clean( *args ):
+    gview.request_clean = 1
+    
+def update_view():
+    view = gview.app.view_manager.get_active_view()
+
+    cur_file = gview.file_list[gview.file_cur]
+    print 'update_view:' + cur_file
+
+    ds = force_load( cur_file )
+    try:
+        ds = force_load( cur_file )
+    except:
+        os.system( 'ls -l ' + cur_file )
+        os.system( 'file ' + cur_file )
+        return
+
+    old_layer = view.active_layer()
+    
+    gview.app.file_open_by_name( cur_file )
+    view.fit_extents( 0, 0, ds.RasterXSize, ds.RasterYSize )
+
+    layer = view.active_layer()
+    layer.set_property( 'force_load', '100' )
+    view.queue_draw()
+    if old_layer is not None:
+        gview.manager.queue_task( 'cleaner', 15, request_clean )
+
+    if old_layer is not None:
+        view.remove_layer( old_layer )
+        
+def advance( step = 1 ):
+    gview.file_cur = gview.file_cur + step
+    if gview.file_cur < 0:
+        gview.file_cur = 0
+    if gview.file_cur >= len(gview.file_list):
+        gview.file_cur = len(gview.file_list)-1
+        
+    update_view()
+    
+def key_press_cb( viewarea, event, *args ):
+    print event.keyval
+    if event.keyval == GDK.space:
+        advance(1)
+
+    if event.keyval == GDK.minus:
+        advance(-1)
+
+    if event.keyval == GDK.q:
+        sys.exit()
+
+    if event.keyval == GDK.j:
+        advance(10)
+
+    if event.keyval == GDK.p:
+        advance(-10)
+
+    if event.keyval == GDK.d:
+        os.unlink( gview.file_list[gview.file_cur] )
+        advance()
+
+    if event.keyval == GDK.r:
+        gview.file_cur = int(random.random() * len(gview.file_list))
+        update_view()
+        
+
+
+# #############################################################################
+# Main
+
+app = gviewapp.GViewApp()
+gview.app = app
+app.subscribe('quit',gtk.mainquit)
+app.show_layerdlg()
+app.new_view(None)
+app.do_auto_imports()
+
+view = gview.app.view_manager.get_active_view()
+view.connect('key-press-event', key_press_cb)
+
+gview.file_list = []
+gview.file_list_ds = [None, None, None]
+gview.file_cur = 0
+
+# Command line parser
+i = 1
+while i < len(sys.argv):
+    arg = sys.argv[i]
+    gview.file_list.append( arg )
+    i = i + 1
+
+update_view()
+
+gview.request_clean = 0
+while 1:
+    gtk.mainiteration()
+
+    if gview.request_clean == 1:
+        clean_old_layer()


Property changes on: packages/openev/branches/upstream/current/pymod/ibrowse.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/layerdlg.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/layerdlg.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/layerdlg.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,432 @@
+##############################################################################
+# $Id: layerdlg.py,v 1.33 2003/09/09 15:18:46 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Layer Management Dialog
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: layerdlg.py,v $
+#  Revision 1.33  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.32  2003/07/28 19:42:34  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.31  2003/02/20 19:27:22  gmwalter
+#  Updated link tool to include Diana's ghost cursor code, and added functions
+#  to allow the cursor and link mechanism to use different gcps
+#  than the display for georeferencing.  Updated raster properties
+#  dialog for multi-band case.  Added some signals to layerdlg.py and
+#  oeattedit.py to make it easier for tools to interact with them.
+#  A few random bug fixes.
+#
+#  Revision 1.30  2001/06/22 13:33:29  warmerda
+#  fix crash when last view removed from layerdlg
+#
+#  Revision 1.29  2000/09/26 15:11:57  srawlin
+#  changed get_selected_layer() to return the active view names as well as layer
+#
+#  Revision 1.28  2000/09/25 14:08:28  srawlin
+#  added get_selected_layer()
+#
+#  Revision 1.27  2000/08/23 15:18:27  srawlin
+#  changed window policy to allow resizing
+#
+#  Revision 1.26  2000/08/15 21:24:37  srawlin
+#  fixed list_layers to work if selected_view is None
+#
+#  Revision 1.25  2000/08/10 15:59:29  warmerda
+#  added help topic
+#
+#  Revision 1.24  2000/08/08 20:59:36  warmerda
+#  use SELECTION_SINGLE, don't force auto-selection of active layer
+#
+#  Revision 1.23  2000/08/08 20:11:55  warmerda
+#  don't reset active layer while updating display
+#
+#  Revision 1.22  2000/07/17 17:12:57  warmerda
+#  register new layers for undo
+#
+#  Revision 1.21  2000/06/28 17:04:19  srawlin
+#  dialog not visible when launched
+#
+#  Revision 1.20  2000/06/20 15:29:58  warmerda
+#  removed debugging statement
+#
+#  Revision 1.19  2000/06/20 12:28:01  warmerda
+#  fixed delete layer, made thumbnails optional
+#
+#  Revision 1.18  2000/06/19 19:20:42  warmerda
+#  implemented layer creation, attempted destruction
+#
+#  Revision 1.17  2000/06/14 21:43:46  warmerda
+#  slightly improve updating logic
+#
+#  Revision 1.16  2000/06/12 19:23:10  warmerda
+#  signal on view changes, allow outside setting of view
+#
+#  Revision 1.15  2000/06/09 01:04:14  warmerda
+#  added standard headers
+#
+
+from gtk import *
+import gview
+import os.path
+import gvsignaler
+import gvhtml
+
+THUMB_W = 24
+THUMB_H = 32
+EYE_W = 24
+
+# FIXME: Need a global tooltips object?
+tooltips = GtkTooltips()
+
+static_layer_dialog = None
+
+def Launch():
+    global static_layer_dialog
+    
+    if static_layer_dialog is None:
+        static_layer_dialog = LayerDlg()
+
+    return static_layer_dialog
+
+class LayerDlg(GtkWindow,gvsignaler.Signaler):
+    def __init__(self):
+        GtkWindow.__init__(self)
+        self.set_title('Layers')
+        self.set_usize(250, 500)
+        self.set_border_width(3)
+        self.set_policy(TRUE,TRUE,FALSE)
+        self.connect('delete-event',self.close)
+        shell = GtkVBox(spacing=3)
+        self.add(shell)
+        gvhtml.set_help_topic(self, "layerdlg.html" );
+
+        # View chooser menu
+        hbox = GtkHBox(spacing=3)
+        shell.pack_start(hbox, expand=FALSE)
+        hbox.pack_start(GtkLabel('View:'), expand=FALSE, padding=3)
+        viewopt = GtkOptionMenu()
+        hbox.pack_start(viewopt)
+        viewmenu = GtkMenu()
+        viewopt.set_menu(viewmenu)
+
+        # Do we want to include a thumbnail?  This is buggy on some platforms.
+        if gview.get_preference('layer_thumbnail') is None \
+           or gview.get_preference('layer_thumbnail') == 'off':
+            self.thumbnail = FALSE
+        else:
+            self.thumbnail = TRUE
+
+        self.updating = FALSE
+
+        # Layer list
+        layerbox = GtkScrolledWindow()
+        shell.pack_start(layerbox)
+        if self.thumbnail:
+            layerlist = GtkCList(cols=3)
+        else:
+            layerlist = GtkCList(cols=2)
+            
+        layerbox.add_with_viewport(layerlist)
+        layerlist.set_shadow_type(SHADOW_NONE)
+        layerlist.set_selection_mode(SELECTION_SINGLE)
+        layerlist.set_row_height(THUMB_H + 4)
+        layerlist.set_column_width(0, EYE_W)
+        if self.thumbnail:
+            layerlist.set_column_width(1, THUMB_W + 4)
+        layerlist.connect('select-row', self.layer_selected)
+        layerlist.connect('button-press-event', self.list_clicked)
+
+        # Option buttons
+        opts = (('new.xpm', 'New layer', self.new_layer),
+                ('raise.xpm', 'Raise layer', self.raise_layer),
+                ('lower.xpm', 'Lower layer', self.lower_layer),
+                ('delete.xpm','Delete layer', self.delete_layer))
+        butbox = GtkHBox(spacing=1)
+        shell.pack_start(butbox, expand=FALSE)
+        for opt in opts:
+            but = GtkButton()
+            butbox.pack_start(but)        
+            but.add(GtkPixmap(self,os.path.join(gview.home_dir,'pics',opt[0])))
+            tooltips.set_tip(but, opt[1])
+            but.connect('clicked', opt[2])
+
+        self.connect('realize', self.realize)
+
+        shell.show_all()
+        self.viewopt = viewopt
+        self.viewmenu = viewmenu
+        self.layerlist = layerlist
+        self.views = {}
+        self.menuitems = {}
+        self.selected_view = None
+
+        self.eye_pixmap = \
+            GtkPixmap(self,os.path.join(gview.home_dir,'pics','eye.xpm'))
+
+        # Publish signals
+        self.publish('active-view-changed')
+        self.publish('deleted-layer')
+        
+        
+    def close(self,*args):
+        self.hide()
+        return TRUE
+
+    def list_layers(self):
+        lst = []
+        if self.selected_view is not None:
+            lst = self.views[self.selected_view].list_layers()
+            # Reverse the list since we want the last draw layer listed first.
+            lst.reverse()
+        return lst
+
+    def add_view(self, name, view):
+        # FIXME: connect to view 'destroy' event ?
+        self.views[name] = view
+        menuitem = GtkMenuItem(name)
+        self.viewmenu.append(menuitem)
+        menuitem.connect('activate', self.view_selected, name)
+        menuitem.show()
+        self.menuitems[name] = menuitem
+        if self.viewmenu.get_active() == menuitem:
+            self.viewopt.set_history(0)
+            menuitem.activate()
+
+    def remove_view(self, name):
+        try:
+            view = self.views[name]
+            menuitem = self.menuitems[name]
+        except KeyError:
+            return
+        self.viewmenu.remove(menuitem)
+        self.viewopt.set_history(0)
+        del self.views[name]
+        del self.menuitems[name]
+        if len(self.menuitems) > 0:
+            newitem = self.viewmenu.get_active()
+            newitem.activate()
+            
+        if len(self.views) == 0:
+            # FIXME: things get kind of screwed up here...
+            # there doesn't seem to be a way to tell GtkMenu/GtkOptionMenu
+            # that there is nothing active.  This at least is stable...
+            # Possible solution: rebuild viewmenu on each view add/remove.
+            view.disconnect(self.active_change_id)
+            self.selected_view = None
+            self.layerlist.clear()
+
+    def view_selected(self, item, name):
+        # don't use item - view_selected() is called from gvapp with item=None
+        if name == self.selected_view: return
+        if self.selected_view:
+            self.views[self.selected_view].disconnect(self.active_change_id)
+        self.selected_view = name
+        i = 0
+        for x in self.viewmenu.children():
+            if x == self.menuitems[name]:
+                self.viewopt.set_history(i)
+                break
+            i = i + 1
+        self.notify('active-view-changed')
+
+        view = self.views[name]
+        self.active_change_id = view.connect('active-changed',
+                                             self.active_layer_changed)
+
+        self.update_layers()
+        self.active_layer_changed(view)
+        
+        self.notify( 'active-view-changed')
+
+    def get_active_view(self):
+        if self.selected_view:
+            return self.views[self.selected_view]
+        else:
+            return None
+
+    def update_layers(self,*args):
+        if not self.flags() & REALIZED: return
+
+        self.updating = TRUE
+        
+        lst = self.layerlist
+        view = self.views[self.selected_view]
+        layers = self.list_layers()
+
+        # get active layer so we can restore after
+        active = view.active_layer()
+        if active is not None and active in layers:
+            active_row = layers.index(active)
+        else:
+            active_row = None
+
+        if self.thumbnail:
+            thumbnail_mask = create_bitmap_from_data(
+                self.get_window(), '\xff' * (THUMB_W/8 * THUMB_H),
+                THUMB_W, THUMB_H)
+            
+        lst.freeze()
+        lst.clear()
+        
+        for i in range(len(layers)):
+            if self.thumbnail:
+                lst.append(('', '', layers[i].get_name()))
+            else:
+                lst.append(('', layers[i].get_name()))
+                
+            if layers[i].is_visible():
+                lst.set_pixmap(i, 0, self.eye_pixmap)
+
+            if self.thumbnail:
+                try:
+                    thumbnail = view.create_thumbnail(layers[i],
+                                                      THUMB_W, THUMB_H)
+                    lst.set_pixmap(i, 1, thumbnail, thumbnail_mask)
+                except:
+                    pass
+
+        # restore active layer selection
+        if active_row is not None:
+            lst.select_row(active_row, -1)
+
+        lst.thaw()        
+        self.updating = FALSE
+
+    def realize(self, widget):
+        if self.selected_view:
+            self.update_layers()
+
+    def active_layer_changed(self, view):
+        self.update_layers()
+        layers = self.list_layers()
+        active = view.active_layer()
+        if active is not None and active in layers:
+            self.layerlist.select_row(layers.index(active), -1)
+        
+    def layer_selected(self, lst, row, col, event):
+        if self.updating:
+            return
+        
+        view = self.views[self.selected_view]
+        layers = self.list_layers()
+        view.signal_handler_block(self.active_change_id)
+        view.set_active_layer(layers[row])
+        view.signal_handler_unblock(self.active_change_id)
+
+    def toggle_visibility(self, row):
+        layers = self.list_layers()
+        lst = self.layerlist
+        if lst.get_cell_type(row, 0) == CELL_PIXMAP:
+            layers[row].set_visible(FALSE)
+        else:
+            layers[row].set_visible(TRUE)
+
+        self.update_layers()
+
+    def launch_properties(self, row):
+        layers = self.list_layers()
+        layer = layers[row]
+        layer.launch_properties()
+            
+    def list_clicked(self, lst, event):
+        try:
+            row, col = lst.get_selection_info(int(event.x), int(event.y))
+        except:
+            return
+        
+        if event.button == 1:
+            if col == 0:
+                lst.emit_stop_by_name('button-press-event')
+                self.toggle_visibility(row)
+
+        elif event.button == 3:
+            lst.emit_stop_by_name('button-press-event')
+            self.launch_properties(row)
+
+    def new_layer(self, *args):
+        if not self.selected_view:
+            return
+        view = self.views[self.selected_view]
+        layer_list = view.list_layers()
+        layer_map = {}
+        for layer in layer_list:
+            layer_map[layer.get_name()] = layer
+
+        counter = 1
+        name = 'UserShapes_'+str(counter)
+        while layer_map.has_key(name):
+            counter = counter + 1
+            name = 'UserShapes_'+str(counter)
+        
+        shapes = gview.GvShapes(name=name)
+        gview.undo_register(shapes)
+        layer = gview.GvShapesLayer(shapes)
+        view.add_layer(layer)
+        view.set_active_layer(layer)
+        
+
+    def raise_layer(self, *args):
+        if not self.selected_view: return
+        view = self.views[self.selected_view]        
+        layers = self.list_layers()
+        row = layers.index(view.active_layer())
+        index = len(layers) - row - 1
+        if row == 0: return
+        self.layerlist.swap_rows(row-1, row)
+        view.swap_layers(index, index+1)
+
+    def lower_layer(self, *args):
+        if not self.selected_view: return
+        view = self.views[self.selected_view]        
+        layers = self.list_layers()
+        row = layers.index(view.active_layer())
+        index = len(layers) - row - 1
+        if index == 0: return
+        self.layerlist.swap_rows(row, row+1)
+        view.swap_layers(index-1, index)
+
+    def delete_layer(self, *args):
+        if not self.selected_view: return
+        view = self.views[self.selected_view]
+        layer = view.active_layer()
+        layername = layer.get_name()
+        if layer is not None:
+            view.remove_layer(layer)
+            
+        self.notify('deleted-layer',view,layername)
+        
+
+    def get_selected_layer(self, *args):
+        """ Returns a tuple with the name of the active view and the object with the currently selected layer
+            in that view.  From the layer you can get the layer name another other useful properties of the
+            layer. """
+        if not self.selected_view: return
+        view = self.views[self.selected_view]
+        return (self.selected_view, view.active_layer())

Added: packages/openev/branches/upstream/current/pymod/makefile.vc
===================================================================
--- packages/openev/branches/upstream/current/pymod/makefile.vc	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/makefile.vc	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,49 @@
+GVIEW_ROOT=..
+
+!INCLUDE ..\nmake.opt
+
+PKG_SRCDIR = $(NM_ROOT)
+PYGTKDIR = $(NM_ROOT)\pygtk-0.6.6
+GTKDIR = $(NM_GTK_DIR)
+GLIBDIR = $(NM_GLIB_DIR)
+
+PYGVIEW_DLL	=	_gv.dll
+PYGTKMISSING_DLL	=	_gtkmissing.dll
+
+INC = /I$(GLIBDIR) /I$(NM_GLIB_DIR)/gmodule /I$(GTKDIR) /I$(GTKDIR)/gdk \
+	/I$(NM_GTKGLAREA_DIR) /I$(NM_GTKGLAREA_DIR) \
+	/I$(GDAL_HOME)\port /I$(GDAL_HOME)\gcore /I$(GDAL_HOME)/ogr \
+	/I.. /I. -I$(NM_PY_DIR)/include -I$(PYGTKDIR)
+
+CFLAGS =	$(INC) $(GV_OPTFLAGS) /nologo
+
+LIBS = $(GDAL_HOME)/gdal_i.lib $(NM_GTKGLAREA_DIR)/gtkglarea.lib \
+	$(GTKDIR)/gtk/gtk-1.3.lib \
+	$(GTKDIR)/gdk/gdk-1.3.lib $(GLIBDIR)/glib-1.3.lib \
+	comdlg32.lib GLU32.lib opengl32.lib gdi32.lib user32.lib shell32.lib
+
+default:	$(PYGVIEW_DLL) $(PYGTKMISSING_DLL)
+
+$(PYGVIEW_DLL):	gvmodule.obj ../gv.lib
+	link /dll /debug \
+		/def:_gview.def gvmodule.obj ../gv.lib $(LIBS) \
+		/LIBPATH:$(NM_PY_DIR)/libs \
+		/out:$(PYGVIEW_DLL)
+	
+$(PYGTKMISSING_DLL):	gtkmissing.obj
+	link /dll /debug \
+		/def:_gtkmissing.def gtkmissing.obj $(LIBS) \
+		/LIBPATH:$(NM_PY_DIR)/libs \
+		/out:$(PYGTKMISSING_DLL)
+	
+.c.obj:	
+	$(CC) $(CFLAGS) /c $*.c
+
+clean:
+	-del *.obj
+	-del *.dll
+	-del *.lib
+
+install:	default
+	copy *.dll $(NM_PYMOD_DIR)
+	copy *.py $(NM_PYMOD_DIR)

Added: packages/openev/branches/upstream/current/pymod/mkgv.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/mkgv.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/mkgv.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,6 @@
+import generate
+
+p = generate.FilteringParser(input='gv.defs',
+                             prefix='gvmodule',
+                             typeprefix='&')
+p.startParsing()

Added: packages/openev/branches/upstream/current/pymod/nls.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/nls.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/nls.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,207 @@
+###############################################################################
+# $Id: nls.py,v 1.6 2005/03/16 18:57:04 zjamesatdm Exp $
+#
+# Project:  OpenEV
+# Purpose:  NLS localisation module
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: nls.py,v $
+#  Revision 1.6  2005/03/16 18:57:04  zjamesatdm
+#  allow leading space in localised strings
+#
+#  Revision 1.5  2003/04/03 21:57:05  pgs
+#  added a method to put values into the nls dictionary
+#
+#  Revision 1.4  2002/08/16 18:58:58  pgs
+#  fixed typo.
+#
+#  Revision 1.3  2002/08/16 17:49:56  pgs
+#  changed prints for gdal.Debug
+#
+#  Revision 1.2  2001/03/21 04:30:05  pgs
+#  handle \\n in input file
+#
+#  Revision 1.1  2001/03/11 21:52:30  pgs
+#  new file
+#
+#
+
+"""
+OpenEV Localization Module.
+
+This module is designed to provide the programmer with a simplified interface
+to provided customizable or multi-lingual interfaces.
+
+After this module is imported, call get_locales() to determine which modules
+are available and set_locale(name) to set the current locale.  If no locale
+is set, the module will always return the default value from
+get(key, default).
+
+Variables:
+----------
+
+_locales - dictionary, a mapping of locale names to locale files
+_locale  - the current locale dictionary
+
+Methods:
+--------
+
+_load_locale(fname) - loads the locale file as the current locale
+get_locales() - returns the names of the locales that can be selected
+set_locale(name) - sets the current locale to name
+get(key, default) - gets the localized value of key or default if not found
+"""
+
+_locales = {}
+_locale = None
+
+
+
+#load locale files from the locale directory and parse them for locale
+#names ... this only happens on the first import in any particular
+#python run-time
+
+import gview
+import os
+import string
+import gdal
+
+gdal.Debug( "nls", 'initializing localization module' )
+
+locale_dir = os.path.join(gview.find_gview(), "locales")
+
+if os.path.isdir(locale_dir):
+    gdal.Debug( "nls", 'loading localization files from %s' % locale_dir )
+    fnames = os.listdir(locale_dir)
+    for fname in fnames:
+        fname = os.path.join(locale_dir, fname)
+        gdal.Debug( "nls",  'processing file %s' % fname )
+        if os.path.isfile(fname):
+            file = open(fname, 'r')
+            loc_dict = {}
+            lines = file.readlines()
+            for line in lines:
+                if string.find(line, '=') >= 0:
+                    key, value = string.split(line, '=')
+
+                    if key == 'locale-name':
+                        value = string.strip(value)
+                        gdal.Debug( "nls", 'loading %s locale' % value )
+                        _locales[value] = fname
+                        break
+            file.close()
+            lines = None
+        else:
+            print '%s is not a file' % fname
+    gdal.Debug( "nls", 'localization initialization complete' )
+else:
+    gdal.Debug( "nls", 'cannot locate localization files' )
+
+def _load_locale(fname):
+    """
+    open a locale file and set the internal locale
+
+    fname - string, the name of the locale file to load
+    """
+    global _locale
+    try:
+        file = open(fname, 'r')
+    except:
+        gdal.Debug( "nls", 'load locale failed' )
+        _locale = None
+        return
+
+    loc_dict = {}
+    lines = file.readlines()
+    for line in lines:
+        if line[0] == '#':
+            continue
+        try:
+            if string.find(line, '=') >= 0:
+                key, value = string.split(line, '=')
+                #take care with CR-LF
+                #does this work on UNIX?
+                value = string.rstrip(value)
+                value = string.replace(value, '\\n', '\n')
+                loc_dict[key] = value
+        except:
+            gdal.Debug( "nls", 'an exception occurred in nls.py' )
+            pass
+
+    file.close()
+    _locale = loc_dict
+    try:
+        gdal.Debug( "nls",  'locale changed to %s' % loc_dict['locale-name'] )
+    except:
+        gdal.Debug( "nls",  'unknown locale loaded' )
+
+def set_locale(locale_name):
+    """
+    change locales to the specified locale name
+
+    locale_name - string, the name of the locale (must be one of the
+                  keys in 'locales'
+    """
+    global _locales
+    try:
+        fname = _locales[locale_name]
+        _load_locale(fname)
+    except:
+        gdal.Debug( "nls",  'locale %s not found' % locale_name )
+
+def get_locales():
+    """
+    Return a list of available locales
+    """
+    global _locales
+    return _locales.keys()
+
+def get(key, default):
+    """
+    Return a localized value from the current locale or the default
+    value if the the value isn't localized in the current locale
+
+    key - the name of the value to get
+    default - the value to return if the key isn't found
+    """
+    global _locale
+
+    if _locale is None:
+        return default
+
+    try:
+        return _locale[key]
+    except:
+        return default
+
+def set( key, value ):
+    """
+    add a value to the locale
+    
+    key - the key to add
+    value - the value to add
+    """
+    global _locale
+    if _locale is None:
+        return
+        
+    _locale[key] = value

Added: packages/openev/branches/upstream/current/pymod/oe_about.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/oe_about.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/oe_about.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: oe_about.py,v 1.1 2005/08/07 18:22:57 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Subscript for OpenEV About Box
+# Author:   Frank Warmerdam <warmerdam at pobox.com>
+#           Gillian Walter <gillian.walter at vexcel.com>
+#
+###############################################################################
+# Copyright (c) 2005, Vexcel Canada Inc. (www.vexcel.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: oe_about.py,v $
+#  Revision 1.1  2005/08/07 18:22:57  warmerda
+#  New
+#
+#
+
+import gtk
+from gtk import FALSE, TRUE
+from GDK import *
+import sys
+import os
+import gview
+import gvconst
+
+def ShowAboutBox( viewwindow ):
+
+    window = gtk.GtkWindow()
+    window.set_title('About OpenEV')
+    vbox = gtk.GtkVBox(homogeneous=FALSE,spacing=15)
+    window.add(vbox)
+
+    vbox.pack_start(gtk.GtkPixmap(viewwindow,
+                                  os.path.join(gview.home_dir,'pics',
+                                               'openev.xpm')))
+    
+    # Contributors
+    contrib = gtk.GtkVBox(homogeneous=FALSE,spacing=3)
+    contrib.pack_start(gtk.GtkLabel('Contributors:'))
+    contrib.pack_start(gtk.GtkLabel('Frank Warmerdam (warmerdam at pobox.com),'))
+    contrib.pack_start(gtk.GtkLabel('Gillian Walter (gillian.walter at vexcel.com),'))
+    contrib.pack_start(gtk.GtkLabel('Peter Farris-Manning (peter.farris-manning at vexcel.com),'))
+    contrib.pack_start(gtk.GtkLabel('Paul Spencer (pagemeba at magma.ca),'))
+    contrib.pack_start(gtk.GtkLabel('Steve Rawlinson,'))
+    contrib.pack_start(gtk.GtkLabel('Steve Taylor,'))
+    contrib.pack_start(gtk.GtkLabel('Paul Lahaie,'))
+    contrib.pack_start(gtk.GtkLabel('and others'))
+    vbox.pack_start(contrib)
+
+    # Funded By
+    funding = gtk.GtkVBox(homogeneous=FALSE,spacing=3)
+    funding.pack_start(gtk.GtkLabel('Funding provided by:'))
+    funding.pack_start(gtk.GtkPixmap(viewwindow,
+                                     os.path.join(gview.home_dir, 'pics', 'vexcel_logo.xpm')))
+    funding.pack_start(gtk.GtkLabel('Vexcel Canada Inc.'))
+    funding.pack_start(gtk.GtkPixmap(viewwindow,
+                                     os.path.join(gview.home_dir,'pics','geo_innovation.xpm')))
+    funding.pack_start(gtk.GtkLabel('GeoInnovations'))
+    vbox.pack_start(funding)
+
+    # Other Info
+    vbox.pack_start(gtk.GtkLabel('Version: 1.8'))        
+    vbox.pack_start(gtk.GtkLabel('Web Site:  http://OpenEV.sourceforge.net'))
+    vbox.pack_start(gtk.GtkLabel('(C) Copyright 2000 Vexcel Canada Inc.  www.vexcel.com'))
+        
+    window.show_all()
+

Added: packages/openev/branches/upstream/current/pymod/oeattedit.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/oeattedit.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/oeattedit.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,302 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: oeattedit.py,v 1.9 2003/02/20 19:27:23 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Shape attribute display, and editing.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: oeattedit.py,v $
+#  Revision 1.9  2003/02/20 19:27:23  gmwalter
+#  Updated link tool to include Diana's ghost cursor code, and added functions
+#  to allow the cursor and link mechanism to use different gcps
+#  than the display for georeferencing.  Updated raster properties
+#  dialog for multi-band case.  Added some signals to layerdlg.py and
+#  oeattedit.py to make it easier for tools to interact with them.
+#  A few random bug fixes.
+#
+#  Revision 1.8  2002/11/14 15:43:14  gmwalter
+#  Fixed a few bugs in the closing/reopening code.
+#
+#  Revision 1.7  2002/09/12 17:08:45  gmwalter
+#  *** empty log message ***
+#
+#  Revision 1.6  2002/09/12 13:08:14  gmwalter
+#  Updated oeattedit.py so that new fields can be added to shapefiles via the
+#  gui.  Minor changes to aclocal.m4/configure to ensure that specified
+#  libraries are picked up rather than defaults.
+#
+#  Revision 1.5  2002/07/12 12:46:06  warmerda
+#  expanded tabs
+#
+#  Revision 1.4  2001/04/09 18:27:45  warmerda
+#  upgraded to support subselection
+#
+#  Revision 1.3  2000/08/11 20:46:48  warmerda
+#  get rid of horizontal scrollbar, fix original width
+#
+#  Revision 1.2  2000/08/04 14:10:48  warmerda
+#  report shapeid in titlebar
+#
+#  Revision 1.1  2000/07/10 20:59:04  warmerda
+#  New
+#
+#
+
+from gtk import *
+import gview
+import string           
+import gvselbrowser
+import gvutils
+import GtkExtra
+import gvsignaler
+
+def launch():
+    try:
+        gview.oeattedit.get_window()._raise()
+        gview.oeattedit.show()
+        gview.oeattedit.reconnect()
+        gview.oeattedit.gui_update()
+    except:
+        try:
+            # oeattedit has been created,
+            # but not shown
+            gview.oeattedit.show()
+            gview.oeattedit.reconnect()
+            gview.oeattedit.gui_update()
+        except:    
+            gview.oeattedit = OEAttEdit()
+            gview.oeattedit.show()
+
+    return gview.oeattedit
+
+def launch_hidden():
+    if not hasattr(gview,'oeattedit'):
+        gview.oeattedit = OEAttEdit()
+        
+class OEAttEdit(GtkWindow,gvsignaler.Signaler):
+
+    def __init__(self):
+        GtkWindow.__init__(self)
+        
+        self.set_title('Shape Attributes')
+        gview.app.sel_manager.subscribe( 'selection-changed',
+                                         self.gui_update )
+        gview.app.sel_manager.subscribe( 'subselection-changed',
+                                         self.gui_update )
+
+        # signal for external tools to connect to
+        self.publish('hidden')
+        self.publish('shown')
+        
+        self.text_contents = ''
+        self.selected_shape = None
+        self.layer = None
+        self.create_gui()
+
+        self.visibility_flag = 0
+        self.gui_update()
+        self.connect('delete-event', self.close)
+
+    def show(self):
+        GtkWindow.show(self)
+        self.visibility_flag = 1
+        self.notify('shown')
+
+    def close(self, *args):
+        gview.app.sel_manager.unsubscribe( 'selection-changed',
+                                           self.gui_update )
+        gview.app.sel_manager.unsubscribe( 'subselection-changed',
+                                           self.gui_update )
+        self.hide()
+        self.visibility_flag = 0
+        self.notify('hidden')
+        
+        return TRUE
+
+    def reconnect(self, *args):
+        gview.app.sel_manager.subscribe( 'selection-changed',
+                                         self.gui_update )
+        gview.app.sel_manager.subscribe( 'subselection-changed',
+                                         self.gui_update )
+        
+    def create_gui(self):
+        box1 = GtkVBox()
+        self.add(box1)
+        box1.show()
+
+        self.selbrowser = gvselbrowser.GvSelBrowser()
+        self.selbrowser.set_border_width(10)
+        box1.pack_start( self.selbrowser, expand=FALSE )
+
+        box2 = GtkVBox(spacing=10)
+        box2.set_border_width(10)
+        box1.pack_start(box2)
+        box2.show()
+
+        table = GtkTable(2, 2)
+        table.set_row_spacing(0, 2)
+        table.set_col_spacing(0, 2)
+        box2.pack_start(table)
+        table.show()
+
+        text = GtkText()
+        text.set_usize(400,100)
+        text.set_line_wrap(FALSE)
+        text.set_word_wrap(FALSE)
+        text.set_editable(TRUE)
+        table.attach(text, 0,1, 0,1)
+        text.show()
+        self.text = text
+        self.text.connect('activate', self.att_update_cb)
+        self.text.connect('leave-notify-event', self.att_update_cb)
+
+        vscrollbar = GtkVScrollbar(text.get_vadjustment())
+        table.attach(vscrollbar, 1,2, 0,1, xoptions=FILL)
+        vscrollbar.show()
+
+
+        separator = GtkHSeparator()
+        box1.pack_start(separator, expand=FALSE)
+        separator.show()
+
+        box2 = GtkVBox(spacing=10)
+        box2.set_border_width(10)
+        box1.pack_start(box2, expand=FALSE)
+        box2.show()
+
+        # new field options
+        box3 = GtkHBox(spacing=10)
+        box3.set_border_width(10)
+        nf_frame = GtkFrame('New field properties: type/width/precision')
+        nf_frame.add(box3)
+        self.new_field_width_entry = GtkEntry(2)
+        self.new_field_width_entry.set_text('20')
+        self.new_field_width_entry.set_editable(TRUE)        
+        self.new_field_precision_entry = GtkEntry(2)
+        self.new_field_precision_entry.set_text('0')
+        self.new_field_precision_entry.set_editable(FALSE)
+        self.new_field_precision_entry.set_sensitive(FALSE)
+        
+        self.new_field_types = ('string','integer','float')
+        self.new_field_type_menu = gvutils.GvOptionMenu(self.new_field_types, self.new_field_precision_cb)
+        self.new_field_type_menu.set_history(0)
+        box3.pack_start(self.new_field_type_menu)
+        box3.pack_start(self.new_field_width_entry,expand=FALSE,fill=FALSE)
+        box3.pack_start(self.new_field_precision_entry,expand=FALSE,fill=FALSE)
+        box2.pack_start(nf_frame)
+        nf_frame.show_all()
+        
+        button = GtkButton("close")
+        button.connect("clicked", self.close)
+        box2.pack_start(button)
+        button.set_flags(CAN_DEFAULT)
+        button.grab_default()
+        button.show()
+
+    def new_field_precision_cb(self,*args):
+        if self.new_field_types[self.new_field_type_menu.get_history()] == 'float':
+            # precision is only relevant for float
+            self.new_field_precision_entry.set_editable(TRUE)
+            self.new_field_precision_entry.set_sensitive(TRUE)
+        else:
+            self.new_field_precision_entry.set_text('0')
+            self.new_field_precision_entry.set_editable(FALSE)
+            self.new_field_precision_entry.set_sensitive(FALSE)
+            
+    def gui_update(self,*args):
+        self.text_contents = ''
+        self.text.freeze()
+        self.text.delete_text(0,-1)
+
+        self.selected_shape = None
+
+        try:
+            self.layer = gview.app.sel_manager.get_active_layer()
+            shapes = self.layer.get_parent()
+            self.selected_shape = self.layer.get_subselected()
+            properties = shapes[self.selected_shape].get_properties()
+            for att_name in properties.keys():
+                self.text_contents = self.text_contents + \
+                        att_name + ': ' + properties[att_name] + '\n'
+            self.text.insert_defaults(self.text_contents)
+        except:
+            pass
+
+        self.text.thaw()
+
+    def att_update_cb(self,*args):
+        if self.text_contents == self.text.get_chars(0,-1):
+            return
+
+        if self.selected_shape is None:
+            return
+
+        shapes = self.layer.get_parent()
+        shape = shapes[self.selected_shape]
+        if shape is None:
+            return
+
+        shape = shape.copy()
+        
+        lines = string.split(self.text.get_chars(0,-1),'\n')
+        for line in lines:
+            tokens = string.split(line,':',1)
+            if len(tokens) == 2:
+                value = string.strip(tokens[1])
+                shape.set_property(tokens[0],value)
+                property_exists=0
+                for cprop in shapes.get_schema():
+                    if cprop[0] == tokens[0]:
+                        property_exists=1
+                if property_exists != 1:
+                    ftype = self.new_field_types[self.new_field_type_menu.get_history()]
+                       
+                    response = \
+                       GtkExtra.message_box('Confirmation',
+                         'Create new ' + ftype + '-type property ' + tokens[0] + '?' ,
+                                            ('Yes','No'))
+                    if response == 'Yes':
+                        try:
+                            fwidth = int(self.new_field_width_entry.get_text())
+                        except:
+                            gvutils.error('Field width must be an integer!')
+                            continue
+                        
+                        if ftype == 'float':
+                            try:
+                                fprec = int(self.new_field_width_entry.get_text())
+                            except:
+                                gvutils.error('Precision width must be an integer!')
+                                continue
+                        else:
+                            fprec = 0
+                            
+                        shapes.add_field(tokens[0],ftype,fwidth,fprec)
+
+        shapes[self.selected_shape] = shape
+        self.gui_update()
+
+    def insert_text_cb(self,new_text,*args):
+        if new_text[0] == '\n':
+            self.att_update_cb()
+            return FALSE

Added: packages/openev/branches/upstream/current/pymod/openev.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/openev.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/openev.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: openev.py,v 1.46 2005/01/14 17:17:18 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  OpenEV Application Mainline
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: openev.py,v $
+#  Revision 1.46  2005/01/14 17:17:18  warmerda
+#  pass base config file names, not full path, to new_view()
+#
+#  Revision 1.45  2003/10/29 14:58:00  gmwalter
+#  Force floating point to use "." instead of "," to avoid problems on
+#  foreign language windows.
+#
+#  Revision 1.44  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.43  2003/07/28 19:42:34  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.42  2002/02/28 18:52:22  gmwalter
+#  Added a point-of-interest tool similar to the region-of-interest
+#  tool (allows a user to select a temporary point without having to add a
+#  new layer).  Added a mechanism to allow some customization of openev
+#  via a textfile defining external modules.
+#
+#  Revision 1.41  2001/11/12 18:42:56  warmerda
+#  GViewApp moved to gviewapp.py
+#
+#  Revision 1.40  2001/10/25 20:30:46  warmerda
+#  added interp_mode preference to control default subpixel interp
+#
+#  Revision 1.39  2001/10/19 16:08:57  warmerda
+#  dont put NUMPY:: files in RFL list
+#
+#  Revision 1.38  2001/10/16 18:52:40  warmerda
+#  added active_layer
+#
+#  Revision 1.37  2001/08/15 13:05:57  warmerda
+#  modified default autoscale std_dev to 2.5
+#
+#  Revision 1.36  2001/08/14 17:03:24  warmerda
+#  added standard deviation autoscaling support
+#
+#  Revision 1.35  2001/06/27 14:32:56  warmerda
+#  added subdataset selection support
+#
+#  Revision 1.34  2001/04/24 14:23:33  warmerda
+#  added label tool
+#
+#  Revision 1.33  2001/04/09 18:33:24  warmerda
+#  instantiate a GvSelectionManager
+#
+
+import gviewapp
+import gview
+import gtk
+import sys
+import os
+import getopt
+
+# Force standard c settings for floating point (. rather than ,)
+import locale
+locale.setlocale(locale.LC_NUMERIC,'C')
+
+
+if __name__ == '__main__':
+
+    # get command line options and args
+    # openev -m menufile -i iconfile -t toolfile image1 image2 ......
+    (options, ifiles) = getopt.getopt(sys.argv[1:], 'm:i:t:p:')
+
+    if os.path.isdir(os.path.join(gview.home_dir, 'xmlconfig')):
+        mfile = 'DefaultMenuFile.xml'
+        if not os.path.isfile(os.path.join(gview.home_dir, 'xmlconfig',mfile)):
+            mfile = None
+
+        ifile = 'DefaultIconFile.xml'
+        if not os.path.isfile(os.path.join(gview.home_dir, 'xmlconfig',ifile)):
+            ifile = None
+
+        pfile = 'DefaultPyshellFile.xml'
+        if not os.path.isfile(os.path.join(gview.home_dir, 'xmlconfig',pfile)):
+            pfile = None
+
+    else:
+        mfile=None
+        ifile=None
+        pfile=None
+        
+    tfile = None
+
+    for opt in options[0:]:
+        if opt[0] == '-m':
+            mfile=opt[1]
+        elif opt[0] == '-i':
+            ifile=opt[1]
+        elif opt[0] == '-p':
+            pfile=opt[1]
+        elif opt[0] == '-t':
+            tfile=opt[1]
+
+    app = gviewapp.GViewApp(toolfile=tfile,menufile=mfile,iconfile=ifile,pyshellfile=pfile)
+    gview.app = app
+    app.subscribe('quit',gtk.mainquit)
+    app.show_layerdlg()
+    app.new_view(title=None)
+    app.do_auto_imports()
+
+    for item in ifiles:
+        app.file_open_by_name(item)
+
+    gtk.mainloop()


Property changes on: packages/openev/branches/upstream/current/pymod/openev.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/pathutils.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pathutils.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pathutils.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,148 @@
+###############################################################################
+#
+# Project:  OpenEV
+# Purpose:  Paths utilities
+# Author:   Julien Demaria, demaria.julien at free.fr
+#
+###############################################################################
+# Copyright (c) 2004, Julien Demaria <demaria.julien at free.fr>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+
+
+import os.path
+
+try:
+    real_path = os.path.realpath
+except:
+    real_path = os.path.abspath
+
+def split_all( path ):
+    """
+    Return a list (whithout any '') corresponding to the splitted path.
+    """
+    splitted_path = []
+    (h, t) = os.path.split( path )
+    old_h = h + 'dummy'
+    while h != old_h:
+        old_h = h
+        if t != '':
+            splitted_path.insert( 0, t )
+        (h, t) = os.path.split( h )
+    if h != '':
+        splitted_path.insert( 0, h )
+    return splitted_path
+
+def join_all( path ):
+    """
+    Return a path from its corresponding splitted list form.
+    """
+    return apply( os.path.join, path )
+
+def relative_path( path, ref_path ):
+    """
+    Return the relative form of path, with ref_path as reference.
+    If path and ref_path don't have any common prefix,
+    raise ValueError, 'Not any common prefix'
+    The function internally works on os.path.realpath() forms
+    of path and ref_path.
+    """
+    realpath = real_path( path )
+    basepath = ''
+    basepath = os.path.basename( realpath )
+    realpath = os.path.dirname( realpath )
+
+    real_ref_path = real_path( ref_path )
+    real_ref_path = os.path.dirname( real_ref_path )
+
+    split_path = split_all( realpath )
+    split_ref_path = split_all( real_ref_path )
+
+    # Find common prefix
+    for i, v in enumerate( zip( split_path, split_ref_path ) ):
+        if v[0] != v[1]:
+            if i == 0:
+                # Case where the 2 paths don't have any common prefix
+                # (Note the under Unix platform there is always at least
+                #  '/' as common prefix...).
+                raise ValueError, 'Not any common prefix'
+            else:
+                break
+
+    if split_path[i] == split_ref_path[i]:
+        # Handle the case of one of the path is the prefix of the other
+        i += 1
+    path_tail = split_path[i:]
+    ref_path_tail = split_ref_path[i:]
+    rel_path = ['..'] * len( ref_path_tail )
+    rel_path.extend( path_tail )
+    if basepath != '':
+        rel_path.append( basepath )
+
+    return join_all( rel_path )
+
+def PortablePathFromXML( str ):
+    t = eval( str )
+    p = PortablePath( join_all( t[0] ) )
+    p.rel_path = t[1]
+    return p
+
+class PortablePath:
+
+    def __init__( self, path, ref_path = None ):
+        """
+        Create a portable path from a path and optionnaly a reference path.
+        A portable path contains :
+            - the normalized absolutized splitted form of the given path ;
+            - the relativized (with ref_path as reference) splitted form of
+              the given path if it exists.
+        A portable path internally uses os.path.realpath() forms of path and
+        ref_path.
+        """
+        self.abs_path = split_all( real_path( path ) )
+        try:
+            self.rel_path = split_all( relative_path( path, ref_path ) )
+        except:
+            self.rel_path = None
+
+    def local_path( self, ref_path = None ):
+        """
+        Return the path corresponding to the portable path for the current
+        local platform.
+        The method tests in the portable path the existence of, in this order :
+            - the normalized absolutized form of the original path ;
+            - the relativized (with ref_path as reference) form of
+              the original path ;
+        and then returns the first existing (in os.path.realpath() form).
+        If none of them exists,
+        raise ValueError, 'Cannot compute a local path from the portable path'
+        The method internally works on os.path.realpath() form of ref_path.
+        """
+        p = join_all( self.abs_path )
+        if os.path.exists( p ):
+            return p
+        else:
+            if self.rel_path is None:
+                raise ValueError, 'Cannot compute a local path from the portable path'
+            r = os.path.dirname( real_path( ref_path ) )
+            p = real_path( os.path.join( r, join_all( self.rel_path ) ) )
+            if os.path.exists( p ):
+                return p
+            raise ValueError, 'Cannot compute a local path from the portable path'
+
+    def serialize( self ):
+        return repr( [self.abs_path, self.rel_path] )


Property changes on: packages/openev/branches/upstream/current/pymod/pathutils.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/pgu.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgu.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgu.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,38 @@
+###############################################################################
+# $Id: pgu.py,v 1.1 2000/06/14 15:12:39 warmerda Exp $
+#
+# Project:  Python Gtk Utility Widgets
+# Purpose:  Core PGU stuff, such as registering new PyGtk classes.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pgu.py,v $
+#  Revision 1.1  2000/06/14 15:12:39  warmerda
+#  New
+#
+#
+
+import gtk; _gtk = gtk; del gtk
+from string import *
+
+def gtk_register(name,class_obj):
+    _gtk._name2cls[name] = class_obj
+    

Added: packages/openev/branches/upstream/current/pymod/pgucolor.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgucolor.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgucolor.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,423 @@
+###############################################################################
+# $Id: pgucolor.py,v 1.12 2003/09/09 15:18:46 gmwalter Exp $
+#
+# Project:  Python Gtk Utility Widgets
+# Purpose:  Color-related widgets and utilities.
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: pgucolor.py,v $
+#  Revision 1.12  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.11  2002/08/09 21:04:48  pgs
+#  added support for DISCRETE color ramps
+#
+#  Revision 1.10  2001/09/17 15:26:33  pgs
+#  moved more GtkColorWell code into gtkmissing.
+#
+#  Revision 1.9  2001/09/17 03:44:40  pgs
+#  added get_color to ColorButton
+#
+#  Revision 1.8  2001/09/17 03:41:52  pgs
+#  removed GtkColorWell (moved to gtkmissing) and added __init__ to
+#  ColorButton to initialize a standard color button.
+#
+#  Revision 1.7  2001/09/16 03:28:37  pgs
+#  Added GtkColorWell class (should be in GtkMissing?) and modified ColorButton
+#  to derive from it.
+#
+#  Revision 1.6  2001/05/08 23:37:07  pgs
+#  fixed bug allowing multiple color choosers for a single color button
+#
+#  Revision 1.5  2001/04/14 14:52:28  pgs
+#  added color_string_to_tuple
+#
+#  Revision 1.4  2001/04/11 03:04:51  pgs
+#  added get_color() to color swatch and opacity to ColorDialog
+#
+#  Revision 1.3  2000/09/28 22:45:15  pgs
+#  fixed up ColorRamp
+#
+#  Revision 1.1  2000/09/27 13:32:33  warmerda
+#  New
+#
+#
+
+MIN_COLOR=0
+MAX_COLOR = 65535
+
+import gtk
+from gtk import TRUE, FALSE
+import GDK
+from gvsignaler import Signaler
+import _gv
+from gtkmissing import GtkColorWell
+
+def color_string_to_tuple(s):
+    from string import replace, split
+    s = replace(s, '(', '')
+    s = replace(s, ')', '')
+    s = replace(s, ',', '')
+    r, g, b, a = split(s, None)
+    return (float(r), float(g), float(b), float(a))
+
+
+class ColorSwatch(gtk.GtkDrawingArea, Signaler):
+    """
+    Class ColorSwatch is a simple widget that
+    displays a color.
+
+    The color attribute is an RGBA tuple.  Internally, however,
+    a GdkColor object is used that has values in the range 0-65535
+    for red, green and blue.  The color display doesn't support
+    the alpha channel (yet?)
+
+    Don't use GdkColor(red, green, blue) to allocate colors ... use
+    GdkColormap.alloc instead and get a reference to the GdkColormap
+    from the widget (self.get_colormap())
+    """
+    def __init__(self, color=(0,0,0,0)):
+        gtk.GtkDrawingArea.__init__(self)
+        self.size(20, 15)
+        self.connect('configure-event', self.configure_event)
+        self.connect('expose-event', self.expose_event)
+        self.connect('realize', self.realize_event)
+        self.connect('unrealize', self.unrealize_event)
+        self.color = color
+        #the color - use GdkColorMap's alloc method to get it
+        cm = self.get_colormap()
+        self.icolor = cm.alloc(int(color[0] * MAX_COLOR), \
+                               int(color[1] * MAX_COLOR), \
+                               int(color[2] * MAX_COLOR))
+        self.publish('color-changed')
+        #cached graphics context
+        self.gc = None
+
+    def realize_event(self, *args):
+        self.gc = self.get_window().new_gc(foreground=self.icolor)
+
+    def unrealize_event(self, *args):
+        #remove references to the gc to prevent leaks?
+        self.gc = None
+
+    def configure_event(self, *args):
+        #is this required?
+        return gtk.FALSE
+
+    def expose_event(self, *args):
+        #get the window and graphic context
+        win = self.get_window()
+        self.draw_rectangle(self.get_style().black_gc, gtk.FALSE, 0, 0, win.width-1, win.height-1)
+        self.draw_rectangle(self.gc, gtk.TRUE, 1, 1, win.width-2, win.height-2)
+        return gtk.FALSE
+
+    def set_color(self, color=(0,0,0,0)):
+        self.color = color
+        #the color - use GdkColorMap's alloc method to get it
+        cm = self.get_colormap()
+        self.icolor = cm.alloc(int(color[0] * 65535), int(color[1] * 65535),
+                               int(color[2] * 65535))
+        if self.gc is not None:
+            self.gc.foreground = self.icolor
+        self.queue_draw()
+        self.notify('color-changed')
+
+    def get_color(self):
+        return self.color
+
+
+class ColorButton(GtkColorWell):
+    """
+    Class ColourButton extends GtkColorWell
+    """
+
+    def __init__(self, color=(0,0,0,0), title='', use_alpha=TRUE, continuous=FALSE, _obj=None):
+        GtkColorWell.__init__(self, _obj)
+        self.set_color( color )
+        self.set_use_alpha( use_alpha )
+        self.set_continuous( continuous )
+
+class ColorDialog(gtk.GtkWindow):
+    """used with a ColorButton when it is clicked"""
+
+    def __init__(self, ok_cb = None, cancel_cb = None, cb_data = None):
+        gtk.GtkWindow.__init__(self)
+        self.set_title('Select a Color')
+        vbox = gtk.GtkVBox(spacing=3)
+        self.add(vbox)
+        self.user_ok_cb = ok_cb
+        self.user_cancel_cb = cancel_cb
+        self.user_cb_data = cb_data
+
+        self.connect('delete-event', self.user_cancel_cb)
+        #add the color selection widget
+        self.colorsel = gtk.GtkColorSelection()
+        self.colorsel.set_opacity(gtk.TRUE)
+        vbox.pack_start(self.colorsel)
+        #add the ok and cancel buttons
+        button_box = gtk.GtkHButtonBox()
+        ok_button = gtk.GtkButton("OK")
+        ok_button.connect('clicked', self.ok_cb, cb_data)
+        cancel_button = gtk.GtkButton("Cancel")
+        cancel_button.connect('clicked', self.cancel_cb, cb_data)
+        button_box.pack_start(ok_button)
+        button_box.pack_start(cancel_button)
+        vbox.pack_start(button_box, expand=gtk.FALSE)
+        vbox.show_all()
+        ok_button.set_flags(gtk.CAN_DEFAULT)
+        ok_button.grab_default()
+
+    def ok_cb(self, *args):
+        if self.user_ok_cb is not None:
+            self.user_ok_cb(self.user_cb_data, self)
+        self.hide()
+        self.destroy()
+
+    def cancel_cb(self, *args):
+        if self.user_cancel_cb is not None:
+            self.user_cancel_cb(self.user_cb_data, self)
+        self.hide()
+        self.destroy()
+
+RAMP_GRADIENT = 0
+RAMP_DISCRETE = 1
+
+class ColorRamp(gtk.GtkFrame, Signaler):
+    """encapsulate the functionality of a color ramp that
+    can apply itself in a linearly interpolated number of
+    steps between several colors positioned along the ramp
+
+    Colors can be returned from the ramp by calling apply_ramp
+    with a callback and the number of colors to calculate.
+    """
+    def __init__(self):
+        """initialize the ramp
+        """
+        gtk.GtkFrame.__init__(self)
+        self.set_shadow_type(gtk.SHADOW_NONE)
+        self.colors = []
+        self.gradient = ColorGradientSwatch(self)
+        self.title = gtk.GtkLabel('Ramp')
+        fix = gtk.GtkFixed()
+        fix.put(self.gradient, 1, 0)
+        fix.put(self.title, 84, 0)
+        self.add(fix)
+        self.type = RAMP_GRADIENT
+
+    def serialize(self, fname = None):
+        """save to a file
+        """
+        
+        result = "%s\n" % self.title.get()
+        result = result + "%s\n" % self.type
+        for n in self.colors:
+            if self.type == RAMP_GRADIENT:
+                result = result + "%s %s\n" % (str(n[0]), str(n[1]))
+            else:
+                result = result + "%s\n" % str(n[1])
+                
+        if fname is not None:
+            f = open(fname, 'w')
+            f.write( result )
+            f.close()
+        else:
+            return result      
+
+    def deserialize(self, fname):
+        """read from a file"""
+        import string
+        fp = open(fname, 'r')
+        lines = fp.readlines()
+        self.title.set_text(lines[0].strip())
+        self.type = int(lines[1].strip())
+        n_colors = len( lines[2:] )
+        for i in range(n_colors):
+            line = lines[2+i]
+            line = string.replace(line, '(', '')
+            line = string.replace(line, ')', '')
+            line = string.replace(line, ',', '')
+            if self.type == RAMP_GRADIENT:
+                pos, r, g, b, a = line.split()
+            else:
+                r, g, b, a = line.split()
+                #assume equally spaced for DISCRETE
+                pos = float(i)/float(n_colors - 1)
+            self.add_color((float(r), float(g), float(b), float(a)), float(pos))
+
+        fp.close()
+        self.queue_draw()
+
+    def add_color(self, color, position):
+        """add a color to the ramp at the given position.
+        color - an rgba tuple
+        position - between 0.0 and 1.0
+        """
+        for i in range(len(self.colors)):
+            if position <= self.colors[i][0]:
+                self.colors.insert(i, (position, color))
+                break
+        else:
+            self.colors.append((position, color))
+
+    def apply_ramp(self, color_cb, ncolors):
+        """
+        return ncolors spread over the ramp by
+        calling the color_cb callback with the
+        current position (in the range 0 to
+        ncolors-1) and color
+        """
+        if len(self.colors) == 0:
+            return
+
+        #insert false entries at 0 and 1 if necessary
+        bLow = gtk.FALSE
+        bHi = gtk.FALSE
+
+        if self.colors[0][0] <> 0.0:
+            self.add_color(self.colors[0][1], 0.0)
+            bLow = gtk.TRUE
+        if self.colors[len(self.colors) - 1][0] <> 1.0:
+            self.add_color(self.colors[len(self.colors)-1][1], 1.0)
+            bHi = gtk.TRUE
+        
+        if ncolors > 1:
+            for i in range(ncolors):
+                if self.type == RAMP_GRADIENT:
+                    color_cb(i, self.calculate_color(float(float(i)/float(ncolors - 1))))
+                elif self.type == RAMP_DISCRETE:
+                    pos = i % len(self.colors)
+                    color_cb(i, self.colors[pos][1] )
+                    
+        else:
+            color_cb( 0, self.calculate_color( 0 ) )
+            
+        #clean up
+        if bLow:
+            del self.colors[0]
+        if bHi:
+            del self.colors[len(self.colors)-1]
+
+    def calculate_color(self, pos):
+        """calculate the color at the given position.  If a color
+        exists at the position, return it. Otherwise get the color
+        before and after it and calculate a linear interpolation
+        between them.
+        """
+        for i in range(len(self.colors)-1):
+            below = self.colors[i]
+            above = self.colors[i+1]
+            if below[0] <= pos and above[0] >= pos:
+                fr = below[1][0]
+                fg = below[1][1]
+                fb = below[1][2]
+                fa = below[1][3]
+                tr = above[1][0]
+                tg = above[1][1]
+                tb = above[1][2]
+                ta = above[1][3]
+                delta = (pos - below[0]) / (above[0] - below[0])
+                cr = fr + ( tr - fr ) * delta
+                cg = fg + ( tg - fg ) * delta
+                cb = fb + ( tb - fb ) * delta
+                ca = fa + ( ta - fa ) * delta
+                return (cr, cg, cb, ca)
+
+    def get_color_list(self, ncolors):
+        self.color_list = []
+        self.apply_ramp(self.color_list_cb, ncolors)
+        return self.color_list
+
+    def color_list_cb(self, num, color):
+        self.color_list.insert(num, color)
+
+class ColorGradientSwatch(ColorSwatch):
+    """
+    Class ColorGradientSwatch extends ColorSwatch to n colors
+    and draws itself as a gradient between the various colors.
+
+    This class is intended primarily to provide a GUI element for
+    ColorRamps
+    """
+    def __init__(self, ramp):
+        ColorSwatch.__init__(self)
+        self.size(80, 15)
+        self.ramp = ramp
+
+    def expose_event(self, *args):
+        #get the window and graphic context
+        win = self.get_window()
+        self.width = win.width
+        self.height = win.height
+        self.cm = self.get_colormap()
+        if self.ramp.type == RAMP_GRADIENT:
+            colors = self.ramp.get_color_list(self.width)
+        else:
+            colors = self.ramp.get_color_list( len(self.ramp.colors) - 1 )
+        bar_width = self.width / len(colors)
+        i = 0
+        for color in colors:
+            self.gc.foreground = self.cm.alloc(int(color[0] * MAX_COLOR),
+                                               int(color[1] * MAX_COLOR),
+                                               int(color[2] * MAX_COLOR))
+            self.draw_rectangle(self.gc, gtk.TRUE, i, 0, bar_width, self.height)
+            i = i + bar_width
+        self.draw_rectangle(self.get_style().black_gc, gtk.FALSE, 0, 0, win.width-1, win.height-1)
+        return gtk.FALSE
+
+
+def test_cb(num, color):
+    print num, ' - ', color
+
+def color_set( widget, obj ):
+    print 'color set to ', widget.get_color()
+
+if __name__ == '__main__':
+    a = ColorRamp()
+    a.add_color((0.0,1.0,0.0,1.0), 0.0)
+    a.add_color((1.0,1.0,0.0,1.0), 0.50)
+    a.add_color((1.0,0.0,0.0,1.0), 1.0)
+    print a.colors
+    a.apply_ramp(test_cb, 5)
+    print a.colors
+    a.serialize('c:\\test_ramp')
+    b = ColorRamp()
+    b.deserialize('c:\\test_ramp')
+    print b.colors
+
+    c = ColorButton((1.0, 0.0, 0.0, 1.0))
+    c.connect('color-set', color_set)
+
+    dlg = gtk.GtkDialog()
+    btn = gtk.GtkButton('OK')
+    btn.connect('clicked', gtk.mainquit)
+
+    dlg.vbox.pack_start(a)
+    dlg.vbox.pack_start(b)
+    dlg.vbox.pack_start(c)
+    dlg.action_area.pack_start(btn)
+    dlg.show_all()
+    dlg.show()
+
+    gtk.mainloop()

Added: packages/openev/branches/upstream/current/pymod/pgucolorsel.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgucolorsel.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgucolorsel.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,201 @@
+###############################################################################
+# $Id: pgucolorsel.py,v 1.1 2000/06/14 15:12:43 warmerda Exp $
+#
+# Project:  Python Gtk Utility Widgets
+# Purpose:  Embeddable color selector.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pgucolorsel.py,v $
+#  Revision 1.1  2000/06/14 15:12:43  warmerda
+#  New
+#
+#
+
+from gtk import *
+from string import *
+import pgu
+from pgucolourswatch import ColourSwatch
+
+
+class ColorControl(GtkHBox):
+
+    """Embeddable Color Selector Control
+
+    The pgucolorsel.ColorControl widget is intended to provide a simple
+    color selection widget that can be embedded in dialogs and that will
+    launch a fill GtkColorSelectionDialog if the user doesn't select one of
+    the list of predefined colors.
+
+    At this time the ColorControl's main widgetry is just an option menu, but
+    it is expected in the future to also include some sort of color swatch
+    showing the currently selected color graphically.
+
+    The application supplies a single callback which is involved when the
+    selected color changes.  All colors are handled as RGBA tuples, with the
+    values being in the range of 0-1 (rather than 0-255). 
+
+    Arguments:
+
+    title -- the title used for the color selection dialog, if displayed.
+
+    callback -- the application function to call when the color changes.  It
+    should take a color tuple (RGBA) and a callback data object as arguments. 
+
+    cb_data -- callback data to pass to callback. 
+
+    Example:
+
+        self.mod_color = pgucolorsel.ColorControl('Modulation Color',
+                                                  self.color_cb,None)
+        self.mod_color.set_color(tcolor)
+
+    def color_cb( self, color, cb_data ):
+        print color[0], color[1], color[2], color[3]
+    """
+
+    color_list = [ ('Red',         (  1,   0,   0,   1)),
+                   ('Green',       (  0,   1,   0,   1)),
+                   ('Blue',        (  0,   0,   1,   1)),
+                   ('White',       (  1,   1,   1,   1)),
+                   ('Black',       (  0,   0,   0,   1)),
+                   ('Transparent', (  0,   0,   0,   0))]
+
+    def __init__(self, title, callback = None, cb_data = None):
+        GtkHBox.__init__(self)
+
+        self.callback = callback
+        self.cb_data = cb_data
+        self.current_color = (1,0,0,1)
+        self.mixer = None
+        self.title = title
+        
+        self.swatch = ColourSwatch(self.current_color)
+
+        self.om = GtkOptionMenu()
+
+        menu = GtkMenu()
+        self.om.set_menu(menu)
+
+        item = None
+        for cinfo in self.color_list:
+            name, value = cinfo
+            item = GtkRadioMenuItem(item,name)
+            item.connect('activate', self.set_color_cb, value)
+            item.show()
+            menu.append(item)
+        
+        item = GtkRadioMenuItem(item,'Custom')
+        item.connect('activate', self.launch_mixer_cb)
+        item.show()
+        menu.append(item)
+        
+        item = GtkRadioMenuItem(item,'Mixer')
+        item.connect('activate', self.launch_mixer_cb)
+        item.show()
+        menu.append(item)
+
+        self.om.set_history(0)
+        
+        self.pack_start(self.swatch, expand=FALSE)
+        self.pack_start(self.om)
+
+    def launch_mixer_cb(self,item):
+        if not item.active:
+            return
+
+        if self.mixer is not None and self.mixer.get_window() is not None:
+            self.mixer.colorsel.set_color( self.current_color )
+            self.mixer.colorsel.set_opacity( self.current_color[3] )
+            self.mixer.get_window()._raise()
+            return
+
+        self.mixer = GtkColorSelectionDialog(self.title)
+        self.mixer.connect('delete-event',self.mixer_delete)
+        self.mixer.colorsel.set_opacity( 1 )
+        self.mixer.colorsel.set_color( self.current_color )
+        self.mixer.ok_button.connect('clicked',self.mixer_ok_cb )
+        self.mixer.ok_button.children()[0].set_text('Apply')
+        self.mixer.cancel_button.connect('clicked',self.mixer_cancel )
+        self.mixer.cancel_button.children()[0].set_text('Close')
+        self.mixer.help_button.hide()
+        self.mixer.show()
+        self.update_om()
+
+    def mixer_delete(self, widget, args):
+        self.mixer = None
+        
+    def mixer_cancel(self, args):
+        self.mixer.hide()
+        self.mixer.destroy()
+        self.mixer = None
+        
+    def mixer_ok_cb(self, args):
+        rgb = self.mixer.colorsel.get_color()
+        self.set_color( (rgb[0], rgb[1], rgb[2], rgb[3] ) )
+        
+    def set_color(self, new_color):
+        if len(new_color) == 3:
+            new_color = (new_color[0],new_color[1],new_color[2],1.0)
+        
+        if new_color[0] > 1 or new_color[1] > 1 \
+           or new_color[2] > 1 or new_color[3] > 1:
+            new_color = (new_color[0] / 255.0,
+                         new_color[1] / 255.0,
+                         new_color[2] / 255.0,
+                         new_color[3] / 255.0)
+            
+        if new_color == self.current_color:
+            return
+        
+        self.current_color = new_color
+        self.swatch.set_colour(self.current_color)
+        self.invoke_callback()
+        self.update_om()
+
+    def update_om(self):
+        for ci_index in range(len(self.color_list)):
+            name, value = self.color_list[ci_index]
+            if value == self.current_color:
+                self.om.set_history(ci_index)
+                return
+
+        # Mark as custom
+        self.om.set_history(len(self.color_list))
+
+    def set_color_from_string(self, new_color):
+        if new_color is None:
+            return
+        
+        red, green, blue, alpha = split(new_color,' ')
+        self.set_color((atof(red), atof(green), atof(blue), atof(alpha)))
+        
+    def invoke_callback(self):
+        if self.callback == None:
+            return
+        self.callback( self.current_color, self.cb_data )
+
+    def set_color_cb(self, item, color):
+        if item.active:
+            self.set_color( color )
+
+pgu.gtk_register('ColorControl',ColorControl)
+

Added: packages/openev/branches/upstream/current/pymod/pgucolourswatch.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgucolourswatch.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgucolourswatch.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,102 @@
+###############################################################################
+# $Id: pgucolourswatch.py,v 1.3 2003/09/09 15:18:46 gmwalter Exp $
+#
+# Project:  Python Gtk Utility Widgets
+# Purpose:  Embeddable color swatch.
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pgucolourswatch.py,v $
+#  Revision 1.3  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.2  2001/03/19 21:57:14  warmerda
+#  expand tabs
+#
+#  Revision 1.1  2000/07/26 21:20:01  warmerda
+#  New
+#
+#
+
+import gtk
+from gvsignaler import Signaler
+
+class ColourSwatch(gtk.GtkDrawingArea, Signaler):
+    '''
+    Class ColourSwatch is a simple widget that
+    displays a colour.
+    
+    The colour attribute is an RGBA tuple.  Internally, however,
+    a GdkColour object is used that has values in the range 0-65535
+    for red, green and blue.      
+    '''
+    def __init__(self, colour=(0,0,0,0)):
+        gtk.GtkDrawingArea.__init__(self)
+        self.size(30, 15)
+        self.connect('configure-event', self.configure_event)
+        self.connect('expose-event', self.expose_event)
+        self.connect('realize', self.realize_event)
+        self.connect('unrealize', self.unrealize_event)
+        self.colour = colour
+        #the color - use GdkColourMap's alloc method to get it
+        cm = self.get_colormap()
+        self.icolour = cm.alloc(int(colour[0] * 65535),
+                                int(colour[1] * 65535),
+                                int(colour[2] * 65535))
+        self.publish('colour-changed')
+        #cached graphics context
+        self.gc = None
+        
+    def realize_event(self, *args):
+        self.gc = self.get_window().new_gc(foreground=self.icolour)
+
+    def unrealize_event(self, *args):
+        #don't know the correct way to destroy a gc yet
+        #self.gc.destroy()
+        pass
+                
+    def configure_event(self, *args):
+        #is this required?
+        return gtk.FALSE
+
+    def expose_event(self, *args):
+        #get the window and graphic context 
+        win = self.get_window()
+        self.draw_rectangle(self.get_style().black_gc, gtk.FALSE, 2, 2, win.width-2, win.height-2)
+        self.draw_rectangle(self.gc, gtk.TRUE, 3, 3, win.width-3, win.height-3)
+        return gtk.FALSE
+        
+    def set_colour(self, colour=(0,0,0,0)):
+        self.colour = colour
+        #the color - use GdkColourMap's alloc method to get it
+        cm = self.get_colormap()
+        self.icolour = cm.alloc(int(colour[0] * 65535),
+                                int(colour[1] * 65535),
+                                int(colour[2] * 65535))
+        if self.gc is not None:
+                self.gc.foreground = self.icolour
+        self.notify('colour-changed')
+        if self.flags() & gtk.REALIZED:
+            self.expose_event()
+

Added: packages/openev/branches/upstream/current/pymod/pgucombo.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgucombo.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgucombo.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,230 @@
+###############################################################################
+# $Id: pgucombo.py,v 1.7 2005/11/08 15:51:44 andrey_kiselev Exp $
+#
+# Project:  CIET Map
+# Purpose:  CIET Map
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: pgucombo.py,v $
+#  Revision 1.7  2005/11/08 15:51:44  andrey_kiselev
+#  Properly import gtkextra module, should work on Debian system now.
+#
+#  Revision 1.6  2004/09/28 17:50:42  warmerda
+#  change fallback for how pygtkextra is imported
+#
+#  Revision 1.5  2003/11/19 15:20:53  warmerda
+#  try looking in alternate location for pygtkextra if import fails
+#
+#  Revision 1.4  2002/08/13 12:44:15  pgs
+#  made dropdown list scrollable and added capture of esc key to
+#  close dropdown
+#
+#  Revision 1.3  2002/08/09 21:04:24  pgs
+#  fixed bug in set_popdown_strings (make copy of list)
+#
+#  Revision 1.2  2002/08/01 14:49:37  pgs
+#  modified, now dependent on gtkextra but much nicer control
+#
+#  Revision 1.1  2001/04/29 15:24:38  pgs
+#  new file
+#
+#
+
+import gtk
+try:
+    from pygtkextra import GtkComboBox
+except:
+    from gtkextra import GtkComboBox
+
+"""
+pgucombo contains a single class, pguCombo, which implements a better combobox
+based on gtkextra's GtkComboBox (and the python bindings)
+"""
+
+class pguCombo(GtkComboBox):
+    """
+    this class mimics GtkCombo but the selection process is somewhat simpler
+    and the entry box only changes when you actually select an item, not when
+    you drag through it.
+    """
+
+    def __init__(self, strings = [ "" ]):
+        """Initialize the combo box, default strings can be passed.
+        """
+        GtkComboBox.__init__(self)
+        
+        self.items = strings
+        self.current_item = 0
+        
+        #widgets to go into the combo box
+        self.list = gtk.GtkCList( cols=1 )
+        self.list.set_selection_mode( gtk.SELECTION_SINGLE )
+        
+        self.entry = gtk.GtkEntry()
+        self.entry.set_editable( gtk.FALSE )
+        
+        #fix up the style of the entry
+        #style = self.entry.get_style().copy()
+
+        #fake a background color the same as the button widget
+        #for i in range(5):
+        #    style.base[i] = self.button.get_style().bg[i]
+        #self.entry.set_style( style )
+        
+        self._scrolled_win = gtk.GtkScrolledWindow()
+        self._scrolled_win.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )
+        self._scrolled_win.add( self.list )
+
+        #################################
+        #
+        # TODO: figure out how to resize this on the fly
+        #
+        #################################
+        self._scrolled_win.set_usize( 150, 175 )
+        self._scrolled_win.show_all()
+        #frame is the popdown window frame
+        self.frame.add( self._scrolled_win )
+        
+        #button is the main area to the left of the dropdown arrow
+        self.button.add( self.entry )
+        
+        #initialize the widgets
+        self.set_popdown_strings( self.items )
+        self.select_item( self.current_item )
+        
+        #connect up signals
+        self.list.connect( 'select-row', self.list_row_selected )
+        
+        self.list.add_events(gtk.GDK.KEY_PRESS_MASK)
+        self.list.connect('key-press-event', self.key_press_cb)
+        
+        self.arrow.connect( 'toggled', self.toggle_cb )
+        
+        
+        #make sure they all get shown
+        self.show_all()     
+        
+        
+    def toggle_cb( self, widget, *args ):
+        if widget.get_active():
+            self.list.grab_focus()
+        else:
+            self.entry.grab_focus()
+
+    def key_press_cb( self, widget, event, *args ):
+        """
+        The user pressed a key.  Find out if we should respond to it
+        """
+        if event.keyval == gtk.GDK.Escape:
+            self.hide_popdown_window()
+        
+    def list_row_selected( self, widget, row, col, event, *args ):
+        """private callback for list selections
+        """
+        self.select_item( row )
+        self.hide_popdown_window()
+             
+    def get_text( self ):
+        """Return the text associated with the currently selected item
+        """
+        return self.items[ self.current_item ]
+        
+    def get_selected_item( self ):
+        """Return the index of the selected item
+        """
+        return self.current_item
+        
+    def select_item( self, item = 0 ):
+        """Select an item by it's index
+        """
+        if item >= 0 and item < len(self.items):
+            self.current_item = item
+            self.entry.set_text( self.items[self.current_item] )
+            return gtk.TRUE
+        else:
+            return gtk.FALSE
+    def select_string( self, string = "" ):
+        """Cause an item to be selected by a string value
+        """
+        try:
+            idx = self.items.index( string )
+            self.select_item( idx )
+            return gtk.TRUE
+        except:
+            return gtk.FALSE
+
+    ############################################################################
+    #
+    # GtkCombo API
+    #
+    ############################################################################
+    def set_popdown_strings( self, items ):
+        """set the strings to display
+        """
+        del self.items
+        self.items = []
+        for item in items:
+            self.items.append( item )
+        self.list.clear()
+        for item in self.items:
+            self.list.append( [item] )
+        self.list.set_column_width( 0, self.list.optimal_column_width( 0 ) )
+        self.list.show_all()
+        self.select_item( 0 )
+        
+    def disable_activate( self ):
+        pass
+
+    def set_use_arrows( self, val):
+        pass
+
+    def set_use_arrows_always( self, val ):
+        pass
+
+    def set_case_sensitive( self, val ):
+        pass
+
+    def set_value_in_list( self, val, ok_if_empty ):
+        pass
+    
+class TestWindow( gtk.GtkWindow ):
+
+    def __init__(self):
+        gtk.GtkWindow.__init__(self)
+        
+        vbox = gtk.GtkVBox()
+        self.add(vbox)
+        
+        self.cmb = pguCombo([ 'test1', 'test2', 'long test string' ])
+        self.cmb.entry.connect( 'changed', self.item_changed )
+        
+        vbox.pack_start( self.cmb )
+        
+        self.connect( 'delete-event', gtk.mainquit )
+        self.show_all()
+        
+    def item_changed( self, *args ):
+        print self.cmb.get_text()
+        
+if __name__ == "__main__":
+    win = TestWindow()
+    gtk.mainloop()

Added: packages/openev/branches/upstream/current/pymod/pguentry.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pguentry.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pguentry.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,12 @@
+import gtk
+
+class pguEntry( gtk.GtkEntry ):
+
+    def __init__(self):
+        gtk.GtkEntry.__init__(self)
+        self.add_events(gtk.GDK.FOCUS_CHANGE_MASK)
+        self.connect('focus-out-event', self.cleanup)
+
+    def cleanup(self, *args):
+        self.select_region(0, 0)
+        self.queue_draw()

Added: packages/openev/branches/upstream/current/pymod/pgufilesel.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgufilesel.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgufilesel.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,202 @@
+###############################################################################
+# $Id: pgufilesel.py,v 1.1 2000/06/14 13:57:30 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Simplified File Selection API.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pgufilesel.py,v $
+#  Revision 1.1  2000/06/14 13:57:30  warmerda
+#  New
+#
+#
+
+import gtk; _gtk = gtk; del gtk
+from string import *
+import os.path
+
+simple_file_sel = None
+simple_file_sel_dir= None
+
+def SFSOkCB( item, cb, cb_data ):
+    global simple_file_sel
+
+    simple_file_sel.hide()
+    cb( os.path.normpath(simple_file_sel.get_filename()), cb_data )
+
+def SFSCancelCB( item, cb, cb_data ):
+    global simple_file_sel
+
+    simple_file_sel.hide()
+    if cb is not None:
+        cb( cb_data )
+
+def SFSDestroyCB( item, cb, cb_data ):
+    global simple_file_sel
+
+    simple_file_sel = None
+    if cb is not None:
+        cb( cb_data )
+
+def SimpleFileSelect( ok_cb,
+                         cb_data = None,
+                         title = None,
+                         default_filename = None,
+                         cancel_cb = None,
+                         help_topic = None ):
+
+    """Simplified File Selection
+
+    This method launches a file selector, and calls the caller supplied
+    OK callback when the user selects a file.  Creation, tailoring and
+    cleanup of the GtkFileselection is managed internally.
+
+    Arguments:
+
+    ok_cb -- callback to call when user selects a file.  It should take
+    a filename and cb_data argument.
+
+    cb_data -- extra data to pass to ok, and cancel callbacks.  Defaults to
+    None.
+
+    title -- the title to use for the dialog.  Defaults to nothing.
+
+    default_filename -- the initial filename to be shown in the file selector.
+    Defaults to no file, and the current (or last accessed) directory.
+
+    cancel_cb -- callback called when the user hits the cancel button or
+    closes the file selection dialog.  If defaulted the caller isn't notified
+    of cancels.  If supplied, the callback should take one argument which is
+    the callback data.
+
+    Example:
+
+    The following code launches a simple file selector, and does an action
+    in the callback.  The title, and default filename are set, but
+        pgufilesel.SimpleFileSelect( self.save_vector_layer_with_file,
+                                     cb_data = layer.get_parent(),
+                                     title = 'Shapefile To Save to',
+                                     default_filename = layer.get_name() )
+
+    def save_vector_layer_with_file( self, filename, shapes_data ):
+        shapes_data.save_to( filename )
+
+    """
+
+    global simple_file_sel
+
+    if simple_file_sel is not None:
+        simple_file_sel.destroy()
+
+    simple_file_sel = _gtk.GtkFileSelection()
+    simple_file_sel.hide_fileop_buttons()
+    simple_file_sel.ok_button.connect('clicked', SFSOkCB, ok_cb, cb_data )
+    simple_file_sel.cancel_button.connect('clicked', SFSCancelCB, cancel_cb, cb_data )
+    simple_file_sel.connect('destroy', SFSDestroyCB, cancel_cb, cb_data)
+
+    if title is not None:
+        simple_file_sel.set_title( title )
+
+    if default_filename is not None:
+        simple_file_sel.set_filename( default_filename )
+    else:
+        if simple_file_sel_dir is not None:
+            simple_file_sel.set_filename(simple_file_sel_dir)
+
+    if help_topic is not None:
+        import gvhtml
+        gvhtml.set_help_topic( simple_file_sel, help_topic )
+
+
+    simple_file_sel.show()
+
+
+def SimpleFileSelectCB( item, ok_cb, *args ):
+
+    """Simple file selection suitable to use as a callback.
+
+    This function is suitable to be used as a callback from a menu item,
+    and will launch a file selector, using the SimpleFileSelect() API.
+    See that function for details on meaning of arguments.
+
+    The callback should be passed one, two or three arguments which
+    would be the SimpleFileSelect() arguments ok_cb, cb_data and title.
+
+    Example:
+
+    The following fragment passed to GtkExtra.MenuFactory creates a File
+    Open menu item, and it launches a file selector.  When a file is called
+    the caller supplied self.file_open_by_name() method is called.
+    
+            ('File/Open', '<control>O', pgufilesel.SimpleFileSelectCB,
+                                        self.file_open_by_name ),
+
+    def file_open_by_name(self, filename, *args):
+        ...
+                                        
+    """
+
+    if len(args) == 0: 
+        SimpleFileSelect( ok_cb )
+    elif len(args) == 1:
+        SimpleFileSelect( ok_cb, cb_data = args[0] )
+    else:
+        SimpleFileSelect( ok_cb, cb_data = args[2], title = args[0] )
+
+
+# For directly grabbing a filename within a single callback
+class pguFileSelection(_gtk.GtkFileSelection):
+    def __init__(self, title, default_filename = None):
+        _gtk.GtkFileSelection.__init__(self)
+        self.set_title(title)
+        if default_filename is not None:
+            self.set_filename(default_filename)
+
+        elif simple_file_sel_dir is not None:
+            self.set_filename(simple_file_sel_dir)
+            
+        self.ok_button.connect  ("clicked", self.ok_cb)
+        self.cancel_button.connect("clicked", self.cancel_cb)
+        self.connect("delete_event", self.cancel_cb)
+        self.ret = None
+        self.set_modal(_gtk.TRUE)
+
+    def ok_cb(self, *args):
+        self.ret = self.get_filename()
+	self.hide()
+	self.destroy()
+	_gtk.mainquit()
+
+    def cancel_cb(self, *args):
+        self.ret = None
+        self.selected = None
+	self.hide()
+	self.destroy()
+	_gtk.mainquit()
+	
+
+def GetFileName(title = 'Select File', default_filename = None):
+    win = pguFileSelection(title, default_filename)
+    win.show_all()
+    _gtk.mainloop()
+    return win.ret
+

Added: packages/openev/branches/upstream/current/pymod/pgufont.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgufont.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgufont.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,440 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: pgufont.py,v 1.4 2004/07/20 12:26:43 pgs Exp $
+#
+# Project:  OpenEV Python GTK Utility classes
+# Purpose:  Embeddable Font class and font utility classes
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: pgufont.py,v $
+#  Revision 1.4  2004/07/20 12:26:43  pgs
+#  added ability to load font and report statistics on it
+#
+#  Revision 1.3  2002/08/28 18:44:17  pgs
+#  fixed typo from last change :(
+#
+#  Revision 1.2  2002/08/28 18:07:35  pgs
+#  removed sys.stdout.flush() to prevent problems on windows with no console
+#
+#  Revision 1.1  2001/04/29 15:23:40  pgs
+#  new file
+#
+#
+
+import gtk
+from gtk import TRUE, FALSE
+import gview
+from gvsignaler import Signaler
+import string
+import sys
+import gdal
+
+
+"""
+pgufont contains utility classes for manipulating and selecting fonts
+"""
+
+class XLFDFontSpec:
+    """
+    encapsulate an X Logical Font Description (XLFD) font specification
+
+    -adobe-helvetica-bold-r-normal--12-120-75-75-p-70-iso8859-1
+
+    The fields in the XLFD are:
+
+    Foundry          the company or organization where the font originated.
+    Family           the font family (a group of related font designs).
+    Weight           A name for the font's typographic weight For example,
+                     'bold' or 'medium').
+    Slant            The slant of the font. Common values are 'R' for Roman,
+                     'I' for italoc, and 'O' for oblique.
+    Set              Width A name for the width of the font. For example,
+                     'normal' or 'condensed'.
+    Add Style        Additional information to distinguish a font from other
+                     fonts of the same family.
+    Pixel Size       The body size of the font in pixels.
+    Point Size       The body size of the font in 10ths of a point. (A point
+                     is 1/72.27 inch)
+    Resolution X     The horizontal resolution that the font was designed for.
+    Resolution Y     The vertical resolution that the font was designed for .
+    Spacing          The type of spacing for the font - can be 'p' for
+                     proportional, 'm' for monospaced or 'c' for charcell.
+    Average Width    The average width of a glyph in the font. For monospaced
+                     and charcell fonts, all glyphs in the font have this width
+    Charset Registry The registration authority that owns the encoding for the
+                     font. Together with the Charset Encoding field, this
+                     defines the character set for the font.
+    Charset Encoding An identifier for the particular character set encoding.
+    """
+
+    xlfd_field_names = ['Foundry','Family','Weight',
+                   'Slant','Set','Add Style','Pixel Size',
+                   'Point Size','Resolution X','Resolution Y',
+                   'Spacing','Average Width','Charset Registry',
+                   'Charset Encoding']
+
+    def __init__(self, fontspec = None):
+        """
+        initialize, optionally parsing a font spec
+        """
+        self.gdk_font = None
+        self.parts = []
+        for i in range(14):
+            self.parts.append('*')
+        if fontspec is not None:
+            self.parse_font_spec(fontspec)
+
+    def parse_font_spec(self, font_spec):
+        """
+        parse a font specification
+        """
+        self.gdk_font = None
+        result = ''
+
+        #coerce XLFDFontSpec classes into strings to parse them
+        font_spec = str(font_spec)
+
+        if font_spec[0:1] != '-':
+            gdal.Debug( "pgufont", "invalid XLFD(%s), should start with -" % font_spec )
+            return
+
+        new_parts = string.split(font_spec, '-')
+        del new_parts[0] #remove first (empty) part produced by split
+
+        if len(new_parts) != 14:
+            gdal.Debug( "pgufont", 'invalid XLFD(%s), should have 14 parts' % font_spec )
+            return
+
+        else:
+            self.parts = new_parts
+
+    def get_font_part(self, field_name=None, field_number=None):
+        """
+        Get one part of the font description by field name or number.  If
+        both are specified, the result of the field name will be returned
+        if it is valid, or if invalid, the field number, or if that is also
+        invalid, the an empty string.
+        """
+        result = ''
+        if field_number is not None:
+            if field_number < 1 or field_number > 14:
+                gdal.Debug( "pgufont", 'invalid field number (%s), should be 1-14' % field_number)
+            else:
+                result = self.parts[field_number]
+
+        if field_name is not None:
+            if self.xlfd_field_names.count(field_name) == 0:
+                gdal.Debug( "pgufont", 'invalid field name (%s), should be one of %s' \
+                    % (field_name, self.xlfd_field_names) )
+            else:
+                result = self.parts[self.xlfd_field_names.index(field_name)]
+
+        return result
+
+    def set_font_part(self, field_name, value):
+        """
+        set a part of the font description
+        """
+        self.gdk_font = None
+        if self.xlfd_field_names.count(field_name) != 0:
+            self.parts[self.xlfd_field_names.index(field_name)] = value
+        else:
+            gdal.Debug( "pgufont", 'invalid field name (%s), should be one of %s' \
+                % (field_name, self.xlfd_field_names) )
+
+    def get_display_string(self):
+        """
+        return a human readable display string for this font
+        """
+        family = self.get_font_part('Family') + ' '
+        weight = self.get_font_part('Weight') + ' '
+        if weight == '* ' or weight == 'normal ': weight = ''
+        slant = self.get_font_part('Slant') + ' '
+        if slant == '* ': slant = ''
+        elif slant == 'r ': slant = ''
+        elif slant == 'i ' or slant == 'o ': slant = 'Italic '
+
+        unit = ' pt'
+        size = self.get_font_part('Point Size')
+        if size == '*' or size == '':
+            size = self.get_font_part('Pixel Size')
+            unit = ' px'
+        else:
+            size = size[0:len(size)-1]
+        return family + weight + slant + size + unit
+
+    def get_font_string(self):
+        """
+        return this font as a string (for cases where automatic coercion
+        doesn't work ...)
+        """
+        return str(self)
+
+    def __str__(self):
+        """
+        return a representation of this xfld as a string
+        """
+        result = ''
+        for val in self.parts:
+            result = result + '-' + val
+
+        return result
+        
+    def load_font( self ):
+        """
+        load the real GDK font associated with this font spec so
+        we can do calculations on it.
+        """
+        if self.gdk_font is not None:
+            return
+        self.gdk_font = gtk.load_font(str(self))
+
+    def text_size( self, text ):
+        """
+        return the size (w,h) of a text string in the current font
+        """
+        self.load_font()
+        w = self.gdk_font.width(text)
+        h = self.gdk_font.height(text)
+        return (w, h)
+        
+class pguFontDisplay(gtk.GtkDrawingArea):
+    """
+    a widget for displaying some text in a given font.
+    """
+
+    def __init__(self, text='Sample', font=None):
+        """
+        """
+        gtk.GtkDrawingArea.__init__(self)
+        self.size(10, 10)
+        self.connect('expose-event', self.expose_event)
+        self.connect('configure-event', self.configure_event)
+        self.connect('realize', self.realize_event)
+        self.connect('unrealize', self.unrealize_event)
+        self.text = text
+        self.font = font
+        if font is not None:
+            self.gdk_font = gtk.load_font(font)
+        else:
+            self.gdk_font = None
+
+        self.set_events(gtk.GDK.BUTTON_PRESS_MASK)
+
+    def realize_event(self, *args):
+        pass
+
+    def unrealize_event(self, *args):
+        self.gdk_font = None
+        pass
+
+    def configure_event(self, *args):
+        #is this required?
+        return gtk.FALSE
+
+    def expose_event(self, *args):
+        #get the window and graphic context
+        resize = FALSE
+
+        if self.gdk_font is None:
+            resize = TRUE
+            if self.font is None:
+                self.gdk_font = self.get_style().font
+            else:
+                self.gdk_font = gtk.load_font(self.font)
+
+        w = self.gdk_font.width(self.text)
+        h = self.gdk_font.height(self.text)
+
+        if resize:
+            self.set_usize(w + 6, h + 6)
+            self.queue_draw()
+
+        win = self.get_window()
+        #self.draw_rectangle(self.get_style().black_gc, gtk.FALSE, 0, 0, win.width-1, win.height-1)
+        self.draw_rectangle(self.get_style().white_gc, gtk.TRUE, 0, 0, win.width-1, win.height-1)
+        self.draw_string(self.gdk_font, self.get_style().black_gc, 3, h+3, self.text)
+        return gtk.FALSE
+
+    def set_text(self, text):
+        """
+        set the text for this widget and resize if appropriate
+        """
+        if self.text == text:
+            return
+
+        self.text = text
+        #try to resize.  If no font, wait for the next redraw
+        if self.gdk_font is not None:
+            w = self.gdk_font.width(self.text)
+            h = self.gdk_font.height(self.text)
+            self.set_usize(w + 6, h + 6)
+        self.queue_draw()
+
+    def set_font(self, font):
+        self.font = font
+        self.gdk_font = None
+        self.queue_draw()
+
+class pguFontControl(gtk.GtkHBox, Signaler):
+    """
+    an embeddable control for selecting fonts.  It displays the current
+    font in a pguFontDisplay widget and provides a button to click to
+    change the font.  If the widget is double-clicked, the displayed
+    text will toggle between the default font and the selected font.
+
+    Uses gvsignaler.Signaler to provide a 'font-changed' signal ... use
+    font_control.subscribe('font-changed', call_back)
+
+    Use font_control.get_font to retrieve an XLFDFontSpec object or
+    font_control.get_font_string to get a font string.
+    """
+
+    def __init__(self, fontspec=None, use_font=FALSE):
+        """
+        Initialize the font selector and optionally load a previous
+        font spec
+        """
+        gtk.GtkHBox.__init__(self)
+
+        self.use_font = use_font
+        self.font = XLFDFontSpec()
+
+        tips = gtk.GtkTooltips()
+
+        if fontspec is not None:
+            self.font.parse_font_spec( fontspec )
+        else:
+            self.font.set_font_part('Family', 'Arial')
+            self.font.set_font_part('Point Size', '100')
+
+        table = gtk.GtkTable()
+        self.pack_start(table)
+
+        table.set_row_spacings(6)
+        table.set_col_spacings(6)
+        table.set_border_width(6)
+
+        self.font_label = pguFontDisplay()
+        self.font_label.set_text(self.font.get_display_string())
+        table.attach(self.font_label, 0, 1, 0, 1, xoptions=gtk.SHRINK,
+                    yoptions=gtk.SHRINK)
+        tips.set_tip(self.font_label, 'double click to toggle sample mode')
+
+        font_button = gtk.GtkButton('...')
+        font_button.connect('clicked', self.show_font_dialog)
+        table.attach(font_button, 1, 2, 0, 1, xoptions=gtk.SHRINK,
+                    yoptions=gtk.SHRINK)
+        tips.set_tip(font_button, 'click to select a different font')
+
+        self.show_all()
+
+        self.font_label.set_events(gtk.GDK.BUTTON_PRESS_MASK)
+        self.font_label.connect('button-press-event', self.label_clicked)
+
+        self.update_gui()
+        self.publish('font-changed')
+
+    def show_font_dialog(self, *args):
+        """
+        show the font selection dialog
+        """
+        dlg = gtk.GtkFontSelectionDialog('Select a font')
+        dlg.set_font_name(str(self.font))
+        dlg.ok_button.connect('clicked', self.update_font, dlg)
+        dlg.cancel_button.connect('clicked', dlg.destroy)
+        dlg.show()
+
+    def update_font(self, widget, dlg):
+        """
+        update the font as a result of the user selecting a new one
+        """
+        self.set_font(dlg.get_font_name())
+        dlg.destroy()
+
+    def update_gui(self, *args):
+        """
+        update the GUI to reflect the state of the current font spec
+        """
+        #style = self.font_label.get_style().copy()
+        #if self.use_font:
+        #    style.font = gtk.load_font(str(self.font))
+        #else:
+        #    style.font = self.get_style().font
+        if self.use_font:
+            self.font_label.set_font(str(self.font))
+        else:
+            self.font_label.set_font(None)
+        self.font_label.set_text(self.font.get_display_string())
+        self.queue_resize()
+
+    def set_font(self, fontspec):
+        """
+        set the font from the fontspec and update the gui
+        """
+        self.font.parse_font_spec(fontspec)
+        self.update_gui()
+        self.notify('font-changed')
+
+    def get_font(self):
+        """
+        return the current font specification
+        """
+        #should this be a string or a font spec?
+        return self.font
+
+    def get_font_string(self):
+        return str(self.font)
+
+    def label_clicked(self, widget, event):
+        """
+        toggle the sample mode if the user double clicks on the label
+        """
+        if event.type == gtk.GDK._2BUTTON_PRESS:
+            self.use_font = (self.use_font == FALSE)
+            self.update_gui()
+
+
+def print_font(widget,font_selector):
+    """
+    test case
+    """
+    font =  font_selector.get_font()
+    print font
+
+if __name__ == '__main__':
+    """
+    test case
+    """
+
+    dlg = gtk.GtkDialog()
+
+    font_selector = pguFontControl(use_font=FALSE)
+    font_selector.subscribe('font-changed', print_font, font_selector)
+    dlg.vbox.pack_start(font_selector)
+
+    ok = gtk.GtkButton('ok')
+    dlg.action_area.pack_start(ok)
+    ok.connect('clicked', gtk.mainquit)
+    dlg.connect('delete-event', gtk.mainquit)
+    dlg.show_all()
+    gtk.mainloop()
\ No newline at end of file

Added: packages/openev/branches/upstream/current/pymod/pgugrid.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgugrid.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgugrid.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,6251 @@
+#! /usr/bin/env python
+###############################################################################
+# $Id: pgugrid.py,v 1.17 2005/10/17 14:32:28 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  General purpose grid widget.
+# Author:   Gillian Walter, gillian.walter at atlantis-scientific.com
+#
+###############################################################################
+# Copyright (c) 2003, Atlantis Scientific Inc. (www.atlantis-scientific.com)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#  
+#  $Log: pgugrid.py,v $
+#  Revision 1.17  2005/10/17 14:32:28  gmwalter
+#  Merge in Bi's changes.
+#
+#  Revision 1.16  2005/04/06 15:56:52  gmwalter
+#  Add ability to set row titles.
+#
+#  Revision 1.15  2005/03/01 20:53:34  gmwalter
+#  Fix some bugs with scrollbars and configuration.
+#
+#  Revision 1.14  2004/12/15 17:29:26  gmwalter
+#  Fix bug that occurs when user deletes
+#  a cell as it is being edited.
+#
+#  Revision 1.13  2004/09/09 15:22:16  gmwalter
+#  Fix bug for variant case.
+#
+#  Revision 1.12  2004/08/31 19:11:56  gmwalter
+#  Added ability to control line drawing
+#  to some degree.
+#
+#  Revision 1.11  2004/08/30 22:20:32  gmwalter
+#  Added another variant, cleaned up a few problems
+#  with drawing, added ability to set scrollbar-showing
+#  policy and a few functions for default style setting.
+#
+#  Revision 1.10  2004/06/27 20:18:14  gmwalter
+#  Fixed xspc/xtics bug in vecplot, added
+#  character-based field width option to
+#  pgugrid.py.
+#
+#  Revision 1.9  2004/03/23 18:08:25  gmwalter
+#  Add another variant to pgugrid and update
+#  comments; update shapesgrid tool to start
+#  editing on single left click.
+#
+#  Revision 1.8  2004/01/19 18:46:35  gmwalter
+#  Add checks for None in _get_data.
+#
+#  Revision 1.7  2004/01/14 19:55:46  gmwalter
+#  Fixes for empty source list.
+#
+#  Revision 1.6  2004/01/13 20:53:36  gmwalter
+#  Fix typo.
+#
+#  Revision 1.5  2004/01/08 22:33:07  gmwalter
+#  Minor fixes for column text justification
+#  and data type checking.
+#
+#  Revision 1.4  2004/01/08 21:55:41  gmwalter
+#  Add ability to delete rows (backspace
+#  or delete key) and to force a column
+#  to be a particular width.
+#
+#  Revision 1.3  2004/01/07 22:44:46  gmwalter
+#  Add text justification settings to set_source, add
+#  tuples/lists of integers or floats or complex
+#  or strings to allowable sources.
+#
+#  Revision 1.2  2003/11/19 16:46:55  gmwalter
+#  Checked into main branch.
+#
+#
+
+# To do soon:
+# - Simplify default window startup
+#
+#
+# To do later (maybe):
+# - Ability to dump entire or visible grid to printer or file
+# - column resizing
+# - row and column title user configuration
+# - per-row editability options (some rows deletable/changeable, some not)
+# - data input checking (may need to be externally influenced)
+# - add up/down/left/right arrow actions.
+
+# NOTE: This grid has to simulate double click behaviour by tracking the
+# last clicked row/column/cell because on windows a) the gtk double click
+# event is not sent out if the callback on the single click event is too
+# long (this can happen for row selection when there are a large number
+# of shapes- on the order of 50000- and the expose event demands a lot
+# of the cpu),
+# and b) even if the double click event is sent out, the associated
+# single press events are sent out in different orders on windows than
+# they are on linux/unix.  The sequence of events sent out for a double
+# click is as follows:
+# linux/unix: BUTTON_PRESS, BUTTON_PRESS, _2BUTTON_PRESS
+# windows: BUTTON_PRESS, BUTTON_PRESS (if single press callback is too long)
+#          BUTTON_PRESS, _2BUTTON_PRESS, BUTTON_PRESS
+#
+# GTK's double click behaviour is reasonable for linux/unix, but for
+# consistency the approach for all platforms has been to treat the
+# double click event as a single click event and track the last
+# clicked cell/row/column internally.  Internally, the grid only uses the
+# simulated double click event to launch cell editing.
+#
+# The better alternative would be to fix gtk itself for windows, but this
+# could be complicated and would require new users to build a special
+# version of gtk for windows...
+#
+# THIS SHOULD BE REVISITED IF OPENEV IS UPGRADED TO GTK2, AS GTK2 MAY
+# NOT SUFFER THESE PROBLEMS.
+
+import gtk
+import gvsignaler
+import Numeric
+import gview
+import gvutils
+import string
+
+
+# Allowable data sources for pgugrid
+SRC_NONE=0 # no source
+SRC_NUMERIC=1 # 2 dimensional python array
+SRC_SHAPES=2  # Shapes
+SRC_SHAPESLAYER=3 # Shapes layer (layer selection follows grid's)
+SRC_LISTLIST=4 # List of lists
+SRC_LISTOBJ=5 # List of objects
+SRC_LISTUNDEF=6 # Empty list (will be updated with either lists or objects)
+
+# scrollbar sizes
+SCROLL_DIM1=18
+SCROLL_DIM2=45
+
+# Internal supporting classes
+class _column_info:
+    """ Class to manage display-related information for
+        a single column in the pgugrid.
+
+        Members:
+        
+          member- Reference to the source being displayed:
+                  SRC_NUMERIC- data column
+                  SRC_SHAPES- property name
+                  SRC_SHAPESLAYER- property name
+                  SRC_LISTLIST- index within sub list
+                  SRC_LISTOBJ- variable name within object
+        
+          title- Column display title.
+                 
+          type- type of value: 'string','integer','float', or 'complex'.
+        
+          format- a string to indicate how values should be
+                  displayed in the column.  Format string also
+                  determines text justification (left, right,
+                  or center) within the cell
+        
+          editable- editable status of the column.  Indicates
+                    whether or not the values in the column
+                    can be changed other than row addition/
+                    deletion.
+                    0- not editable
+                    1- editable
+        
+          Note: The editability of a given cell is determined by
+                the intersection of the column and row editability
+                parameters.  Add/delete is determined entirely
+                by the row add/delete settings.  The contents of
+                the cell can only be altered (aside from add/delete)
+                if both the row and the column permit change.
+                Editable status of rows can currently only be set
+                on the grid as a whole, not per-row (though
+                this may be added later).  Row editability
+                follows these rules:
+                    0- not editable (000)
+                    1- rows can be deleted, but not added/changed (001)
+                    2- rows can be added, but not deleted/changed (010)
+                    4- rows can be changed, but not deleted/added (100)
+                    3- rows can be deleted/added, but not changed (011)
+                    5- rows can be changed or deleted, but not added (101)
+                    6- rows can be changed or added, but not deleted (110)
+                    7- rows can be changed and added/deleted (111)        
+                   
+        
+          nodata- string to display if no data is available
+        
+          width- width of the column
+
+          start_x- x location of top left corner of column header
+
+          justification- text justification in the column: 0
+                         for right, 1 for left, 2 for center.
+                         
+          title_justification- text justification of column title: 0
+                         for right, 1 for left, 2 for center.
+
+          force_width- force a column to be a particular width.
+
+          force_width_chars- force a column to be a particular number
+                             of characters wide.  If active, will overwrite
+                             force_width with force_width_chars * maximum
+                             character width for the current font.  Set
+                             to -1 to inactivate, or an integer >= 0
+                             corresponding to number of characters
+                             to allow to activate.  Defaults to -1.
+
+          entry_chars- number of characters to allow user to type
+                       (only relevant if column is editable).  90
+                       by default.
+                             
+                         
+
+          
+    """
+    def __init__(self,member,title,type,format=None,editable=0,
+                 nodata='',width=0,start_x=0,justification=0,
+                 title_justification=2,force_width=None,
+                 force_width_chars=-1, entry_chars=0):
+
+        self.title=title
+        self.member=member
+        self.type=type
+        self.format=format
+        self.editable=editable
+        self.nodata=nodata
+        self.width=width
+        self.start_x=start_x
+        self.justification=justification
+        self.title_justification=title_justification
+        # force_width is used to limit a column's width 
+        # width for display (set to None to auto-choose).
+        self.force_width=force_width
+        self.force_width_chars=force_width_chars
+        self.entry_chars=entry_chars
+        
+
+
+# Configurable events that grid can respond to
+
+
+# Cell area has been clicked on
+_cell_clickevents=('cell-left','cell-shift-left','cell-ctrl-left',\
+                   'cell-double-left','cell-right',\
+                   'cell-shift-right','cell-ctrl-right',\
+                   'cell-double-right')
+
+# Row title has been clicked on
+_row_clickevents=('row-left','row-shift-left','row-ctrl-left',\
+                  'row-double-left','row-right','row-shift-right',\
+                  'row-ctrl-right','row-double-right')
+
+# Column title has been clicked on
+_column_clickevents=('column-left','column-shift-left','column-ctrl-left',\
+                  'column-double-left','column-right','column-shift-right',\
+                  'column-ctrl-right','column-double-right')
+
+_cell_keyevents=()
+
+
+_row_select=[(),\
+           ('select-single-row','unselect-single-row','toggle-single-row'),\
+           ('select-single-row','unselect-single-row','toggle-single-row',\
+            'toggle-block-rows','toggle-multiple-rows','select-block-rows',
+            'select-multiple-rows')]
+ 
+_cell_select=[(),\
+                  ('select-single-cell','unselect-single-cell',\
+                   'toggle-single-cell'),\
+                  ('select-single-cell','unselect-single-cell',\
+                   'toggle-single-cell','toggle-block-cells',\
+                   'toggle-multiple-cells')]
+
+_column_select=[(),
+                 ('select-single-column','unselect-single-column',\
+                  'toggle-single-column'),\
+                 ('select-single-column','unselect-single-column',\
+                  'toggle-single-column','toggle-block-columns',\
+                  'toggle-multiple-columns','select-block-columns',
+                  'select-multiple-columns')]
+
+    
+class _selection_info:
+    """Selection behaviour parameters:
+
+    Several parameters must be defined in order to indicate the selection
+    behaviour within the grid.  Here, row selection refers to the selection
+    of an entire row or rows, column selection refers to the selection of
+    an entire column or columns, and cell selection refers to the
+    selection of a single or multiple individual cells.  The possible
+    values of each are 0 (no selection), 1 (single selection), or
+    2 (multiple selection).  The combination parameters indicate whether
+    or not different types of selection can be present at the same time
+    For instance, setting the row-cell selection
+    parameter to 2 indicates that the last selected row can remain
+    selected while a cell within that row or another row is selected.
+    Setting it to 1 indicates that the last selected row can only remain
+    selected if the cell just selected is within that row.  Setting it
+    to 0 indicates that the row will be unselected when a cell is 
+    selected.  Setting it to 3 indicates that selecting a cell should
+    force selection of that row if it is not already selected.
+    For row-column selection, the two are orthogonal, so only
+    0 (rows/columns can't be selected at the same time) and
+    1 (rows/columns can be selected at the same time) are available.
+
+    Note: a cell, row, or column must be selected in order to be
+          changed or deleted through the pgugrid.
+
+    1) row selection- 0, 1, or 2
+    2) column selection- 0, 1, or 2
+    3) cell selection- 0, 1, or 2
+    4) row-cell selection- 0, 1, 2, 3
+    5) column-cell selection- 0, 1, 2, 3
+    6) row-column selection- 0 or 1
+
+    Currently, this class just keeps track of which selection
+    actions should be permitted for a given selection configuration
+    (verified through the get_allowed_actions function).
+    """  
+
+    def __init__(self,row_selection=1,column_selection=1,cell_selection=0,
+               row_cell=0, column_cell=0, row_column=0):
+
+        if row_selection not in [0,1,2]:
+            raise AttributeError,'row_selection must be 0, 1, or 2'
+
+        if cell_selection not in [0,1,2]:
+            raise AttributeError,'cell_selection must be 0, 1, or 2'
+
+        if column_selection not in [0,1,2]:
+            raise AttributeError,'column_selection must be 0, 1, or 2'
+
+        if row_cell not in [0,1,2,3]:
+            raise AttributeError,'row_cell must be 0, 1, 2, or 3'
+        
+        if column_cell not in [0,1,2,3]:
+            raise AttributeError,'column_cell must be 0, 1, 2, or 3'
+
+        if row_column not in [0,1]:
+            raise AttributeError,'row_column must be 0 or 1'
+        
+        self.row=row_selection
+        self.cell=cell_selection
+        self.column=column_selection
+        self.row_cell=row_cell
+        self.column_cell=column_cell
+        self.row_column=row_column
+
+    def get_allowed_actions(self):
+        select_actions=[]
+        for item in _row_select[self.row]:
+            select_actions.append(item)
+
+        for item in _cell_select[self.cell]:
+            select_actions.append(item)
+
+        for item in _column_select[self.column]:
+            select_actions.append(item)
+
+        return select_actions
+
+
+class _pgugrid_options:
+
+    """Class to set up pgugrid configuration.
+    Stores whether or not row and column titles will be shown,
+    and configures event mapping within the grid.
+    Input parameter is a tuple of 9 values indicating configuration
+    information.
+
+    NOTE 1: These parameters control selection through clicking on the grid.
+            In cases where selection can be done from outside (eg.
+            shapes layer through selection tool, or by directly calling
+            a grid function), it is the responsibility of the calling
+            application to ensure that selection consistency is maintained.
+            For instance, if row selection is single, the layer's
+            selection mode (for select tool) should also be set to
+            single; otherwise the grid may end up with multiple
+            selected rows (it stays consistent with the layer selection
+            regardless of parameter settings).
+
+    NOTE 2: NOT ALL CONFIGURATIONS HAVE BEEN IMPLEMENTED/TESTED YET,
+            AND SOME MAY NOT BE IMPLEMENTED EVER.
+
+    SUPPORTED/VALID CONFIGURATIONS:
+
+    Parameter: 0   1   2   3   4   5   6   7 
+
+               0 - 0 - 0 - 0 - 0 - 0 - 0 - 0
+                           1   4
+                           2
+                           
+               1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 
+                   1       1   4   1          
+                   2       2       2
+                                                          
+               2 - 0 - 0 - 0 - 0 - 0 - 0 - 0 
+                       1   1   4               
+                       2   2
+                                                                
+               3 - 0 - 0 - 0 - 0 - 0 - 0 - 0 
+                   1   1   1   4   1           
+                   2   2   2       2
+                
+
+               0 - 1 - 0 - 1 - 0 - 3 - 0 - 0
+               1               4   
+               2          
+               3
+               
+               0 - 2 - 0 - 2 - 0 - 3 - 0 - 0
+               1               4   
+               2          
+               3
+               
+
+    Parameters:
+
+    [0]: Row/Column titles
+    
+         0- no row or column titles
+         1- row titles only
+         2- column titles only
+         3- row and column titles
+
+    [1]: Row selection mode
+    
+         0- rows may not be selected
+
+         If row titles are present:
+             1- rows may be singly selected
+             2- multiple rows may be selected
+
+    [2]: Column selection mode
+    
+         0- columns may not be selected
+
+         If column titles are present:
+             1- columns may be singly selected
+             2- multiple columns may be selected
+
+    [3]: Cell selection mode
+    
+         0- cells may not be selected
+         1- cells may be singly selected
+         2- cells may be multiply selected
+
+    [4]: Row editability
+    
+         0- rows are not editable (000)
+         1- rows can be deleted, but not added/changed (001)
+         2- rows can be added, but not deleted/changed (010)
+         4- rows can be changed, but not deleted/added (100)
+         3- rows can be deleted/added, but not changed (011)
+         5- rows can be changed or deleted, but not added (101)
+         6- rows can be changed or added, but not deleted (110)
+         7- rows can be changed and added/deleted (111)        
+
+         Note: column editability is set by the user using define_columns
+         (if user doesn't set it, defaults are used).
+
+
+    [5]: Row/Cell cross selection behaviour
+    
+         0- rows and cells may not remain simultaneously selected
+         1- rows and cells can remain selected if the selected cell
+            is in the selected row
+         2- rows and cells can remain selected simultaneously even
+            if the cell isn't in the row
+         3- selecting a cell forces selection of that row, clearing
+            rows that do not contain selected cells, and vice versa
+            (row selection is synchronized with cell selection).
+            NOTE: IN THIS MODE, LEFT CLICKING A CELL TOGGLES ITS
+            SELECTION STATE RATHER THAN ALWAYS FORCING IT TO BE
+            SELECTED IF THE EDITING PARAMETER IS TURNED OFF.
+
+    [6]: Column/Cell cross selection behaviour
+    
+         0- columns and cells may not remain simultaneously selected
+         1- columns and cells can remain selected if the selected cell
+            is in the selected column
+         2- columns and cells can remain selected simultaneously even
+            if the cell isn't in the column
+         3- selecting a cell forces selection of that column, clearing
+            columns that do not contain selected cells.  Note:
+            in this case cell selection parameter is ignored (when
+            a column is selected, all cells in that column are selected).
+
+    [7]: Row/Column cross selection behaviour
+    
+         0- rows and columns may not remain simultaneously selected
+         1- rows and columns may be simultaneously selected
+
+    [8]  Variant parameter. Used to do slight variations on the above
+         configuration parameters (eg. turning column sorting on/off,
+         alterations to which events trigger selection/editing etc.,
+         turning off grid lines).  ALL variants must be documented here:
+
+         0 or None: No variation
+         1,3,...: No column sorting (note: some configurations don't anyway)
+         2,3,5,6,..: For configurations where cells are editable and cell
+              selection does not trigger row selection, have cell editing
+              trigger on double left click rather than single click.
+              IE.  This only has an effect if config[5] is not 3, and
+              config[4] is one of 4,5,6, or 7.
+         4,5,...: For configurations where row and/or column selection
+              is enabled, unselection using a second click is disabled
+              (it just triggers selection again).
+         
+         Variant parameter= (column sorting off)*1 +
+                            (start editing on double left click)*2 +
+                            (second click unselects)*4
+
+    Input parameters: configuration
+
+    Valid options for configuration:
+    
+       - click events:
+
+           Row selection (if enabled): NOTE: REQUIRES THAT ROW TITLES
+                                             BE PRESENT!
+               single left click on a row title- toggle row's selection
+
+               shift left click on row title-
+                   multiple consecutive row selection
+
+               control left click on row title-
+                   multiple nonconsecutive row selection
+
+               control right click on row title- If the source is a
+                   shapes layer, translate the view to that row and
+                   add that row to the current selection if possible
+                   (clear other selections if only single selection
+                   is enabled).
+
+           Cell selection (if enabled):
+               if self.config[5] is 3:
+               
+                   control right click on cell- If the source is a
+                       shapes layer, translate the view to that row and
+                       add that row to the current selection if possible
+                       (clear other selections if only single selection
+                       is enabled).
+                   
+               if self.config[5] is not 3:
+               
+               single left click a cell- select a cell.  If self.config[4] is
+                                in [4,5,6,7], enable cell editing if
+                                appropriate.  Press escape or select another
+                                cell to unselect (and stop editing, if
+                                editing).
+                                          
+               if self.config[5] is 3 and self.config[4] is not in
+               [4,5,6,7]:
+                   single left click a cell- toggle a cell/row's selection.
+
+                   
+           Column selection (if enabled): NOTE: REQUIRES THAT COLUMN TITLES
+                                                BE PRESENT!
+
+               single left click on column title- toggle column's selection
+
+               shift left click on column title-
+                   multiple consecutive column selection
+
+               control left click on column title-
+                   multiple nonconsecutive column selection               
+                                    
+           Cell editing (if enabled):
+           
+               left clicking a cell-
+                   Start cell editing.
+                   Pressing Enter or Tab, or selecting
+                   another cell will finish editing
+                   and store the changes.  Enter will
+                   finish by unselecting the cell
+                   and selecting the next one down
+                   in the column for editting.
+                   Tab will finish by unselecting the
+                   cell and selecting the one in the
+                   next column for editting.  Pressing
+                   Escape will exit editing mode, clear
+                   the cell selection, and cancel the
+                   changes.
+                   
+               If Variant parameter is 2, 3,
+               5, 6, ... etc., start editing on double
+               left click rather than single click
+               (single click will just select).
+  
+           Other behaviours:
+               alt left click on table- if the source is SRC_SHAPESLAYER,
+                                        recenter the view on the selected
+                                        shape.
+               
+               single right click on column title- sort by the values in
+                                                      that column (reverse at
+                                                      each click)
+
+                                    
+               arrow keys: (NOT IMPLEMENTED YET)
+               up/down:
+               - if a single row is selected, move the selected row up/down
+               - if a cell is selected, unselect the cell, move up/down,
+                 and select the next cell.  If the cell is being edited,
+                 store the changes before unselecting and moving on, and
+                 select the next cell for editing as well.
+                 
+               right/left:
+               - if a cell is selected, unselect the cell, move right/left,
+                 and select the next cell.
+               - if a cell is selected and being edited, move right/left
+                 within the entry.
+
+           Note on row-cell selection behaviour (column-cell is analogous):
+
+           row-cell        row selected             cell selected
+           parameter
+
+              0          unselect all cells,        unselect all rows
+                         cancel cell editing
+
+              1          unselect all cells        unselect all rows that
+                         not in a selected         don't contain a
+                         row, cancel cell          selected cell
+                         editing if edited cell
+                         is unselected
+
+              2          no effect on cells        no effect on rows
+
+              3          select first cell in      add cell's row to selected
+                         the row if no cell in            row list
+                         that row is selected yet
+
+
+           row-cell        row unselected             cell unselected
+           parameter
+
+              0          no effect on cells        no effect on rows
+
+              1          if # rows is still        if # cells is still 
+                         > 0, unselect any         > 0, unselect any
+                         cells in the unselected   rows that don't
+                         row and cancel cell       contain a selected
+                         editing of unselected     cell
+                         cells
+
+              2          no effect on cells        no effect on rows
+              
+              3          unselect all selected     unselect that row
+                         cells in that row         if no selected cells
+                                                   remain in the row
+                                                   
+
+    """
+    
+    def __init__(self,configuration=None):
+        
+        # Set defaults where user has not specified anything
+        cfg_defaults=(2,2,0,0,0,2,0,0,0)
+        if configuration is None:
+            configuration=cfg_defaults
+        else:
+            cfg=[]
+            for idx in range(9):
+                if len(configuration) <= idx:
+                    cfg.append(cfg_defaults[idx])
+                elif configuration[idx] is None:
+                    cfg.append(cfg_defaults[idx])
+                else:
+                    cfg.append(configuration[idx])
+
+            configuration=tuple(cfg)
+                
+        self.config=configuration
+        
+        # Check configuration
+        _ranges=[]
+        _ranges.append([0,1,2,3])
+        _ranges.append([0,1,2])
+        _ranges.append([0,1,2])
+        _ranges.append([0,1,2])
+        _ranges.append([0,1,2,3,4,5,6,7])
+        _ranges.append([0,1,2,3])
+        _ranges.append([0,1,2,3])
+        _ranges.append([0,1])
+        _names=['display','row select','column select','cell select','edit',
+                'row-cell cross selection','column-cell cross selection',
+                'row-column cross selection']
+
+        _invalid=[(0,None,1,None,None,None,None,None),
+                  (0,None,2,None,None,None,None,None)]
+        
+        for idx in range(len(_ranges)):
+            if self.config[idx] not in _ranges[idx]:
+                txt="_pgugrid_options: invalid configuration specification"+\
+                    ".\nValid ranges are (0-3,0-2,0-2,0-2,0-7,0-3,0-3,0-1)."
+                raise AttributeError,txt
+
+        for cfgtype in _invalid:
+            matches=0
+            idx=0
+            for item in cfgtype:
+                if item is None:
+                    matches=matches+1
+                elif item == self.config[idx]:
+                    matches=matches+1
+                idx=idx+1
+            if matches == 8:
+                txt="_pgugrid_options: invalid configuration combination.\n"
+                idx=0
+                for item in cfgtype:
+                    if item is not None:
+                        txt=txt+_names[idx]+": "+str(self.config[idx])+"\n"
+                        raise AttributeError,txt
+        
+        self.events={}
+        self._allowed_events=[]
+
+        for item in _cell_clickevents:
+            self._allowed_events.append(item)
+            
+        for item in _cell_keyevents:
+            self._allowed_events.append(item)
+
+        # row title type: only used if row titles are shown
+        # Should be 'grid' to show grid row, 'source' to show
+        # underlying source index.
+        
+        self.row_title_type = 'grid'
+        
+        if self.config[0] in [1,3]:
+            self.show_row_titles=1
+            for item in _row_clickevents:
+                self._allowed_events.append(item)
+        else:
+            self.show_row_titles=0
+            
+        if self.config[0] in [2,3]:
+            self.show_column_titles=1
+            for item in _column_clickevents:
+                self._allowed_events.append(item)
+        else:
+            self.show_column_titles=0
+
+        self._set_selection_info()
+        self._set_event_mapping()
+
+    def set_row_title_type(self,type):
+        """Define whether row titles should reflect grid
+           row number, or underlying source index.
+           Parameters:
+               type- either 'grid' (to show grid row) or
+                     'source' (to show source row index) or
+                     'user-defined' (user specifies row titles
+                     as a list).
+        """
+        if type not in ['grid','source','user-defined']:
+            raise AttributeError,"_pgugrid_options: row title type must be "+\
+                                 "either 'grid' or 'source'"
+        
+        self.row_title_type = type
+
+    def _set_selection_info(self):
+        s1=self.config[1]
+        s2=self.config[2]
+        s3=self.config[3]
+        s4=self.config[5]
+        s5=self.config[6]
+        s6=self.config[7]
+        self.selection_info=_selection_info(s1,s2,s3,s4,s5,s6)
+
+    def _extract_variant_options(self):
+        """ Extract the variant parameters and return a tuple
+            of which ones are turned on (0 if they're off,
+            1 if they're on:
+            (column sorting, editing starts on single left click,
+            second click unselects)
+        """
+        leftover = self.config[8]
+        if (leftover >= 4):
+            par2 = 1
+            leftover = leftover - 4
+        else:
+            par2 = 0
+            
+        if (leftover >= 2):
+            par1 = 1
+            leftover = leftover - 2
+        else:
+            par1 = 0
+
+        par0 = leftover
+
+        return (par0, par1, par2)
+            
+    def _set_event_mapping(self):
+        """Map events onto behaviours."""
+
+        # NOTE: the setting of some events to both double and single
+        # clicks is because of the hacky way double clicks are detected
+        # here (two consecutive clicks on the same thing any time
+        # apart) because of problems on windows.  These ensure that
+        # single click events behave okay, but still allows user have
+        # a pseudo-double click event.  NEEDS TO BE FIXED UP!!!
+        vopts = self._extract_variant_options()
+        # row selection
+        if self.show_row_titles == 1:
+            if self.selection_info.row > 0:
+                if vopts[2] == 1:
+                    self.events['row-left']='select-single-row'
+                    self.events['row-double-left']='select-single-row'
+                else:
+                    self.events['row-left']='toggle-single-row'
+                    self.events['row-double-left']='toggle-single-row'
+                self.events['row-ctrl-right']='translate-view-to-row'
+            if self.selection_info.row > 1:
+                if vopts[2] == 1:
+                    self.events['row-shift-left']='select-block-rows'
+                    self.events['row-ctrl-left']='select-multiple-rows'
+                else:
+                    self.events['row-shift-left']='toggle-block-rows'
+                    self.events['row-ctrl-left']='toggle-multiple-rows'
+                    
+        # column sorting and selection
+        if ((self.show_column_titles == 1) and
+            (vopts[0] == 0)):
+            self.events['column-right']='toggle-sort-by-column'
+            self.events['column-double-right']='toggle-sort-by-column'
+            
+            if self.selection_info.column > 0:
+                if vopts[2] == 1:
+                    self.events['column-left']='select-single-column'
+                    self.events['column-double-left']='select-single-column'
+                else:
+                    self.events['column-left']='toggle-single-column'
+                    self.events['column-double-left']='toggle-single-column'
+                
+            if self.selection_info.column > 1:
+                if vopts[2] == 1:
+                    self.events['column-shift-left']='select-block-columns'
+                    self.events['column-ctrl-left']='select-multiple-columns'
+                else:
+                    self.events['column-shift-left']='toggle-block-columns'
+                    self.events['column-ctrl-left']='toggle-multiple-columns'
+
+        # cell selection and editing
+        if self.selection_info.cell > 0:
+            if self.config[5] == 3:
+                if self.config[4] in [4,5,6,7]:
+                    self.events['cell-left']='select-single-cell'
+                    self.events['cell-double-left']='start-cell-edit'
+                else:
+                    if vopts[2] == 1:
+                        self.events['cell-left']='select-single-cell'
+                        self.events['cell-double-left']='select-single-cell'
+                    else:
+                        self.events['cell-left']='toggle-single-cell'
+                        self.events['cell-double-left']='toggle-single-cell'
+                self.events['cell-ctrl-right']='translate-view-to-row'
+            else:
+                if vopts[1] == 1:
+                    self.events['cell-left']='select-single-cell'
+                    if self.config[4] in [4,5,6,7]:
+                        # Note: cell editing function needs to check that
+                        # column is editable before altering anything.
+                        self.events['cell-double-left']='start-cell-edit'
+                else:
+                    if self.config[4] in [4,5,6,7]:
+                        # Note: cell editing function needs to check that
+                        # column is editable before altering anything.
+                        self.events['cell-left']='start-cell-edit'
+                    else:
+                        self.events['cell-left']='select-single-cell'
+                    
+            if self.selection_info.cell > 1:
+                self.events['cell-shift-left']='toggle-block-cells'
+                self.events['cell-ctrl-left']='toggle-multiple-cells'
+                
+
+        # The remaining code in this function is used to
+        # validate a new configuration.
+        # It can be commented out for run-time.
+        
+        # get allowable selection actions
+        allowed=self.selection_info.get_allowed_actions()
+
+        # sorting columns is always permitted
+        allowed.extend(['sort-by-column','reverse-sort-by-column',
+                        'toggle-sort-by-column'])
+
+        allowed.extend(['translate-view-to-row'])
+        
+        # edit-related actions
+        if self.config[4] in [4,5,6,7]:
+            allowed.extend(['start-cell-edit'])
+
+        # check that this configuration does not define
+        # invalid actions (eg. editing in non-editable grid)
+        for action in self.events.values():
+            if action not in allowed:
+                raise 'Invalid action mapping in pgugrid configuration'
+
+        # check that configuration doesn't define callbacks
+        # for events that won't happen
+        for cevent in self.events.keys():
+            if cevent not in self._allowed_events:
+                raise 'Invalid event in pgugrid configuration'
+
+# Grid
+class pguGrid(gtk.GtkHBox,gvsignaler.Signaler):
+    def __init__(self,config):
+        """ Class to create a tabular display grid.
+
+            Parameters:
+                config- configuration to use (controls
+                        whether row and column titles
+                        are displayed, and how rows
+                        and columns can be selected).
+                        See _pgugrid_options class
+                        documentation for more.
+        """
+                          
+        #gtk.GtkTable.__init__(self,rows=2,cols=3)
+        gtk.GtkHBox.__init__(self)
+        self.set_spacing(0)
+        self.vshell = gtk.GtkVBox()
+        self.vshell.set_spacing(0)
+        self.pack_start(self.vshell)
+        
+        
+        self.opts=_pgugrid_options(config)
+        self._ColumnDefs=[]
+        
+        # GUI setup
+
+        # default style
+        self.default_style = None
+        self.default_style_reset_flag = 0
+        self.default_row_title_style = None
+        self.default_row_title_style_reset_flag = 0
+        self.default_col_title_style = None
+        self.default_col_title_style_reset_flag = 0
+	
+	#add flag for numeric range check
+	self.doRangeCheck = False
+	self.numericRange = None
+	
+        self.draw_row_lines = 2
+        self.draw_col_lines = 2
+        
+        #fonts for drawing titles and cells put here
+        self.title_font = None
+        self.cell_font = None
+
+        # Message to display when there is no data
+        self.empty_msg= "NO DATA TO DISPLAY"
+
+        # The height of a single row (not including the
+        # line between rows) and column title row (if present),
+        # width of row titles column (if present).  These
+        # get reset in configure.
+        self.row_height = 0
+        self.column_title_height=0
+        self.row_title_width = 0
+            
+        # padding between lines and text in cells
+        # (included in column width/row heights)
+        self.pad=4 
+    
+        # Build GUI
+        self.hadj = gtk.GtkAdjustment()
+        self.vadj = gtk.GtkAdjustment()
+        
+        self._hscroll = gtk.GtkHScrollbar( adj = self.hadj )
+        self._vscroll = gtk.GtkVScrollbar( adj = self.vadj )
+        self.hsframe = gtk.GtkFrame()
+        self.hsframe.set_shadow_type(gtk.SHADOW_NONE)
+        self.vsframe = gtk.GtkFrame()
+        self.vsframe.set_shadow_type(gtk.SHADOW_NONE)
+        self.hsframe.add(self._hscroll)
+        self.vsframe.add(self._vscroll)
+        self.hscroll_shown=1
+        self.vscroll_shown=1
+        self.hscroll_policy = 0
+        self.vscroll_policy = 0
+        
+        self._area = gtk.GtkDrawingArea()
+        self._pixmap = None
+        
+        #this mask also seems to enable scrolling???
+        evt_mask = gtk.GDK.BUTTON_PRESS_MASK | gtk.GDK.BUTTON_RELEASE_MASK | \
+                   gtk.GDK.KEY_RELEASE_MASK | \
+                   gtk.GDK.FOCUS_CHANGE_MASK | gtk.GDK.EXPOSURE_MASK 
+        self._area.set_events( evt_mask )
+        self._area.set_flags( gtk.CAN_FOCUS | gtk.HAS_GRAB )
+        
+        #flag to recalculate the adjustments
+        self.bCalcAdjustments = gtk.TRUE
+        
+        #set to true if changing some value that would end up causing multiple
+        #expose events or an endless loop even.
+        self.updating = gtk.FALSE
+
+        #frm = gtk.GtkFrame()
+        #frm.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
+        #frm.add(self._area)
+        self._area.set_usize(300,400)
+
+        self._layout=gtk.GtkLayout()
+        self._layout.put(self._area,0,0)
+        self._layout.show_all()
+        
+        #self.attach( frm, 0, 1, 0, 1,
+        #                    xoptions=gtk.FILL, yoptions=gtk.FILL )
+        self.vshell.pack_start(self._layout,expand=gtk.TRUE)
+        self.vshell.pack_start(self.hsframe,expand=gtk.FALSE)
+        self.pack_start(self.vsframe,expand=gtk.FALSE)
+        #self.attach( self._layout, 0, 1, 0, 1,
+        #                    xoptions=gtk.FILL, yoptions=gtk.FILL )
+        #self.attach( self._vscroll, 1, 2, 0, 1, xoptions=gtk.SHRINK)
+        #self.attach( self._hscroll, 0, 1, 1, 2, yoptions=gtk.SHRINK )
+
+        # floating editing entry box 
+        self.editbox = gtk.GtkEntry()
+        self.editbox.connect( 'key_press_event', self.entry_key_press )
+        self.editbox.hide()
+
+        self.editing_cell=None
+
+        
+        dw=300
+        dh=400
+        self.set_usize(dw,dh)
+
+        #self._layout=gtk.GtkLayout()
+        self._layout.put(self.editbox,5000,5000)        
+        #self.add(self._layout)
+        self.show_all()
+
+        # Style list: used to colour rows.  Initially
+        # empty except for default style placeholders
+        # (filled in upon first expose).
+        self.style_list=[None,None,None]
+
+        # new styles can't actually be added until expose.
+        # Use this to store info up until then
+        self.styles_to_add=None
+        
+        # Initialize parameters relating to source displayed in grid
+        self._initialize_settings()
+
+        # User defined row titles (only used if row title type
+        # is reset to 'user-defined')
+        self.row_titles = []
+        
+        # signals: Note that the right-click (button 3) event
+        # is a special case used internally to select cells for
+        # editing.
+        self.publish('row-selection-changed')
+        #self.publish('row-changed')
+        #self.publish('row-added')
+        self.publish('rows-deleted')
+        self.publish('cell-selection-changed')
+        self.publish('cell-changed')
+        self.publish('column-selection-changed')
+        #self.publish('column-changed')
+        #self.publish('column-added')
+        #self.publish('column-deleted')
+
+        # Pass on click events with grid row and column
+        # (-1 if title clicked)
+        self.publish('clicked')
+
+        
+        self._area.connect( 'expose-event', self.expose )
+        self._area.connect( 'configure-event', self.configure )
+        self._area.connect( 'button-press-event', self.click )
+        self._area.connect( 'key_press_event', self.area_key_press )
+        self.hadj.connect( 'value-changed', self.changed )
+        self.vadj.connect( 'value-changed', self.changed )
+        self.connect( 'style-set', self.expose )
+        self.connect('size-allocate',self.size_allocate)
+
+
+    def reset_configuration(self,config):
+        """ Reset the grid configuration. """
+        self.opts=_pgugrid_options(config)
+
+        self._update_column_title_height()
+        self._update_row_title_width()
+            
+        self.expose()
+
+
+    def set_empty_message(self,msg):
+        """ Set the message to display when there is no data. """
+        self.empty_msg=msg
+ 
+    def set_source(self,source,view=None,subset=None,members=None,titles=None,
+                   editables=None,formats=None,types=None,nodata=None,
+                   justify=None,title_justify=None,force_width=None,
+                   force_width_chars=None, entry_chars=None, expose=0,
+                   redefine_columns=1):
+        """ Set the source for the grid.
+            Note: for Numeric arrays, the
+            grid operates on a copy of the
+            source data.  For all other
+            types, it operates on the
+            original.
+
+            Parameters:
+                source- one of:
+                        a) 1 or 2-D Numeric python array
+                        b) List of same-length lists
+                        c) List of objects
+                        d) GvShapes object
+                        e) GvShapesLayer object (requires view)
+                        f) None (to clear the grid or just show titles)
+
+                view- only supplied for GvShapesLayer
+
+                subset- subset to initialize the display with (a list of
+                        source row indices). [optional]
+
+                members, titles, editables, formats, types,
+                nodata, justify, title_justify, force_width,
+                force_width_chars, entry_chars- initial defining
+                        column information (see define_columns). [optional].
+
+                expose- whether or not to expose the grid immediately (1
+                        to expose, 0 not to).  Defaults to 0.
+                        
+                redefine_columns- set to 0 if column definitions should not
+                                  change (if it is 0, then members, titles,
+                                  etc. will be ignored).  If this is set to
+                                  0, it is the calling function's
+                                  responsibility to ensure that the columns
+                                  are still valid for the new source.
+                                  Defaults to 1.
+                        
+                        
+        """
+        if self.src is not None:
+            self.clear()
+
+        self.subset=subset
+        self.bCalcAdjustments = gtk.TRUE
+        
+        if ((source is None) or
+            ((type(source) == type((1,))) and (len(source) == 0))):
+            # put tuples in with None rather than LISTUNDEF because
+            # a tuple cannot be updated, so an empty tuple is
+            # equivalent to None for display.
+            self.src=None
+            self.src_type=SRC_NONE
+            if redefine_columns == 1:
+                self._ColumnDefs=[]
+                if members is not None:
+                    self.define_columns(members,titles,editables,formats,types,
+                            nodata,justify,title_justify,force_width,
+                            force_width_chars, entry_chars,
+                            expose=0)
+            else:
+                # Reset g_columns to previous number
+                # of columns so titles are displayed
+                self.g_columns=len(self._ColumnDefs)
+                self._update_column_widths(expose=0)
+                
+            if expose == 1:
+                self.expose()
+            return
+
+        if (type(source) == type([1,])) and (len(source) == 0):
+            self.src=source
+            self.src_type = SRC_LISTUNDEF
+            if redefine_columns == 1:
+                self._ColumnDefs=[]
+                if (members is not None):
+                    self.define_columns(members,titles,editables,formats,types,
+                            nodata,justify,title_justify,force_width,
+                            force_width_chars, entry_chars,
+                            expose=expose)
+            else:
+                # Reset g_cols to previous number
+                # of columns so titles are displayed
+                self.g_columns=len(self._ColumnDefs)
+                self._update_column_widths(expose=0)
+                
+            if expose == 1:
+                self.expose()
+            return
+
+        if type(source) == type((1,)):
+            source=list(source)
+            
+        if type(source) == type(Numeric.ones((4,4))):
+            shp=Numeric.shape(source)
+            if len(shp) == 1:
+                self.src=Numeric.reshape(source,(1,shp[0]))
+            elif len(shp) == 2:
+                self.src=source
+            else:
+                txt='pgugrid: only 1 or 2-D Numeric arrays are supported'
+                raise AttributeError,txt
+            self.src_type=SRC_NUMERIC
+        elif ((type(source) == type([])) and
+              (type(source[0]) == type([]))):
+            self.src=source
+            self.src_type=SRC_LISTLIST
+        elif ((type(source) == type([])) and
+              (type(source[0]) == type((1,)))):
+            osrc=source
+            source=[]
+            for nexttuple in osrc:
+                source.append(list(nexttuple))
+            self.src=source
+            self.src_type=SRC_LISTLIST
+        elif type(source) == type([]):
+            self.src=source
+            self.src_type=SRC_LISTOBJ
+        elif hasattr(source,'__len__'):
+            self.src=source
+            self.src_type=SRC_SHAPES
+            self.source_changed_id=self.src.connect('changed',
+                                                    self.refresh)
+        else:
+            try:
+                self.src=source.get_parent()
+                self.src_type=SRC_SHAPESLAYER
+                
+                self.source_changed_id=self.src.connect('changed',
+                                                    self.refresh)
+                self.layer=source
+                self.layer_selection_changed_id = \
+                     self.layer.connect('selection-changed',
+                                        self.layer_selection_cb)
+                self.layer_subselection_changed_id = \
+                     self.layer.connect('subselection-changed',
+                                        self.layer_subselection_cb)
+                self.layer_teardown_id = \
+                self.layer.connect('teardown',self.clear_and_expose)
+                    
+                if view is None:
+                    txt='pgugrid: if source is a shapeslayer, a viewarea\n'
+                    txt=txt+'must be supplied.'
+                    raise AttributeError,txt
+                
+                self.view=view
+            except:
+                txt='pgugrid: source must be one of:\n'
+                txt=txt+'a) 1 or 2-D Numeric python array\n'
+                txt=txt+'b) List of same-length lists\n'
+                txt=txt+'c) List of objects with common elements\n'
+                txt=txt+'d) GvShapes object\n'
+                txt=txt+'e) GvShapesLayer object\n'
+                raise AttributeError,txt
+                
+
+        if ((view is not None) and (self.src_type != SRC_SHAPESLAYER)):
+            txt='pgugrid: view updates are only supported for\n'
+            txt=txt+'the shapeslayer source datatype.'
+            raise AttributeError,txt
+
+            
+        self._generate_row_indices()
+
+        if redefine_columns == 1:
+            self.define_columns(members,titles,editables,formats,types,
+                                nodata,justify,title_justify,force_width,
+                                force_width_chars, entry_chars,
+                                expose=expose)
+        else:
+            self._update_column_widths(expose=expose)
+                
+            
+        if self.src_type == SRC_SHAPESLAYER:
+            # Make sure grid is initialized
+            # to proper selection settings
+            self.layer_selection_cb(self.layer)
+            self.layer_subselection_cb(self.layer)
+
+    def set_subset(self,indices=None,expose=1):
+        """ Only display a subset of the items in the
+            grid.
+
+            Parameters:
+                indices- a list of indices to source
+                         for display.  Set to None
+                         to display all rows.
+
+                expose- 0 (do not redisplay grid) or
+                        1 (redisplay grid).  Defaults
+                        to 1.
+        """
+
+        if indices is not None:
+            # Store a copy of the list so it cannot
+            # be updated from outside
+            self.subset=list(indices)
+        else:
+            self.subset=None
+
+        self._generate_row_indices()
+
+        # clear any selections not in the current subset
+        if self.subset is not None:
+            usrows=[]
+            uscells=[]
+            for item in self.selected_rows:
+                if item not in self.subset:
+                    usrows.append(item)
+            for cell in self.selected_cells:
+                if cell[0] not in self.subset:
+                    uscells.append(cell)
+
+            if len(usrows) > 0:
+                self.unselect_rows(usrows,expose=0)
+                self.notify('row-selection-changed',tuple(self.selected_rows))
+                if self.src_type == SRC_SHAPESLAYER:
+                    self.layer.display_change()
+
+            if len(uscells) > 0:
+                self.unselect_cells(uscells,expose=0)
+                self.notify('cell-selection-changed',
+                            tuple(self.selected_cells))
+
+        if expose == 1:
+            self.expose()
+
+    def get_selected_row_indices(self,*args):
+        return tuple(self.selected_rows)
+
+    def get_unselected_row_indices(self,*args):
+        unsel=1-self.row_selectstate
+        unselected=Numeric.compress(unsel > 0, Numeric.arange(len(unsel)))
+        return tuple(unselected)
+
+    def get_selected_column_indices(self,*args):
+        return tuple(self.selected_columns)
+
+    def get_unselected_column_indices(self,*args):
+        unsel=1-self.column_selectstate
+        unselected=Numeric.compress(unsel > 0, Numeric.arange(len(unsel)))
+        return tuple(unselected)
+
+    def get_selected_cell_indices(self,*args):
+        return tuple(self.selected_cells)
+
+    def get_source(self,*args):
+        """ Use to get the underlying source.  This returns the
+            original source, not a copy, so should normally only be used
+            in read-only fashion.
+        """
+        return self.src
+
+    def start_cell_edit(self,cell):
+        """ Start editing new cell. """
+        if self.editing_cell is not None:
+            self.end_cell_edit()
+
+        if self._ColumnDefs[cell[1]].editable == 0:
+            # Column is not editable.
+            self.editing_cell=None
+            self._layout.move(self.editbox,5000,5000)
+            return
+
+        if self.opts.config[4] not in [4,5,6,7]:
+            # Rows are not changeable.
+            self.editing_cell=None
+            self._layout.move(self.editbox,5000,5000)
+            return
+
+        self.editing_cell=cell
+        self.reset_startrow(cell[0])
+        self.reset_startcolumn(cell[1])
+        
+        # Make sure only currently edited cell is selected
+        if ((len(self.selected_cells) == 0) or
+            (len(self.selected_cells) > 1) or
+            (self.selected_cells[0][0] != cell[0]) or
+            (self.selected_cells[0][1] != cell[1])):
+            self.select_cell(cell,clearfirst=1,expose=1)
+            
+        cwidth=self._ColumnDefs[cell[1]].width
+        self.editbox.set_usize(cwidth,self.row_height)
+        self.editbox.set_max_length(self._ColumnDefs[cell[1]].entry_chars)
+        self.editbox.set_text(self._get_datastr(cell[0],cell[1]))
+        self.editbox.set_position(len(self._get_datastr(cell[0],cell[1])))
+        locx=self._ColumnDefs[cell[1]].start_x-\
+                      self._ColumnDefs[self.start_column].start_x+\
+                      self.row_title_width+1
+        locy=self.column_title_height+2+\
+              ((self.src2row[cell[0]]-self.start_row)*(self.row_height+1))
+        if self.opts.show_row_titles == 1:
+            locy=locy+1
+   
+        self._layout.move(self.editbox,locx,locy)
+ 
+    def cancel_cell_edit(self):
+        """ Cancel the current cell edit, not saving changes. """
+        self.editing_cell=None
+        self._layout.move(self.editbox,5000,5000)
+
+    def end_cell_edit(self):
+        """ End current cell edit, saving changes. """
+        if self.editing_cell is not None:
+            newval=self.editbox.get_text()
+            # TO DO: type checking (probably in set datastr itself though)
+            self._set_datastr(self.editing_cell[0],self.editing_cell[1],newval)
+            self.notify('cell-changed',tuple(self.editing_cell))
+            
+            self.editing_cell=None
+            self._layout.move(self.editbox,5000,5000)
+
+    def entry_key_press(self,entry,event):
+        """ Edit box had a key press event.  If enter was pressed,
+            save changes to source and go on to next cell down (if
+            there is one).  If tab was pressed, save and go on to next
+            cell across.  If escape was pressed, cancel cell editing without
+            saving changes.
+        """
+
+        # If a key was pressed, clear the information used
+        # to detect "double" click (hack- see info in _initialize_settings
+        # function)
+        self.last_click_cell=None
+        self.last_click_row=None
+        self.last_click_column=None
+        self.last_click_button=None
+        # End of hack
+            
+        if self.editing_cell is None:
+            return 
+
+        if event.keyval == gtk.GDK.Return:
+            last_cell=self.editing_cell
+            self.end_cell_edit()
+            next_cell=(self.src2row[last_cell[0]]+1,last_cell[1])
+            if next_cell[0] < len(self.row2src):
+                next_cell=(self.row2src[next_cell[0]],last_cell[1])
+                self.start_cell_edit(next_cell)
+            else:
+                self.unselect_all_cells()
+                self._area.grab_focus()
+        elif event.keyval == gtk.GDK.Tab: 
+            last_cell=self.editing_cell
+            self.end_cell_edit()
+            next_cell=(last_cell[0],last_cell[1]+1)
+            if next_cell[1] >= len(self._ColumnDefs):
+                n_row=self.src2row[last_cell[0]]+1
+                if n_row >= len(self.row2src):
+                    next_row=self.row2src[0]
+                else:
+                    next_row=self.row2src[n_row]
+                next_cell=(next_row,0)
+
+            if self._ColumnDefs[next_cell[1]].editable == 1:  
+                self.start_cell_edit(next_cell)
+            else:
+                self.unselect_all_cells()
+                
+            # This next line avoids the next tab
+            # being treated as part of the text
+            # on alternate tabs (ie. jumping
+            # from start to end within text
+            # rather than resetting the
+            # editable cell
+            self._area.grab_focus()
+            
+        elif event.keyval == gtk.GDK.Escape:
+            self.cancel_cell_edit()
+            self.unselect_all_cells()
+            self._area.grab_focus()
+
+    def area_key_press(self,area,event):
+        """ If escape is pressed, clear all current cell selections
+            and cancel any editing."""
+        # If a key was pressed, clear the information used
+        # to detect "double" click (hack- see info in _initialize_settings
+        # function)
+        self.last_click_cell=None
+        self.last_click_row=None
+        self.last_click_column=None
+        self.last_click_button=None
+        # End of hack
+
+        if event.keyval == gtk.GDK.Escape:
+            self.cancel_cell_edit()
+            self.unselect_all_cells()
+        elif ((event.keyval == gtk.GDK.BackSpace) or
+              (event.keyval == gtk.GDK.Delete)):
+            if self.opts.config[4] in [1,3,5,7]:
+                # row deletion supported by configuration
+                rows=self.get_selected_row_indices()
+                if len(rows) > 0:
+                    self.delete_rows(rows)
+        
+    def _generate_row_indices(self):
+
+        """ Create row index mappings.
+            Source and grid are both indexed
+            from 0.  Initially, all non-None source rows
+            are displayed.  If a subset is specified,
+            row2src will be subset minus any None's
+            contained within the subset list.  Otherwise,
+            row2src will map all non-None rows of source
+            onto the grid.
+        """
+
+        # TO DO: Incorporate sorting property, select logic
+        # (want to retain selections where possible)
+        self.src2row=[]
+        self.row2src=[]
+
+        if self.src_type in [SRC_NONE,SRC_LISTUNDEF]:
+            return
+            
+        if self.src_type == SRC_NUMERIC:
+            # Numeric arrays don't have rows of None
+            if self.subset is None:
+                self.src2row=range(Numeric.shape(self.src)[0])
+                self.row2src=range(Numeric.shape(self.src)[0])
+            else:
+                grididx=0
+                for idx in range(Numeric.shape(self.src)[0]):
+                    if idx in self.subset:
+                        self.src2row.append(grididx)
+                        grididx=grididx+1
+                        self.row2src.append(idx)
+                    else:
+                        self.src2row.append(-1)
+                        
+        else:
+            if self.subset is None:
+                grididx=0
+                for idx in range(len(self.src)):
+                    if self.src[idx] != None:
+                        self.src2row.append(grididx)
+                        grididx=grididx+1
+                        self.row2src.append(idx)
+                    else:
+                        self.src2row.append(-2)
+            else:
+                grididx=0
+                for idx in range(len(self.src)):
+                    if idx in self.subset:
+                        if self.src[idx] is None:
+                            self.src2row.append(-2)
+                        else:
+                            self.src2row.append(grididx)
+                            grididx=grididx+1
+                            self.row2src.append(idx)
+                    elif self.src[idx] != None:
+                        # row is not None, but it
+                        # isn't in subset
+                        self.src2row.append(-1)
+                    else:
+                        self.src2row.append(-2)
+
+        self.s_rows=len(self.src2row)
+        self.g_rows=len(self.row2src)
+
+        # If selection state has not been specified, initialize
+        # to zeros.  If selection state has been specified and
+        # source is the same length as before,
+        # leave select state alone.  Otherwise,
+        # reset row and cell matrices.
+        if ((self.row_selectstate is None) or
+            (len(self.row_selectstate) != self.s_rows)):
+            self.row_selectstate=Numeric.zeros((self.s_rows,))
+            self.cell_selectstate=Numeric.zeros((self.s_rows,self.g_columns))
+            self.last_selected_row=None
+            self.last_toggled_row=None
+            self.last_selected_cell=None
+            self.last_toggled_cell=None
+
+        # If a sort column has been specified, do sorting
+        if self.sort_column != -1:
+            self.sort_by_column()
+            
+        if self.last_selected_row is not None:
+            self.reset_startrow(self.last_selected_row)
+
+        self._update_row_title_width()
+
+        # Row colouring:
+        # If no style index exists already, initialize
+        # all rows to the default style.
+        #
+        # If the source is a shapes or shapes layer
+        # object and a row style index exists that is
+        # less than the length of the length of the
+        # source, some rows have been added to the end
+        # of the source (new shapes are always appended
+        # to gvshapes; there are no functions for
+        # inserting) so keep the existing row colours
+        # the same and add more of the default colour
+        # to the end (shapes are always appended).
+        # In the shapes/shapeslayer cases, the rows of the 
+        # source are just set to None when they are
+        # deleted, and the list only shrinks if the
+        # last shape is deleted (it shrinks until it
+        # encounters a non-None shape).  This means that
+        # if the row style index is longer than the source,
+        # the row style index will still be valid for the
+        # length of the source, and can just be truncated.
+        #
+        # For all the other types of objects (lists, tuples,
+        # arrays), no assumptions can be made about how to
+        # update the row style index if its length differs
+        # from that of the source because rows can be
+        # deleted/inserted at any point.  In these cases,
+        # the row style index will be reset to the default
+        # values for the length of the source if the two
+        # differ in length; if they are the same, the
+        # colours are left alone.  For these objects,
+        # internal pgugrid functions such as delete_rows
+        # should update the style list appropriately before
+        # the row indices are regenerated so that
+        # colours are maintained; if the source is updated
+        # externally, the colours will be reset to the
+        # default.
+        if self.src_type in [SRC_SHAPES,SRC_SHAPESLAYER]: 
+            if (self.row_style_index is None):
+                self.row_style_index=Numeric.zeros((self.s_rows,))
+            elif (len(self.row_style_index) > self.s_rows):
+                self.row_style_index=self.row_style_index[:self.s_rows]
+            elif (len(self.row_style_index) < self.s_rows):
+                temp=self.row_style_index
+                self.row_style_index=Numeric.zeros((self.s_rows,))
+                self.row_style_index[:len(temp)]=temp
+        else:
+            if ((self.row_style_index is None) or
+                (len(self.row_style_index) != self.s_rows)):
+                self.row_style_index=Numeric.zeros((self.s_rows,))
+                
+        # Below: if row title styles are set, add code here:
+                       
+        self.bCalcAdjustments=gtk.TRUE
+
+    def add_style(self,style_or_tuple):
+        """ Add a style to the list of styles available to
+            expose in the grid.  style_or_tuple may be either
+            a GtkStyle, or a tuple consisting of
+            (bg_gc_normal,fg_gc_normal,bg_gc_selected,fg_gc_selected,
+             bg_gc_insensitive,fg_gc_insensitive),
+            where bg=background, fg=foreground. Not all
+            of these are used yet (may never be).
+            Currently, the grid only distinguishes between
+            selected and unselected rows and columns.
+            Cell selection colours follow the ROW selection
+            colours.  If a row and column are simultaneously
+            selected, the column selection colours will override the
+            row selection colours.
+            Each of these entries should be either None
+            (then the default value is used) or a tuple
+            of 3 integer (0-65535) values.
+            Returns the index of the new style.  
+        """
+
+        if type(style_or_tuple) == type(self.get_style()):
+            self.style_list.append(style_or_tuple.copy())
+            return len(self.style_list)-1
+        else:
+            # Style will be created at next expose (can't
+            # be created if no expose has taken place)
+            idx=len(self.style_list)
+            if self.styles_to_add is None:
+                self.styles_to_add=[]
+                
+            self.styles_to_add.append((idx,style_or_tuple))
+            self.style_list.append(None)
+            return idx
+
+    def _add_style(self,cidx,ctuple):
+        """ Internal function that actually creates the requested
+            style.
+        """
+        if self.style_list[cidx] is None:
+            style=self.get_style()
+        else:
+            # use existing style for defaults if it is
+            # present
+            style = self.style_list[cidx]
+            
+        nstyle=style.copy()
+        for idx in range(max(len(ctuple),6)):
+            item=ctuple[idx]
+            if item is None:
+                if (idx == 0) and (style.bg_gc[gtk.STATE_NORMAL] is not None):
+                    nstyle.bg_gc[gtk.STATE_NORMAL] = \
+                                 style.bg_gc[gtk.STATE_NORMAL]
+                elif (idx == 1) and (style.fg_gc[gtk.STATE_NORMAL] is not None):
+                    nstyle.fg_gc[gtk.STATE_NORMAL] = \
+                                 style.fg_gc[gtk.STATE_NORMAL]
+                elif (idx == 2) and (style.bg_gc[gtk.STATE_SELECTED] is not None):
+                    nstyle.bg_gc[gtk.STATE_SELECTED] = \
+                                 style.bg_gc[gtk.STATE_SELECTED]
+                elif (idx == 3) and (style.fg_gc[gtk.STATE_SELECTED] is not None):
+                    nstyle.fg_gc[gtk.STATE_SELECTED] = \
+                                 style.fg_gc[gtk.STATE_SELECTED]
+                elif (idx == 4) and (style.bg_gc[gtk.STATE_INSENSITIVE] is not None):
+                    nstyle.bg_gc[gtk.STATE_INSENSITIVE] = \
+                                 style.bg_gc[gtk.STATE_INSENSITIVE]
+                elif (idx == 5) and (style.fg_gc[gtk.STATE_INSENSITIVE] is not None):
+                    nstyle.fg_gc[gtk.STATE_INSENSITIVE] = \
+                                 style.fg_gc[gtk.STATE_INSENSITIVE]
+                    
+                continue
+
+            if idx == 0:
+                ngc=self._get_new_gc(item,style.bg_gc[gtk.STATE_NORMAL])
+                nstyle.bg_gc[gtk.STATE_NORMAL]=ngc
+            elif idx == 1:
+                ngc=self._get_new_gc(item,style.fg_gc[gtk.STATE_NORMAL])
+                nstyle.fg_gc[gtk.STATE_NORMAL]=ngc
+            elif idx == 2:
+                ngc=self._get_new_gc(item,style.bg_gc[gtk.STATE_SELECTED])
+                nstyle.bg_gc[gtk.STATE_SELECTED]=ngc
+            elif idx == 3:
+                ngc=self._get_new_gc(item,style.fg_gc[gtk.STATE_SELECTED])
+                nstyle.fg_gc[gtk.STATE_SELECTED]=ngc
+            elif idx == 4:
+                ngc=self._get_new_gc(item,
+                                     style.bg_gc[gtk.STATE_INSENSITIVE])
+                nstyle.bg_gc[gtk.STATE_INSENSITIVE]=ngc
+            elif idx == 5:
+                ngc=self._get_new_gc(item,
+                                     style.fg_gc[gtk.STATE_INSENSITIVE])
+                nstyle.fg_gc[gtk.STATE_INSENSITIVE]=ngc
+
+        # None placeholder was created when user called add_style to
+        # request the new style.  Here, just replace the None.
+        self.style_list[cidx]=nstyle
+
+    def _get_new_gc(self,color_tuple, ref_gc):
+        """ Get a new graphics context for a style. """
+        # WARNING: Don't try printing out the gc's foreground and
+        # background values- it seems that the printed values are
+        # unrelated to the actual set values.  Setting them does
+        # seem to work though.
+        cmap=self.get_colormap()
+        new_color=cmap.alloc(color_tuple[0],color_tuple[1],color_tuple[2])
+        if ref_gc is None:
+            ref_gc=self.get_style().white_gc
+            
+        ngc=self.get_window().new_gc(foreground=new_color,
+        #                             background=new_color)
+            background=new_color,font=ref_gc.font, fill=ref_gc.fill,
+            subwindow_mode=ref_gc.subwindow_mode,
+	    ts_x_origin=ref_gc.ts_x_origin, ts_y_origin=ref_gc.ts_y_origin,
+      	    clip_x_origin=ref_gc.clip_x_origin,
+            clip_y_origin=ref_gc.clip_y_origin,
+	    line_width=ref_gc.line_width, line_style=ref_gc.line_style,
+            cap_style=ref_gc.cap_style,
+	    join_style=ref_gc.join_style)
+        
+        return ngc
+
+    def set_line_drawing(self,rlines=2, clines=2, expose=1):
+        """ Set which lines in the grid are drawn:
+            Inputs:
+                rlines- 0 for no lines between rows
+                        1 for lines between row titles
+                        2 for lines under whole rows
+            
+                clines- 0 for no lines between columns
+                        1 for lines between column titles
+                        2 for lines under whole columns
+                
+                
+        """
+        self.draw_row_lines = rlines
+        self.draw_col_lines = clines
+        if expose == 1:
+            self.expose()
+    
+    def set_default_style(self,tuple):
+        """ Set the default style:  a tuple consisting of
+            (bg_gc_normal,fg_gc_normal,bg_gc_selected,fg_gc_selected,
+             bg_gc_insensitive,fg_gc_insensitive),
+            where bg=background, fg=foreground. Not all
+            of these are used yet (may never be).
+            Each of these entries should be either None
+            (then the default value is used) or a tuple
+            of 3 integer (0-65535) values.  
+        """
+        self.default_style_reset_flag = 1
+        self.default_style = tuple
+    
+    def set_default_row_title_style(self,tuple):
+        """ Set the default row title style:  a tuple consisting of
+            (bg_gc_normal,fg_gc_normal,bg_gc_selected,fg_gc_selected,
+             bg_gc_insensitive,fg_gc_insensitive),
+            where bg=background, fg=foreground. Not all
+            of these are used yet (may never be).
+            Each of these entries should be either None
+            (then the default value is used) or a tuple
+            of 3 integer (0-65535) values.  
+        """
+        self.default_row_title_style_reset_flag = 1
+        self.default_row_title_style = tuple
+
+    def set_default_col_title_style(self,tuple):
+        """ Set the default column title style:  a tuple consisting of
+            (bg_gc_normal,fg_gc_normal,bg_gc_selected,fg_gc_selected,
+             bg_gc_insensitive,fg_gc_insensitive),
+            where bg=background, fg=foreground. Not all
+            of these are used yet (may never be).
+            Each of these entries should be either None
+            (then the default value is used) or a tuple
+            of 3 integer (0-65535) values.  
+        """
+        self.default_col_title_style_reset_flag = 1
+        self.default_col_title_style = tuple
+
+    def set_default_selection_colour(self,ctuple):
+        """ Set the default selection colour: a tuple
+            of three integers.
+        """
+        self.set_default_style((None,None,ctuple,None,None,None))
+        
+    def set_row_style(self,row_index,style_index):
+        """ Assign a style index to a row. row_index
+            is either a single integer index or a list
+            of indices.  If it is -1, all rows will
+            be set to have style style_index.  If it is
+            a list, the rows (in source coordinates) in
+            that list will have that style.  If it is
+            a single integer that is > -1, only that
+            row will be set to have that style.
+            style_index is always an integer.
+        """
+
+        if type(row_index) == type([]):
+            for row in row_index:
+                self.row_style_index[row]=style_index
+        elif row_index == -1:
+            self.row_style_index=\
+                style_index*Numeric.ones((self.s_rows,))
+        else:
+            self.row_style_index[row_index]=style_index
+
+
+    def set_column_style(self,column_index,style_index):
+        """ Assign a style index to a column. column_index
+            is either a single integer index or a list
+            of indices.  If it is -1, all columns will
+            be set to have style style_index.  If it is
+            a list, the columns (in grid coordinates) in
+            that list will have that style.  If it is
+            a single integer that is > -1, only that
+            column will be set to have that style.
+            style_index is always an integer.  Note that
+            only the selected states of column styles
+            will be seen- in drawing, the unselected
+            row color overrides the unselected column
+            color.
+        """
+
+        if type(column_index) == type([]):
+            for column in column_index:
+                self.column_style_index[column]=style_index
+        elif column_index == -1:
+            self.column_style_index=\
+                style_index*Numeric.ones((self.g_columns,))
+        else:
+            self.column_style_index[column_index]=style_index
+
+            
+    
+    def set_row_title_type(self,type='grid',expose=1,titles=None):
+        """Define whether row titles should reflect grid
+           row number, or underlying source index.
+           NOTE: If len(titles) doesn't match the source length,
+                 the first len(source) values will be used.  If
+                 len(titles) < len(source), '' will be used for
+                 source indices > len(titles)-1.
+                 
+           Parameters:
+               type- either 'grid' (to show grid row) or
+                     'source' (to show source row index) or
+                     'user-defined' (user specifies row
+                     titles as a list).  In the 'user-defined'
+                     case, the title list is indexed
+                     as the source row indices (ie. title[0]
+                     corresponds to source[0] as opposed
+                     to grid[0]).  Default type is 'grid'.
+               expose- 1 if pgugrid should be redrawn
+                       immediately; 0 if not.  Defaults
+                       to 1.
+               titles- None for 'grid' or 'source'; a list
+                       of the same length as the source
+                       for 'user-defined'.
+        """
+        self.opts.set_row_title_type(type)
+
+        if self.opts.show_row_titles != 1:
+            print 'Warning- setting row title type on a grid that\n'+\
+                  '         is not configured to display row titles!'
+
+        if type == 'user-defined':
+            if titles is None:
+                raise AttributeError,'User-defined row titles must be a list!'
+            
+            self.row_titles = titles
+            self._update_row_title_width() 
+            
+        if expose == 1:
+            self.expose()
+            
+    def define_columns(self,members=None,titles=None,editables=None,
+                       formats=None,types=None,nodata=None,
+                       justify=None,title_justify=None,
+                       force_width=None,force_width_chars=None,
+                       entry_chars=None,expose=1):
+        """ Define which columns to include in display.
+            Parameters:
+                members- Relates the column to the underlying source.
+                         Is either None (use defaults) or a list.
+                         List contains elements described here:
+                         SRC_NUMERIC: column index (integer)
+                         SRC_SHAPES: shape property
+                         SRC_SHAPESLAYER: shape property
+                         SRC_LISTLIST: sublist index
+                         SRC_LISTOBJ: object member, or None if the
+                                      object is a string, float, integer,
+                                      or complex.
+
+                title- Describes the display title for each column.
+                       Is either None or a list of strings.  If list, the
+                       list must have a one-to-one correspondence
+                       to the members list (if members list is None,
+                       the length of this list must be the same as
+                       the number of members detected by default).
+
+                editables- Is either None (use global editable parameter
+                           used in initial grid definition), 0 (no
+                           editable columns), 1 (all editable columns),
+                           or a list of 1's and zeros that has a one-to-one
+                           correspondence with the members list.
+
+                formats- Is either None (use default formatting), a single
+                         expression that should be used to format all
+                         columns, or a list of expressions that has a
+                         one-to-one correspondence with the members
+                         list.
+
+                types- Is either None (try to auto-detect type), a
+                       single type describing members of all columns,
+                       or a list of types that has a one-to-one
+                       correspondence with the members list.
+
+                nodata- Is either None, a single string describing
+                        the nodata string for members of all columns,
+                        or a list of nodata strings that has a one-to-one
+                        correspondence with the members list.
+
+                justify- Is either None, a single integer to describe
+                         all column justifications (0=right, 1=left,
+                         2=center), or a list of integers (0-2)
+                         that has a one-to-one correspondence with the
+                         members list. Defaults to 0.
+
+                title_justify- Is either None, a single integer to describe
+                               all column title justifications (0=right,
+                               1=left, 2=center), or a list of integers (0-2)
+                               that has a one-to-one correspondence with the
+                               members list. Defaults to 2.
+
+                force_width- Used to force a particular column's width.
+                             Is either None, a single width to describe
+                             all column widths, or a list of widths
+                             that has a one-to-one correspondence with the
+                             members list. Defaults to None (auto-select
+                             column widths). 
+
+                force_width_chars- whether width is specified in terms of
+                                   pixels (-1) or characters (> 0).  Is either
+                                   None, a single value to describe all
+                                   columns, or a list of values with a
+                                   one-to-one correspondence with the
+                                   members list.  Defaults to -1.
+
+                entry_chars- number of characters to allow user to type
+                             (only relevant if column is editable).  Is
+                             either None, a single value to describe all
+                             columns, or a list of values with a one-to-one
+                             correspondence with the members list.  Defaults
+                             to 90.
+        """
+
+
+        self._ColumnDefs=[]
+
+        if (self.src_type in [SRC_NONE,SRC_LISTUNDEF]) and (members is None):
+            return
+
+        if type(members) == type((1,)):
+            members=list(members)
+        if type(titles) == type((1,)):
+            titles=list(titles)
+        if type(editables) == type((1,)):
+            editables=list(editables)
+        if type(formats) == type((1,)):
+            formats=list(formats)
+        if type(types) == type((1,)):
+            types=list(types)
+        if type(nodata) == type((1,)):
+            nodata=list(nodata)
+        if type(justify) == type((1,)):
+            justify=list(justify)
+        if type(title_justify) == type((1,)):
+            title_justify=list(title_justify)
+        if type(force_width) == type((1,)):
+            force_width=list(force_width)
+        if type(force_width_chars) == type((1,)):
+            force_width_chars=list(force_width_chars)
+        if type(entry_chars) == type((1,)):
+            entry_chars=list(entry_chars)
+        
+        startx=self.row_title_width+1
+        if self.opts.show_row_titles == 1:
+            startx=startx+1
+
+        if members is None:
+            if self.src_type == SRC_NUMERIC:
+                # Default: one column per array column, with
+                # column number as the title              
+                ci_mems=range(Numeric.shape(self.src)[1])
+            elif ((self.src_type == SRC_SHAPES) or
+                  (self.src_type == SRC_SHAPESLAYER)):
+                ci_mems=[]
+                schema=self.src.get_schema()
+                if len(schema) > 0:
+                    for item in schema:
+                        ci_mems.append(item[0])
+                else:
+                    if len(self.src) > 0:
+                        props=self.src[0].get_properties()
+                        for ckey in props.keys():
+                            ci_mems.append(ckey)
+            elif (self.src_type == SRC_LISTLIST):
+                ci_mems=range(len(self.src[0]))
+            elif (self.src_type == SRC_LISTOBJ):
+                if type(self.src[0]) == type(''):
+                    ci_mems=[None]
+                elif type(self.src[0]) == type(1):
+                    ci_mems=[None]
+                elif type(self.src[0]) == type(5.2):
+                    ci_mems=[None]
+                elif type(self.src[0]) == type(complex(1,1)):
+                    ci_mems=[None]
+                else:    
+                    members=dir(self.src[0])
+                    ci_mems=[]
+                    for ckey in members:
+                        cmem=eval('self.src[0].'+ckey)
+                        oktypes=[type(''),type(1),type(5.2),type(complex(1,1))]
+                        if type(cmem) in oktypes:
+                            ci_mems.append(ckey)
+        else:
+            ci_mems=members
+
+        ci_titles=[]
+        if titles is None:
+            if self.src_type in [SRC_NUMERIC,SRC_LISTLIST]:
+                for idx in ci_mems:
+                    ci_titles.append('Column '+str(idx))
+            elif (self.src_type in
+              [SRC_SHAPES,SRC_SHAPESLAYER,SRC_LISTOBJ,SRC_NONE,SRC_LISTUNDEF]):
+                for mem in ci_mems:
+                    if mem is not None:
+                        ci_titles.append(mem)
+                    else:
+                        ci_titles.append('Column 0')
+                        
+        elif type(titles) == type(''):
+            for idx in ci_mems:
+                ci_titles.append(titles)
+        elif type(titles) == type([]):
+            ci_titles=titles
+            if len(ci_titles) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                                   'title per member!'
+        else:
+            raise RuntimeError,'define_columns: titles must be either '+\
+                               'None, a string, or a list.'
+
+        ci_edit=[]
+        if editables is None:
+            if self.opts.config[4] in [4,5,6,7]:
+                editopts=1
+            else:
+                editopts=0
+                
+            for idx in ci_mems:
+                ci_edit.append(editopts)
+                    
+        elif editables in [0,1]:
+            for idx in ci_mems:
+                ci_edit.append(editables)
+        elif type(editables) == type([]):
+            ci_edit=editables
+            if len(ci_edit) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                           'edit property per member if editables is a list!'
+        else:
+            raise RuntimeError,'define_columns: editables must be either '+\
+                               'None, 0, 1, or a list.'
+                
+
+        ci_fmts=[]
+        if formats is None:
+            if ((self.src_type == SRC_SHAPES) or
+                  (self.src_type == SRC_SHAPESLAYER)):
+                schema=self.src.get_schema()
+                schema_fmts={}
+                for item in schema:
+                    if item[1] == 'integer':
+                        schema_fmts[item[0]]="%"+str(item[2])+"d"
+                    elif item[1] == 'float':
+                        schema_fmts[item[0]]="%"+str(item[2])+"."+\
+                                              str(item[3])+"f"
+                        
+                for idx in ci_mems:
+                    if schema_fmts.has_key(idx):
+                        ci_fmts.append(schema_fmts[idx])
+                    else:
+                        ci_fmts.append(None)
+                        
+            else:
+                for idx in ci_mems:
+                    ci_fmts.append(None)
+        elif type(formats) == type(''):
+            for idx in ci_mems:
+                ci_fmts.append(formats)
+        elif type(formats) == type([]):
+            ci_fmts=formats
+            if len(ci_fmts) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                                   'format per member if formats is a list!'
+        else:
+            raise RuntimeError,'define_columns: formats must be either '+\
+                               'None, a string, or a list.'
+
+        ci_types=[]
+        if types is None:
+            if self.src_type == SRC_NUMERIC:
+                #inttypes=['1','l','s','i','u','b','w']
+                inttypes=[Numeric.Int,Numeric.Int0,Numeric.Int8,Numeric.Int16,
+                          Numeric.Int32,Numeric.UnsignedInteger,Numeric.UInt8,
+                          Numeric.UInt16,Numeric.UInt32]
+                #complextypes=['D','F']
+                complextypes=[Numeric.Complex,Numeric.Complex0,
+                              Numeric.Complex8,
+                              Numeric.Complex16,Numeric.Complex32,
+                              Numeric.Complex64]
+                if self.src.typecode() in inttypes:
+                    ctype='integer'
+                elif self.src.typecode() in complextypes:
+                    ctype='complex'
+                else:
+                    ctype='float'
+
+                for mem in ci_mems:
+                    ci_types.append(ctype)
+                    
+            elif self.src_type in [SRC_SHAPES,SRC_SHAPESLAYER]:
+                schema=self.src.get_schema()
+                sd={}
+                for item in schema:
+                    sd[item[0]]=item[1]
+                sdkeys=sd.keys()    
+                for mem in ci_mems:
+                    if mem in sdkeys:
+                        ci_types.append(sd[mem])
+                    else:
+                        ci_types.append('string')
+                        
+            elif self.src_type == SRC_LISTLIST:
+                for idx in ci_mems:
+                    if type(self.src[0][idx]) == type(1.2):
+                        ctype='float'
+                    elif type(self.src[0][idx]) == type(1):
+                        ctype='integer'
+                    elif type(self.src[0][idx]) == type(complex(1,1)):
+                        ctype='complex'
+                    else:
+                        ctype='string'
+                    ci_types.append(ctype)
+                    
+            elif self.src_type == SRC_LISTOBJ:
+                if ci_mems[0] is None:
+                    if type(self.src[0]) == type(''):
+                        ci_types.append('string')
+                    elif type(self.src[0]) == type(1):
+                        ci_types.append('integer')
+                    elif type(self.src[0]) == type(5.2):
+                        ci_types.append('float')
+                    elif type(self.src[0]) == type(complex(1,1)):
+                        ci_types.append('complex')
+                else:
+                    for mem in ci_mems:
+                        cmem=eval('self.src[0].'+mem)
+                        if type(cmem) == type(complex(1,1)):
+                            ci_types.append('complex')
+                        elif type(cmem) == type(5.3):
+                            ci_types.append('float')
+                        elif type(cmem) == type(1):
+                            ci_types.append('integer')
+                        else:
+                            ci_types.append('string')
+            elif self.src_type in [SRC_NONE,SRC_LISTUNDEF]:
+                for mem in ci_mems:
+                    ci_types.append(None)
+                    
+        elif type(types) == type(''):
+            for idx in ci_mems:
+                ci_types.append(types)
+        elif type(types) == type([]):
+            ci_types=list(types)
+            if len(ci_types) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                                   'type per member if types is a list!'
+        else:
+            raise RuntimeError,'define_columns: types must be either '+\
+                               'None, a string, or a list.'
+
+
+        ci_nodata=[]
+        if nodata is None:
+            for idx in ci_mems:
+                ci_nodata.append('')
+        elif type(nodata) == type(''):
+            for idx in ci_mems:
+                ci_nodata.append(nodata)
+        elif type(nodata) == type([]):
+            ci_nodata=nodata
+            if len(ci_nodata) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                                   'string per member if nodata is a list!'
+        else:
+            raise RuntimeError,'define_columns: nodata must be either '+\
+                               'None, a string, or a list.'
+
+
+        ci_just=[]
+        if justify is None:
+            for idx in ci_mems:
+                ci_just.append(0)
+        elif type(justify) == type(1):
+            for idx in ci_mems:
+                ci_just.append(justify)
+        elif type(justify) == type([]):
+            ci_just=justify
+            if len(ci_just) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                                   'integer per member if justify is a list!'
+        else:
+            raise RuntimeError,'define_columns: justify must be either '+\
+                               'None, a string, or a list.'
+            
+
+        ci_tjust=[]
+        if title_justify is None:
+            for idx in ci_mems:
+                ci_tjust.append(2)
+        elif type(title_justify) == type(1):
+            for idx in ci_mems:
+                ci_tjust.append(title_justify)
+        elif type(title_justify) == type([]):
+            ci_tjust=title_justify
+            if len(ci_tjust) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                         'integer per member if title_justify is a list!'
+        else:
+            raise RuntimeError,'define_columns: title_justify must be '+\
+                               'either None, a string, or a list.'
+              
+        ci_fwidth=[]
+        if force_width is None:
+            for idx in ci_mems:
+                ci_fwidth.append(None)
+        elif type(force_width) == type(1):
+            for idx in ci_mems:
+                ci_fwidth.append(force_width)
+        elif type(force_width) == type([]):
+            ci_fwidth=force_width
+            if len(ci_fwidth) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                         'integer per member if force_width is a list!'
+        else:
+            raise RuntimeError,'define_columns: force_width must be '+\
+                               'either None, a string, or a list.'
+              
+        ci_fwidthc=[]
+        if force_width_chars is None:
+            for idx in ci_mems:
+                ci_fwidthc.append(-1)
+        elif type(force_width_chars) == type(1):
+            for idx in ci_mems:
+                ci_fwidthc.append(force_width_chars)
+        elif type(force_width_chars) == type([]):
+            ci_fwidthc=force_width_chars
+            if len(ci_fwidthc) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                         'integer per member if force_width_chars is a list!'
+        else:
+            raise RuntimeError,'define_columns: force_width_chars must be '+\
+                               'either None, a string, or a list.'
+     
+        ci_entryc=[]
+        if entry_chars is None:
+            for idx in ci_mems:
+                ci_entryc.append(90)
+        elif type(entry_chars) == type(1):
+            for idx in ci_mems:
+                ci_entryc.append(entry_chars)
+        elif type(entry_chars) == type([]):
+            ci_entryc=entry_chars
+            if len(ci_entryc) != len(ci_mems):
+                raise RuntimeError,'define_columns: must specify one '+\
+                         'integer per member if entry_chars is a list!'
+        else:
+            raise RuntimeError,'define_columns: entry_chars must be '+\
+                               'either None, a string, or a list.'
+                
+        style = self.get_style()
+
+        if self.title_font is None:
+            try:
+                self.title_font = gtk.load_font( self.title_font_spec )
+            except:
+                self.title_font = style.font
+                
+            self._update_column_title_height()
+            self._update_row_title_width()         
+
+        if self.cell_font is None:
+            try:
+                self.cell_font = gtk.load_font( self.cell_font_spec )
+            except:
+                self.cell_font = style.font
+            self.row_height = self.cell_font.ascent + 2*self.pad
+                
+        for idx in range(len(ci_mems)):
+            cwidth=30
+            cnew=_column_info(ci_mems[idx],ci_titles[idx],ci_types[idx],
+                              ci_fmts[idx],ci_edit[idx],ci_nodata[idx],
+                              cwidth,startx,ci_just[idx],ci_tjust[idx],
+                              ci_fwidth[idx], ci_fwidthc[idx], ci_entryc[idx])
+            self._ColumnDefs.append(cnew)            
+            # Cycle though first few rows to update column width
+            if ((self._ColumnDefs[idx].force_width is not None) or
+                (self._ColumnDefs[idx].force_width_chars > -1)):
+                if self._ColumnDefs[idx].force_width_chars == -1:
+                    cwidth = self._ColumnDefs[idx].force_width
+                else:
+                    # This assumes that 'W' has pretty much the maximum
+                    # width for most fonts (rather than cycling through
+                    # all possible characters).  May need to update...
+                    cwidth = (max(self.title_font.width('W'),
+                                 self.cell_font.width('W'))*
+                         self._ColumnDefs[idx].force_width_chars) + 2*self.pad
+                    self._ColumnDefs[idx].force_width = cwidth
+                    
+            else:
+                cwidth=self.title_font.width(self._ColumnDefs[idx].title)+\
+                    2*self.pad
+                tmprows=min([10,len(self.row2src)])
+                for cr in range(tmprows):
+                    cwidth=max([cwidth,self.cell_font.width(
+                        self._get_datastr(cr,idx))+2*self.pad])
+                cwidth=max(cwidth,30)
+            self._ColumnDefs[idx].width=cwidth
+            startx=startx+cwidth+1
+
+
+        self.g_rows=len(self.row2src)    
+        self.g_columns=len(self._ColumnDefs)
+
+        self.column_widths=[]
+        for item in self._ColumnDefs:
+            self.column_widths.append(item.width)
+
+        self.column_selectstate=Numeric.zeros((self.g_columns,))
+        self.cell_selectstate=Numeric.zeros((self.s_rows,self.g_columns))
+        self.last_selected_column=None
+        self.last_selected_cell=None
+        self.last_toggled_column=None
+        self.last_toggled_cell=None
+
+        self.column_style_index=Numeric.zeros((self.g_columns,))
+        # Below: not used yet, but will be later
+        #self.columntitle_style_index=Numeric.ones((self.g_columns,))
+
+        self.bCalcAdjustments = gtk.TRUE
+        
+        if expose == 1:
+            self.expose()
+
+
+    def _update_column_widths(self,expose=1):
+        """ Update column widths for new source with same column defs. """
+                
+        style = self.get_style()
+
+        if self.title_font is None:
+            try:
+                self.title_font = gtk.load_font( self.title_font_spec )
+            except:
+                self.title_font = style.font
+                
+            self._update_column_title_height()
+            self._update_row_title_width()  
+
+        if self.cell_font is None:
+            try:
+                self.cell_font = gtk.load_font( self.cell_font_spec )
+            except:
+                self.cell_font = style.font
+            self.row_height = self.cell_font.ascent + 2*self.pad
+                        
+        cwidth=30
+        startx=self.row_title_width+1
+        if self.opts.show_row_titles == 1:
+            startx=startx+1
+        for idx in range(len(self._ColumnDefs)):
+            if (( self._ColumnDefs[idx].force_width is not None) or
+                (self._ColumnDefs[idx].force_width_chars > -1)):
+                if self._ColumnDefs[idx].force_width_chars == -1:
+                    cwidth = self._ColumnDefs[idx].force_width
+                else:
+                    # This assumes that 'W' has pretty much the maximum
+                    # width for most fonts (rather than cycling through
+                    # all possible characters).  May need to update...
+                    cwidth = (max(self.title_font.width('W'),
+                                 self.cell_font.width('W'))*
+                        self._ColumnDefs[idx].force_width_chars) + 2*self.pad
+                    self._ColumnDefs[idx].force_width = cwidth
+            else:
+                cwidth=self.title_font.width(self._ColumnDefs[idx].title)+\
+                    2*self.pad
+                tmprows=min([10,len(self.row2src)])
+                for cr in range(tmprows):
+                    cwidth=max([cwidth,self.cell_font.width(
+                        self._get_datastr(cr,idx))+2*self.pad])
+                cwidth=max(cwidth,30)
+            self._ColumnDefs[idx].width=cwidth
+            self._ColumnDefs[idx].start_x=startx
+            startx=startx+cwidth+1
+
+        self.bCalcAdjustments = gtk.TRUE  
+        if expose == 1:
+            self.expose()
+            
+    def _get_datastr(self,row,column):
+        """ Get the underlying source data in cell row,column as a string. """
+        cdata=self._get_data(row,column)
+
+        if cdata is None:
+            cdata=self._ColumnDefs[column].nodata
+            return cdata
+        
+        if self._ColumnDefs[column].format in ['',None]:
+            return str(cdata)
+        else:
+            return self._ColumnDefs[column].format % cdata
+
+    def _get_data(self,row,column):
+        """ Get the source data in cell row,column. """
+        if self.src_type == SRC_NUMERIC:
+            return self.src[row,self._ColumnDefs[column].member]
+        elif self.src_type == SRC_SHAPES:
+            if self.src[row] is not None:
+                datastr = self.src[row].get_property(
+                    self._ColumnDefs[column].member)
+            else:
+                return None
+            
+            if datastr is None:
+                return None
+            
+            if self._ColumnDefs[column].type == 'float':
+                data=float(datastr)
+            elif self._ColumnDefs[column].type == 'integer':
+                data=int(datastr)
+            else:
+                data=datastr
+                
+            return data
+        
+        elif self.src_type == SRC_SHAPESLAYER:
+            if self.src[row] is not None:
+                datastr = self.src[row].get_property(
+                    self._ColumnDefs[column].member)
+            else:
+                return None
+
+            if datastr is None:
+                return None
+            
+            if self._ColumnDefs[column].type == 'float':
+                data=float(datastr)
+            elif self._ColumnDefs[column].type == 'integer':
+                data=int(datastr)
+            else:
+                data=datastr
+                
+            return data
+        
+        elif self.src_type == SRC_LISTLIST:
+            if self.src[row] is not None:
+                datastr= self.src[row][self._ColumnDefs[column].member]
+            else:
+                return None
+            
+            if self._ColumnDefs[column].type == 'float':
+                data=float(datastr)
+            elif self._ColumnDefs[column].type == 'integer':
+                data=int(datastr)
+            elif self._ColumnDefs[column].type == 'complex':
+                data=complex(datastr)
+            else:
+                data=datastr
+                
+            return data
+        
+        elif self.src_type == SRC_LISTOBJ:
+            if self._ColumnDefs[column].member is None:
+                datastr=self.src[row]
+            else:
+                datastr = eval('self.src[row].'+
+                               self._ColumnDefs[column].member)
+                
+            if self._ColumnDefs[column].type == 'float':
+                data=float(datastr)
+            elif self._ColumnDefs[column].type == 'integer':
+                data=int(datastr)
+            elif self._ColumnDefs[column].type == 'complex':
+                data=complex(datastr)
+            else:
+                data=datastr
+                
+            return data
+
+    def get_cell_data(self,row,column):
+        """ Get the source data in cell (source) row, (grid) column. """
+        return self._get_data(row,column)
+
+    def get_cell_data_string(self,row,column):
+        """ Get the source data in cell (source) row,(grid) column
+            as a string. """
+        return self._get_datastr(row,column)
+    
+    def set_cell_data(self,row,column,value):
+        """ Set the source data in cell (source) row, (grid) column. """
+        return self._set_data(row,column,value)
+
+    def set_cell_data_string(self,row,column,value):
+        """ Set the source data string in cell (source) row,(grid) column. """
+        return self._set_datastr(row,column,value)
+    
+    def set_rangeCheck(self,flag= False):	
+	"""Set range check flag """
+	self.doRangeCheck = flag
+	
+    def set_numericRange(self,range):
+	"""Set valid range[min,max] for range check of input value """
+	if range is None or type(range) != type([1,]):
+		gvutils.error('Invalid range input. [min,max]')
+		return
+	else:	
+		self.numericRange = range
+	
+    def _set_datastr(self,row,column,value):
+        """ Set the source data in cell row,column to value.
+            Note that value is entered as a string that
+            must be converted to the required type."""
+        
+        if self._ColumnDefs[column].type == 'string':
+            nvalue=value
+        elif self._ColumnDefs[column].type == 'integer':
+            try:
+                nvalue=int(value)
+		if self.doRangeCheck and self.numericRange is not None:
+			if nvalue <self.numericRange[0] or nvalue>self.numericRange[1]:
+				gvutils.error('Input out of range. '+str(nvalue)+" "+str(self.numericRange))
+				return
+		
+            except:
+                # If empty string was entered, user may have
+                # clicked on an empty cell and not typed
+                # anything, so don't set the value or send
+                # an error
+                if len(value) == 0:
+                    return
+                
+                gvutils.error('Invalid data entry.  Integer required.')
+                return
+            
+        elif self._ColumnDefs[column].type == 'float':
+            try:
+                nvalue=float(value)
+                if self.doRangeCheck and self.numericRange is not None:
+                    if nvalue <self.numericRange[0] or nvalue>self.numericRange[1]:
+                        gvutils.error('Input out of range. '+str(nvalue)+" "+str(self.numericRange))
+                        return
+            except:
+                if len(value) == 0:
+                    return
+                
+                gvutils.error('Invalid data entry.  Float required.')
+                return
+            
+        elif self._ColumnDefs[column].type == 'complex':
+            try:
+                nvalue=complex(value)
+            except:
+                if len(value) == 0:
+                    return
+                
+                gvutils.error('Invalid data entry.  Complex required.')
+                return
+            
+        self._set_data(row,column,nvalue)
+
+    def _set_data(self,row,column,value):
+        """ Set the source data in cell row,column to value.
+            Necessary conversions have already taken
+            place (use _set_datastr if value is still a
+            string that needs to be converted).
+        """
+        if self.src_type == SRC_NUMERIC:
+            try:
+                self.src[row,self._ColumnDefs[column].member]=value
+            except:
+                dtype=self.src.typecode()
+                txt='Invalid entry for array of typecode '+dtype
+                gvutils.error(txt)                
+        elif self.src_type == SRC_SHAPES:
+            s1=string.strip(str(value))
+            pval=self.src[row].get_property(
+                self._ColumnDefs[column].member)
+            if pval is not None:
+                s2=string.strip(pval)
+            else:
+                s2=None
+                if s1 == '':
+                    return
+                
+            if s1 != s2:
+                # The copying is necessary for undo to work properly
+                shape=self.src[row].copy()
+                shape.set_property(self._ColumnDefs[column].member,
+                                       s1)
+                # avoid regenerating row indices
+                self.src.signal_handler_block(self.source_changed_id)
+                self.src[row]=shape
+                self.src.signal_handler_unblock(self.source_changed_id)
+        elif self.src_type == SRC_SHAPESLAYER:
+            s1=string.strip(str(value))
+            pval=self.src[row].get_property(
+                self._ColumnDefs[column].member)
+            if pval is not None:
+                s2=string.strip(pval)
+            else:
+                s2=None
+                if s1 == '':
+                    return
+                
+            if s1 != s2:
+                shape=self.src[row].copy()
+                shape.set_property(self._ColumnDefs[column].member,
+                                           s1)
+                self.src.signal_handler_block(self.source_changed_id)
+                self.src[row]=shape
+                self.src.signal_handler_unblock(self.source_changed_id)
+        elif self.src_type == SRC_LISTLIST:
+            self.src[row][self._ColumnDefs[column].member]=value
+        elif self.src_type == SRC_LISTOBJ:
+            if self._ColumnDefs[column].member is None:
+                self.src[row]=value
+            else:
+                setattr(self.src[row],self._ColumnDefs[column].member,value)
+        
+
+    def translate_view_to_row(self,row):
+        """ If source is a shapeslayer, translate the view
+            to center on the row'th source shape and select
+            that row if it isn't already selected and selection
+            is enabled.
+        """
+        
+        if self.view is not None:
+            if row > (len(self.src)-1):
+                raise RuntimeError,'translate_row_to_view: tried to '+\
+                      'translate view to center on non-existent node.'
+
+            cnode=self.src[row].get_node()
+            self.view.set_translation(-cnode[0],-cnode[1])
+            if self.row_selectstate[row] == 0:
+                if self.opts.selection_info.row == 1:
+                    self.select_row(row,clearfirst=1,expose=1)
+                elif self.opts.selection_info.row == 2:
+                    self.select_row(row,clearfirst=0,expose=1)
+
+    def delete_row(self,row):
+        """ Delete source row row.  Note: this function will
+            reset the source in the Numerical array case 
+            (necessary because array must be re-allocated when
+            it changes size).
+        """
+
+        self.delete_rows([row])
+
+    def delete_rows(self,row_list):
+        """ Delete source rows in row_list.  Note: this function will
+            reset the source in the Numerical array case 
+            (necessary because array must be re-allocated when
+            it changes size).
+        """
+
+        if type(row_list) == type((1,)):
+            row_list=list(row_list)
+
+        if len(row_list) == 0:
+            return
+        
+        self.bCalcAdjustments=gtk.TRUE
+        
+        if self.src_type in [SRC_SHAPES,SRC_SHAPESLAYER]:
+            self.src.delete_shapes(row_list)
+            
+        elif self.src_type in [SRC_LISTLIST,SRC_LISTOBJ]:
+            # sort the row list in descending order so that
+            # deleting one row won't alter the indices
+            # of the next one.
+            row_list.sort()
+            row_list.reverse()
+            rstyle_index=list(self.row_style_index)
+            for item in row_list:
+                self.src.pop(item)
+                rstyle_index.pop(item)
+                
+            self.row_style_index=Numeric.array(rstyle_index)
+                
+            self.refresh()
+        else:
+            row_list.sort()
+            newrows=self.src.shape[0]-len(row_list)
+            cols=self.src.shape[1]
+            rm_rows=0
+            first_row=0
+            newarr=Numeric.zeros((newrows,cols),self.src.typecode())
+            for item in row_list:
+                newarr[first_row:item-rm_rows,:]=\
+                             self.src[first_row+rm_rows:item,:]
+                first_row=item-rm_rows
+                rm_rows=rm_rows+1
+                
+            newarr[first_row:newrows,:]=self.src[first_row+rm_rows:]
+            
+            # Update row style list (colours)
+            row_list.reverse()
+            rstyle_index=list(self.row_style_index)
+            
+            for item in row_list:
+                rstyle_index.pop(item)
+                
+            self.row_style_index=Numeric.array(rstyle_index)
+            
+            self.src=newarr
+            self.refresh()
+
+        self.notify('rows-deleted',row_list)
+    
+    def select_rows(self,row_list,clearfirst=0,expose=1):
+        """ Trigger row selection in the grid.
+          
+          Parameters:
+              row_list- list of integers corresponding
+                        to source (not grid or subset) index
+                        coordinate to select
+
+              clearfirst- 0 (don't clear existing selections before
+                          selecting new ones) or 1 (clear exisiting
+                          selections).  Defaults to 0.
+
+              expose- 0 (redraw grid), or 1 (do not redraw grid).
+                      Defaults to 1. 
+        """
+
+        self._flags['selecting-rows']=1
+        if self.src_type == SRC_SHAPESLAYER:
+            # block so that grid doesn't refresh until the end.
+            self.layer.signal_handler_block(self.layer_selection_changed_id)
+            self.layer.signal_handler_block(self.layer_subselection_changed_id)
+            if clearfirst == 1:
+                self.layer.clear_selection()
+            for idx in range(len(row_list)-1):
+                self.layer.select_shape(row_list[idx])
+            self.layer.signal_handler_unblock(
+                             self.layer_selection_changed_id)
+            self.layer.signal_handler_unblock(
+                             self.layer_subselection_changed_id)
+            # The layer's selection-changed signal should
+            # trigger a callback that calls _select_rows
+            if len(row_list) > 0:
+                self.layer.select_shape(row_list[len(row_list)-1]) 
+        else:
+            if clearfirst == 1:
+                self._unselect_all_rows()
+            self._select_rows(row_list)
+
+        self._flags['selecting-rows']=0
+        
+        if expose == 1:
+
+            nlist=self._rows_updated()
+            
+            if len(row_list) > 0:
+                self.reset_startrow(row_list[len(row_list)-1])
+                
+            self.expose()
+            
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+            
+        return 1
+               
+
+    def unselect_rows(self,row_list,clearfirst=0,expose=1):
+        """ Trigger row selection in the grid.
+          
+          Parameters:
+              row_list- list of integers corresponding
+                        to source (not grid or subset) index
+                        coordinate to select
+
+              clearfirst- 0 (don't alter existing selections before
+                          unselecting) or 1 (clear all values to
+                          selected before unselecting requested
+                          rows). Defaults to 0.
+
+              expose- 0 (redraw grid), or 1 (do not redraw grid).
+                      Defaults to 1. 
+        """
+
+        self._flags['selecting-rows']=1
+        if self.src_type == SRC_SHAPESLAYER:
+            # block so that grid doesn't refresh until the end.
+            self.layer.signal_handler_block(self.layer_selection_changed_id)
+            self.layer.signal_handler_block(self.layer_subselection_changed_id)
+            if clearfirst == 1:
+                self.layer.select_all()
+            for idx in range(len(row_list)-1):
+                self.layer.deselect_shape(row_list[idx])
+            self.layer.signal_handler_unblock(
+                             self.layer_selection_changed_id)
+            self.layer.signal_handler_unblock(
+                             self.layer_subselection_changed_id)
+            # The layer's selection-changed signal should
+            # trigger a callback that calls _select_rows
+            if len(row_list) > 0:
+                self.layer.deselect_shape(row_list[len(row_list)-1])
+        else:
+            if clearfirst == 1:
+                self._select_all_rows()
+                
+            self._unselect_rows(row_list)
+
+        self._flags['selecting-rows']=0
+
+        if len(row_list) > 0:
+            self.last_toggled_row=row_list[len(row_list)-1]
+            
+        if expose == 1: 
+
+            nlist=self._rows_updated()
+                     	    
+            self.expose()
+
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+            
+        return 1
+
+    def select_row(self,row,clearfirst=0,expose=1):
+        """Select a single row."""
+
+        self.select_rows([row],clearfirst,expose)
+
+    def unselect_row(self,row,clearfirst=0,expose=1):
+        """Unselect a single row."""
+
+        self.unselect_rows([row],clearfirst,expose)
+
+    def toggle_row(self,row,expose=1):
+        """Toggle a single row."""
+
+        if self.row_selectstate[row] == 0:
+            self.select_row(row,0,expose)
+        else:
+            self.unselect_row(row,0,expose)
+            
+    def toggle_rows(self,row_list,expose=1):
+        """Toggle multiple rows."""
+
+        select=[]
+        unselect=[]
+
+        for row in row_list:
+            if self.row_selectstate[row] == 0:
+                select.append(row)
+            else:
+                unselect.append(row)
+
+        if len(unselect) > 0:
+            self.unselect_rows(unselect,0,0)
+            
+        if len(select) > 0:
+            self.select_rows(select,0,0)
+            
+        if expose == 1:
+
+            nlist=self._rows_updated()
+            
+            self.expose()
+
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                
+    def toggle_block_rows(self,end_row,expose=1):
+        """Toggle a block of rows between self.last_toggled_row
+           and end_row.
+
+           Parameters:
+               end_row- last row to include in toggle block
+
+               expose- 0 (don't redraw grid) or 1 (redraw grid).
+                       Defaults to 1.
+
+        """
+
+        if self.last_toggled_row is None:
+            return
+        
+        g1=self.src2row[self.last_toggled_row]
+        g2=self.src2row[end_row]
+        if g1 < g2:
+            tlist=list(self.row2src[g1+1:g2+1])
+        else:
+            tlist=list(self.row2src[g2:g1])
+
+        self.toggle_rows(tlist,expose=0)
+        self.last_toggled_row=end_row
+        if self.row_selectstate[end_row] == 1:
+            self.last_selected_row=end_row
+
+        if expose == 1:
+            
+            nlist=self._rows_updated()
+            
+            self.reset_startrow(end_row)
+            
+            self.expose()
+
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                
+    def select_block_rows(self,end_row,expose=1):
+        """Select a block of rows between self.last_toggled_row
+           and end_row.
+
+           Parameters:
+               end_row- last row to include in toggle block
+
+               expose- 0 (don't redraw grid) or 1 (redraw grid).
+                       Defaults to 1.
+
+        """
+
+        if self.last_toggled_row is None:
+            return
+        
+        g1=self.src2row[self.last_toggled_row]
+        g2=self.src2row[end_row]
+        if g1 < g2:
+            tlist=list(self.row2src[g1+1:g2+1])
+        else:
+            tlist=list(self.row2src[g2:g1])
+
+        self.select_rows(tlist,expose=0)
+        self.last_toggled_row=end_row
+        self.last_selected_row=end_row
+
+        if expose == 1:
+            
+            nlist=self._rows_updated()
+            
+            self.reset_startrow(end_row)
+            
+            self.expose()
+
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                           
+    def select_all_rows(self,expose=1):
+        """Select all rows (updates internal matrices,
+           triggers layer selection if relevant,
+           and redraws grid if requested).
+        """
+
+        # TO DO: ADD CODE TO CHECK PGUGRID OPTIONS
+        # AND MAKE SURE NO ILLEGAL SELECTION
+        # IS PERMITTED (RAISE ERROR IF IT TRIES)
+        
+        self._flags['selecting-rows']=1
+        if self.src_type == SRC_SHAPESLAYER:
+            self.layer.select_all()
+        else:
+            self._select_all_rows()
+            
+        self._flags['selecting-rows']=0
+
+        self.last_toggled_row=None
+        self.last_selected_row=None
+        
+        if expose == 1:
+
+            nlist=self._rows_updated()
+            
+            self.expose()
+
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                
+    def unselect_all_rows(self,expose=1):
+        """Unselect all rows (updates internal matrices,
+           triggers layer selection if relevant,
+           and redraws grid if requested).
+        """
+
+        self._flags['selecting-rows']=1
+        if self.src_type == SRC_SHAPESLAYER:
+            self.layer.clear_selection()
+        else:
+            self._unselect_all_rows()
+            
+        self._flags['selecting-rows']=0
+
+        self.last_toggled_row=None
+        self.last_selected_row=None
+        
+        if expose == 1:
+            
+            nlist=self._rows_updated()
+            
+            self.expose()
+
+            if self.src_type == SRC_SHAPESLAYER:
+                self.layer.display_change()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                
+    def _select_all_rows(self):
+        """ Update internal selection matrices. """
+        if self.row_selectstate is None:
+            return 0
+
+        self.row_selectstate[:] = 1
+        self.selected_rows=range(len(self.row_selectstate))
+        
+        # check for cross-selection
+        if self.opts.selection_info.row_cell == 0:
+            if len(self.selected_cells) > 0:
+                self.unselect_all_cells(expose=0)
+                self.notify('cell-selection-changed',())
+
+        if self.opts.selection_info.row_column == 0:
+            if len(self.selected_columns) > 0:
+                self.unselect_all_columns(expose=0)
+                self.notify('column-selection-changed',())
+
+
+        return 1
+
+    def _unselect_all_rows(self):
+        """ Update internal selection matrices. """
+        if self.row_selectstate is None:
+            return 0
+
+        self.row_selectstate[:] = 0
+        self.selected_rows=[]
+        
+        return 1
+    
+    def _select_rows(self,row_list):
+        """ Helper function for select_rows.  Updates
+            the selection matrices internally.  
+        """
+        if self.row_selectstate is None:
+            # No source is set
+            return 0
+
+        for row in row_list:
+            if (len(self.row_selectstate) <= row):
+                raise RuntimeError,'pgugrid: tried to select nonexistent row'
+            else:
+                if self.row_selectstate[row] == 0:
+                    self.row_selectstate[row]=1
+                    self.selected_rows.append(row)
+
+            
+        if len(row_list) > 0:
+            self.last_selected_row=row_list[len(row_list)-1]
+            self.last_toggled_row=row_list[len(row_list)-1]
+        
+            # check for cross-selection
+            if self.opts.selection_info.row_cell == 0:
+                if len(self.selected_cells) > 0:
+                    self.unselect_all_cells(expose=0)
+                    self.notify('cell-selection-changed',())
+            elif self.opts.selection_info.row_cell == 1:
+                if len(self.selected_cells) > 0:
+                    uscells=[]
+                    for item in self.selected_cells:
+                        if self.row_selectstate[item[0]] == 0:
+                            uscells.append(item)
+                    if len(uscells) > 0:          
+                        self.unselect_cells(uscells,expose=0)
+                        self.notify('cell-selection-changed',
+                                    tuple(self.selected_cells))
+
+            if self.opts.selection_info.row_column == 0:
+                if len(self.selected_columns) > 0:
+                    self.unselect_all_columns(expose=0)
+                    self.notify('column-selection-changed',())
+        
+        return 1
+     
+    def _unselect_rows(self,row_list):
+        """ Helper function for unselect_rows.  Updates
+            the selection matrices internally.  
+        """
+        if self.row_selectstate is None:
+            # No source is set
+            return 0
+
+        for row in row_list:
+            if (len(self.row_selectstate) <= row):
+                raise RuntimeError,'pgugrid: tried to select nonexistent row'
+            else:
+                if self.row_selectstate[row] == 1:
+                    self.row_selectstate[row]=0
+                    self.selected_rows.remove(row)
+                
+        return 1
+
+    def select_column(self,column,clearfirst=0,expose=1):
+        """Select a single column."""
+        
+        rval = self.select_columns([column],clearfirst,expose)
+        return rval
+
+    def select_columns(self,column_list,clearfirst=0,expose=1):
+        """Select multiple columns."""
+        
+        if self.column_selectstate is None:
+            # No source is set
+            return 0
+
+        if clearfirst == 1:
+            self.unselect_all_columns(expose=0)
+            
+        for column in column_list:
+            if (len(self.column_selectstate) <= column):
+                raise RuntimeError,'pgugrid: tried to select nonexistent '+\
+                      'column'
+            else:
+                self.column_selectstate[column]=1
+
+            if column not in self.selected_columns:
+                self.selected_columns.append(column)
+
+            
+        if len(column_list) > 0:
+            self.last_selected_column=column_list[len(column_list)-1]
+            self.last_toggled_column=column_list[len(column_list)-1]
+        
+
+        if expose == 1:
+
+            nlist=self._columns_updated()
+                    
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1
+    
+    def select_all_columns(self,expose=1):
+        """Select all columns."""
+
+        if self.column_selectstate is None:
+            return 0
+
+        self.column_selectstate[:]=1
+        self.selected_columns=range(len(self.column_selectstate))
+
+        self.last_toggled_column=None
+        self.last_selected_column=None
+        
+        if expose == 1:
+
+            nlist = self._columns_updated()
+            
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1
+
+    def unselect_column(self,column,clearfirst=0,expose=1):
+        """Unselect a single column."""
+        
+        rval = self.unselect_columns([column],clearfirst,expose)
+        return rval
+        
+
+    def unselect_columns(self,column_list,clearfirst=0,expose=1):
+        """Unselect multiple columns."""
+        
+        if self.column_selectstate is None:
+            # No source is set
+            return 0
+
+        if clearfirst == 1:
+            self.select_all_columns(expose=0)
+            
+        for column in column_list:
+            if (len(self.column_selectstate) <= column):
+                raise RuntimeError,'pgugrid: tried to select nonexistent '+\
+                      'column'
+            else:
+                self.column_selectstate[column]=0
+
+            if column in self.selected_columns:
+                self.selected_columns.remove(column)
+
+        if len(column_list) > 0:
+            self.last_toggled_column=column_list[len(column_list)-1]
+            
+        if expose == 1:
+
+            nlist = self._columns_updated()
+            
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1        
+
+    def unselect_all_columns(self,expose=1):
+        """Unselect all columns."""
+
+        if self.column_selectstate is None:
+            return 0
+
+        self.column_selectstate[:]=0
+        self.selected_columns=[]
+
+        self.last_toggled_column=None
+        self.last_selected_column=None
+        
+        if expose == 1:
+
+            nlist = self._columns_updated()
+            
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1
+        
+
+    def toggle_column(self,column,expose=1):
+        """Toggle a single column."""
+
+        if self.column_selectstate[column] == 0:
+            self.select_column(column,0,expose)
+        else:
+            self.unselect_column(column,0,expose)
+                    
+
+    def toggle_columns(self,column_list,expose=1):
+        """Toggle multiple columns."""
+
+        select=[]
+        unselect=[]
+
+        for column in column_list:
+            if self.column_selectstate[column] == 0:
+                select.append(column)
+            else:
+                unselect.append(column)
+
+        if len(unselect) > 0:
+            self.unselect_columns(unselect,0,0)
+            
+        if len(select) > 0:
+            self.select_columns(select,0,0)
+            
+        if expose == 1:
+
+            nlist = self._columns_updated()
+            
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                    
+
+    def toggle_block_columns(self,end_column,expose=1):
+        """Toggle a block of columns between self.last_toggled_column
+           and end_column.
+
+           Parameters:
+               end_column- last column to include in toggle block
+
+               expose- 0 (don't redraw grid) or 1 (redraw grid).
+                       Defaults to 1.
+
+        """
+
+        if self.last_toggled_column is None:
+            return
+
+        if self.last_toggled_column < end_column:
+            tlist=range(self.last_toggled_column+1,end_column+1)
+        else:
+            tlist=range(end_column,self.last_toggled_column)
+
+        self.toggle_columns(tlist,expose=0)
+        self.last_toggled_column=end_column
+        if self.column_selectstate[end_column] == 1:
+            self.last_selected_column=end_column
+
+        if expose == 1:
+
+            nlist = self._columns_updated()
+            
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+            
+
+    def select_block_columns(self,end_column,expose=1):
+        """Select a block of columns between self.last_toggled_column
+           and end_column.
+
+           Parameters:
+               end_column- last column to include in toggle block
+
+               expose- 0 (don't redraw grid) or 1 (redraw grid).
+                       Defaults to 1.
+
+        """
+
+        if self.last_toggled_column is None:
+            return
+
+        if self.last_toggled_column < end_column:
+            tlist=range(self.last_toggled_column+1,end_column+1)
+        else:
+            tlist=range(end_column,self.last_toggled_column)
+
+        self.select_columns(tlist,expose=0)
+        self.last_toggled_column=end_column
+        self.last_selected_column=end_column
+
+        if expose == 1:
+
+            nlist = self._columns_updated()
+            
+            self.expose()
+
+            self.notify('column-selection-changed',
+                        tuple(self.selected_columns))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+      
+    def select_cell(self,cell,clearfirst=0,expose=1):
+        """Select a single cell."""
+        rval = self.select_cells([cell],clearfirst,expose=expose)
+        return rval
+        
+
+    def select_cells(self,cell_list,clearfirst=0,expose=1):
+        """Select multiple cells.  cell_list is a list of
+           (source row, grid column) tuples.
+        """
+        
+        if self.cell_selectstate is None:
+            # No source is set
+            return 0
+
+        if clearfirst == 1:
+            self.unselect_all_cells(expose=0)
+
+        for cell in cell_list:
+            if ((self.cell_selectstate.shape[0] <= cell[0]) or
+                (self.cell_selectstate.shape[1] <= cell[1])):
+                raise RuntimeError,'pgugrid: tried to select nonexistent '+\
+                      'cell'
+            else:
+                if self.cell_selectstate[cell[0],cell[1]] == 0:
+                    self.cell_selectstate[cell[0],cell[1]]=1
+                    self.selected_cells.append(cell)
+
+            
+        if len(cell_list) > 0:
+            self.last_selected_cell=cell_list[len(cell_list)-1]
+            self.last_toggled_cell=cell_list[len(cell_list)-1]
+
+                
+        if expose == 1:
+            nlist=self._cells_updated()
+                      
+            self.expose()
+
+            self.notify('cell-selection-changed',
+                        tuple(self.selected_cells))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1
+            
+
+    def select_all_cells(self,expose=1):
+        """Select all cells.
+           NOT USED YET- MAY NOT EVER BE.
+        """
+
+        if self.cell_selectstate is None:
+            return 0
+
+        self.cell_selectstate[:,:]=1
+        # NOTE: it would be more efficient to implement
+        # selected cells so that it could be a tuple of
+        # slices or a tuple of indices, but this is
+        # simpler to implement (the indices method, that
+        # is).  This also applies to row and column
+        # selection.  May want to revisit later.  
+        self.selected_cells = []
+        
+        for i in range(self.cell_selectstate.shape[0]):
+            for j in range(self.cell_selectstate.shape[1]):
+                self.selected_cells.append((i,j))
+
+
+        self.last_toggled_cell=None
+        self.last_selected_cell=None
+        
+        if expose == 1:
+
+            nlist=self._cells_updated()
+            
+            self.expose()
+
+            self.notify('cell-selection-changed',
+                        tuple(self.selected_cells))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                    
+
+        return 1
+        
+
+    def unselect_cell(self,cell,clearfirst=0,expose=1):
+        """Unselect a single cell."""
+        
+        rval = self.unselect_cells([cell],clearfirst,expose)
+        return rval
+        
+
+    def unselect_cells(self,cell_list,clearfirst=0,expose=1):
+        """Unselect multiple cells.  cell_list is a list of
+           (source row, grid column) tuples.
+        """
+        
+        if self.cell_selectstate is None:
+            # No source is set
+            return 0
+
+        if clearfirst == 1:
+            self.select_all_cells(expose=0)
+            
+        for cell in cell_list:
+            if ((self.cell_selectstate.shape[0] <= cell[0]) or
+                (self.cell_selectstate.shape[1] <= cell[1])):
+                raise RuntimeError,'pgugrid: tried to select nonexistent '+\
+                      'cell'
+            else:
+                if self.cell_selectstate[cell[0],cell[1]] == 1:
+                    self.cell_selectstate[cell[0],cell[1]]=0
+                    self.selected_cells.remove(cell)
+
+        # TO DO: check this!!!
+        if len(cell_list) > 0:
+            self.last_toggled_cell=cell_list[len(cell_list)-1]
+
+
+        if expose == 1:
+
+            nlist = self._cells_updated()
+            
+            self.expose()
+
+            self.notify('cell-selection-changed',
+                        tuple(self.selected_cells))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1
+                    
+
+    def unselect_all_cells(self,expose=1):
+        """Unselect all cells."""
+
+        if self.cell_selectstate is None:
+            return 0
+
+        self.cell_selectstate[:,:]=0
+        self.selected_cells = []
+
+        self.last_toggled_cell=None
+        self.last_selected_cell=None
+
+        if expose == 1:
+            
+            nlist=self._cells_updated()
+
+            self.expose()
+
+            self.notify('cell-selection-changed',
+                        tuple(self.selected_cells))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+        return 1
+                
+
+    def toggle_cell(self,cell,expose=1):
+        """Toggle a single cell."""
+
+        if self.cell_selectstate[cell[0],cell[1]] == 0:
+            self.select_cell(cell,0,expose)
+        else:
+            self.unselect_cell(cell,0,expose)
+         
+
+    def toggle_cells(self,cell_list,expose=1):
+        """Toggle multiple cells."""
+
+        select=[]
+        unselect=[]
+
+        for cell in cell_list:
+            if self.cell_selectstate[cell] == 0:
+                select.append(cell)
+            else:
+                unselect.append(cell)
+
+        if len(unselect) > 0:
+            self.unselect_cells(unselect,0,0)
+            
+        if len(select) > 0:
+            self.select_cells(select,0,0)
+            
+        if expose == 1:
+
+            nlist = self._cells_updated()     
+                                                
+            self.expose()
+
+            self.notify('cell-selection-changed',
+                        tuple(self.selected_cells))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+                    
+        
+
+    def toggle_block_cells(self,end_cell,expose=1):
+        """Toggle a block of cells between self.last_toggled_cell
+           and end_cell.
+
+           Parameters:
+               end_cell- last cell to include in toggle block,
+                         a (row,column) tuple.
+
+               expose- 0 (don't redraw grid) or 1 (redraw grid).
+                       Defaults to 1.
+
+        """
+
+        if self.last_toggled_cell is None:
+            return
+        
+        g1=self.src2row[self.last_toggled_cell[0]]
+        g2=self.src2row[end_cell[0]]
+        if g1 < g2:
+            rlist=list(self.row2src[g1:g2+1])
+        else:
+            rlist=list(self.row2src[g2:g1+1])
+
+        if self.last_toggled_cell[1] < end_cell[1]:
+            clist=range(self.last_toggled_cell[1],end_cell[1]+1)
+        else:
+            clist=range(end_cell[1],self.last_toggled_cell[1]+1)
+
+        tlist=[]
+        for row in rlist:
+            for column in clist:
+                ncell=(row,column)
+                if ncell != self.last_toggled_cell:
+                    tlist.append(ncell)
+
+        self.toggle_cells(tlist,expose=expose)
+        
+        self.last_toggled_cell=end_cell
+        if self.cell_selectstate[end_cell[0],end_cell[1]] == 1:
+            self.last_selected_cell=end_cell
+
+    def _cells_updated(self):
+        """ Internal function called when cell selection has
+            changed.  Ensures that row/column selections is
+            consistent with current cross-selection settings.
+            Returns notifications that should be sent out
+            after expose event.
+        """
+        
+        notifylist=[]
+        if len(self.selected_cells) > 0:
+            if self.opts.selection_info.row_cell == 0:
+                if len(self.selected_rows) > 0:
+                    self.unselect_all_rows(expose=0)
+                    if self.src_type == SRC_SHAPESLAYER:
+                        self.layer.display_change()
+                    notifylist.append(('row-selection-changed',
+                            tuple(self.selected_rows)))
+            elif self.opts.selection_info.row_cell == 1:
+                # find rows that have selected cells in them,
+                # unselect any selected rows that don't have
+                # selected cells in them
+                rstate=Numeric.where(Numeric.sum(self.cell_selectstate,1)>0,
+                                     1,0)
+                unselect=Numeric.where(
+                    self.row_selectstate*2+rstate == 2,
+                    Numeric.arange(self.s_rows),-1)
+                ulist=list(Numeric.compress(unselect > -1, unselect))
+                if len(ulist) > 0:
+                    self.unselect_rows(ulist,expose=0)
+                    if self.src_type == SRC_SHAPESLAYER:
+                        self.layer.display_change()    
+                    notifylist.append(('row-selection-changed',
+                                tuple(self.selected_rows)))
+            elif self.opts.selection_info.row_cell == 3:
+                # rows that contain selected cells should be
+                # selected; those that don't should not be
+                # selected
+                rstate=Numeric.where(Numeric.sum(self.cell_selectstate,1)>0,
+                                     1,0)
+                unselect=Numeric.where(
+                    self.row_selectstate*2+rstate == 2,
+                    Numeric.arange(self.s_rows),-1)
+                ulist=list(Numeric.compress(unselect > -1, unselect))
+                if len(ulist) > 0:
+                    self.unselect_rows(ulist,expose=0)
+                    
+                select=Numeric.where(
+                    self.row_selectstate*2+rstate == 1,
+                    Numeric.arange(self.s_rows),-1)
+                slist=list(Numeric.compress(select > -1, select))
+                if len(slist) > 0:
+                    self.select_rows(slist,expose=0)
+                    
+                if ((len(ulist) > 0) or (len(slist) > 0)): 
+                    if self.src_type == SRC_SHAPESLAYER:
+                        self.layer.display_change()   
+                    notifylist.append(('row-selection-changed',
+                                tuple(self.selected_rows)))
+                
+            if self.opts.selection_info.column_cell == 0:
+                if len(self.selected_columns) > 0:
+                    self.unselect_all_columns(expose=0)
+                    notifylist.append(('column-selection-changed',
+                            tuple(self.selected_columns)))                
+            elif self.opts.selection_info.column_cell == 1:
+                cstate=Numeric.where(Numeric.sum(self.cell_selectstate,0)>0,
+                                     1,0)
+                unselect=Numeric.where(
+                    self.column_selectstate*2+cstate == 2,
+                    Numeric.arange(self.g_columns),-1)
+                ulist=list(Numeric.compress(unselect > -1, unselect))
+                if len(ulist) > 0:
+                    self.unselect_columns(ulist,expose=0)
+                    notifylist.append(('column-selection-changed',
+                                tuple(self.selected_columns)))                
+            elif self.opts.selection_info.column_cell == 3:
+                cstate=Numeric.where(Numeric.sum(self.cell_selectstate,0)>0,
+                                     1,0)
+                unselect=Numeric.where(
+                    self.column_selectstate*2+cstate == 2,
+                    Numeric.arange(self.g_columns),-1)
+                ulist=list(Numeric.compress(unselect > -1, unselect))
+                if len(ulist) > 0:
+                    self.unselect_columns(ulist,expose=0)
+                    
+                select=Numeric.where(
+                    self.column_selectstate*2+cstate == 1,
+                    Numeric.arange(self.g_columns),-1)
+                slist=list(Numeric.compress(select > -1, select))
+                if len(slist) > 0:
+                    self.select_columns(slist,expose=0)
+
+                if ((len(ulist) > 0) or (len(slist) > 0)): 
+                    notifylist.append(('column-selection-changed',
+                                tuple(self.selected_columns)))
+
+        else:
+            if self.opts.selection_info.row_cell == 3:
+                if len(self.selected_rows) > 0:
+                    self.unselect_all_rows(expose=0)
+                    if self.src_type == SRC_SHAPESLAYER:
+                        self.layer.display_change()
+                    notifylist.append(('row-selection-changed',()))
+                    
+            if self.opts.selection_info.column_cell == 3:
+                if len(self.selected_columns) > 0:
+                    self.unselect_all_columns(expose=0)
+                    notifylist.append(('column-selection-changed',()))
+
+        if ((self.editing_cell is not None) and
+            (self.cell_selectstate[self.editing_cell[0],
+             self.editing_cell[1]] == 0)):
+            self.end_cell_edit()
+
+        return notifylist
+
+    def _rows_updated(self):
+        """ Internal function called when row selection has
+            changed.  Ensures that cell/column selections is
+            consistent with current cross-selection settings.
+            Returns a list of notifications to send out.
+        """
+
+        notifylist=[]       
+        if self.g_columns == 0:
+            # No columns are defined yet, so
+            # there must be no column/cell selections
+            return notifylist
+        
+        if len(self.selected_rows) > 0:
+            if self.opts.selection_info.row_cell == 0:
+                if len(self.selected_cells) > 0:
+                    self.unselect_all_cells(expose=0)                 
+                    notifylist.append(('cell-selection-changed',
+                            tuple(self.selected_cells)))
+            elif self.opts.selection_info.row_cell == 1:
+                # unselect any selected cells that aren't contained
+                # in selected rows
+                rvec=Numeric.repeat(Numeric.reshape(self.row_selectstate,
+                             (self.s_rows,1)),self.g_columns,1)
+                                 
+                unselect=Numeric.reshape(Numeric.where(
+                    self.cell_selectstate > rvec,1,0),
+                    (self.s_rows*self.g_columns,))
+                cind=Numeric.indices((self.s_rows,self.g_columns))
+                rarr=Numeric.reshape(cind[0],(self.s_rows*self.g_columns,))
+                carr=Numeric.reshape(cind[1],(self.s_rows*self.g_columns,))
+                
+                rcarr=Numeric.compress(unselect > 0,rarr)
+                ccarr=Numeric.compress(unselect > 0,carr)
+                uarr=Numeric.zeros((rcarr.shape[0],2))
+                uarr[:,0]=rcarr
+                uarr[:,1]=ccarr
+                
+                ulist=map(tuple,uarr)
+                if len(ulist) > 0:
+                    self.unselect_cells(ulist,expose=0)
+                    notifylist.append(('cell-selection-changed',
+                                tuple(self.selected_cells)))
+                    
+            elif self.opts.selection_info.row_cell == 3:
+                # unselect any selected cells that aren't contained
+                # in selected rows.  Force selection of first cell
+                # in any row that doesn't contain any selected cells
+                # (ensures that row can be unselected again through
+                # grid in shapeslayer case if no row titles are shown)
+                
+                rvec=Numeric.repeat(Numeric.reshape(self.row_selectstate,
+                             (self.s_rows,1)),self.g_columns,1)
+                                 
+                unselect=Numeric.reshape(Numeric.where(
+                    self.cell_selectstate > rvec,1,0),
+                    (self.s_rows*self.g_columns,))
+                cind=Numeric.indices((self.s_rows,self.g_columns))
+                urarr=Numeric.reshape(cind[0],(self.s_rows*self.g_columns,))
+                ucarr=Numeric.reshape(cind[1],(self.s_rows*self.g_columns,))
+                
+                urcarr=Numeric.compress(unselect > 0,urarr)
+                uccarr=Numeric.compress(unselect > 0,ucarr)
+                uarr=Numeric.zeros((urcarr.shape[0],2))
+                uarr[:,0]=urcarr
+                uarr[:,1]=uccarr
+                
+                ulist=map(tuple,uarr)
+                if len(ulist) > 0:
+                    self.unselect_cells(ulist,expose=0) 
+
+                sumc=Numeric.sum(self.cell_selectstate,1)
+                select=Numeric.where(sumc < self.row_selectstate,
+                                     Numeric.arange(self.s_rows),-1)
+                srcarr=Numeric.compress(select > -1,select)
+                sarr=Numeric.zeros((srcarr.shape[0],2))
+                sarr[:,0]=srcarr
+                sarr[:,1]=Numeric.zeros((srcarr.shape[0],))
+
+                slist=map(tuple,sarr)
+                if len(slist) > 0:
+                    self.select_cells(slist,expose=0)
+                
+                if (len(ulist) > 0) or (len(slist) > 0):                    
+                    notifylist.append(('cell-selection-changed',
+                                tuple(self.selected_cells)))
+                    
+            if self.opts.selection_info.row_column == 0:
+                if len(self.selected_columns) > 0:
+                    self.unselect_all_columns(expose=0)
+                    notifylist.append(('column-selection-changed',
+                            tuple()) )
+
+        else:
+            if self.opts.selection_info.row_cell == 3:
+                if len(self.selected_cells) > 0:
+                    self.unselect_all_cells(expose=0) 
+                    notifylist.append(('cell-selection-changed',()))
+
+        # If row selection changes while a cell is being edited,
+        # cancel editing and clear cell selections.
+        if (self.editing_cell is not None):
+            self.cancel_cell_edit()
+            self.unselect_all_cells()
+           
+        return notifylist
+                    
+
+    def _columns_updated(self):
+        """ Internal function called when column selection has
+            changed.  Ensures that row/cell selections is
+            consistent with current cross-selection settings.
+            Returns a list of notifications to send out.
+        """
+
+        notifylist=[]
+        if len(self.selected_columns) > 0:
+            if self.opts.selection_info.column_cell == 0:
+                if len(self.selected_cells) > 0:
+                    self.unselect_all_cells(expose=0)
+                    notifylist.append(('cell-selection-changed',
+                            tuple(self.selected_cells)))
+            elif self.opts.selection_info.column_cell == 1:
+                # unselect any selected cells that aren't contained
+                # in selected columns
+                if self.s_rows > 0:
+                    rvec=Numeric.repeat(Numeric.reshape(
+                             self.column_selectstate,
+                             (1,self.g_columns)),self.s_rows,0)
+                                 
+                    unselect=Numeric.reshape(Numeric.where(
+                        self.cell_selectstate > rvec,1,0),
+                        (self.s_rows*self.g_columns,))
+                    cind=Numeric.indices((self.s_rows,self.g_columns))
+                    rarr=Numeric.reshape(cind[0],(self.s_rows*self.g_columns,))
+                    carr=Numeric.reshape(cind[1],(self.s_rows*self.g_columns,))
+                
+                    rcarr=Numeric.compress(unselect > 0,rarr)
+                    ccarr=Numeric.compress(unselect > 0,carr)
+                    uarr=Numeric.zeros((rcarr.shape[0],2))
+                    uarr[:,0]=rcarr
+                    uarr[:,1]=ccarr
+                
+                    ulist=map(tuple,uarr)
+                    if len(ulist) > 0:
+                        self.unselect_cells(ulist,expose=0)
+                        notifylist.append(('cell-selection-changed',
+                                tuple(self.selected_cells)))
+                    
+            elif self.opts.selection_info.column_cell == 3:
+                # unselect any selected cells that aren't contained
+                # in selected columns.  Force selection of first cell
+                # in any column that doesn't contain any selected cells
+                # (ensures that column can be unselected again through
+                # grid in shapeslayer case if no column titles are shown)
+
+                if self.s_rows > 0:                
+                    rvec=Numeric.repeat(Numeric.reshape(
+                             self.column_selectstate,
+                             (1,self.g_columns)),self.s_rows,0)
+                                 
+                    unselect=Numeric.reshape(Numeric.where(
+                        self.cell_selectstate > rvec,1,0),
+                        (self.s_rows*self.g_columns,))
+                    cind=Numeric.indices((self.s_rows,self.g_columns))
+                    urarr=Numeric.reshape(cind[0],
+                                          (self.s_rows*self.g_columns,))
+                    ucarr=Numeric.reshape(cind[1],
+                                          (self.s_rows*self.g_columns,))
+                
+                    urcarr=Numeric.compress(unselect > 0,urarr)
+                    uccarr=Numeric.compress(unselect > 0,ucarr)
+                    uarr=Numeric.zeros((urcarr.shape[0],2))
+                    uarr[:,0]=urcarr
+                    uarr[:,1]=uccarr
+                
+                    ulist=map(tuple,uarr)
+                    if len(ulist) > 0:
+                        self.unselect_cells(ulist,expose=0)
+
+                    sumc=Numeric.sum(self.cell_selectstate,1)
+                    select=Numeric.where(sumc < self.row_selectstate,
+                                     Numeric.arange(self.s_rows),-1)
+                    srcarr=Numeric.compress(select > -1,select)
+                    sarr=Numeric.zeros((srcarr.shape[0],2))
+                    sarr[:,0]=srcarr
+                    sarr[:,1]=Numeric.zeros((srcarr.shape[0],))
+
+                    slist=map(tuple,sarr)
+                    if len(slist) > 0:
+                        self.select_cells(slist,expose=0)
+                
+                    if (len(ulist) > 0) or (len(slist) > 0):           
+                        notifylist.append(('cell-selection-changed',
+                                tuple(self.selected_cells)))
+                    
+            if self.opts.selection_info.row_column == 0:
+                if len(self.selected_rows) > 0:
+                    self.unselect_all_rows(expose=0)
+                    if self.src_type == SRC_SHAPESLAYER:
+                        self.layer.display_change()
+                    notifylist.append(('row-selection-changed',
+                            tuple()) )
+
+        else:
+            if self.opts.selection_info.column_cell == 3:
+                if len(self.selected_cells) > 0:
+                    self.unselect_all_cells(expose=0) 
+                    notifylist.append(('cell-selection-changed',()))
+
+
+
+        if ((self.editing_cell is not None) and
+            (self.cell_selectstate[self.editing_cell[0],
+                                   self.editing_cell[1]] == 0)):
+            self.end_cell_edit()
+
+        return notifylist
+
+    def _update_row_title_width(self):
+        if self.opts.show_row_titles == 1:
+            if ( (self.title_font is not None) and
+                 (self.opts.row_title_type == 'user-defined') ):
+                width = 0
+                for item in self.row_titles:
+                    width = max( self.title_font.width(item)+2*self.pad,
+                                 width )
+                self.row_title_width = width
+            elif (self.title_font is not None) and (self.src is not None):
+                self.row_title_width=self.title_font.width(
+                    str(len(self.src2row)))+2*self.pad
+            else:
+                self.row_title_width=0
+        else:
+            self.row_title_width=0
+
+    def _update_column_title_height(self):
+        if self.opts.show_column_titles == 1:
+            if self.title_font is not None:
+                self.column_title_height = self.title_font.ascent + 2*self.pad
+            else:
+                self.column_title_height=0 
+        else:
+            self.column_title_height = 0
+                                           
+    def _update_column_width(self,column,cell_width):
+        """Reset a column's width, and update other
+           columns' start positions accordingly.
+        """
+
+        self._ColumnDefs[column].width=cell_width
+
+        startx=self._ColumnDefs[column].start_x +\
+               self._ColumnDefs[column].width + 1
+        
+        for idx in range(column+1,self.g_columns):
+            self._ColumnDefs[idx].start_x=startx
+            startx=startx+self._ColumnDefs[idx].width+1
+
+    def reset_startrow(self, row):
+        """ Check if row is visible.  If it is,
+            return without doing anything.  If it
+            isn't, update the GUI so that it
+            is visible. row should be in source
+            coordinates.  Does nothing if row
+            is None or isn't in the current subset.
+        """
+        if not (self.flags() & gtk.REALIZED):
+            return
+        
+        c_row = self.src2row[row]
+        if c_row < 0:
+            # requested row is either None, or is not
+            # part of the currently displayed subset.
+            return
+        
+        win = self._area.get_window()
+        height = win.height
+
+        base_height = height
+        column_title_height = self.column_title_height
+        
+        # The extra 1's account for the 1-pixel wide lines drawn
+        # between cells.
+        data_height = base_height - column_title_height -1
+        if self.opts.show_column_titles == 1:
+            data_height=data_height - 1
+            
+        disp_rows = int(data_height / ( self.row_height + 1 )) + 1
+        first_row = self.start_row
+        last_row = first_row + disp_rows - 1
+
+        if ((c_row >= self.start_row) and (c_row < last_row)):
+            return
+
+        # If c_row isn't in current window, scroll
+        # so it is the new start row.
+        vmax = self.vadj.__getattr__('upper')
+        if c_row >= vmax:
+            self.vadj.set_value(vmax)
+        elif c_row < 0:
+            self.vadj.set_value(0)
+        else:
+            self.vadj.set_value(c_row)
+
+        self.vadj.value_changed()
+
+
+    def reset_startcolumn(self, column):
+        """ Check if column is visible.  If it is,
+            return without doing anything.  If it
+            isn't, update the GUI so that it
+            is visible. Does nothing if column
+            is outside the range of columns.
+        """
+        if not (self.flags() & gtk.REALIZED):
+            return
+        
+        
+        win = self._area.get_window()
+        width = win.width
+        
+        base_width = width
+        row_title_width = self.row_title_width
+        
+        # The extra 1's account for the 1-pixel wide lines drawn
+        # between cells.
+        data_width = base_width - row_title_width -1
+        if self.opts.show_row_titles == 1:
+            data_width=data_width - 1
+            
+        cstartx = self._ColumnDefs[self.start_column].start_x
+        cendx=cstartx+data_width
+
+        ccolumn=column
+        if ccolumn < 0:
+            ccolumn=0
+        elif ccolumn >= self.g_columns:
+            ccolumn=self.g_columns-1
+
+        nstartx=self._ColumnDefs[ccolumn].start_x
+        nendx=nstartx+self._ColumnDefs[ccolumn].width
+        
+        if ((nstartx >= cstartx) and (nendx < cendx)):
+            return
+
+        # If ccolumn isn't in current window, scroll
+        # so it is the new start column.
+        self.hadj.set_value(ccolumn)
+        self.hadj.value_changed()
+
+                        
+    def click( self, widget, event ):
+        """ User has clicked on the widget.
+        """
+
+        if len(self.column_widths) < 1:
+            return
+
+        self._area.grab_focus()
+        #
+        # determine the column that the user clicked in by first offsetting
+        # for the current start column (accounts for columns scrolled off the
+        # left edge).
+        #
+        # After this if-block, nColumn should be -1 if a row title
+        # was clicked, None if the user clicked off the right
+        # edge of the grid, and the column number if a user
+        # clicked on a cell.
+        #
+
+        if ((self.opts.show_row_titles == 1) and
+           (event.x < self.row_title_width+2)):
+            # clicked on row title column
+            gColumn = -1
+        else:
+            #
+            # now actually look for the right column.
+            # If gColumn is None at the end
+            # then the user clicked off the right edge.
+            #
+            gColumn = None
+            current = 1
+            if self.opts.show_row_titles == 1:
+                current = self.row_title_width+2
+                
+            for i in range(self.start_column,len(self.column_widths)):
+                current = current + self.column_widths[i] + 1
+                if event.x < current:
+                    gColumn = i
+                    break
+            
+        
+        #
+        # Determine the row.  If its -1 then they clicked a 'title'.  If it
+        # is None then they clicked off the bottom edge.
+        #
+        gRow = None
+        if ((self.opts.show_column_titles == 1) and
+            (event.y < (self.column_title_height + 2))):
+            gRow = -1
+        else:
+            # NOTE: the max(self.start_row-1,0) below is a kludge to
+            # avoid an offset problem after scrolling (first scroll
+            # click doesn't seem to actually cause the window to scroll,
+            # even though vadj updates)
+            current=1
+            if self.opts.show_column_titles == 1:
+                current = self.column_title_height+2
+                
+            row = self.start_row+\
+                  int(Numeric.floor((event.y-current) /(self.row_height + 1)))
+                      
+            if row < self.g_rows:
+                gRow = row
+
+        if ((gColumn is None) or (gRow is None) or
+            ((gColumn == -1) and (gRow == -1))):
+            # User did not click on a cell or
+            # row/column header
+            return
+
+        if gRow != -1:
+            sRow=self.row2src[gRow]
+        else:
+            sRow = -1
+            
+        clickstr=None
+        clickarg=None
+
+        if (event.state & gtk.GDK.SHIFT_MASK):
+            # last click information only applies
+            # if the click was not modified by
+            # shift or control (for the purposes
+            # of pgugrid).  Last click is not
+            # related to last selected.
+            self.last_click_cell=None
+            self.last_click_row=None
+            self.last_click_column=None
+            self.last_click_button=None
+            
+            if event.button == 1:
+                if gRow == -1:
+                    # A column header was clicked
+                    clickstr='column-shift-left'
+                    clickarg=(gColumn,)
+                elif gColumn == -1:
+                    clickstr='row-shift-left'
+                    clickarg=(sRow,)
+                else:
+                    clickstr='cell-shift-left'
+                    clickarg=(sRow,gColumn)
+            elif event.button == 3:
+                if gRow == -1:
+                    # A column header was clicked
+                    clickstr='column-shift-right'
+                    clickarg=(gColumn,)
+                elif gColumn == -1:
+                    clickstr='row-shift-right'
+                    clickarg=(sRow,)
+                else:
+                    clickstr='cell-shift-right'
+                    clickarg=(sRow,gColumn)
+                    
+        elif (event.state & gtk.GDK.CONTROL_MASK):
+            self.last_click_cell=None
+            self.last_click_row=None
+            self.last_click_column=None
+            self.last_click_button=None
+            
+            if event.button == 1:
+                if gRow == -1:
+                    # A column header was clicked
+                    clickstr='column-ctrl-left'
+                    clickarg=(gColumn,)
+                elif gColumn == -1:
+                    clickstr='row-ctrl-left'
+                    clickarg=(sRow,)
+                else:
+                    clickstr='cell-ctrl-left'
+                    clickarg=(sRow,gColumn)
+            elif event.button == 3:
+                if gRow == -1:
+                    # A column header was clicked
+                    clickstr='column-ctrl-right'
+                    clickarg=(gColumn,)
+                elif gColumn == -1:
+                    clickstr='row-ctrl-right'
+                    clickarg=(sRow,)
+                else:
+                    clickstr='cell-ctrl-right'
+                    clickarg=(sRow,gColumn)
+        else:
+            # Current click event is not modified
+            # by shift or control.  First, establish
+            # if this is a double click event or not.
+
+            if self.last_click_button == event.button:
+                if event.button == 1:
+                    if ((sRow == self.last_click_row) and
+                        (gColumn == -1)):
+                        clickstr='row-double-left'
+                        clickarg=(sRow,)
+                    elif ((gColumn == self.last_click_column) and
+                          (gRow == -1)):
+                        clickstr='column-double-left'
+                        clickarg=(gColumn,)
+                    elif ((sRow == self.last_click_cell[0]) and
+                          (gColumn == self.last_click_cell[1])):
+                        clickstr='cell-double-left'
+                        clickarg=(sRow,gColumn)
+                elif event.button == 3:
+                    if ((sRow == self.last_click_row) and
+                        (gColumn == -1)):
+                        clickstr='row-double-right'
+                        clickarg=(sRow,)
+                    elif ((gColumn == self.last_click_column) and
+                          (gRow == -1)):
+                        clickstr='column-double-right'
+                        clickarg=(gColumn,)
+                    elif ((sRow == self.last_click_cell[0]) and
+                          (gColumn == self.last_click_cell[1])):
+                        clickstr='cell-double-right'
+                        clickarg=(sRow,gColumn)
+
+            if clickstr is not None:
+                # a double click event has been detected.
+                # clear the last_click values
+                self.last_click_cell=(None,None)
+                self.last_click_row=None
+                self.last_click_column=None
+                self.last_click_button=None
+            else:
+                self.last_click_cell=(None,None)
+                self.last_click_row=None
+                self.last_click_column=None
+                self.last_click_button=event.button                
+                if event.button == 1:
+                    if gRow == -1:
+                        # A column header was clicked
+                        clickstr='column-left'
+                        clickarg=(gColumn,)
+                        self.last_click_column=gColumn
+                    elif gColumn == -1:
+                        clickstr='row-left'
+                        clickarg=(sRow,)
+                        self.last_click_row=sRow
+                    else:
+                        clickstr='cell-left'
+                        clickarg=(sRow,gColumn)
+                        self.last_click_cell=(sRow,gColumn)
+                elif event.button == 3:
+                    if gRow == -1:
+                        # A column header was clicked
+                        clickstr='column-right'
+                        clickarg=(gColumn,)
+                        self.last_click_column=gColumn
+                    elif gColumn == -1:
+                        clickstr='row-right'
+                        clickarg=(sRow,)
+                        self.last_click_row=sRow
+                    else:
+                        clickstr='cell-right'
+                        clickarg=(sRow,gColumn)
+                        self.last_click_cell=(sRow,gColumn)                
+
+    
+        if ((clickstr is not None) and
+            (self.opts.events.has_key(clickstr))):
+            # click event is recognized, and current
+            # configuration has defined an event for it.
+            self._perform_click_action(clickstr,clickarg)
+
+        # Pass clicked event with grid row, column
+        self.notify('clicked',gRow,gColumn,event)
+
+
+
+    def _perform_click_action(self,clickstr,clickarg):
+        """Map the click event and arguments onto the
+           correct function call for the current configuration.
+        """
+        # Find out what this action is supposed to do
+        clickfunc=self.opts.events[clickstr]
+
+        if clickfunc == 'select-single-row':
+            # Select a single row at a time, clearing all
+            # previous selections.
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.select_row(clickarg[0],clearfirst=1,expose=1)
+        elif clickfunc == 'unselect-single-row':
+            # Unselect the current row (and only that row)
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.unselect_row(clickarg[0],clearfirst=0,expose=1)
+        elif clickfunc == 'toggle-single-row':
+            # Toggle a single row, making sure all other rows
+            # are unselected.
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            if self.row_selectstate[clickarg[0]] == 1:
+                self.unselect_all_rows()
+            else:
+                self.unselect_all_rows(expose=0)
+                self.select_row(clickarg[0])
+                       
+        elif clickfunc == 'toggle-block-rows':
+            # Toggle a block of rows between the last selected
+            # row and the current one.
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.toggle_block_rows(clickarg[0],expose=1)             
+        elif clickfunc == 'toggle-multiple-rows':
+            # Toggle a single row without affecting other rows
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.toggle_row(clickarg[0],expose=1)
+                       
+        elif clickfunc == 'select-block-rows':
+            # Select a block of rows between the last selected
+            # row and the current one.
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.select_block_rows(clickarg[0],expose=1)             
+        elif clickfunc == 'select-multiple-rows':
+            # Select a single row without affecting other rows
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.select_row(clickarg[0],expose=1)
+        elif clickfunc == 'select-single-column':
+            # Select a single column at a time, clearing all
+            # previous selections.
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            self.select_column(clickarg[0],clearfirst=1,expose=1)
+        elif clickfunc == 'unselect-single-column':
+            # Unselect the current column
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            self.unselect_column(clickarg[0],clearfirst=0,expose=1)
+        elif clickfunc == 'toggle-single-column':
+            # Toggle a single column, making sure all other columns
+            # are unselected.
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            if self.column_selectstate[clickarg[0]] == 1:
+                self.unselect_all_columns()
+            else:
+                self.unselect_all_columns(expose=0)
+                self.select_column(clickarg[0])
+            
+        elif clickfunc == 'toggle-block-columns':
+            # Toggle a block of columns between the last selected
+            # column and the current one.
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            self.toggle_block_columns(clickarg[0],expose=1)             
+            
+        elif clickfunc == 'toggle-multiple-columns':
+            # Toggle a single column without affecting other columns
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            self.toggle_column(clickarg[0],expose=1)
+            
+        elif clickfunc == 'select-block-columns':
+            # Select a block of columns between the last selected
+            # column and the current one.
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            self.select_block_columns(clickarg[0],expose=1)             
+            
+        elif clickfunc == 'select-multiple-columns':
+            # Toggle a single column without affecting other columns
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+            
+            self.select_column(clickarg[0],expose=1)
+            
+        elif clickfunc == 'select-single-cell':
+            if clickstr not in _cell_clickevents:
+                txt='pgugrid: cell cannot be selected in response\n'
+                txt=txt+'          to a row or column click event.'
+                raise RuntimeError,txt
+
+            self.select_cell(clickarg,clearfirst=1,expose=1)
+            
+        elif clickfunc == 'unselect-single-cell':
+            if clickstr not in _cell_clickevents:
+                txt='pgugrid: cell cannot be selected in response\n'
+                txt=txt+'          to a row or column click event.'
+                raise RuntimeError,txt
+            
+            self.unselect_cell(clickarg,clearfirst=0,expose=1)
+            
+        elif clickfunc == 'toggle-single-cell':
+            if clickstr not in _cell_clickevents:
+                txt='pgugrid: cell cannot be selected in response\n'
+                txt=txt+'          to a row or column click event.'
+                raise RuntimeError,txt
+            
+            if self.cell_selectstate[clickarg[0],clickarg[1]] == 1:
+                self.unselect_all_cells()
+            else:
+                self.unselect_all_cells(expose=0)
+                self.select_cell(clickarg)
+             
+        elif clickfunc == 'toggle-block-cells':
+            if clickstr not in _cell_clickevents:
+                txt='pgugrid: cell cannot be selected in response\n'
+                txt=txt+'          to a row or column click event.'
+                raise RuntimeError,txt
+            
+            self.toggle_block_cells(clickarg,expose=1)
+            
+        elif clickfunc == 'toggle-multiple-cells':
+            if clickstr not in _cell_clickevents:
+                txt='pgugrid: cell cannot be selected in response\n'
+                txt=txt+'          to a row or column click event.'
+                raise RuntimeError,txt
+
+            self.toggle_cell(clickarg,expose=1)
+            
+        elif clickfunc == 'start-cell-edit':
+            if clickstr not in _cell_clickevents:
+                txt='pgugrid: cell cannot be selected in response\n'
+                txt=txt+'          to a row or column click event.'
+                raise RuntimeError,txt
+            self.start_cell_edit(clickarg)
+            
+        elif clickfunc == 'sort-by-column':
+            # Sort by column (ascending)
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+
+            self.sort_reverse=0
+            if clickstr in _column_clickevents:
+                self.sort_by_column(clickarg[0],expose=1)
+            else: 
+                self.sort_by_column(clickarg[1],expose=1)
+                
+        elif clickfunc == 'reverse-sort-by-column':
+            # Sort by column (descending)
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+
+            self.sort_reverse=1
+            if clickstr in _column_clickevents:
+                self.sort_by_column(clickarg[0],expose=1)
+            else: 
+                self.sort_by_column(clickarg[1],expose=1)       
+        elif clickfunc == 'toggle-sort-by-column':
+            # Sort by column (alternate between ascending
+            # and descending with consecutive calls).  If
+            # a new column is being sorted, start with
+            # ascending sort regardless of the ascending/descending
+            # state for the last column sorted.
+            if clickstr in _row_clickevents:
+                txt='pgugrid: column cannot be selected in response\n'
+                txt=txt+'         to a row click event'
+                raise RuntimeError,txt
+
+            self.sort_reverse=abs(self.sort_reverse-1)
+            if clickstr in _column_clickevents:
+                if self.sort_column != clickarg[0]:
+                    self.sort_reverse=0
+                self.sort_by_column(clickarg[0],expose=1)
+            else:
+                if self.sort_column != clickarg[1]:
+                    self.sort_reverse=0 
+                self.sort_by_column(clickarg[1],expose=1)
+        elif clickfunc == 'translate-view-to-row':
+            # If source type is shapes layer, translate view
+            # to that row.
+            if clickstr in _column_clickevents:
+                txt='pgugrid: row cannot be selected in response\n'
+                txt=txt+'         to a column click event'
+                raise RuntimeError,txt
+            
+            self.translate_view_to_row(clickarg[0])
+            
+        else:
+            raise '_perform_click_action: unrecognized click function'
+
+        
+    def changed( self, widget ):
+        """Track changes to the scrollbars and record the row/column 
+        """
+        self.start_row = int(self.vadj.value)
+        self.start_column = int(self.hadj.value)
+        self.expose()
+        
+    def refresh( self, *args ):
+        """ Refresh grid from source (use if source has been
+            internally or externally changed).
+        """
+        # cancel any editing operations
+        if self.editing_cell is not None:
+            self.cancel_cell_edit()
+            self.unselect_all_cells(expose=0)
+        
+        if self.src_type == SRC_LISTUNDEF:
+            if len(self.src) == 0:
+                self.expose()
+                return
+            
+            if type(self.src[0]) == type([]):
+                self.src_type=SRC_LISTLIST
+            elif type(self.src[0]) == type((1,)):
+                self.src_type=SRC_LISTLIST
+            else:
+                self.src_type=SRC_LISTOBJ
+                
+        self._generate_row_indices()
+  
+        self.expose()
+        
+
+    def resize_to_default(self,max_width=400,max_height=300):
+        """ Resize to the default size calculated by
+            get_default_size, tp a maximum width of max_width
+            and maximum height of max_height.
+        """
+
+        newsize=self.get_default_size(max_width,max_height)
+        self.set_usize(newsize[0],newsize[1])
+        
+    def select_next_row(self,clearfirst=1,expose=1):
+        """If a row is selected, select the next
+           row in the current sorting scheme.  If no row
+           is currently selected, do nothing.  Uses the
+           last selected row if multiple rows are selected.
+           Clears all other selections if clearfirst is 1.
+        """
+        if len(self.selected_rows) == 0:
+            return
+
+        srow=self.selected_rows[len(self.selected_rows)-1]
+        
+        if srow not in self.row2src:
+            # selected row is not in the display.  do nothing.
+            return
+
+        g_row=self.src2row[srow]
+        g_row=g_row+1
+        if g_row >= len(self.row2src):
+            g_row=0
+
+        nsrow=self.row2src[g_row]
+        self.select_row(nsrow,clearfirst,expose)
+
+    def grid2src(self,grid_row):
+        """ Convert grid row to source row (returns -1 if the
+            row is out of range)
+        """
+
+        if type(grid_row) in [type([]),type(())]:
+            if len(grid_row) == 0:
+                return ()
+            
+            garr=Numeric.array(grid_row)
+            mask=Numeric.where(garr >= len(self.row2src),
+                               -1,
+                               Numeric.where(garr<0,-1,1))
+            garr=Numeric.where(garr >= len(self.row2src),
+                               0,
+                               Numeric.where(garr<0,0,garr))
+            sarr=Numeric.where(mask>0,Numeric.take(self.row2src,list(garr)),
+                               mask)
+            return tuple(sarr)
+        else:
+            if (grid_row > -1) and (grid_row < len(self.row2src)):
+                return self.row2src[grid_row]
+            else:
+                return -1
+
+    def src2grid(self,src_row):
+        """ Convert source row to grid row (returns -1 if source
+            row is not in grid- this may be the case if a subset
+            is specified or if the source row is None or if the
+            row is out of range)
+        """
+
+        if type(src_row) in [type([]),type(())]:
+            if len(src_row) == 0:
+                return ()
+
+            sarr=Numeric.array(src_row)
+            mask=Numeric.where(sarr >= len(self.src2row),
+                               -1,
+                               Numeric.where(sarr<0,-1,1))
+            sarr=Numeric.where(sarr >= len(self.src2row),
+                               0,
+                               Numeric.where(sarr<0,0,sarr))
+            garr=Numeric.where(mask>0,Numeric.take(self.src2row,list(sarr)),
+                               mask)
+            garr=Numeric.maximum(garr,-1)
+                               
+            return tuple(garr)
+        else:
+            if (src_row > -1) and (src_row < len(self.src2row)):
+                return max(self.src2row[src_row],-1)
+            else:
+                return -1
+
+    def get_current_columns(self):
+        """ Return the current column info.  Is a tuple
+            of lists:
+            (members,titles,editables,formats,types,
+            nodatas,justifys,title_justifys,widths,start_x's,force_widths,
+            force_width_chars, entry_chars)
+        """
+        members=[]
+        titles=[]
+        editables=[]
+        formats=[]
+        types=[]
+        nodatas=[]
+        justifys=[]
+        title_justifys=[]
+        widths=[]
+        start_xs=[]
+        force_widths=[]
+        force_width_chars=[]
+        entry_chars=[]
+        for item in self._ColumnDefs:
+            members.append(item.member)
+            titles.append(item.title)
+            editables.append(item.editable)
+            formats.append(item.format)
+            types.append(item.type)
+            nodatas.append(item.nodata)
+            justifys.append(item.justification)
+            title_justifys.append(item.title_justification)
+            widths.append(item.width)
+            start_xs.append(item.start_x)
+            force_widths.append(item.force_width)
+            force_width_chars.append(item.force_width_chars)
+            entry_chars.append(item.entry_chars)
+            
+        return (members,titles,editables,formats,types,nodatas,
+                justifys,title_justifys,widths,start_xs,force_widths,
+                force_width_chars, entry_chars)
+
+        
+    def get_default_size(self,max_width=400,max_height=300):
+        """ Calculate a sensible size to allocate for the grid.
+            If size is greater than max_width x max_height,
+            try to break on column and row boundaries.
+            Returns a (width,height) tuple.
+        """
+
+        if len(self._ColumnDefs) == 0:
+            return (max_width,max_height)
+        
+        # offsets account for scrollbars and finite line width
+        if self.vscroll_shown == 1:
+            cwidth=self._ColumnDefs[self.g_columns-1].start_x+\
+                   self._ColumnDefs[self.g_columns-1].width+20
+        else:
+            cwidth=self._ColumnDefs[self.g_columns-1].start_x+\
+                   self._ColumnDefs[self.g_columns-1].width+2
+            
+        idx=self.g_columns-1
+        while ((cwidth > max_width) and (idx > 0)):
+            cwidth=cwidth-self._ColumnDefs[idx].width-1
+            idx=idx-1
+        
+        cwidth=min([cwidth,max_width])
+
+        if self.hscroll_shown == 1: 
+            cheight=(self.row_height+1)*len(self.row2src)+20
+        else:
+            cheight=(self.row_height+1)*len(self.row2src)+2
+            
+        if self.opts.show_column_titles == 1:
+            cheight=cheight+self.column_title_height+1
+
+        if (cheight > max_height):
+            cheight=cheight-((self.row_height+1)*(int(
+             Numeric.ceil(float((cheight-max_height))/(self.row_height+1)))))
+
+        # Make sure that minimum height accounts for vertical
+        # scrollbar, minimum width for horizontal, if shown
+        # (if grid is small enough, scrollbars will determine
+        # minimum size)
+        mswidth=0
+        msheight=0
+        
+        if self.hscroll_shown == 1:
+            mswidth = mswidth + SCROLL_DIM2
+            msheight = msheight + SCROLL_DIM1
+            
+        if self.vscroll_shown == 1:
+            mswidth = mswidth + SCROLL_DIM1
+            msheight = msheight + SCROLL_DIM2
+
+        cwidth = max(cwidth, mswidth)
+        cheight = max(cheight, msheight)
+
+
+        return (cwidth,cheight)
+        
+    def expose( self, *args ):
+        """Draw the widget
+        """
+        if not (self.flags() & gtk.REALIZED):
+            return
+
+        if self._flags['frozen'] == 1:
+            return
+        
+        #
+        # create a memory pixmap to render into
+        #
+        try:
+            win=self._area.get_window()
+            width = win.width
+            height = win.height
+        except:
+            return
+        
+        pix = self._pixmap
+        
+        #
+        # prefetch the style
+        #
+        style = self.get_style()
+            
+        if self.style_list[0] is None:
+            # Start by creating default styles.  Note that before,
+            # gtk.STATE_PRELIGHT was used to indicate selected cells,
+            # and gtk.STATE_SELECTED was used for titles (regardless
+            # of selection).  By default, revert back to this
+            # convention.
+            cellstyle=style.copy()
+            cellstyle.bg_gc[gtk.STATE_NORMAL]=style.white_gc
+        
+            cellstyle.bg_gc[gtk.STATE_SELECTED]=style.bg_gc[gtk.STATE_PRELIGHT]
+            self.style_list[0]=cellstyle
+            
+            # row titles
+            titlestyle=style.copy()
+            titlestyle.bg_gc[gtk.STATE_NORMAL]=style.bg_gc[gtk.STATE_NORMAL]
+            self.style_list[1]=titlestyle
+
+            # column titles
+            titlestyle=style.copy()
+            titlestyle.bg_gc[gtk.STATE_NORMAL]=style.bg_gc[gtk.STATE_NORMAL]
+            self.style_list[2]=titlestyle
+
+        if self.default_style_reset_flag == 1:
+            # User has reset default style
+            self._add_style(0,self.default_style)
+            self.default_style_reset_flag = 0
+
+        if self.default_row_title_style_reset_flag == 1:
+            # User has reset default row title style
+            self._add_style(1,self.default_row_title_style)
+            self.default_row_title_style_reset_flag = 0
+
+        if self.default_col_title_style_reset_flag == 1:
+            # User has reset default column title style
+            self._add_style(2,self.default_col_title_style)
+            self.default_col_title_style_reset_flag = 0
+
+        if self.styles_to_add is not None:
+            for item in self.styles_to_add:
+                self._add_style(item[0],item[1])
+                
+            self.styles_to_add=None
+
+        # Note on drawing order:
+        #
+        # - background (cell style default background)
+        # - empty message (if relevant): returns
+        # - If row titles present, draw the rectangle
+        #   background covering all row titles, then
+        #   text row number of first row title, second
+        #   row title, etc.
+        # - For i in range (first column, last column+1)
+        #       - If column titles are present, draw the
+        #         rectangle for the current column title
+        #         background, then the text.
+        #       - For each cell in the column, draw the background
+        #         rectangle, then the text.
+        #
+        
+        #
+        # clear the pixmap
+        #
+        gtk.draw_rectangle( pix, self.style_list[0].bg_gc[gtk.STATE_NORMAL],
+                            gtk.TRUE, 0, 0, width, height )
+
+        #print 'exposing...'
+        #print 'pix: ',self._pixmap
+        if ((self.src_type in [SRC_NONE, SRC_LISTUNDEF]) and
+            (len(self._ColumnDefs) == 0)):
+            msg = self.empty_msg
+            if (msg is not None) and (len(msg) > 0):
+                msg_width = self.title_font.width( msg )
+                msg_height = self.title_font.height( msg )
+                msg_x = (width / 2) - (msg_width / 2)
+                msg_y = (height / 2) + (msg_height / 2 )
+                gtk.draw_text( pix, self.title_font, 
+                           style.fg_gc[gtk.STATE_INSENSITIVE], 
+                           msg_x, msg_y, msg )
+            self._area.draw_pixmap(style.white_gc, self._pixmap, 
+                                   0, 0, 0, 0, 
+                                   width, height )
+
+            if self.bCalcAdjustments:
+                self.calc_adjustments()
+                
+            return gtk.FALSE
+   
+        
+        #
+        # track changes in column width because of wide columns
+        #
+        bResetAdj = gtk.FALSE
+        
+        #
+        # calculate the number of rows to draw
+        #
+        base_height = height
+        column_title_height = self.column_title_height
+        data_height = base_height - column_title_height - 1
+        if self.opts.show_column_titles == 1:
+            data_height=data_height-1
+            
+        disp_rows = int(Numeric.floor(
+            data_height / ( self.row_height + 1 ))) + 1
+
+        first_row = self.start_row
+        last_row = first_row + disp_rows - 1
+        if last_row > self.g_rows-1:
+            last_row = self.g_rows-1
+            disp_rows=last_row-first_row+1
+  
+        first_column = self.start_column
+        last_column = self.g_columns
+
+        sum_row_heights=disp_rows*(self.row_height+1)
+
+        if self.opts.show_row_titles == 1:
+            rt_height= disp_rows*( self.row_height + 1 ) + \
+                       self.column_title_height
+            rt_offset=self.column_title_height + self.row_height + 1 - \
+                      self.pad
+            
+            if self.opts.show_column_titles == 1:
+                rt_height = rt_height + 1
+                rt_offset = rt_offset + 1
+   
+            gtk.draw_rectangle(pix,self.style_list[1].bg_gc[gtk.STATE_NORMAL],
+                               gtk.TRUE,0,0,
+                               self.row_title_width+1,
+                               rt_height+1)
+
+            for c_row in range(first_row,last_row+1):
+                if self.opts.row_title_type == 'user-defined':
+                    idx=self.row2src[c_row]
+                    if idx > len(self.row_titles)-1:
+                        txt = ''
+                    else:
+                        txt=self.row_titles[idx]
+                    gtk.draw_text(pix,self.title_font,
+                                  style.fg_gc[gtk.STATE_NORMAL],
+                                  self.pad,rt_offset,txt)
+                elif self.opts.row_title_type == 'source':
+                    gtk.draw_text(pix,self.title_font,
+                                  style.fg_gc[gtk.STATE_NORMAL],
+                                  self.pad,rt_offset,str(self.row2src[c_row]))
+                else:
+                    gtk.draw_text(pix,self.title_font,
+                                  style.fg_gc[gtk.STATE_NORMAL],
+                                  self.pad,rt_offset,str(c_row))
+  
+                rt_offset = rt_offset + self.row_height + 1
+                          
+            
+        #
+        # starting x for the the first column (far left line)
+        #
+        x = self.row_title_width
+        if self.opts.show_row_titles == 1:
+            x = x+1
+        
+
+        #
+        # loop through a column at a time
+        #
+        row_is_selected=[]
+        
+        rstyles=[]   # Draw styles
+        cstyles=[]
+        cjust=[]   # Column justification
+        ctjust=[]   # Column title justification
+        for i in range( first_column, last_column ):
+            #
+            # don't bother drawing if we're going to start past the right edge
+            #
+            if x > width:
+                continue
+            
+            cells=[]            
+            # Info on whether or not shapes are selected (1 if selected)
+            cell_is_selected = []
+            cstyles.append(self.style_list[self.column_style_index[i]])
+            cjust.append(self._ColumnDefs[i].justification)
+            ctjust.append(self._ColumnDefs[i].title_justification)
+            force_width=self._ColumnDefs[i].force_width
+
+            for j in range( first_row, last_row+1 ):
+                idx = self.row2src[j]
+
+                if idx == -1:
+                    continue
+
+		txt=self._get_datastr(idx,i)
+                
+                if txt is None:
+                    txt = ""
+
+                if force_width is not None:
+                    cell_width = force_width
+                    real_cell_width = min(force_width,
+                                 self.cell_font.width( txt ) + 2*self.pad)
+                else:
+                    cell_width = self.cell_font.width( txt ) + 2*self.pad
+                    real_cell_width = cell_width
+                    
+                cells.append( (txt, real_cell_width) )
+                cell_is_selected.append(
+                    self.cell_selectstate[idx,i])
+                
+                if i == first_column:
+                    row_is_selected.append(self.row_selectstate[idx])
+                    rstyles.append(self.style_list[self.row_style_index[idx]])
+   
+                if cell_width > self.column_widths[i]:
+                    bResetAdj = gtk.TRUE
+                    self.column_widths[i] = cell_width
+                    self._update_column_width(i,cell_width)
+
+            #
+            # figure out the size and placement of the title text
+            #
+            y = 1
+            if force_width is not None:
+                title_width=force_width
+                real_title_width = min(force_width,
+                        self.title_font.width(self._ColumnDefs[i].title ) +
+                                       2*self.pad)
+            else:
+                title_width = self.title_font.width(
+                        self._ColumnDefs[i].title ) + 2*self.pad
+                real_title_width = title_width
+                
+            if title_width > self.column_widths[i]:
+                bResetAdj=gtk.TRUE
+                self.column_widths[i]=title_width
+                self._update_column_width(i,title_width)
+            
+            #
+            # draw the 'button'
+            #
+            bx = x
+            by = 0 
+            bw = self.column_widths[i]+1
+            if self.opts.show_column_titles == 1:
+                bh = self.column_title_height+1
+            else:
+                bh = 0
+
+            if self.opts.show_column_titles == 1:
+                gtk.draw_rectangle( pix,
+                                    self.style_list[2].bg_gc[gtk.STATE_NORMAL],
+                                    gtk.TRUE, bx, by, bw, bh)
+
+                #
+                # draw the title
+                #
+                if ctjust[i-first_column] == 0:
+                    tx = x + ( self.column_widths[i] - real_title_width )
+                elif ctjust[i-first_column] == 1:
+                    tx = x
+                else:
+                    tx = x + ( ( self.column_widths[i] -
+                                 real_title_width ) / 2 )
+                    
+                gtk.draw_text( pix, self.title_font, 
+                               style.fg_gc[gtk.STATE_NORMAL], 
+                               tx+self.pad,
+                               y + self.column_title_height - self.pad,
+                               self._ColumnDefs[i].title )
+
+                y = y+self.column_title_height + 1
+                
+            #
+            # draw the horizontal line below the title
+            #
+
+            
+            for j in range( len(cells) ): 
+                if cell_is_selected[j]:  
+                   gtk.draw_rectangle( pix,
+                                       rstyles[j].bg_gc[gtk.STATE_SELECTED],
+                                       gtk.TRUE, 
+                                       x, 
+                                       y, 
+                                       self.column_widths[i]+1,
+                                       self.row_height+1)                    
+                elif self.column_selectstate[i]:
+                   gtk.draw_rectangle( pix,
+                            cstyles[i-first_column].bg_gc[gtk.STATE_SELECTED],
+                                       gtk.TRUE, 
+                                       x, 
+                                       y, 
+                                       self.column_widths[i]+1,
+                                       self.row_height+1)                     
+                elif row_is_selected[j]:
+                   gtk.draw_rectangle( pix,
+                                       rstyles[j].bg_gc[gtk.STATE_SELECTED],
+                                       gtk.TRUE, 
+                                       x, 
+                                       y, 
+                                       self.column_widths[i]+1,
+                                       self.row_height+1)  
+                else:
+                   gtk.draw_rectangle( pix,
+                                       rstyles[j].bg_gc[gtk.STATE_NORMAL],
+                                       gtk.TRUE, 
+                                       x, 
+                                       y, 
+                                       self.column_widths[i]+1,
+                                       self.row_height+1)  
+                    
+                y = y + self.row_height + 1
+
+                if cjust[i-first_column] == 0:
+                    cx = x + self.column_widths[i] - cells[j][1]
+                elif cjust[i-first_column] == 1:
+                    cx = x
+                else:
+                    cx = x + ( (self.column_widths[i] - cells[j][1]) / 2 )
+
+                gtk.draw_text(pix, self.cell_font, 
+                              style.fg_gc[gtk.STATE_NORMAL], 
+                              cx+self.pad, y-self.pad, cells[j][0])
+
+                              
+            #
+            # where does the line go
+            #
+            ly = y - 1
+            lx = x + self.column_widths[i] + 1
+  
+            #
+            #advance to next column
+            #
+            x = x + self.column_widths[i] + 1
+
+        # Redraw the area to the right of the last column in case there
+        # were any text over-runs:
+        gtk.draw_rectangle( pix,
+                            self.style_list[0].bg_gc[gtk.STATE_NORMAL],
+                            gtk.TRUE, 
+                            x+1, 
+                            0, 
+                            width-x-1,
+                            height)          
+
+
+        # Different line drawing options- represent with one
+        # number for shorter if's.
+        # draw all the lines (none for dlcase == 0)
+        dlcase = self.draw_row_lines*3 + self.draw_col_lines
+        if (dlcase == 1) and (self.opts.show_column_titles == 0):
+            dlcase = 0
+        elif (dlcase == 3) and (self.opts.show_row_titles == 0):
+            dlcase = 0
+        elif ((dlcase == 4) and (self.opts.show_row_titles == 0) and
+              (self.opts.show_column_titles == 0)):
+            dlcase = 0
+        elif ((dlcase == 4) and (self.opts.show_row_titles == 1) and
+              (self.opts.show_column_titles == 0)):
+            dlcase = 3
+        elif ((dlcase == 4) and (self.opts.show_row_titles == 0) and
+              (self.opts.show_column_titles == 1)):
+            dlcase = 1
+        elif ((dlcase == 5) and (self.opts.show_row_titles == 0) and
+              (self.opts.show_column_titles == 0)):
+            dlcase = 2
+
+        # calculate total width/height of grid
+        if self.opts.show_row_titles == 0:
+            gwidth = 0
+        else:
+            gwidth = self.row_title_width + 1
+
+        for i in range( first_column, last_column):
+            gwidth = gwidth + self.column_widths[i] + 1
+
+        if self.opts.show_column_titles == 0:
+            gheight = 0
+        else:
+            gheight = self.column_title_height + 1
+
+        for i in range(first_row, last_row+1):
+            gheight = gheight + self.row_height + 1
+        
+        if dlcase in [2,4,5,6,7,8]:
+            # lines left, right, top, bottom
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          0,0,gwidth,0)
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          0,gheight,gwidth,gheight)
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          0,0,0,gheight)
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          gwidth,0,gwidth,gheight)
+
+        if dlcase in [1,4,5,7,8] and (self.opts.show_column_titles == 1):
+            # Lines around column titles
+            if self.opts.show_row_titles == 0:
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              0,0,gwidth,0)
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              0,self.column_title_height+1,
+                              gwidth,self.column_title_height+1)
+                roffset = 0
+            else:
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              self.row_title_width+1,0,gwidth,0)
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              self.row_title_width+1,
+                              self.column_title_height+1,
+                              gwidth,self.column_title_height+1)
+                roffset = self.row_title_width + 1
+                
+            for i in range( first_column, last_column):
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              roffset,0,
+                              roffset,self.column_title_height+1)
+                roffset = roffset + self.column_widths[i] + 1
+                
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          roffset,0,
+                          roffset,self.column_title_height+1)
+
+        if (dlcase in [3,4,5,7,8]) and (self.opts.show_row_titles == 1):
+            # Lines around row titles
+            if self.opts.show_column_titles == 0:
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              0,0,0,gheight)
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              self.row_title_width+1,0,
+                              self.row_title_width+1,gheight)
+                coffset = 0
+            else:
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              0,self.column_title_height+1,0,gheight)
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              self.row_title_width+1,
+                              self.column_title_height+1,
+                              self.row_title_width+1,gheight)
+                coffset = self.column_title_height + 1
+                
+            for i in range( first_row, last_row+1):
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              0,coffset,
+                              self.row_title_width+1,coffset)
+                coffset = coffset + self.row_height + 1
+                
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          0,coffset,
+                          self.row_title_width+1,coffset)
+
+        if dlcase in [2,5,8]:
+            # lines between columns
+            if self.opts.show_row_titles == 0:
+                roffset = 0
+            else:
+                roffset = self.row_title_width + 1
+                
+            for i in range( first_column, last_column):
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              roffset,0,
+                              roffset,gheight)
+                roffset = roffset + self.column_widths[i] + 1
+                
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          roffset,0,
+                          roffset,gheight)
+                
+        if dlcase in [6,7,8]:
+            # lines between rows
+            if self.opts.show_column_titles == 0:
+                coffset = 0
+            else:
+                coffset = self.column_title_height + 1
+                
+            for i in range( first_row, last_row):
+                gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                              0,coffset,
+                              gwidth,coffset)
+                coffset = coffset + self.row_height + 1
+            gtk.draw_line(pix, style.fg_gc[gtk.STATE_INSENSITIVE],
+                          0,coffset,
+                          gwidth,coffset)
+                
+            
+        #draw the backing pixmap onto the screen
+        self._area.draw_pixmap(style.white_gc, self._pixmap, 0, 0, 0, 0, 
+                               width, height )
+
+        if self.editing_cell is not None:
+            # shift editing cell if necessary
+            cell=self.editing_cell
+            if self.src2row[cell[0]] < first_row:
+                self._layout.move(self.editbox,5000,5000)
+            elif cell[1] < first_column:
+                self._layout.move(self.editbox,5000,5000)
+            else:
+                cwidth=self._ColumnDefs[cell[1]].width
+                self.editbox.set_usize(cwidth,self.row_height)
+                self.editbox.set_max_length(
+                    self._ColumnDefs[cell[1]].entry_chars)
+                locx=self._ColumnDefs[cell[1]].start_x-\
+                      self._ColumnDefs[first_column].start_x+\
+                      self.row_title_width+1
+                if self.opts.show_row_titles == 1:
+                    locx=locx+1
+                locy=self.column_title_height+2+\
+                  ((self.src2row[cell[0]]-self.start_row)*(self.row_height+1))
+                if self.opts.show_column_titles == 1:
+                    locy=locy+1
+
+                self._layout.move(self.editbox,locx,locy)
+            
+        if bResetAdj or self.bCalcAdjustments:
+            self.calc_adjustments()
+
+        return gtk.FALSE
+
+    def size_allocate(self,*args):
+        """Track changes in table size and pass them on to
+           drawing area.
+        """
+        nsizetuple=self.get_allocation()
+        if self.vscroll_shown == 1:
+            nwidth=nsizetuple[2] - SCROLL_DIM1
+        else:
+            nwidth=nsizetuple[2]
+
+        if self.hscroll_shown == 1:
+            nheight=nsizetuple[3] - SCROLL_DIM1
+        else:    
+            nheight=nsizetuple[3]
+            
+        self._area.set_usize(nwidth,nheight)
+
+
+    def configure( self, widget, event, *args ):
+        """Track changes in width, height
+        """
+        #only do this if we have been realized
+        if not self.flags() & gtk.REALIZED:
+            return
+
+        # create a memory pixmap to render into
+        a_win = self._area.get_window()
+        self._pixmap = gtk.create_pixmap( a_win, a_win.width, a_win.height )
+        
+        style = self.get_style()
+
+        if self.title_font is None:
+            try:
+                self.title_font = gtk.load_font( self.title_font_spec )
+            except:
+                self.title_font = style.font
+
+            self._update_column_title_height()
+            self._update_row_title_width()
+            
+        if self.cell_font is None:
+            try:
+                self.cell_font = gtk.load_font( self.cell_font_spec )
+            except:
+                self.cell_font = style.font
+            self.row_height = self.cell_font.ascent + 2*self.pad
+
+        self.bCalcAdjustments=gtk.TRUE
+      
+    def calc_adjustments( self ):
+        """Recalculate the adjustment settings
+        """
+        if not (self.flags() & gtk.REALIZED):
+            self.bCalcAdjustments = gtk.TRUE
+            return
+
+        self.updating = gtk.TRUE
+        #horizontal min/max are 0 and max line length - page size
+        hpos = self.hadj.value
+        
+        vpos = self.vadj.value
+        
+        h_min = 0
+        v_min = 0
+
+        # Compute available grid area without scrollbars (twidth,theight)
+        # and with them (tmswidth, tmsheight)
+        totalsize = self.get_allocation()
+        
+        # Extra 1's account for 1-pixel wide lines between cells;        
+        twidth = totalsize[2] - 1 - self.row_title_width
+        if self.opts.show_row_titles == 1:
+            twidth = twidth - 1
+            
+        theight = totalsize[3] - self.column_title_height - 1
+                                
+        if self.opts.show_column_titles == 1:
+            theight=theight - 1
+        
+        tmswidth = twidth - SCROLL_DIM1
+        tmsheight = theight - SCROLL_DIM1
+        
+ 
+
+        # Number of columns in window will change if columns
+        # have different widths, so calculate how many
+        # clicks it will take to show all of the last column
+        # assuming win_width > width(last_column).
+        # rather than just setting hadj to the number of
+        # columns not visible initially.  Note: one click
+        # should move grid over by one column.
+
+        # Compute number of needed clicks in each direction
+        # if scrollbars aren't shown, and number if they are.
+        tneeded_clicks=0
+        tmsneeded_clicks=0
+
+        if len(self.column_widths) > 0:
+            while ((Numeric.sum(Numeric.array(
+                self.column_widths[tneeded_clicks:])+1) > twidth) and
+                (tneeded_clicks < self.g_columns-1)):
+                tneeded_clicks=tneeded_clicks+1
+            while ((Numeric.sum(Numeric.array(
+                self.column_widths[tmsneeded_clicks:])+1) > tmswidth) and
+                (tmsneeded_clicks < self.g_columns-1)):
+                tmsneeded_clicks=tmsneeded_clicks+1
+
+
+        trows = Numeric.floor(float(theight) / (self.row_height+1))
+        tmsrows = Numeric.floor(float(tmsheight) / (self.row_height+1))
+        
+        tv_max = max( 0, self.g_rows - trows ) + 1
+        tmsv_max = max( 0, self.g_rows - tmsrows ) + 1
+
+        # decide if horizontal and vertical scrollbars should
+        # be displayed or not
+        show_hscroll = 1
+        show_vscroll = 1
+        if self.hscroll_policy == 1:
+            if ( tneeded_clicks == 0 ) and ( tv_max == 1 ):
+                show_hscroll = 0
+                show_vscroll = 0
+            elif ( tmsneeded_clicks == 0 ):
+                show_hscroll = 0
+                show_vscroll = 1
+            elif ( tmsv_max == 1 ):
+                show_hscroll = 1
+                show_vscroll = 0
+            else:
+                show_hscroll = 1
+                show_vscroll = 1     
+        elif self.hscroll_policy == 2:
+            show_hscroll = 0
+            show_vscroll = 0
+
+        if show_hscroll == 0:
+            h_max = tneeded_clicks+1
+        else:
+            h_max = tmsneeded_clicks + 1
+
+        if show_vscroll == 0:
+            v_max = tv_max
+        else:
+            v_max = tmsv_max
+      
+        self.hadj.set_all( hpos, h_min, h_max, 1, 1, 1 )
+        self.hadj.changed()
+ 
+        self.vadj.set_all( vpos, v_min, v_max, 1, 1, 1)
+        self.vadj.changed()
+
+        
+        if (show_hscroll == 1) and (self.hscroll_shown != 1):
+            self.hsframe.show()
+            self.hscroll_shown = 1
+        elif (show_hscroll == 0) and (self.hscroll_shown != 0):
+            self.hsframe.hide()
+            self.hscroll_shown = 0
+ 
+        if (show_vscroll == 1) and (self.vscroll_shown != 1):
+            self.vsframe.show()
+            self.vscroll_shown = 1
+        elif (show_vscroll == 0) and (self.vscroll_shown != 0):
+            self.vsframe.hide()
+            self.vscroll_shown = 0
+              
+            
+        self.bCalcAdjustments = gtk.FALSE
+        self.updating = gtk.FALSE
+
+        if len(self.row2src) < 1:
+            return
+
+        # This resets the start row/column to the first one
+        # in the case where the whole grid is now shown
+        if ( ( self.vadj.lower == 0 ) and
+             ( self.vadj.upper == 1 ) and
+             ( self.vadj.value > 0 ) ):
+            nrow = self.row2src[0]
+            self.reset_startrow( nrow )
+            
+        if ( ( self.hadj.lower == 0 ) and
+             ( self.hadj.upper == 1 ) and
+             ( self.hadj.value > 0 ) ):
+            self.reset_startcolumn( 0 )
+
+            
+    def set_scroll_policy(self, hpolicy, vpolicy, expose=1):
+        """ Set the policy for showing horizontal
+            and vertical scrollbars.
+            
+            Inputs:
+                hpolicy- integer
+                vpolicy- integer
+                expose- whether or not to immediately
+                        redraw the grid (0 to not redraw,
+                        1 to redraw- defaults to 1)
+                        
+            Policy values: 0- always
+                           1- automatic
+                           2- never
+
+            Policy is 0 (always) by default.
+        """
+        self.hscroll_policy = hpolicy
+        self.vscroll_policy = vpolicy
+        self.bCalcAdjustments = gtk.TRUE
+        if expose == 1:
+            self.expose()
+        
+    def clear(self,*args):
+        """ Clear all grid settings, disconnect from
+            source signals.
+        """
+
+        # End cell editing, if it is occurring (save changes)
+        self.end_cell_edit()
+
+        # Disconnect from layer signals (SRC_SHAPESLAYER only)
+        if self.layer_selection_changed_id is not None:
+            self.layer.disconnect(self.layer_selection_changed_id)
+            self.layer_selection_changed_id = None
+        if self.layer_subselection_changed_id is not None:
+            self.layer.disconnect(self.layer_subselection_changed_id)
+            self.layer_subselection_changed_id = None
+        if self.layer_teardown_id is not None:
+            self.layer.disconnect(self.layer_teardown_id)
+            self.layer_teardown_id = None
+
+        # Disconnect from changed signals (SRC_SHAPES, SRC_SHAPESLAYER)    
+        if ((self.source_changed_id is not None) and
+           (self.src is not None)):
+            self.src.disconnect(self.source_changed_id)
+            self.source_changed_id=None
+        elif (self.source_changed_id is not None):
+            print 'clear: Warning- encountered undisconnected signal'
+
+        # Clear settings
+        self._initialize_settings()
+
+    def clear_and_expose(self,*args):
+        """ Clear and expose. """
+        self.clear()
+        self.expose()
+
+    def _initialize_settings(self):
+        """ Set up the grid for a new source. """
+
+        #the row/column to put in the top left corner
+        self.start_row = 0
+        self.start_column = 0
+
+        self.last_row = 0
+        
+        self.column_widths = []
+        
+        #flag to recalculate the adjustments
+        self.bCalcAdjustments = gtk.TRUE
+
+        # Lists of currently selected rows,
+        # columns, cells.  Selection state
+        # is stored two ways for easy
+        # access in different situations.
+        # Selected rows always refer to
+        # the source row, not the grid row.
+        # (this makes it easier to deal
+        # with sorting)
+        # Selected column refers to the
+        # grid column because the underlying
+        # source may have no concept of
+        # "column" (eg. shapes have properties,
+        # but there is no intrinsic order to
+        # them).
+        # Cells are referenced by the source
+        # row and grid column (a tuple)
+        self.selected_rows=[] 
+        self.selected_columns=[]
+        self.selected_cells=[]
+
+        # Select state indices.  Each element
+        # of these is either:
+        #     0- not selected
+        #         or
+        #     1- selected.
+        # For rows and columns these are vectors,
+        # for the cells this is a matrix.
+        # The sizes are:
+        # row_selectstate: number of source rows (nR)
+        # column_selectstate: number of grid columns (nC)
+        # cell_selectstate: nR x nC
+        #
+        # row_selectstate refers to the source row
+        # in the original source order (ie. unsorted)
+        # column_selectstate refers to the column index
+        # in the grid display.
+        # cells are indexed by source row (unsorted) and
+        # displayed column index.
+        self.row_selectstate=None
+        self.column_selectstate=None
+        self.cell_selectstate=None
+
+        # Selection tracking
+        self.last_selected_row=None
+        self.last_selected_column=None
+        self.last_selected_cell=None
+        self.last_toggled_row=None
+        self.last_toggled_column=None
+        self.last_toggled_cell=None
+
+        # click event tracking (hack used to get
+        # around the fact that double clicking
+        # doesn't seem to work well on windows for longish callbacks-
+        # disadvantage is that the consecutive clicks
+        # can be arbitrarily far apart...).  Hack is used on all
+        # platforms for consistency.
+        self.last_click_cell=(None,None)
+        self.last_click_row=None
+        self.last_click_column=None
+        self.last_click_button=None
+        
+        #set to true if changing some value that would end up causing multiple
+        #expose events or an endless loop even.
+        self.updating = gtk.FALSE
+        # Define source parameters; initialize them
+        # to empty values
+        self.src=None
+        self.src_type=SRC_NONE
+        
+        # Indices to map from source index to
+        # grid row index, and vice versa.
+        # -2 is in src2row to indicate that
+        # the source data for that row is
+        # None and should not be used.  -1
+        # is used to indicate that the
+        # source data for that row is not
+        # part of the current subset and
+        # should not be used.
+        self.src2row=[]
+        self.row2src=[]
+
+        # More parameters
+        self.g_columns=0 # Number of grid columns
+        self.g_rows=0 # Number of grid rows
+        self.s_rows=0 # Number of source rows
+
+        # Sorting parameters
+        self.sort_column=-1  # No sorting to start with
+        self.sort_reverse=0  # ascending (0) or descending (1)
+
+        # source changed: used for SRC_SHAPES
+        # and SRC_SHAPESLAYER
+        self.source_changed_id=None
+
+        # layer and view: only used for
+        # SRC_SHAPESLAYER
+        self.layer=None
+        self.layer_selection_changed_id=None
+        self.layer_subselection_changed_id=None
+        self.layer_teardown_id=None
+        self.view=None
+
+        # Flags used to control callback behaviour in
+        # different contexts (eg. to avoid exposes
+        # in row selection if necessary)
+        self._flags={}
+        self._flags['selecting-rows']=0
+        self._flags['frozen']=0
+
+        self.row_style_index=None           
+        self.column_style_index=None        
+        # Below: not used yet, but will be later.
+        #self.rowtitle_style_index=None
+        #self.columntitle_style_index=None
+
+        self.row_titles=[]
+
+        self.editing_cell=None
+        
+    def layer_subselection_cb(self,layer):
+        # When a selection changes, a the selected list is changed, 
+        # then the selection-changed signal is sent out, then
+        # the subselection changes, then the subselection-changed
+        # signal is sent out. To reset the start row, you have to hook
+        # into the subselection-changed callback, since all
+        # selection-changed callbacks are executed before
+        # subselection is updated, so calling get_subselection from the
+        # selection-changed callback will lag the most recent selection
+        # by one.
+        subselection = layer.get_subselected()
+
+        if len(self.src) < self.s_rows:
+            # selection changed signal is sent
+            # out before shapes changed signal
+            # when an invalid area is drawn.
+            # Return if this is the case.
+            return
+        
+        if subselection != -1:
+            self.last_selected_row=subselection
+            self.last_toggled_row=subselection
+            self.reset_startrow(subselection)
+
+        # If this selection was triggered by actions
+        # on the view rather than through the grid,
+        # redraw the grid (if this was triggered through
+        # the grid this function is called from select_rows,
+        # which does the expose if necessary).
+        if self._flags['selecting-rows'] == 0:
+            self.expose()
+
+    def layer_selection_cb(self, layer, *args):
+        """ Shapeslayer case- selection has been changed in view. """
+        shps = layer.get_selected()
+
+        if len(self.src) < self.s_rows:
+            # selection changed signal is sent
+            # out before shapes changed signal
+            # when an invalid area is drawn.
+            # Return if this is the case.
+            return
+
+        if len(shps) == 1:
+            self.last_selected_row=shps[0]
+            self.last_toggled_row=shps[0]
+        else:
+            self.last_selected_row=None
+            self.last_toggled_row=None
+
+        self._unselect_all_rows()    
+        self._select_rows(shps)
+
+        if self._flags['selecting-rows'] == 0:
+            
+            nlist=self._rows_updated()
+                    
+            if (len(shps) == 0) or (len(nlist) > 0):
+                # If no shapes are selected, layer_subselection_cb
+                # won't be called, so layer_selection_cb should do
+                # the expose (otherwise layer_subselection_cb will)
+                self.expose()
+
+            self.notify('row-selection-changed',tuple(self.selected_rows))
+            
+            if len(nlist) > 0:
+                for item in nlist:
+                    self.notify(item[0],item[1])
+
+    def freeze(self):
+        """ Freeze and allow internal changes without exposing. """
+        if self.source_changed_id is not None:
+            self.src.signal_handler_block(self.source_changed_id)
+        if self.layer_selection_changed_id is not None:
+            self.src.signal_handler_block(self.layer_selection_changed_id)
+        if self.layer_subselection_changed_id is not None:
+            self.src.signal_handler_block(self.layer_subselection_changed_id)
+        self._flags['frozen']=1
+        
+    def thaw(self,expose=1):
+        """ Thaw and expose if desired. """
+        if self.source_changed_id is not None:
+            self.src.signal_handler_unblock(self.source_changed_id)
+        if self.layer_selection_changed_id is not None:
+            self.src.signal_handler_unblock(self.layer_selection_changed_id)
+        if self.layer_subselection_changed_id is not None:
+            self.src.signal_handler_unblock(
+                self.layer_subselection_changed_id)
+        self._flags['frozen']=0
+
+        if expose == 1:
+            self.expose()
+                    
+    def sort_by_column( self, column=None,reverse=None,expose=1 ):
+        """ Sort the grid rows according to the
+            values in one of the columns.
+        """
+        
+        if ((self.src is None) or (len(self.src) < 1)):
+            return
+
+        if column is None:
+            # default to last one if sort property not specified
+            column=self.sort_column
+        else:
+            self.sort_column=column
+            
+        if reverse is None:
+            reverse=self.sort_reverse
+        else:
+            self.sort_reverse=reverse
+          
+        if column > len(self._ColumnDefs):
+            txt='pgugrid: attempted to sort by nonexistent column '+str(column)
+            raise RuntimeError,txt
+
+        ind_list=[]
+        if self._ColumnDefs[self.sort_column].type == 'complex':
+            # complex numbers can't be sorted with list sorting
+            for s_row in self.row2src:
+                ind_list.append((self._get_datastr(s_row,self.sort_column),
+                                s_row))
+        else:
+            for s_row in self.row2src:
+                ind_list.append((self._get_data(s_row,self.sort_column),s_row))
+            
+        ind_list.sort()
+        if self.sort_reverse == 1:
+            ind_list.reverse()
+
+        for idx in range(len(ind_list)):
+            self.row2src[idx]=ind_list[idx][1]
+            self.src2row[ind_list[idx][1]]=idx
+
+            
+        if expose == 1:
+            if self.last_selected_row is not None:
+                self.reset_startrow(self.last_selected_row)
+            
+            self.expose()
+
+          
+class pguGridWin(gtk.GtkWindow):
+    def __init__(self,title,selection_mode=1,source=None,config=None):
+        """ selection mode: 0 for no row selection, 1
+            for single selection, 2 for multiple.
+
+            source: source to initialize grid with
+        """
+        
+        gtk.GtkWindow.__init__(self)
+        self.set_title(title)
+        if config is None:
+            if selection_mode == 0:
+                config=(2,0,0,0,0,0,0,0)
+            elif selection_mode == 1:
+                config=(2,1,0,0,0,0,0,0)
+            else:
+                config=(2,2,0,0,0,0,0,0)
+            
+        self.grid=pguGrid(config)
+        self.add(self.grid)
+        
+        self.set_policy(gtk.TRUE,gtk.TRUE,gtk.TRUE)
+        if source is not None:
+            self.set_source(source)
+            
+        self.show_all()
+        if source is not None:
+            self.grid.resize_to_default(800,600)
+            
+            
+    def set_source(self,src,view=None):
+        self.grid.set_source(src,view)
+
+    def set_subset(self,indices):
+        self.grid.set_subset(indices)
+
+                  
+class pguTestGridWin(gtk.GtkWindow):
+    def __init__(self,title,config=None,source=None):
+        gtk.GtkWindow.__init__(self)
+        self.set_title(title)
+        if config is None:
+            config=(2,1,0,1,0,1,0,0)
+        self.grid=pguGrid(config)
+        self.add(self.grid)
+        
+        self.set_policy(gtk.TRUE,gtk.TRUE,gtk.TRUE)
+        if source is not None:
+            self.set_source(source)
+            self.grid.resize_to_default()
+            
+        self.show_all()
+
+    def set_source(self,src,view=None):
+        self.grid.set_source(src,view)
+
+    def set_subset(self,indices):
+        self.grid.set_subset(indices)
+        
+
+class _test_listobj:
+    def __init__(self,strmem,intmem,floatmem,cplxmem):
+        self.stringval=strmem
+        self.intval=intmem
+        self.floatval=floatmem
+        self.complexval=cplxmem
+        self.array=Numeric.array([1,2,3])
+
+    def dummy_function(self):
+        pass
+        
+if __name__ == "__main__":
+
+    import sys
+
+
+    with_def=1  # set to 1 to test with define_columns
+
+    config_list=[]
+    i=1
+    while i < min(len(sys.argv),9):
+        config_list.append(int(sys.argv[i]))
+        i=i+1
+
+    if len(sys.argv) > 9:
+        try:
+            lastarg=int(sys.argv[9])
+            config_list.append(lastarg)
+            shpfile=None
+        except:
+            shpfile=sys.argv[9]
+            config_list.append(None)
+    else:
+        shpfile=None
+        config_list.append(None)
+        
+    cfg=tuple(config_list)
+
+    if len(sys.argv) == 11:
+        shpfile=sys.argv[10]
+
+    cfgstr=str(cfg)
+    win = pguTestGridWin('Test grid 1: shps with schema, '+cfgstr,config=cfg)
+    p1=['test1','test2','test3','test4','test5']
+    p2=[11.5,1.5,1.2,1.66,5.4]
+    p3=[1,2,3,4,600]
+    shps=gview.GvShapes()
+    shps.add_field('prop1-string','string',10)
+    shps.add_field('prop2-float','float',10,5)
+    shps.add_field('prop3-int','integer',10)
+    for idx in range(30):
+        shps.append(gview.GvShape())
+        shps[idx].set_node(5,3)
+        shps[idx].set_property('prop1-string',str(p1[idx%5]))
+        shps[idx].set_property('prop2-float',str(p2[idx%5]))
+        shps[idx].set_property('prop3-int',str(p3[idx%5]))
+
+    win.set_source(shps)
+    win.grid.set_row_title_type('user-defined',0,['row 1','row 2','row 3'])
+
+    if with_def == 1:
+        win.grid.define_columns(members=['prop1-string','prop2-float',
+                                         'prop3-int'],
+                            titles=['STRING title','FLOAT title','INT title'],
+                            editables=None,
+                            formats=["%-s","%-10.3f","%3d"],
+                            types=['string','float','integer'],
+                            justify=[0,1,2],title_justify=[0,1,2])
+        print 'Test grid 1 column titles: right, left, center'
+        print 'Test grid 1 columns      : right, left, center'
+        
+    idx=win.grid.add_style(((65000,0,0),None,(65000,30000,30000),None,None,None))
+    idx2=win.grid.add_style(((0,0,65000),None,(30000,30000,65000),None,None,None))
+    win.grid.set_row_style([1,2,3,4,5],idx)
+    win.grid.set_column_style([0,2],idx2)
+    win.grid.set_default_style(((65000,65000,0),None,(0,65000,65000),None,None,None))
+    win.connect( 'delete-event', gtk.mainquit )
+    win.set_uposition(20,20)
+    
+    win2=pguTestGridWin('Test grid 2: Numpy, '+cfgstr,config=cfg)
+    numpy=Numeric.array([[1,2,3,4,5,6,7,8,9,0,1,2],[2,3,4,5,6,6,7,8,9,0,1,2],
+                         [6,5,4,3,2,6,7,8,9,0,1,2],[1,3,5,7,9,6,7,8,9,0,1,2]])
+    win2.set_source(numpy)
+    if cfg[0] in [1,3]:
+        win2.grid.set_row_title_type('source',expose=0)
+
+    if with_def == 1:    
+        win2.grid.define_columns(members=[0,1,2,3,4,5,6,7,8,9,10,11],
+                            titles=['c0','c1','c2','c3','c4','Column Five',
+                                    'c6','Seventh Column with Long Title',
+                                    'c8','c9',
+                                    'c10- yet another very long title',
+    'c11-really,really,really,really,really,really,really,really long title'],
+                            editables=None,
+                            types='float',
+                            justify=1,title_justify=0,force_width=90)
+        print 'Test grid 2 column titles: all right'
+        print 'Test grid 2 columns      : all left'
+        
+
+    win2.set_uposition(400,20)
+    idx=win2.grid.add_style(((65000,0,65000),None,(65000,30000,65000),None,None,None))
+    idx2=win2.grid.add_style(((0,65000,0),None,(30000,65000,30000),None,None,None))
+    win2.grid.set_row_style([1,2],idx)
+    win2.grid.set_column_style([0,1,2],idx2)
+    win2.grid.set_default_row_title_style(((0,0,45000),None,None,None,None,None))
+    win2.grid.set_default_col_title_style(((65000,40000,10000),None,None,None,None,None))
+    listlist=[[1,'hello',1.2],[2,'hi',3.4],[3,'bonjour',5.6],
+              [4,'hola',3.2],[5,'guten tag',1.4]]
+
+    win2.grid.set_scroll_policy(1,1)
+    
+    win3 = pguTestGridWin('Test grid 3: list of lists '+cfgstr,config=cfg)
+    win3.set_source(listlist)
+    if with_def == 1:    
+        win3.grid.define_columns(members=[1,0,2,1],
+                        titles=['c11','cl0','c12','c11again'],
+                                 justify=0,title_justify=2,
+                                 force_width=50)
+        print 'Test grid 3 column titles: all center'
+        print 'Test grid 3 columns      : all right'
+
+    win3.set_uposition(800,20)
+    
+    shps2=gview.GvShapes()
+    for idx in range(5):
+        shps2.append(gview.GvShape())
+        shps2[idx].set_node(5,3)
+        shps2[idx].set_property('prop1',str(p1[idx]))
+        shps2[idx].set_property('prop2',str(p2[idx]))
+    win4 = pguTestGridWin('Test grid 1b: shps without schema, '+cfgstr,
+                          config=cfg,source=shps2)
+    if with_def == 1:
+        win4.grid.define_columns(members=['prop2'],
+                            titles=['FLOAT title'],
+                            editables=None,
+                            types=['float'],justify=1,title_justify=1)
+        print 'Test grid 1b column titles: all left'
+        print 'Test grid 1b columns      : all left'
+
+    win4.set_uposition(20,300)
+    win4.grid.set_default_selection_colour((0,0,65000))
+
+    listobj=[]
+    listobj.append(_test_listobj('obj0',0,0.1,complex(1,-1)))
+    listobj.append(_test_listobj('obj1',2,0.2,complex(2,-1)))
+    listobj.append(_test_listobj('obj2',4,3.7,complex(3,1)))
+    listobj.append(_test_listobj('obj3',1,2.0,complex(0,-5)))
+    listobj.append(_test_listobj('obj4',3,0.1,complex(1,0)))
+    win5 = pguTestGridWin('Test grid 4: list of objects '+cfgstr,config=cfg)
+    if with_def == 1:
+        win5.grid.define_columns(members=['stringval','intval'],justify=[1,2],
+                                 title_justify=[0,2])
+        print 'Test grid 4 column titles: right, center'
+        print 'Test grid 4 columns      : left,center'
+        win5.grid.set_source(listobj,redefine_columns=0)
+        win5.grid.resize_to_default()
+    else:
+        win5.set_source(listobj)
+
+    win5.set_uposition(400,300)
+
+
+    if with_def == 1:
+        win.grid.resize_to_default()
+        win2.grid.resize_to_default()
+        win3.grid.resize_to_default()
+        win4.grid.resize_to_default()
+        win5.grid.resize_to_default()
+    
+    # If shapefile specified
+    print 'shpfile: ',shpfile
+    if shpfile is not None:
+        shps6=gview.GvShapes(shapefilename=shpfile)
+        win6 = pguTestGridWin('Test grid 5: shapefile '+cfgstr,config=cfg,
+                          source=shps6)
+        #if with_def == 1:
+        #    win6.grid.define_columns(formats=[None,None,'%11.2f','%11.2f'])
+
+        win6.set_uposition(800,300)
+        win6.grid.resize_to_default()
+        print 'Test grid 5 column titles: all center'
+        print 'Test grid 5 columns      : all right'
+
+    listobj2=[1,2,3,4,5]
+    win7=pguTestGridWin('Test grid 6: list of integers '+cfgstr,config=cfg)
+    win7.set_source(listobj2)
+    win7.set_uposition(20,600)
+
+    listobj2=('one','two','three','four','five','six','seven')
+    win8=pguTestGridWin('Test grid 7: tuple of strings '+cfgstr,config=cfg)
+    win8.set_source(listobj2)
+    win8.set_uposition(400,600)
+
+
+    listobj2=((1,'hello',3,5.2),(8,'hi',7,9.2),(64,'hola',1,3.8))
+    win9=pguTestGridWin('Test grid 8: tuple of tuples '+cfgstr,config=cfg)
+    win9.set_source(listobj2)
+    win9.set_uposition(800,600)
+    win9.grid.set_scroll_policy(1,1)
+
+    emptylist=[]
+    win10=pguTestGridWin('Test grid 9: emptylist '+cfgstr,config=cfg)
+    win10.set_source(emptylist)
+    win10.grid.set_empty_message(None)
+    win10.set_uposition(20,800)
+
+    win11=pguTestGridWin('Test grid 10: titled emptylist '+cfgstr,config=cfg)
+    win11.grid.set_source(emptylist,
+                          members=(1,2,3),titles=('one','two','three'))
+    win11.set_uposition(400,800)
+
+    win12=pguTestGridWin('Test grid 10: titled None '+cfgstr,config=cfg)
+    win12.grid.set_source(emptylist,members=('col one','col two','col three'))
+    win12.set_uposition(800,800)
+
+    win.grid.set_line_drawing(2,2)
+
+    if with_def == 1:
+        win7.grid.resize_to_default()
+        win8.grid.resize_to_default()
+        win9.grid.resize_to_default()
+        win10.grid.resize_to_default()
+        win11.grid.resize_to_default()
+        win12.grid.resize_to_default()
+    
+    
+    gtk.mainloop()
+    

Added: packages/openev/branches/upstream/current/pymod/pgumenu.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgumenu.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgumenu.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,275 @@
+###############################################################################
+# $Id: pgumenu.py,v 1.3 2002/08/13 16:09:29 pgs Exp $
+#
+# Project:  OpenEV
+# Purpose:  Extended Menu handling classes
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: pgumenu.py,v $
+#  Revision 1.3  2002/08/13 16:09:29  pgs
+#  added a pguToggleMenuItem and an option in the menu factory for adding
+#  a <toggle> menu item.
+#
+#  Revision 1.2  2002/07/23 17:29:21  warmerda
+#  Recover if menu pixmap not found.
+#
+#  Revision 1.1  2001/05/02 03:32:18  pgs
+#  new file
+#
+#
+
+import gtk
+from gtk import TRUE, FALSE
+import string
+import os
+import gview
+import pgu
+
+MENU_FACTORY_MENU_BAR    = 0
+MENU_FACTORY_MENU        = 1
+MENU_FACTORY_OPTION_MENU = 2
+
+# type is MENU_FACTORY_{MENU,MENU_BAR,OPTION_MENU}
+class pguMenuFactory:
+    """
+    Utility class for creating different kinds of menus.  Taken verbatim
+    from the pygtk MenuFactory class in GtkExtra.py with an enhancement
+    to add images to the menu and with comments added.
+
+    TODO:
+    right now, all menu items are indented by a spacing factor of 22 pixels
+    if no image is specified (to make everything line up.  This means that
+    any images put in the menu should be 22 pixels wide.
+
+    """
+
+    def __init__(self, type=MENU_FACTORY_MENU_BAR):
+        """
+        Initialize the menu factory
+        """
+        self.accelerator = gtk.GtkAccelGroup()
+        if type == MENU_FACTORY_MENU_BAR:
+            self.__w = gtk.GtkMenuBar()
+            self.__ret = self.__w
+        elif type == MENU_FACTORY_MENU:
+            self.__w =gtk. GtkMenu()
+            self.__w.set_accel_group(self.accelerator)
+            self.__ret = self.__w
+        elif type == MENU_FACTORY_OPTION_MENU:
+            self.__w = gtk.GtkMenu()
+            self.__w.set_accel_group(self.accelerator)
+            self.__ret = gtk.GtkOptionMenu()
+            self.__ret.set_menu(self.__w)
+        self.__menus = {}
+        self.__items = {}
+
+    def __getattr__(self, key):
+        """
+        map getattr calls through to the menu instead of this object
+        """
+        return getattr(self.__ret, key)
+
+    def add_entries(self, entries):
+        """
+        add multiple entries at once
+        """
+        for entry in entries:
+            apply(self.create, tuple(entry))
+
+    def create(self, path, accelerator=None, callback=None, *args):
+        """
+        create a single menuitem and add it to one of the menus already
+        created (or create a new one)
+        """
+        last_slash = string.rfind(path, '/')
+        if last_slash < 0:
+            parentmenu = self.__w
+        else:
+            parentmenu = self.get_menu(path[:last_slash])
+        label = path[last_slash+1:]
+        if label == '<separator>':
+            item = gtk.GtkMenuItem()
+        elif label[:7] == '<image:':
+            end = string.find(label, '>')
+            img_name = label[7:end]
+            hbox = gtk.GtkHBox(spacing=2)
+            try:
+                hbox.pack_start(self.create_pixmap(img_name), expand=FALSE)
+            except:
+                print 'Unable to load menu pixmap: ' + img_name
+        
+            lbl = gtk.GtkLabel(label[end+1:])
+            lbl.set_justify(gtk.JUSTIFY_LEFT)
+            hbox.pack_start(lbl, expand=FALSE)
+            item = gtk.GtkMenuItem()
+            item.add(hbox)
+            item.show_all()
+        elif label[:8] == '<toggle>':
+            item = pguToggleMenuItem(label[8:])
+                
+        elif label[:7] == '<check>':
+            item = gtk.GtkCheckMenuItem(label[7:])
+        else:
+            if parentmenu == self.__w:
+                item = gtk.GtkMenuItem(label)
+            else:
+                hbox = gtk.GtkHBox()
+                spc = gtk.GtkLabel('')
+                spc.set_usize(22,18)
+                hbox.pack_start(spc, expand=FALSE)
+                lbl = gtk.GtkLabel(label)
+                lbl.set_justify(gtk.JUSTIFY_LEFT)
+                hbox.pack_start(lbl, expand=FALSE)
+                item = gtk.GtkMenuItem()
+                item.add(hbox)
+        if label != '<nothing>':
+            item.show()
+        if accelerator:
+            key, mods = self.parse_accelerator(accelerator)
+            item.add_accelerator("activate", self.accelerator,
+                         key, mods, 'visible')
+        if callback:
+            apply(item.connect, ("activate", callback) + args)
+        # right justify the help menu automatically
+        if string.lower(label) == 'help' and parentmenu == self.__w:
+            item.right_justify()
+        parentmenu.append(item)
+        self.__items[path] = item
+        return item
+
+    def get_menu(self, path):
+        """
+        get the menu rooted at the given path
+        """
+        if path == '':
+            return self.__w
+        if self.__menus.has_key(path):
+            return self.__menus[path]
+        wid = self.create(path)
+        menu = gtk.GtkMenu()
+        menu.set_accel_group(self.accelerator)
+        wid.set_submenu(menu)
+        self.__menus[path] = menu
+        return menu
+
+    def parse_accelerator(self, accelerator):
+        """
+        parse an accelerator entry
+        """
+        key = 0
+        mods = 0
+        done = FALSE
+        while not done:
+            if accelerator[:7] == '<shift>':
+                mods = mods | gtk.GDK.SHIFT_MASK
+                accelerator = accelerator[7:]
+            elif accelerator[:5] == '<alt>':
+                mods = mods | gtk.GDK.MOD1_MASK
+                accelerator = accelerator[5:]
+            elif accelerator[:6] == '<meta>':
+                mods = mods | gtk.GDK.MOD1_MASK
+                accelerator = accelerator[6:]
+            elif accelerator[:9] == '<control>':
+                mods = mods | gtk.GDK.CONTROL_MASK
+                accelerator = accelerator[9:]
+            else:
+                done = TRUE
+                key = ord(accelerator[0])
+        return key, mods
+
+    def remove_entry(self, path):
+        """
+        remove a single entry by its path
+        """
+        if path not in self.__items.keys():
+            return
+        item = self.__items[path]
+        item.destroy()
+        length = len(path)
+        # clean up internal hashes
+        for i in self.__items.keys():
+            if i[:length] == path:
+                del self.__items[i]
+        for i in self.__menus.keys():
+            if i[:length] == path:
+                del self.__menus[i]
+    def remove_entries(self, paths):
+        """
+        remove menuitems based on the menu path name used to add them
+        """
+        for path in paths:
+            self.remove_entry(path)
+
+    def find(self, path):
+        """
+        find a menuitem instance by the path for the menu item.  This
+        is the text used in the add_entries call.
+        """
+        return self.__items[path]
+
+    def create_pixmap(self, filename):
+        """
+        create a pixmap from a filename
+
+        filename - string, the filename to create the pixmap from
+        """
+        full_filename = os.path.join(gview.home_dir, 'pics', filename)
+        if not os.path.isfile(full_filename):
+            print '%s filename not found, using default.xpm' % full_filename
+            full_filename = os.path.join(gview.home_dir, 'pics', 'default.xpm')
+        pix, mask = gtk.create_pixmap_from_xpm(self, None, full_filename)
+        return gtk.GtkPixmap(pix, mask)
+
+class pguToggleMenuItem( gtk.GtkMenuItem ):
+    
+    def __init__(self, label=""):
+        import pgutogglebutton
+        gtk.GtkMenuItem.__init__( self )
+            
+        hbox = gtk.GtkHBox( spacing = 2 )
+        self.tb = pgutogglebutton.pguToggleButton( )
+        self.tb.connect( 'toggled', self.toggled_cb)
+        hbox.pack_start( self.tb, expand=FALSE )
+        
+        evt_box = gtk.GtkEventBox()
+        evt_box.connect( 'button-release-event', self.toggle_it )
+        
+        lbl = gtk.GtkLabel( label )
+        evt_box.add( lbl )
+        
+        hbox.pack_start( evt_box )
+        self.add( hbox )
+        self.show_all()
+        
+    def set_active( self, bState ):
+        self.tb.set_active( bState )
+        
+    def get_active( self ):
+        return self.tb.get_active()
+        
+    def toggle_it( self, widget, event, *args ):
+        self.set_active( not self.tb.get_active() )
+        
+    def toggled_cb( self, widget, *args ):
+        self.activate()
+        self.get_ancestor( gtk.GtkMenuShell.get_type() ).deactivate()
+        
\ No newline at end of file

Added: packages/openev/branches/upstream/current/pymod/pguprogress.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pguprogress.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pguprogress.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,103 @@
+###############################################################################
+# $Id: pguprogress.py,v 1.1 2000/06/27 13:22:20 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Simplified progress monitor dialog.
+# Author:   Frank Warmerdam, warmerda at home.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pguprogress.py,v $
+#  Revision 1.1  2000/06/27 13:22:20  warmerda
+#  New
+#
+#
+#
+
+from gtk import *
+
+class PGUProgressDialog(GtkDialog):
+    def __init__(self, title = 'Progress', cancel = FALSE ):
+        GtkDialog.__init__(self)
+        self.set_title( title )
+        self.min = 0.0
+        self.max = 1.0
+        self.message = "complete"
+        self.cancelled = FALSE
+
+        vbox = GtkVBox(spacing=5)
+        vbox.set_border_width(10)
+        self.vbox.pack_start(vbox)
+        
+        label = GtkLabel(" 0% "+self.message)
+        label.set_alignment(0, 0.5)
+        vbox.pack_start(label, expand=TRUE)
+        self.label = label
+        
+        pbar = GtkProgressBar()
+        pbar.set_usize(200, 20)
+        vbox.pack_start(pbar)
+
+        if cancel:
+            button = GtkButton("cancel")
+            self.cancel = button
+            button.connect( "clicked", self.CancelCB )
+            self.action_area.pack_start(button)
+
+        self.pbar = pbar
+        self.show_all()
+
+    def CancelCB( self, *args ):
+        self.cancelled = TRUE
+
+    def Reset(self):
+        self.cancelled = FALSE
+
+    def SetRange( self, min, max ):
+        self.min = min
+        self.max = max
+
+    def SetDefaultMessage( self, message ):
+        self.message = message
+        
+    def ProgressCB( self, complete, message, *args ):
+
+        self.complete = self.min + (self.max-self.min) * complete
+        if message == "":
+            message = self.message
+            
+        message = str(int(complete*100)) + "% " + message
+        self.label.set_text(message)
+        
+        self.pbar.update( complete )
+        while events_pending():
+            mainiteration(FALSE)
+
+        if self.cancelled:
+            return 0
+        else:
+            return 1
+    
+
+if __name__ == '__main__':
+    pdialog = PGUProgressDialog( "Progress Test" )
+
+    mainloop()
+        

Added: packages/openev/branches/upstream/current/pymod/pgushapesgrid.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgushapesgrid.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgushapesgrid.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1046 @@
+###############################################################################
+# $Id: pgushapesgrid.py,v 1.10 2003/07/27 04:58:44 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Scrollable text area widget for displaying GvShapes data
+# Author:   Paul Spencer, spencer at dmsolutions.ca
+#
+# Developed by DM Solutions Group (www.dmsolutions.ca) for CIETcanada
+#
+###############################################################################
+# Copyright (c) 2000-2002, CIETcanada
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pgushapesgrid.py,v $
+#  Revision 1.10  2003/07/27 04:58:44  warmerda
+#  dos2unix
+#
+#  Revision 1.9  2003/04/14 18:49:39  gmwalter
+#  Fix typo.
+#
+#  Revision 1.8  2003/04/14 18:37:54  gmwalter
+#  Updated to include sorting/indexing functions.
+#
+#  Revision 1.7  2002/09/14 16:17:13  pgs
+#  added support for selecting a cell
+#
+#  Revision 1.6  2002/08/27 19:12:29  pgs
+#  added notion of 'selected' shapes
+#
+#  Revision 1.5  2002/08/19 17:13:13  pgs
+#  small fix for NULL values
+#
+#  Revision 1.4  2002/08/15 18:17:23  pgs
+#  fixed  bug in expose when a property is None.
+#
+#  Revision 1.3  2002/08/13 17:42:32  pgs
+#  added a nice frame ;)
+#
+#  Revision 1.2  2002/08/13 16:08:52  pgs
+#  several changes to rendering logic to allow for resetting the adjustments
+#  if the column widths change, plus easier calculations of cell spacings for
+#  consistency
+#
+#
+
+"""
+
+"""
+
+import gtk,GDK
+import gview
+import Numeric
+import gvsignaler
+
+class pguShapesGrid( gtk.GtkTable, gvsignaler.Signaler ):
+    
+    def __init__(self,editable=1):
+        
+        self._editable=editable
+        if self._editable == 1:
+            gtk.GtkTable.__init__( self, rows=3, cols=2 )
+        else:
+            gtk.GtkTable.__init__( self, rows=2, cols=2 )
+            
+        self.hadj = gtk.GtkAdjustment()
+        self.vadj = gtk.GtkAdjustment()
+        
+        self._hscroll = gtk.GtkHScrollbar( adj = self.hadj )
+        self._vscroll = gtk.GtkVScrollbar( adj = self.vadj )
+        self._area = gtk.GtkDrawingArea()
+        self._pixmap = None
+        #this mask also seems to enable scrolling???
+        evt_mask = gtk.GDK.BUTTON_PRESS_MASK | gtk.GDK.BUTTON_RELEASE_MASK | \
+                   gtk.GDK.KEY_PRESS_MASK | gtk.GDK.KEY_RELEASE_MASK
+        self._area.set_events( evt_mask )
+
+
+        if self._editable == 1:
+            self._entry = gtk.GtkEntry()
+            self._entry.set_sensitive( gtk.FALSE )
+            self._entry.connect( 'changed', self.entry_changed )
+        
+        #the data source
+        self.source = None
+        self.source_changed_id = None
+        self.subset = []
+
+        # indices/info for sorting (indices maps source index to nRow; inv_indices
+        # maps nRow to source index, and similar for subindices).
+        self.indices = None
+        self.inv_indices = None
+        self.subindices = None
+        self.inv_subindices = None
+        self.sort_reverse=0
+        
+        #string values to use as titles
+        self.titles = [ ]
+        
+        #fonts for drawing titles and cells put here
+        self.title_font = None
+        self.cell_font = None
+        
+        #the overall size of the data set
+        self.n_rows = 0
+        self.n_cols = 0
+        
+        #the height of a single row and title row
+        self.row_height = 0
+        self.title_height = 0
+        
+        #the row/col to put in the top left corner
+        self.start_row = 0
+        self.start_col = 0
+        
+        #the current row/col selected (when we support clicking :)
+        self.current_row = 0      # current row in display widget coordinates
+        self.current_row_src = -1 # current row in source coordinates (source index)
+        self.current_col = 0
+        
+        self.col_widths = []
+               
+        #the number of pixels around each cell
+        self.cell_half = 4
+        self.cell_full = (self.cell_half) * 2 + 1
+        
+        self.max_width = 0
+        self.max_height = 0
+        
+        #flag to recalculate the adjustments
+        self.bCalcAdjustments = gtk.TRUE
+
+        # list of indices of currently selected shapes (NOT the same as the
+        # currently editable cell)
+        self.selected_shapes=None
+        
+        #set to true if changing some value that would end up causing multiple
+        #expose events or an endless loop even.
+        self.updating = gtk.FALSE
+        
+        frm = gtk.GtkFrame()
+        frm.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
+        frm.add(self._area)
+
+        if self._editable == 1:
+            self.attach( self._entry, 0, 1, 0, 1, xoptions=gtk.FILL, yoptions=gtk.SHRINK )
+            self.attach( frm, 0, 1, 1, 2, xoptions=gtk.FILL, yoptions=gtk.FILL )
+            self.attach( self._vscroll, 1, 2, 1, 2, xoptions=gtk.SHRINK)
+            self.attach( self._hscroll, 0, 1, 2, 3, yoptions=gtk.SHRINK )
+        else:
+            self.attach( frm, 0, 1, 0, 1, xoptions=gtk.FILL, yoptions=gtk.FILL )
+            self.attach( self._vscroll, 1, 2, 0, 1, xoptions=gtk.SHRINK)
+            self.attach( self._hscroll, 0, 1, 1, 2, yoptions=gtk.SHRINK )
+            
+        self.show_all()
+
+        # signals: Note that the right-click (button 3) event
+        # is a special case used internally to select cells for
+        # editing.
+        self.publish('clicked-selected-row')
+        self.publish('clicked-unselected-row')
+        self.publish('title-clicked')
+        
+        self._area.connect( 'expose-event', self.expose )
+        self._area.connect( 'configure-event', self.configure )
+        self._area.connect( 'button-press-event', self.click )
+        self.hadj.connect( 'value-changed', self.changed )
+        self.vadj.connect( 'value-changed', self.changed )
+        self.connect( 'style-set', self.expose )
+        
+    def entry_changed( self, widget ):
+        """called when the user has changed the text in an entry
+        """
+        if self.current_row is not None and self.current_col is not None:
+            if self.current_row > 0:
+                self.source[self.current_row_src].set_property( \
+                    self.titledict[self.titles[self.current_col]], self._entry.get_text() )
+                    
+        self.expose()
+        
+    def click( self, widget, event ):
+        """the user clicked the widget, select a cell?
+        """
+        
+        # If left button clicked, select/delect a row; if right button
+        # clicked, select a column.
+        if len(self.col_widths) < 1:
+            return
+        
+        #
+        # determine the column that the user clicked in by first offsetting
+        # for the current start column (accounts for columns scrolled off the
+        # left edge)
+        #
+        current = 0
+        for i in range(self.start_col):
+            current = current + self.col_widths[i] + self.cell_full
+        
+        #
+        # now actually look for the right column.  If nCol is None at the end
+        # then the user clicked off the right edge.
+        #
+        nCol = None
+        current_temp = 0
+        for i in range(self.start_col, len(self.col_widths)):
+            current_temp = current_temp + self.col_widths[i] + self.cell_full
+            if event.x < current_temp:
+                nCol = i
+                break
+            
+        current = current + current_temp
+        
+        #
+        # now determine the row.  If its 0 then they clicked a 'title'.  If it
+        # is None then they clicked off the bottom edge.
+        # Use the same trick as for the columns to account for ones scrolled
+        # off the top, but its trickier because the first row is always the
+        # titles.  And its easier because all rows are the same height :)
+        #
+        nRow = None
+        if event.y < (self.row_height + self.cell_full):
+            nRow = 0
+        else:
+            # NOTE: the max(self.start_row-1,0) below is a kludge to
+            # avoid an offset problem after scrolling (first scroll
+            # click doesn't seem to actually cause the window to scroll,
+            # even though vadj updates)
+            row = max(self.start_row-1,0) + int(event.y / (self.row_height + self.cell_full))
+            if row <= self.n_rows:
+                nRow = row
+        
+        if nRow == 0 and nCol is not None:
+            cprop=self.titledict[self.titles[nCol]]
+            self.notify('title-clicked',nCol,cprop)
+
+        if (event.button != 3):
+            # Notify that a selected or unselected row was clicked,
+            # and send event.
+            if nRow is not None and nRow > 0:            
+                if self.selected_shapes[self.grid2src(nRow)] == 1:
+                    self.notify('clicked-selected-row',self.grid2src(nRow),0,event)
+                else:
+                    self.notify('clicked-unselected-row',self.grid2src(nRow),0,event)
+        elif (event.state & GDK.SHIFT_MASK):
+            # stop editing
+            self.current_row = 0
+            self.current_row_src = -1
+            self.current_col = 0
+            if self._editable == 1:
+                self._entry.set_sensitive( gtk.FALSE )
+            self.expose()
+         
+        else:
+            self.current_row = nRow
+            self.current_row_src = self.grid2src(nRow)
+            self.current_col = nCol
+
+            if self._editable == 1:
+                if nRow is not None and nCol is not None and nRow > 0:
+                    self._entry.set_sensitive( gtk.TRUE )
+                    val = self.source[self.grid2src(nRow)].get_property(self.titledict[self.titles[nCol]])
+                    self._entry.set_text( val )
+                    self._entry.grab_focus()
+                else:
+                    self._entry.set_text( '' )
+                    self._entry.set_sensitive( gtk.FALSE )
+
+            self.expose()           
+        
+            
+    def changed( self, widget ):
+        """Track changes to the scrollbars and record the 
+        """
+        
+        self.start_row = int(self.vadj.value)
+        self.start_col = int(self.hadj.value)
+        self.expose()
+        
+    def set_subset( self, subset = []):
+        """Sets an array of shape indexes that constitute a displayable
+        subset of the shapes
+        
+        subset - list of integer values
+        
+        If selected is None or an empty list, then all records will be
+        displayed.
+        """
+        if subset is None:
+            subset = []
+            
+        self.subset = subset
+        
+        self.start_row = 0
+        self.start_col = 0
+        if len(self.subset) > 0:
+            self.n_rows = len(self.subset)
+        
+        self.bCalcAdjustments = gtk.TRUE
+        self.source_sort()
+        self.expose()
+
+    def set_source( self, source, titledict=None, hidden=None ):
+        """Set the data source
+        
+        shapes - a GvShapes instance
+
+        titledict - dictionary of titles for properties (optional)
+
+        hidden - properties to hide
+        
+        reset all the internal parameters
+        """       
+        self.clear()
+        #trap setting to None or an invalid shapes object       
+        if source == None or source._o == None or len(source) == 0:
+            return
+
+        self.source = source
+        self.source_changed_id = self.source.connect( 'changed', self.source_changed_cb )
+        self.subset = []
+
+        schema = source.get_schema()
+        self.n_cols = len(schema)
+        self.n_rows = len(source)
+
+        # no shapes initially selected.
+        # Shapes are selected by source index.
+        self.selected_shapes=Numeric.zeros([len(source),1])
+        # Sorted position of shape- initially in order of location in source.
+        # Used to map nRow->source index and vice versa.
+        self.indices=Numeric.array(range(len(source)))+1
+        self.inv_indices=Numeric.argsort(self.indices)
+        self.sort_reverse=0
+        self.subindices=None
+        self.inv_subindices=None
+        
+        self.sort_property=None
+        
+        self.titledict={}
+        if titledict is None:
+            titledict={}
+
+        self._hidden_titles=[]    
+        if hidden is not None:
+            for item in hidden:
+                self._hidden_titles.append(item)
+
+        for i in range(len(schema)):
+            title = schema[i][0]
+
+            if title not in self._hidden_titles:
+                if titledict.has_key(title):
+                    self.titles.append(titledict[title])
+                    self.titledict[titledict[title]]=title
+                else:
+                    self.titles.append(title)
+                    self.titledict[title]=title
+                
+        #update the scrollbars
+        self.bCalcAdjustments = gtk.TRUE
+        self.expose()
+
+    def ssrc2grid( self, ss_index ):
+        # Convert from source index (0...Nsrc-1 or 0...Nsubset-1)
+        # to grid row # (1...Nsrc or 1...Nsubset)
+        if ((self.source is None) or (ss_index > len(self.source)-1) or (ss_index < 0)):
+            return 0
+        
+        if len(self.subset) > 0:
+            if ss_index > len(self.subset)-1:
+                return 0
+            grid_row = self.subindices[ss_index] 
+        else:
+            grid_row = self.indices[ss_index]
+
+        return grid_row
+
+    def src2grid( self, s_index ):
+        # Convert from source index
+        # to grid row # (1...Nsrc or 1...Nsubset)
+        if ((self.source is None) or (s_index > len(self.source)-1) or (s_index < 0)):
+            return 0
+        
+        if len(self.subset) > 0:
+            ss_index=None
+            for ind in range(len(self.subset)):
+                if self.subset[ind] == s_index:
+                    ss_index=ind
+            if ss_index is None:
+                return 0
+            
+            grid_row = self.subindices[ss_index] 
+        else:
+            grid_row = self.indices[s_index]
+
+        return grid_row
+
+    def grid2ssrc( self, grid_row ):
+        # Convert from grid row (0...Nsrc or 0...Nsubset)
+        # to source or subset index # (1...Nsrc-1 or 1...Nsubset-1)
+        if ((self.source is None) or (grid_row > len(self.source)) or (grid_row < 1)):
+            return -1
+        
+        if len(self.subset) > 0:
+            if grid_row > len(self.subset):
+                return -1
+            ss_index = self.inv_subindices[grid_row-1] 
+        else:
+            ss_index = self.inv_indices[grid_row-1]
+
+        return ss_index
+
+    def grid2src( self, grid_row ):
+        # Convert from grid row (0...Nsrc or 0...Nsubset)
+        # to source index # (1...Nsrc-1)
+        if ((self.source is None) or (grid_row > len(self.source)) or (grid_row < 1)):
+            return -1
+        
+        if len(self.subset) > 0:
+            if grid_row > len(self.subset):
+                return -1
+            src_index = self.subset[self.inv_subindices[grid_row-1]] 
+        else:
+            src_index = self.inv_indices[grid_row-1]
+
+        return src_index
+
+            
+    def source_sort( self, sort_property=None,reverse=None ):
+        # Need to call expose event after sorting to display
+        # inv_indices/inv_subindices map (nRow-1)->source/subset
+        # index; indices/subindices map source/subset index->nRow
+        if ((self.source is None) or (len(self.source) < 1)):
+            return
+
+        # clear editing cell selections
+        self.current_row = 0
+        self.current_row_src = -1
+        
+        if sort_property is None:
+            # default to last one if sort property not specified
+            sort_property=self.sort_property
+            
+        if reverse is None:
+            reverse=self.sort_reverse
+          
+        if (len(self.subset) == 0):
+            self.subindices=None
+            self.inv_subindices=None
+            if sort_property is None:
+                self.sort_property=sort_property
+                self.sort_reverse=reverse
+                if self.sort_reverse == 0:
+                    self.inv_indices=Numeric.array(range(len(self.source)))
+                else:
+                    self.inv_indices=Numeric.array(range(len(self.source)-1,-1,-1))
+                # Grid rows are from 1...N (titles are 0)                    
+                self.indices=Numeric.argsort(self.inv_indices)+1
+                return
+        else:
+            # If a subset is defined, the main set shouldn't be
+            # used at all in expose.  Don't bother sorting it.
+            self.indices=None
+            self.inv_indices=None
+            if sort_property is None:
+                self.sort_property=sort_property
+                self.sort_reverse=reverse                
+                if self.sort_reverse == 0:
+                    self.inv_subindices=Numeric.array(range(len(self.subset)))
+                else:
+                    self.inv_subindices=Numeric.array(range(len(self.subset)-1,-1,-1))
+                self.subindices=Numeric.argsort(self.inv_subindices)+1
+                return
+            
+            
+        # Check that requested sort property exists
+        have_prop=0
+        prop_type='string'
+        for cprop in self.source.get_schema():
+            if cprop[0] == sort_property:
+                have_prop=1
+                prop_type=cprop[1]
+                
+        if have_prop == 1:
+            self.sort_property=sort_property
+            self.sort_reverse=reverse             
+            if (len(self.subset) == 0):
+                ind_list=[]
+                count=0
+                if ((prop_type == 'float') or (prop_type == 'integer')):
+                    for cshape in self.source:
+                        # convert to float so sorting is numeric
+                        ind_list.append((float(cshape.get_property(sort_property)),count))
+                        count=count+1
+                else:
+                    # sort as a string
+                    for cshape in self.source:
+                        ind_list.append((cshape.get_property(sort_property),count))
+                        count=count+1
+                        
+                ind_list.sort()
+                if self.sort_reverse == 1:
+                    ind_list.reverse()
+                self.inv_indices=Numeric.zeros((count,))
+                for c_ind in range(count):
+                    self.inv_indices[c_ind]=ind_list[c_ind][1]
+                self.indices=Numeric.argsort(self.inv_indices)+1
+            else:
+                ind_list=[]
+                if ((prop_type == 'float') or (prop_type == 'integer')):
+                    count = 0
+                    for cindex in self.subset:
+                        ind_list.append((float(self.source[cindex].get_property(sort_property)),count))
+                        count=count+1
+                else:
+                    count=0
+                    for cindex in self.subset:
+                        ind_list.append((self.source[cindex].get_property(sort_property),count))
+                        count=count+1
+                        
+                ind_list.sort()
+                if self.sort_reverse == 1:
+                    ind_list.reverse()                
+                self.inv_subindices=Numeric.zeros((len(self.subset),))
+                for c_ind in range(len(self.subset)):
+                    self.inv_subindices[c_ind]=ind_list[c_ind][1]
+                self.subindices=Numeric.argsort(self.inv_subindices)+1
+        else:
+            print 'Invalid sort property.'
+            
+    def source_changed_cb( self, *args ):
+        # If shapes have been added/deleted, update relevant info:
+        if len(self.source) > self.n_rows:
+            temp=self.selected_shapes
+            self.selected_shapes=Numeric.zeros([len(self.source),1])
+            self.selected_shapes[:self.n_rows]=temp
+            self.n_rows=len(self.source)
+        elif len(self.source) < self.n_rows:
+            temp=self.selected_shapes
+            self.selected_shapes=Numeric.zeros([len(self.source),1])
+            self.selected_shapes[:len(self.source)]=temp
+            self.n_rows=len(self.source)
+            # Remove deleted shapes from subset:
+            if len(self.subset) > 0:
+                new_subset=[]
+                for item in self.subset:
+                    if item < self.n_rows:
+                        new_subset.append(item)
+                self.subset=new_subset
+
+        # Update schema, if necessary
+        new_schema=self.source.get_schema()
+        if len(new_schema) != self.n_cols:
+            self.n_cols=len(new_schema)
+            for i in range(len(new_schema)):
+                if new_schema[i][0] not in self._hidden_titles:
+                    if not (titledict.has_key(new_schema[i][0])):
+                        self.titledict[new_schema[i][0]]=new_schema[i][0]
+                        self.titles.append(new_schema[i][0])
+            self.schema=new_schema
+            
+        self.source_sort()
+        self.bCalcAdjustments = gtk.TRUE
+        self.expose()
+
+    def calc_adjustments( self ):
+        """Recalculate the adjustment settings
+        """
+        if not (self.flags() & gtk.REALIZED) or len(self.col_widths) == 0:
+            self.bCalcAdjustments = gtk.TRUE
+            return
+
+        self.updating = gtk.TRUE
+        #horizontal min/max are 0 and max line length - page size
+        hpos = self.hadj.value
+        h_min = 0
+        win_width = self._area.get_window().width - self.cell_full
+        for i in range( len(self.col_widths) - 1, -1, -1):
+            win_width = win_width - self.col_widths[i] - self.cell_full
+            if win_width < 0:
+                break;
+                
+        if i == 0 and win_width >= 0:
+            h_max = 0
+        elif i == 0:
+            h_max = 1
+        else:
+            h_max = i + 2
+        
+        self.hadj.set_all( hpos, h_min, h_max, 1, 1, 1 )
+        self.hadj.changed()
+
+        vpos = self.vadj.value
+        v_min = 0
+        cells_height = self._area.get_window().height - \
+                       self.row_height - self.cell_full
+        row_height = self.row_height + self.cell_full
+        rows = cells_height / row_height
+        
+        if len(self.subset) == 0:
+            v_max = len(self.source)
+        else:
+            v_max = len(self.subset)
+        #v_max = max( 0, v_max - rows ) + 1
+        v_max = max( 0, v_max - rows ) + 2 
+        
+        self.vadj.set_all( vpos, v_min, v_max, 1, 1, 1)
+        self.vadj.changed()
+        self.bCalcAdjustments = gtk.FALSE
+        self.updating = gtk.FALSE
+
+    def select_row(self,row,col=0,expose=1):
+        # Row should be in SOURCE INDEX coordinates, NOT
+        # grid or subset coordinates.
+        # Note: col argument is only there to be consistent with
+        # gtk clist- I don't really see why it is needed, since
+        # row number should be enough to select a given row.
+
+        if self.selected_shapes is None:
+            print 'No shapes to select...'
+            return 0
+        elif (len(self.selected_shapes) <= row):
+            print 'Selected shape index out of range...'
+            return 0
+        else:
+            self.selected_shapes[row]=1
+            if expose == 1:
+                self.expose()  
+            return 1
+
+    def unselect_row(self,row,col=0,expose=1):
+        # Row should be in SOURCE INDEX coordinates, NOT
+        # grid or subset coordinates.        
+        # Note: col argument is only there to be consistent with
+        # gtk clist- I don't really see why it is needed, since
+        # row number should be enough to select a given row.
+
+        if self.selected_shapes is None:
+            print 'No shapes to unselect...'
+            return 0
+        elif (len(self.selected_shapes) <= row):
+            print 'Selected shape index out of range...'
+            return 0
+        else:        
+            self.selected_shapes[row]=0
+            if expose == 1:
+                self.expose()                  
+            return 1
+        
+    def select_all(self,expose=1):
+        if self.selected_shapes is not None:
+            self.selected_shapes[:]=1
+            if expose == 1:
+                self.expose()   
+            return 1
+        else:
+            print 'No shapes to select...'
+            return 0
+        
+    def unselect_all(self,expose=1):
+        if self.selected_shapes is not None:
+            self.selected_shapes[:]=0
+            if expose == 1:
+                self.expose()
+            return 1
+        else:
+            print 'No shapes to unselect...'
+            return 0
+
+    def get_shape_coords(self,row):
+        if ((self.source is not None) and (len(self.source) > row)):
+            return self.source[row].get_node()
+        else:
+            return None
+        
+    def expose( self, *args ):
+        """Draw the widget
+        """
+        if not (self.flags() & gtk.REALIZED):
+            return
+
+        if len(self.col_widths) != len(self.titles):
+            self.max_width = self.cell_full
+            self.col_widths=[]
+            for title in self.titles:
+                col_width = self.title_font.width( title )
+
+                self.col_widths.append( col_width )
+
+                self.max_width = self.max_width + col_width + self.cell_full
+
+            self. max_length = self.cell_full + \
+                               ( len(self.titles) * ( self.row_height + self.cell_full ) )
+
+        
+        #
+        # pre-calculate half a cell spacing because we use it a lot
+        #
+        cell_half = self.cell_half
+        cell_full = self.cell_full
+
+        #
+        # create a memory pixmap to render into
+        #
+        win = self._area.get_window()
+        width = win.width
+        height = win.height
+        pix = self._pixmap
+        
+        #
+        # prefetch the style
+        #
+        style = self.get_style()
+        
+        #
+        # clear the pixmap
+        #
+        gtk.draw_rectangle( pix, style.white_gc, gtk.TRUE, 
+                            0, 0, width, height )
+                            
+        if self.source == None or self.source._o == None or len(self.source) == 0:
+            msg = "NO DATA TO DISPLAY"
+            msg_width = self.title_font.width( msg )
+            msg_height = self.title_font.height( msg )
+            msg_x = (width / 2) - (msg_width / 2)
+            msg_y = (height / 2) + (msg_height / 2 )
+            gtk.draw_text( pix, self.title_font, 
+                           style.fg_gc[gtk.STATE_INSENSITIVE], 
+                           msg_x, msg_y, msg )
+            self._area.draw_pixmap(style.white_gc, self._pixmap, 
+                                   0, 0, 0, 0, 
+                                   width, height )
+            
+            return gtk.FALSE
+            
+        
+        #
+        # track changes in column width because of wide columns
+        #
+        bResetAdj = gtk.FALSE
+        
+        #
+        # calculate the number of rows to draw
+        #
+        base_height = height
+        title_height = self.title_height
+        data_height = base_height - title_height
+        disp_rows = int(data_height / ( self.row_height + 3 ))
+        
+        #
+        # starting x for the the first column
+        #
+        x = cell_half 
+        
+        first_row = self.start_row
+        last_row = first_row + disp_rows
+        first_col = self.start_col
+        last_col = len(self.titles)
+        
+        #
+        # loop through a column at a time
+        #
+        for i in range( first_col, last_col ):
+            #
+            # don't bother drawing if we're going to start past the right edge
+            #
+            if x > width:
+                continue
+            
+            #
+            # pre-calculate the column width for this draw
+            # and remember the text values to draw
+            #
+            cells = []
+            
+            # Info on whether or not shapes are selected (1 if selected)
+            cell_is_selected = []
+
+            for j in range( first_row, last_row ):
+                idx = self.grid2src(j)
+                if idx == -1:
+                    continue
+                
+                txt = self.source[idx].get_property( self.titledict[self.titles[i]] )
+                if txt is None:
+                    txt = ""
+
+                cell_width = self.cell_font.width( txt )
+                cells.append( (txt, cell_width) )
+                cell_is_selected.append(self.selected_shapes[idx])
+                if cell_width > self.col_widths[i]:
+                    bResetAdj = gtk.TRUE
+                    self.col_widths[i] = cell_width
+
+            #
+            # figure out the size and placement of the title text
+            #
+            y = self.title_height + cell_half
+            title_width = self.title_font.width( self.titles[i] )
+            self.col_widths[i] = max( self.col_widths[i], title_width )
+            
+            #
+            # draw the 'button'
+            #
+            bx = x - cell_half + 1
+            by = y - self.title_height - cell_half
+            bw = self.col_widths[i] + cell_full - 1
+            bh = self.title_height + cell_full
+            gtk.draw_rectangle( pix, style.bg_gc[gtk.STATE_NORMAL], 
+                                gtk.TRUE, bx, by, bw, bh)
+            
+            gtk.draw_line( pix, style.bg_gc[gtk.STATE_PRELIGHT],
+                           bx, by, bx, by + bh - 1 )
+            gtk.draw_line( pix, style.bg_gc[gtk.STATE_PRELIGHT],
+                           bx, by, bx + bw, by )
+            #
+            # draw the title
+            #
+            tx = x + ( ( self.col_widths[i] - title_width ) / 2 )
+            gtk.draw_text( pix, self.title_font, 
+                           style.fg_gc[gtk.STATE_NORMAL], 
+                           tx, y, self.titles[i] )
+            
+            #
+            # draw the horizontal line below the title
+            #            
+            ly = y + cell_half + 1
+            lx = x + self.col_widths[i] + cell_half - 1
+            gtk.draw_line( pix, style.fg_gc[gtk.STATE_INSENSITIVE], 
+                           0, ly - 1, lx, ly - 1 ) 
+
+            # Calculate total width of all cells
+            sum_col_widths=0.0
+            for cwidth in self.col_widths:
+                sum_col_widths = sum_col_widths + cwidth + self.cell_full
+
+            #
+            # draw the contents of the cells
+            #
+            for j in range( len(cells) ):                    
+                if cell_is_selected[j] == 1 and i == first_col:
+                    gtk.draw_rectangle( pix, style.bg_gc[gtk.STATE_PRELIGHT],
+                                       gtk.TRUE, 
+                                       0, 
+                                       y + cell_half, 
+                                       sum_col_widths,
+                                       self.row_height + cell_full )                    
+                elif self.current_row is not None and \
+                   self.current_col is not None and \
+                   self.current_row != 0 and \
+                   self.current_col == i and \
+                   j + max(self.start_row-1,0) == self.current_row - 1:  
+                   gtk.draw_rectangle( pix, style.bg_gc[gtk.STATE_PRELIGHT],
+                                       gtk.TRUE, 
+                                       x - cell_half, 
+                                       y + cell_half, 
+                                       self.col_widths[i] + cell_full,
+                                       self.row_height + cell_full )
+                y = y + self.row_height + cell_full
+                cx = x + self.col_widths[i] - cells[j][1]
+                gtk.draw_text(pix, self.cell_font, 
+                              style.fg_gc[gtk.STATE_NORMAL], 
+                              cx, y, cells[j][0])
+                #
+                # only draw the line under each row once, when we are drawing
+                # the last column
+                #
+                if x + self.col_widths[i] + cell_full > width or \
+                   i == last_col - 1:
+                    ly = y + cell_half + 1
+                    lx = x + self.col_widths[i] + cell_half - 1
+                    gtk.draw_line( pix, style.fg_gc[gtk.STATE_INSENSITIVE], 
+                                   0, ly - 1, lx, ly - 1) 
+                              
+            #
+            # where does the line go
+            #
+            ly = y + cell_half + 1
+            lx = x + self.col_widths[i] + cell_half
+            
+            #
+            # special case for first column, start under the title row
+            #
+            if i == 0:
+                gtk.draw_line( pix, style.fg_gc[gtk.STATE_INSENSITIVE], 
+                               0, bh , 0, ly - 1 )
+
+            #
+            # draw the vertical lines to the right of each column
+            #
+            gtk.draw_line( pix, style.fg_gc[gtk.STATE_INSENSITIVE], 
+                           lx, 1, lx , y + cell_half )
+            
+            #
+            #advance to next column
+            #
+            x = x + self.col_widths[i] + cell_full
+            
+        #draw the backing pixmap onto the screen
+        self._area.draw_pixmap(style.white_gc, self._pixmap, 0, 0, 0, 0, 
+                               width, height )
+
+        if bResetAdj or self.bCalcAdjustments:
+            self.calc_adjustments()
+
+        return gtk.FALSE
+           
+    def configure( self, widget, event, *args ):
+        """Track changes in width, height
+        """
+        #only do this if we have been realized
+        if not self.flags() & gtk.REALIZED:
+            return
+            
+        # create a memory pixmap to render into
+        a_win = self._area.get_window()
+        self._pixmap = gtk.create_pixmap( a_win, event.width, event.height )    
+        
+        style = self.get_style()
+
+        if self.title_font is None:
+            try:
+                self.title_font = gtk.load_font( self.title_font_spec )
+            except:
+                self.title_font = style.font
+            self.title_height = self.title_font.ascent
+
+        if self.cell_font is None:
+            try:
+                self.cell_font = gtk.load_font( self.cell_font_spec )
+            except:
+                self.cell_font = style.font
+            self.row_height = self.cell_font.ascent
+
+        self.bCalcAdjustments=gtk.TRUE
+        
+    def clear( self, *args ):
+        if self.source_changed_id is not None and self.source is not None:
+            self.source.disconnect( self.source_changed_id )
+        self.source = None
+        self.source_changed_id = None
+        self.titles = []
+        self.titledict={}
+        self._hidden_titles=[]
+        self.col_widths = []
+        self.n_rows = 0
+        self.n_cols = 0
+        self.start_row = 0
+        self.start_col = 0
+        self.current_row = 0
+        self.current_row_src = -1
+        self.current_col = 0
+        
+        # indices/info for sorting (indices maps source index to nRow; inv_indices
+        # maps nRow to source index, and similar for subindices).
+        self.indices = None
+        self.inv_indices = None
+        self.subindices = None
+        self.inv_subindices = None
+        self.sort_reverse=0
+        self.expose()
+
+    def reset_startrow(self, c_row):
+        # Check if c_row is visible.  If not,
+        # reset start row to c_row in widget
+        win = self._area.get_window()
+        width = win.width
+        height = win.height
+
+        base_height = height
+        title_height = self.title_height
+        data_height = base_height - title_height
+        # The 3 is there to make sure that we don't scroll
+        # 1 or 2 off the bottom of the widget: it may make 
+        # the jump to the top occur sooner than it has to.
+        disp_rows = int(data_height / ( self.row_height + self.cell_full + 3 ))
+        first_row = self.start_row
+        last_row = first_row + disp_rows
+
+        if ((c_row >= self.start_row) and (c_row <= last_row)):
+            return
+
+        # If c_row isn't in current window, scroll
+        # so it is the new start row.
+        vmax = self.vadj.__getattr__('upper')
+        if c_row >= vmax:
+            self.vadj.set_value(vmax)
+        elif c_row < 1:
+            self.vadj.set_value(1)
+        else:
+            self.vadj.set_value(c_row)
+
+        self.vadj.value_changed()
+        
+        
+class TestGrid( gtk.GtkWindow ):
+
+    def __init__(self):
+        gtk.GtkWindow.__init__( self )
+        self.set_title("Test ShapesGrid")
+        
+        vbox = gtk.GtkVBox()
+        self.grid = pguShapesGrid()
+        self.grid.set_usize( 300, 300 )
+        self.entry = gtk.GtkEntry()
+        self.button = gtk.GtkButton( "set shapes file" )
+        self.button.connect( "clicked", self.set_shapes )
+        
+        vbox.pack_start( self.grid )
+        vbox.pack_start( self.entry, expand=gtk.FALSE )
+        vbox.pack_start( self.button, expand=gtk.FALSE )
+        self.add(vbox)
+        self.show_all()
+        
+    def set_shapes( self, widget=None, src = None ):
+        if src is None:
+            src = self.entry.get_text()
+        else:
+            self.entry.set_text( src )
+            
+            
+        shapes = gview.GvShapes( shapefilename = src )
+        if shapes is not None and shapes._o is not None:
+            self.grid.set_source( shapes )
+        
+        
+if __name__ == "__main__":
+    win = TestGrid()
+    win.set_shapes( src = "c:\\projects\\dmsolutions\\ciet\\ciet_data\\wcsite.shp" )
+    win.connect( 'delete-event', gtk.mainquit )
+    gtk.mainloop()
+    

Added: packages/openev/branches/upstream/current/pymod/pgutextarea.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgutextarea.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgutextarea.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,304 @@
+###############################################################################
+# $Id: pgutextarea.py,v 1.5 2003/02/14 17:59:14 pgs Exp $
+#
+# Project:  OpenEV
+# Purpose:  Scrollable text area widget
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pgutextarea.py,v $
+#  Revision 1.5  2003/02/14 17:59:14  pgs
+#  added page_up and page_down
+#
+#  Revision 1.4  2002/08/13 17:42:23  pgs
+#  added a nice frame ;)
+#
+#  Revision 1.3  2002/07/26 19:12:24  pgs
+#  added freeze/thaw methods to enhance performance for many updates
+#  from a single action.
+#
+#  Revision 1.2  2002/07/23 19:17:48  pgs
+#  performance fix, draw to pixmap first
+#
+#  Revision 1.1  2002/07/23 16:38:52  pgs
+#  new file
+#
+#
+
+"""
+This widget is very simple, it just allows you to append text to it.  It allows
+you to scroll around in the text in a very limited fashion.  PyGTK 0.6.6 
+doesn't define the events for GDK_SCROLL so it's commented out here.
+
+
+Also, the width calculations seem to be off so the horizontal page size
+calculations don't give a perfect page width.  However, the width always seems 
+to be wider than necessary so you can at least see everything.
+
+To use it, simply put it in a container ( the scrollbars are embedded )
+
+Call append_text to append a string to it.  Carriage returns are acceptable
+
+text_area.contents is a list containing one entry per line.
+
+use scroll_to to scroll the window to a particular line - it will attempt to
+put the line at the top, taking into account the vertical page size and the
+overall size of the buffer.  This means that you will at least see from the
+requested line to the end of the buffer if the requeseted line doesn't end
+up at the top.
+
+All scroll calculations are done in terms of characters, not pixels.  I tried
+this and it gave very nice scrolling but the calculations for optimizing the
+expose function were kinda scary, so I dropped.  The end result is that it
+looks better if you use a fixed width font.
+
+Freeze/Thaw shouldn't be necessary - I should be checking to see if the added
+text is visible and if not, just updating the scrollbars (but even that can
+take time)
+
+Wish-list:
+remove the scrollbars and use in a GtkScrolledWindow
+make scrolling pixel based
+mouse scrolling support
+accessor functions
+documentation ;)
+"""
+
+import gtk
+
+class pguTextArea( gtk.GtkTable ):
+    
+    def __init__(self):
+        gtk.GtkTable.__init__( self, rows=2, cols=2 )
+        
+        self.hadj = gtk.GtkAdjustment()
+        self.vadj = gtk.GtkAdjustment()
+        
+        self._hscroll = gtk.GtkHScrollbar( adj = self.hadj )
+        self._vscroll = gtk.GtkVScrollbar( adj = self.vadj )
+        self._area = gtk.GtkDrawingArea()
+        #set events for scrolling (not defined in GDK
+        #self._area.set_events(1 << 21)
+        self.contents = []
+        self.max_width = 0
+        self.max_length = 0
+        self.height = 0
+        self.line_height = 0
+        self.start_line = 0
+        self.start_col = 0
+        self.freeze_count = 0
+        self.updating = gtk.FALSE
+        
+        frm = gtk.GtkFrame()
+        frm.set_shadow_type( gtk.SHADOW_ETCHED_OUT )
+        frm.add(self._area)
+        self.attach( frm, 0, 1, 0, 1 )
+        self.attach( self._vscroll, 1, 2, 0, 1, xoptions=gtk.SHRINK)
+        self.attach( self._hscroll, 0, 1, 1, 2, yoptions=gtk.SHRINK )
+                                                
+        self.show_all()
+        
+        self._area.connect( 'expose-event', self.expose )
+        self.connect( 'configure-event', self.configure )
+        self.hadj.connect( 'value-changed', self.changed )
+        self.vadj.connect( 'value-changed', self.changed )
+        self._area.connect( "scroll-event", self.event )
+        self.connect( 'style-set', self.expose )
+        
+    def freeze( self ):
+        """Freeze the widget - no further updates until thawed
+        """
+        self.freeze_count = self.freeze_count + 1
+        return
+        
+    def thaw( self ):
+        """Thaw the widget - this may not cause an update unless
+        the freeze count reaches 0
+        """
+        self.freeze_count = max( self.freeze_count - 1, 0 )
+        if self.freeze_count == 0:
+            self.calc_adjustments()
+            self.expose()
+        
+    """
+    this would handle scroll events but it seems to get into an endless
+    loop sometimes????
+    def event( self, widget, event ):
+        #event type for scrolling not defined in GDK
+        #and pygtk0.6.6 doesn't include a scroll event
+        try:
+            if event.type == 31:
+                dir = 1
+                if event.direction == 0:
+                    dir = -1
+                newval = max( self.vadj.value + dir, self.vadj.lower )
+                self.vadj.value = min( newval, self.vadj.upper - self.vadj.page_size)
+                self.vadj.value_changed()
+        except:
+            pass
+    """        
+    def changed( self, widget ):
+        """Track changes to the scrollbars and record the 
+        """
+        self.start_line = int(self.vadj.value)
+        self.start_col = int(self.hadj.value)
+        self.expose()
+
+    def append_text( self, text ):
+        """Append a block of text to the widget
+        
+        Record new max width/length for scrollbar calculations
+        """
+        if len(text) > 0 and text[-1] == '\n':
+            text = text[:-1]
+        font = self.get_style().font
+        rows = text.split( "\n" )
+        for row in rows:
+            idx = self.contents.append( row )
+            self.max_width = max( font.measure(row), self.max_width )
+            self.max_length = max( len(row), self.max_length )
+            #record line height only once,
+            #use ascent only, height (ascent+descent) includes extra whitespace
+            if self.line_height == 0:
+                self.line_height = int( font.extents(row)[3] )
+        
+        if self.freeze_count == 0:
+            self.calc_adjustments()
+
+    def calc_adjustments( self ):
+        """Recalculate the adjustment settings
+        """
+        if self.freeze_count > 0:
+            return
+            
+        self.updating = gtk.TRUE
+        geom = self._area.get_allocation()
+        #horizontal min/max are 0 and max line length - page size
+        hpos = self.hadj.value
+        if self.max_length == 0:
+            hpage = 1
+        else:
+            hpage = int(geom[2]/(self.max_width/self.max_length)/2)
+        hstep = int( hpage / 4 )
+        self.hadj.set_all( hpos, 0, self.max_length - hpage, 1, hstep, hpage )
+        self.hadj.changed()
+
+        vpos = self.vadj.value
+        if self.line_height == 0:
+            vpage = 1
+        else:
+            vpage = int(geom[3]/(self.line_height))
+        vstep = int( vpage / 4 )
+        vmax = max( 1, len(self.contents) )
+        self.vadj.set_all( vpos, 0, vmax, 1, vstep, vpage )
+        self.vadj.changed()
+        self.updating = gtk.FALSE
+        
+    def expose( self, *args ):
+        """Draw the widget
+        """
+        if self.freeze_count > 0:
+            return
+            
+        if not (self.flags() & gtk.REALIZED):
+            return
+        if not (self.flags() & gtk.MAPPED):
+            return
+            
+        geom = self._area.get_allocation()
+        pix = gtk.create_pixmap( self._area.get_window(), geom[2], geom[3] )    
+        style = self.get_style()
+        gtk.draw_rectangle(pix, style.white_gc, gtk.TRUE, 0, 0, 
+                                  geom[2], geom[3])
+        gtk.draw_rectangle(pix, style.black_gc, gtk.FALSE, 0, 0, 
+                                  geom[2], geom[3])
+        font = self.get_style().font
+        
+        v_offset = 0
+        for line in self.contents[self.start_line:]:
+            v_offset = v_offset + self.line_height
+            gtk.draw_string( pix, font, style.black_gc, 3, 
+                                    v_offset, line[self.start_col:] )
+            if v_offset > geom[3]:
+                break
+        
+        self._area.draw_pixmap(style.white_gc, pix, 0, 0, 0, 0, geom[2]-1, geom[3]-1 )
+
+        return gtk.FALSE
+           
+    def configure( self, widget, event, *args ):
+        """Track changes in width, height
+        """
+        self.resize_children()
+        self.calc_adjustments()
+        self.expose()
+        
+    def scroll_to( self, line ):
+        """Scroll the window to the requested line number
+        
+        Attempt to put the line at the top.  If there are less lines
+        after the requested line than will fit the page, then it will
+        move to a lower line number to display the whole last page
+        """
+        line = min( line, len(self.contents))
+        line = min( line, len(self.contents) - self.vadj.page_size)
+        if line != int(self.vadj.value):
+            self.vadj.set_value( line )
+        if int(self.hadj.value) != 0:
+            self.hadj.set_value( 0 )
+            
+    def page_up(self):
+        self.scroll_to( self.vadj.value - self.vadj.page_size )
+        
+    def page_down(self):
+        self.scroll_to( self.vadj.value + self.vadj.page_size )
+    
+
+class TestText( gtk.GtkWindow ):
+
+    def __init__(self):
+        gtk.GtkWindow.__init__( self )
+        self.set_title("Test TextArea")
+        
+        vbox = gtk.GtkVBox()
+        self.text = pguTextArea()
+        self.entry = gtk.GtkEntry()
+        self.button = gtk.GtkButton( "insert" )
+        self.button.connect( "clicked", self.insert_text )
+        
+        vbox.pack_start( self.text )
+        vbox.pack_start( self.entry, expand=gtk.FALSE )
+        vbox.pack_start( self.button, expand=gtk.FALSE )
+        self.add(vbox)
+        self.show_all()
+        
+    def insert_text( self, *args ):
+        self.text.append_text( self.entry.get_text() )
+        self.entry.set_text( "" )
+        
+        
+if __name__ == "__main__":
+    win = TestText()
+    for i in range(3):
+        win.text.append_text( "This is a\ntest of some\nlonger inserts and should work really well" )
+    win.connect( 'delete-event', gtk.mainquit )
+    gtk.mainloop()
+    
\ No newline at end of file

Added: packages/openev/branches/upstream/current/pymod/pgutogglebutton.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pgutogglebutton.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pgutogglebutton.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: pgutogglebutton.py,v 1.1 2002/08/13 16:07:17 pgs Exp $
+#
+# Project:  OpenEV Python GTK Utility classes
+# Purpose:  Embeddable, configurable toggle widget
+# Author:   Paul Spencer, pgs at magma.ca
+#
+###############################################################################
+# Copyright (c) 2000, DM Solutions Group Inc. (www.dmsolutions.on.ca)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: pgutogglebutton.py,v $
+#  Revision 1.1  2002/08/13 16:07:17  pgs
+#  new file
+#
+
+import gtk
+from gtk import TRUE, FALSE
+import gview
+import os.path
+
+class pguToggleButton(gtk.GtkToggleButton):
+    """
+    a widget for displaying toggled state (on/off).
+    """
+
+    def __init__(self, pix_on = "ck_on_l.xpm", pix_off = "ck_off_l.xpm"):
+        """
+        """
+        gtk.GtkToggleButton.__init__( self )
+        
+        filename = os.path.join(gview.home_dir, 'pics', pix_on)
+        pix, mask = gtk.create_pixmap_from_xpm(self, None, filename)
+        self.pix_on = gtk.GtkPixmap( pix, mask )
+        self.pix_on.show()
+        
+        filename = os.path.join(gview.home_dir, 'pics', pix_off)
+        pix, mask = gtk.create_pixmap_from_xpm(self, None, filename)
+        self.pix_off = gtk.GtkPixmap( pix, mask )
+        self.pix_off.show()
+        
+        self.add( self.pix_off )
+        
+        self.active_pix = self.pix_off
+        
+        self.set_usize( pix.width, pix.height )
+       
+        self.connect( 'toggled', self.expose )
+        self.connect( 'expose-event', self.expose )
+        self.show()
+        
+    def expose( self, *args ):
+        
+        if not self.flags() & gtk.REALIZED:
+            return
+            
+        
+        if self.get_active():
+            active_pix = self.pix_on
+        else:
+            active_pix = self.pix_off
+        if active_pix != self.active_pix:
+            self.remove( self.active_pix )
+            self.active_pix = active_pix
+            self.add( self.active_pix )
+        
+if __name__ == "__main__":
+    dlg = gtk.GtkDialog()
+    filename = os.path.join(gview.home_dir, 'pics')
+    print 'pixs from ', filename
+    tb = pguToggleButton()
+    dlg.vbox.pack_start( tb )
+    
+    btn = gtk.GtkButton( "OK" )
+    btn.connect( 'clicked', gtk.mainquit )
+    dlg.action_area.pack_start( btn )
+    dlg.connect( 'delete-event', gtk.mainquit )
+    dlg.show_all()
+    gtk.mainloop()
+    
\ No newline at end of file

Added: packages/openev/branches/upstream/current/pymod/pyshell.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/pyshell.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/pyshell.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2440 @@
+#! /usr/bin/env python
+###############################################################################
+#
+# Project:  OpenEV
+# Purpose:  GTK interface to Python Shell
+# Author:   Steve Rawlinson  srawlin at atlsci.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: pyshell.py,v $
+#  Revision 1.14  2003/09/09 15:18:46  gmwalter
+#  Update openev.py so that if default xml files are not present in xmlconfig
+#  directory, old configuration is used.  Get rid of deprecation warnings
+#  for python 2.3 by updating clist get_selection_info calls and colour
+#  allocation (alloc) calls to use integers instead of floats.
+#
+#  Revision 1.13  2003/07/28 19:42:34  gmwalter
+#  Checked in Diana's xml changes (modified to include tools), added
+#  python shell xml configuration.
+#
+#  Revision 1.12  2003/02/14 14:26:21  warmerda
+#  Fixed cntl-d support on windows.
+#  http://bugzilla.remotesensing.org/show_bug.cgi?id=285
+#
+#  Revision 1.11  2003/01/02 20:45:17  gmwalter
+#  Fixed a GtkAdjustment (vscrollbar) setting to avoid seg-faults on windows.
+#
+#  Revision 1.10  2002/12/12 07:43:15  warmerda
+#  get command processing working again
+#
+#  Revision 1.9  2002/07/18 17:45:13  warmerda
+#  various upgrades related to gvcommand support, and scroll fixes
+#
+#  Revision 1.8  2002/07/12 12:46:06  warmerda
+#  expanded tabs
+#
+#  Revision 1.7  2001/11/14 14:05:13  warmerda
+#  freeze()/thaw() text widget to avoid overwrites on windows
+#
+#  Revision 1.6  2001/11/12 18:43:17  warmerda
+#  add locals pseudo-command
+#
+#  Revision 1.5  2001/10/02 21:19:43  warmerda
+#  specify iso8859 for the font
+#
+#  Revision 1.4  2001/05/22 12:42:57  warmerda
+#  Patch to make cntl-D only close window if there is no input like
+#  the shell version.  Provided by Jeffery D. Collins.
+#
+#  Revision 1.3  2000/11/08 16:07:07  warmerda
+#  fixed gdkcolor values to stay in 16bit signed range
+#
+#  Revision 1.2  2000/07/26 14:35:07  srawlin
+#  increased font size, fixed Ctrl-D bug, changed key bindings to use GDK defined values
+#
+#  Revision 1.1  2000/07/25 21:26:05  srawlin
+#  new - Gtk interface to python shell
+#
+#
+#
+
+MAX_COMMAND_PATHS = 15
+MAX_MODULE_PATHS = 15
+
+from gtk import *
+import GtkExtra, GDK
+import code, string, sys, os
+import gvutils
+
+def launch(pyshellfile=None):
+    import gview
+    try:
+        gview.app.shell.get_window()._raise()
+        gview.app.shell.show()
+    except:
+        import gvcorecmds
+
+        shell = Shell(pyshellfile=pyshellfile)
+        gview.app.shell = shell
+
+        # Initialization Commands
+        shell.command('from Numeric import *')
+        shell.command('from gdalnumeric import *')
+        shell.command('from gvshell import *')
+        shell.command('from gvplot import plot')
+        shell.command('gview.shell_base_vars = dir()')
+        shell.interp.clear_history()
+        gvcorecmds.Register( shell )
+
+        shell.show_all()
+    
+    return gview.app.shell
+
+def launch_standalone(pyshellfile=None):
+    import gview
+    import gvcorecmds
+
+    shell = Shell(pyshellfile=pyshellfile,standalone=1)
+    gview.app.shell = shell
+
+    # Initialization Commands 
+    shell.command('from Numeric import *')
+    shell.command('from gdalnumeric import *')
+    shell.command('from gvshell import *')
+    shell.command('from gvplot import plot')
+    shell.command('gview.shell_base_vars = dir()')
+    gvcorecmds.Register( shell )
+
+    shell.show_all()
+
+    
+
+class MyInteractiveConsole(code.InteractiveConsole):
+
+    def __init__(self, text_shell, inherit=None, history_list=None,
+                 status_bar=None):
+        self.text_shell = text_shell
+        locals = sys.modules['__main__'].__dict__
+        if inherit:
+            code.InteractiveConsole.__init__(self, locals=locals)
+        else:
+            code.InteractiveConsole.__init__(self)
+                                         
+        self.cmdlist = {}
+
+        style = text_shell.get_style()
+        self.fg = style.fg[STATE_NORMAL]
+        self.bg = style.white
+        self.font = load_font(
+            "-*-courier-medium-r-normal-*-14-140-*-*-*-*-iso8859-*")
+
+        # Avoid using GdkColor to allocate colours
+        self.redtext=self.text_shell.get_colormap().alloc(25000,0,0)
+        self.bluetext=self.text_shell.get_colormap().alloc(0,0,32757)
+        self.greentext=self.text_shell.get_colormap().alloc(0,25000,0)
+
+        self.history_list = history_list
+        self.status_bar = status_bar
+        self.status_msg_id = None
+
+        # Journaling-related variables
+        # If journal_fh is None, journaling is off.
+        self.journal_fh = None # file handle for journaling
+        self.journal_fname = None # file name
+
+        # List of helpfiles to check for command/
+        # function help (full paths).
+        self.helpfiles=[]
+        self.help_cmdtxt={}
+        self.help_functxt={}
+        self.help_builtintxt={}
+
+        # Macro flag:
+        #     in_macro- use to avoid macro commands going into history buffer
+        self.in_macro = 0 # Not currently in a macro
+
+        # Error flag: use to exit potentially nested macros without completing
+        #             them if an error is encountered.
+        self.last_err = 0 # error status of last push command
+
+    def clearFlags(self):
+        # Function for top level shell to use to clear interpreter
+        # macro and error flags.
+        self.in_macro = 0
+        self.last_err = 0
+
+    def showProgress(self,percent,msg=None):
+        if self.status_bar is None:
+            return
+        self.status_bar.progress_bar.set_percentage(percent/100.0)
+        if msg is not None:
+            if (self.status_msg_id is not None):
+                self.status_bar.remove(self.status_bar.shell_context,self.status_msg_id)
+                
+            self.status_msg_id = self.status_bar.push(self.status_bar.shell_context,msg)
+
+    def clear_history(self):
+        if self.history_list is None:
+            return
+        self.history_list.freeze()
+        self.history_list.clear()
+        self.history_list.thaw()
+
+        
+    def push(self, line):
+        if ((self.in_macro == 0) and (self.history_list is not None)):
+            self.history_list.freeze()
+            self.history_list.insert(0,([line]))
+            # limit history list to 200 commands
+            # (gtk doesn't like long clists)
+            if self.history_list.rows > 200:
+                for count in range(self.history_list.rows-200):
+                    self.history_list.remove(200)          
+            self.history_list.thaw()
+
+        # locals replaced by a proper command    
+        #if line[:6] == 'locals':
+        #    line = 'local_vars_list(locals())'
+
+        s_line=string.lstrip(line)
+        if ((len(s_line) > 8) and (s_line[:6] == 'macro ')):
+            fname=string.strip(s_line[6:])
+
+            if ((len(fname) > 11) and (fname[:11] == 'macro_file=')):
+                # User has specified macro_file keyword
+                fname=fname[11:]
+                
+            # Search order for macro:
+            # 1) fname
+            # 2) OPENEV_MACRO_PATH/fname (multiple semi-colon separated
+            #    paths may be specified in the OPENEV_MACRO_PATH
+            #    environment variable).
+            # 3) OPENEVHOME/fname
+            # 4) OPENEV_HOME/fname
+            #
+            if not (os.path.isfile(fname)):
+                macropaths=os.environ.get('OPENEV_MACRO_PATH')
+                if macropaths is not None:
+                    for mpath in string.split(macropaths,";"):
+                        mspath=string.strip(mpath)
+                        if os.path.isfile(os.path.join(mspath,fname)):
+                            fname=os.path.join(mspath,fname)
+                            break
+
+            if not (os.path.isfile(fname)):
+                oevpath=os.environ.get('OPENEVHOME')
+                if oevpath is not None:
+                    temp=os.path.join(oevpath,'macros')
+                    temp=os.path.join(temp,fname)
+                    if os.path.isfile(temp):
+                        fname=temp
+
+            if not (os.path.isfile(fname)):
+                oevpath=os.environ.get('OPENEV_HOME')
+                if oevpath is not None:
+                    temp=os.path.join(oevpath,'macros')
+                    temp=os.path.join(temp,fname)
+                    if os.path.isfile(temp):
+                        fname=temp
+                
+                    
+            if os.path.isfile(fname):
+                # keep track of indentation, in case macros
+                # are called within for loops of other
+                # macros
+                mac_indent_level=len(line)-len(s_line)
+                indent_txt=''
+                for count in range(mac_indent_level):
+                    indent_txt=indent_txt+' '
+                    
+                fh=open(fname)
+                line1=fh.read(20)
+                if (string.find(line1,"openev macro") > 0):
+                    fh.seek(0)
+                    fh.readline()
+                    commandlines=fh.readlines()
+                    for cline in commandlines:
+                        # Treat each line of a macro as though it was
+                        # entered at the command prompt.
+                        cline=string.replace(cline,chr(10),"")
+                        cline=indent_txt+cline
+                        self.my_write(cline+chr(10),'command')
+                        
+                        # Set in_macro flag before each command, in
+                        # case there is a macro within a macro, and
+                        # in_macro gets set to 0 after exiting the
+                        # nested macro
+                        self.in_macro=1
+                        
+                        self.push(string.rstrip(cline))
+
+                        # If an error has been encountered, return
+                        # immediately leaving error flag intact so
+                        # that higher-level macros can also detect
+                        # it and exit.
+                        #
+                        # NOTE: later, may want to add an option
+                        # to the macro command or a flag on the shell
+                        # that tells macros to force their way through
+                        # even if an error is encountered...
+                        if self.last_err == 1:
+                            return 0
+                        
+                    self.history_pos = None
+                    if self.history_list is not None:
+                        self.history_list.unselect_all()
+                        
+                    fh.close()
+            else:
+                self.showText(fname+' does not exist or is not a file.','error')
+
+            self.in_macro=0
+            return 0
+        
+        # If journaling is on, write the line unless the line
+        # is itself a journal command
+        if self.journal_fh is not None:
+            if not ((len(line) >= 7) and (line[:7] == 'journal')):
+                self.journal_fh.writelines(line+'\n')
+                self.journal_fh.flush()
+            
+        
+        if len(self.cmdlist) > 0:
+            cmd_name, remainder = parse_interpreter_line(line)
+            if self.cmdlist.has_key( cmd_name ):
+                # HISTORICAL NOTE: the change from cc.execute()'ing at
+                # this level to executing inside the shell through 
+                # _run_command_line was necessary for commands to work
+                # within a for-loop.  For instance:
+                #
+                # for i in range(3):
+                #     print i
+                #     newview
+                #
+                # Before, this used to immediately launch one view, then
+                # print the integers 1, 2, and 3 on separate lines because
+                # only the for-line and print-line actually got passed
+                # through to the the interpreter.
+                # The actual sequence of events was:
+                # - push the line "for i in range(3):" into interpreter.
+                #   Interpreter sees that this is an incomplete loop and
+                #   stores it in a buffer, doing nothing.
+                # - push the line "    print i" into interpreter.
+                #   Since end of loop has not been detected, interpreter
+                #   stores line in buffer.
+                # - new view command is detected: run newview's
+                #   execution code.
+                # - push the line "" into the interpreter.  Interpreter
+                #   now has a complete loop that can be executed, executes
+                #   it, and flushes the buffer.
+                #
+                # Changing the code so that
+                # it is executed within the interpreter allows the newview
+                # to launch as one would expect because the code is now
+                # all executed in the context of the interpreter:
+                #
+                # for i in range(3):
+                #     print i
+                #     _run_command_line('newview')
+                #
+                # This does introduce one complication: COMMANDS MUST NOT
+                # CALL THE interp.push OR code.InteractiveConsole.push
+                # FUNCTIONS IN THEIR EXECUTION CODE BECAUSE THE BUFFER 
+                # FOR EACH PUSH COMMAND IS NOT CLEARED UNTIL IT COMPLETES,
+                # SO CALLING PUSH WITHIN PUSH RESULTS IN A RECURSIVE LOOP.
+
+                # Old code
+                #cc.execute()
+                #return code.InteractiveConsole.push(self,'')
+                # end of old code
+
+                # Get the indentation level of the line
+                temp_line=string.lstrip(line)
+                indent_level=len(line)-len(temp_line)
+                txt=''
+                for count in range(indent_level):
+                    txt=txt+' '
+                        
+                if string.find(line,'"') == -1:
+                    txt=txt+'_run_command_line("'+line+'")'
+                elif string.find(line,"'") == -1:
+                    txt=txt+"_run_command_line('"+line+"')"
+                else:
+                    line2=string.replace(line,"'",'"')
+                    txt=txt+"_run_command_line('"+line2+"')"                        
+
+                return code.InteractiveConsole.push(self,txt)
+
+
+        return code.InteractiveConsole.push(self,line)
+           
+    # This is a CommandInterpreter method as per gvcommand.py
+    def isInteractive( self ):
+        return 1
+
+    # This is a CommandInterpreter method as per gvcommand.py
+    def showText( self, text, text_class ):
+        text = text + chr(10)
+        if text_class == 'error':
+            self.my_write( text, 'stderr' )
+        else:
+            self.my_write( text, 'stdout' )
+
+
+    def showtraceback(self):
+        """Display the exception that just occurred.
+
+        We remove the first stack item because it is our own code.
+
+        The output is written by self.write(), below.
+
+        """
+        try:
+            import traceback
+            if sys.exc_info()[2] is not None:
+                exc_info = traceback.extract_tb(sys.exc_info()[2])
+                txt = 'Unexpected Error:'
+                txt = txt + '\n  Type  : '+str(sys.exc_type)                
+                if sys.exc_info()[1] is None: 
+                    txt = txt + '\n  Description: '+ 'Undefined\n'
+                
+                else:		
+                    txt = txt + '\n  Description: '+str(sys.exc_info()[1]) + '\n'
+                
+                if len(exc_info) > 2:
+                    # Ignore the first 2 tuples- in the context of the interpreter,
+                    # these are just the console and the exec statement and are
+                    # irrelevant
+                    txt = txt + '\n  Traceback:\n'
+                    for ctuple in exc_info[2:]:
+                        txt = txt + '\n    file       : '+os.path.basename(ctuple[0])
+                        txt = txt + '\n    line number: '+str(ctuple[1])
+                        txt = txt + '\n    function   : '+str(ctuple[2])
+                        txt = txt + '\n    line       : '+str(ctuple[3])+'\n'
+            
+            else:
+                txt = 'Unexpected Error:'
+                txt = txt + '\n    No description available.\n'
+        except:
+            txt = 'Unexpected Error:'
+            txt = txt + '\n    No description available.\n'
+            
+        self.write(txt)
+
+        # Status of last line pushed via toplevel shell's echo function
+        self.last_err = 1
+        
+    def write(self, data):
+        # Override base class write
+        # Red - tracebacks
+        # This method is only used when an error has occurred (eg. a
+        # syntax error)- stdout is redirected to my_write.
+        self.text_shell.insert(self.font, self.redtext, self.bg, data)
+
+        self.last_err = 1
+
+    def my_write(self, data, name):
+        self.text_shell.freeze()
+        # Comment line just below this is deprecated- GdkColor isn't used now
+        # Colours need to be in function calls for some reason else segfaults!?!
+
+        # Normal output is reported in GREEN
+        if name == 'stdout':
+            self.text_shell.insert(self.font, self.greentext, self.bg, data)  
+        # Anticipated errors are reported in BLUE
+        elif name == 'stderr':
+            self.text_shell.insert(self.font, self.bluetext, self.bg, data)
+            # should still set error flag
+            self.last_err = 1
+        # Titles are bigger and BLUE
+        elif name == 'title':
+            self.text_shell.insert( 
+              load_font("-*-courier-bold-o-normal-*-14-140-*-*-*-*-iso8859-*"),
+              self.bluetext,
+              self.bg, data )
+
+        # Everything else is just the normal foreground color (black)
+        else:
+            self.text_shell.insert(self.font, self.fg, self.bg, data)  
+
+        self.text_shell.thaw()
+
+        vscrollbar = self.text_shell.get_vadjustment()
+        
+        try:
+            # This offset seems to be necessary on windows, which
+            # segfaults if vscrollbar.upper or larger values are
+            # used.  
+            hoffset=self.text_shell.get_window().height
+            vscrollbar.set_value( vscrollbar.upper - hoffset )
+        except:
+            pass
+
+    def get_command_help(self, command,quiet=0):
+        # quiet- if quiet is 0, include module,
+        #        and group info; otherwise don't.
+        
+        if self.cmdlist.has_key(command):
+            # Command is loaded.  Determine what module
+            # it is in.
+            if hasattr(self.cmdlist[command],'__module__'):
+                modname=os.path.basename(self.cmdlist[command].__module__)
+                mname,ext=os.path.splitext(modname)      
+            else:
+                mname=None
+
+            # check for registered help for command
+            # under module mname.
+            if self.help_cmdtxt.has_key(command):
+                if mname is None:
+                    # If module unknown, return all
+                    # text help.
+                    ctext=''
+                    for centry in self.help_cmdtxt[command]:
+                        if quiet == 0:
+                            ctext=ctext+'Module: '+centry[0]+'\n'
+                            ctext=ctext+'Group: '+centry[1]+'\n\n'
+                            
+                        ctext=ctext+centry[3] + '\n'
+                    return ctext
+
+                for centry in self.help_cmdtxt[command]:
+                    if centry[0] == mname:
+                        ctext=''
+                        if quiet == 0:
+                            ctext='Module: '+centry[0]+'\n'
+                            ctext=ctext+'Group: '+centry[1]+'\n\n'
+                            
+                        ctext=ctext+centry[3] + '\n'
+                        
+                        return ctext
+
+
+            # No suitable text help found, but command found so
+            # construct basic help.
+            if quiet == 0:
+                txt='Module: '
+                if mname is None:
+                    txt=txt+'Unknown\n'
+                else:
+                    txt=txt+mname+'\n'
+                
+                if hasattr(self.cmdlist[command],'Group'):
+                    txt=txt+'Group: '+self.cmdlist[command].Group + '\n\n'
+                else:
+                    txt='Group: None\n\n'
+            else:
+                txt=''
+                
+            txt=txt+'Usage: '+self.cmdlist[command].Usage + '\n'
+            if hasattr(self.cmdlist[command],'__doc__'):
+                txt=txt+_format_doc(self.cmdlist[command].__doc__)+'\n'
+                
+            return txt
+            
+        elif self.help_cmdtxt.has_key(command):
+            # If module unknown, return all
+            # text help.
+            ctext=''
+            for centry in self.help_cmdtxt[command]:
+                if quiet == 0:
+                    ctext=ctext+'Module: '+centry[0]+'\n'
+                    ctext=ctext+'Group: '+centry[1]+'\n'
+                            
+                ctext=ctext+centry[3] + '\n\n'
+                 
+            return ctext
+
+        else:                            
+            return None    
+
+
+    def get_function_help(self, func, module_name=None, quiet=0):
+        """ Search for documentation on function func.
+            If module_name is set to None, search
+            through the local shell variables for func
+            and try to determine func's module from
+            its attributes; otherwise, look for any
+            help on function func.
+            If module_name is not None, check if
+            a func from module module_name is in the shell
+            variables or in the help.  If not, try to
+            import it.
+        """
+        import Numeric
+
+        dtxt=None # default text
+        # mname- name of module for shell variable func
+        #        if func is present in shell.
+        mname=None 
+
+        # Get default python documentation, but don't return
+        # it unless module name is okay.
+        if ((self.locals.has_key(func)) and
+              (type(self.locals[func]) == type(launch)) and
+              (hasattr(self.locals[func],'__doc__'))):
+
+            if quiet == 0:
+                if hasattr(self.locals[func],'func_code'):
+                    modname=os.path.basename(self.locals[func].func_code.co_filename)
+                    mname,ext=os.path.splitext(modname)
+                    dtxt='Module: '+mname+'\n\n'      
+                else:
+                    dtxt='Module: unknown\n\n'
+            else:
+                dtxt=''
+                
+            dtxt=dtxt+_format_doc(self.locals[func].__doc__) + '\n'
+        
+        elif ((self.locals.has_key(func)) and
+              (type(self.locals[func]) == type(Numeric.cos)) and
+              (hasattr(self.locals[func],'__doc__'))):
+
+            # Can't determine module from a ufunc's attributes
+            if quiet == 0:
+                dtxt='Universal function (ufunc)\n\n'
+            else:
+                dtxt=''
+                
+            dtxt=dtxt+_format_doc(self.locals[func].__doc__) + '\n'
+
+        if self.help_functxt.has_key(func):
+            # If user specified particular module to
+            # search, only return help for that
+            # module.
+            if module_name is not None:
+                for centry in self.help_functxt[func]:
+                    if centry[0] == module_name:
+                        ctext=''
+                        if quiet == 0:
+                            ctext='Module: '+centry[0]+'\n\n'
+
+                        ctext=ctext+centry[2]
+                        return ctext
+
+            # If module name was not specfied, but
+            # func is present in the shell and module
+            # can be determined from func, return
+            # help for that module only.
+            elif mname is not None:
+                for centry in self.help_functxt[func]:
+                    if centry[0] == mname:
+                        ctext=''
+                        if quiet == 0:
+                            ctext='Module: '+centry[0]+'\n\n'
+
+                        ctext=ctext+centry[2]
+                        return ctext
+                    
+            # If no module specified or found, return
+            # all text file help available on func.
+            else:
+                ctext=''
+                for centry in self.help_functxt[func]:
+                    if quiet == 0:
+                        ctext=ctext+'Module: '+centry[0]+'\n\n'
+                        
+                    ctext=ctext+centry[2]+'\n\n'
+                    
+                return ctext
+
+        # At this point, no suitable text file help has been found
+        if ((module_name is not None) and
+            (mname != module_name)):
+            # shell variable doesn't match requested module.
+            # try to import module and create help.
+            try:
+                exec 'import '+module_name
+                exec 'funcinst='+module_name+'.'+func
+                exec 'docstr=funcinst.__doc__'
+            except:
+                # couldn't load module or function, or couldn't
+                # locate documentation string once loaded
+                return None
+
+            if (type(funcinst) == type(launch)):
+                if quiet == 0:
+                    dtxt='Module: '+module_name+'\n\n'
+                else:
+                    dtxt=''
+                dtxt=dtxt+_format_doc(docstr)+'\n'
+                return dtxt
+            
+            elif (type(funcinst) == type(Numeric.cos)):
+                if quiet == 0:
+                    dtxt='Module: '+module_name+'\n\n'
+                    dtxt=dtxt+'Universal function (ufunc)\n\n'
+                else:
+                    dtxt=''
+                dtxt=dtxt+_format_doc(docstr)+'\n'
+                return dtxt
+            
+            else:
+                # Not a recognized non-builtin function
+                # (type must be either function or ufunc)
+                return None
+                
+        else:
+            return dtxt
+
+
+    def get_builtin_help(self, func, module_name=None,quiet=0):
+        if self.help_builtintxt.has_key(func):
+            if module_name is not None:
+                for centry in self.help_builtintxt[func]:
+                    if centry[0] == module_name:
+                        ctext=''
+                        if quiet == 0:
+                            ctext='Module: '+centry[0]+'\n\n'
+
+                        ctext=ctext+centry[2]
+                        return ctext
+            else:
+                ctext=''
+                for centry in self.help_builtintxt[func]:
+                    if quiet == 0:
+                        ctext=ctext+'Module: '+centry[0]+'\n\n'
+                        
+                    ctext=ctext+centry[2]+'\n\n'
+                    
+                return ctext
+
+        # If code gets to here, no suitable help
+        # for func has been found.
+        if module_name is None:
+            if ((self.locals.has_key(func)) and
+                  (type(self.locals[func]) == type(hasattr)) and
+                  (hasattr(self.locals[func],'__doc__'))):   
+                txt=_format_doc(self.locals[func].__doc__) + '\n'
+                return txt
+            else:
+                try:
+                    # builtin function
+                    txt=_format_doc(self.locals['__builtins__'][func].__doc__)+ '\n'
+                    return txt
+                except:
+                    return None
+                
+        # No help has been found yet, and specific module has been requested
+        try:
+            exec 'import '+module_name
+            exec 'funcinst='+module_name+'.'+func
+            exec 'docstr=funcinst.__doc__'
+            if (type(funcinst) == type(hasattr)):
+                if quiet == 0:
+                    txt='Module: '+module_name+'\n\n'
+                else:
+                    txt=''
+                txt=txt+_format_doc(docstr) + '\n'
+                return txt
+            else:
+                return None
+        except:
+            # couldn't load module or function, or couldn't
+            # locate documentation string once loaded
+            return None
+
+    def add_helpfile(self, helpfilename):
+        if helpfilename in self.helpfiles:
+            # Already registered.
+            return 1
+        
+        if os.path.isfile(helpfilename) == 0:
+            self.showText('Warning: help file '+helpfilename+'\n does not exist','error')
+            return 0
+
+        self.helpfiles.append(helpfilename)
+        fh=open(helpfilename)
+        helplines=fh.readlines()
+        ckey=None
+        ctext=''
+        ctype=None
+        
+        # add a dummy line to the end to force the last
+        # command to be assigned (since assignment is
+        # done once a new command is started).
+        helplines.append('COMMAND_NAME=dummyhelpline')
+        
+        for cline in helplines:
+            # linetype: 0 if an ordinary line is read, 1 if help for
+            # a new command is being defined, 2 if help for a new
+            # function is being defined, 3 if help for a new
+            # builtin function is being defined.
+            linetype=0
+            if(( len(cline) > 13) and (cline[:13] == 'COMMAND_NAME=')):
+                linetype=1
+                
+            if(( len(cline) > 14) and (cline[:14] == 'FUNCTION_NAME=')):
+                linetype=2
+                
+            if(( len(cline) > 13) and (cline[:13] == 'BUILTIN_NAME=')):
+                linetype=3
+                
+            if (linetype > 0):
+                if (ckey is not None) and (ctype is not None):
+                    # Assign last function/command's text, if
+                    # present, before going on to next one.
+                    if ctype == 'cmd':
+                        # split help text into module, group,
+                        # html filename, text.
+                        parsed_help=_parse_cmdhelp_text(ctext)
+                        if parsed_help is None:
+                            errtxt='Invalid helpfile '+helpfilename+\
+                                    ':\nBad entry for command'+ckey+'.'
+                            raise errtxt
+                        
+                        mname=parsed_help[0]
+                        gname=parsed_help[1]
+                        hname=parsed_help[2]
+                        ctext=parsed_help[3]
+                        
+                        if self.help_cmdtxt.has_key(ckey):
+                            # Some help has already been registered
+                            # for a function of this name.
+                            # Check if module already has help registered.
+                            conflict=0
+                            for idx in range(len(self.help_cmdtxt[ckey])):
+                                item=self.help_cmdtxt[ckey][idx]
+                                if item[0] == mname:
+                                    txt='Warning: multiple sets of help found for '+\
+                                        'command '+ckey+', module '+mname+'.'+\
+                                        '\nIgnoring all but first.\n'
+                                    self.showText(txt,'error')
+                                    conflict=1
+
+                            if conflict == 0:
+                                self.help_cmdtxt[ckey].append([mname,gname,hname,ctext])
+                        else:
+                            self.help_cmdtxt[ckey]=[]
+                            self.help_cmdtxt[ckey].append([mname,gname,hname,ctext])                          
+                            
+                    elif ctype == 'blt':
+                        parsed_help=_parse_funchelp_text(ctext)
+                        if parsed_help is None:
+                            errtxt='Invalid helpfile '+helpfilename+\
+                                    ':\nBad entry for built-in function '+ckey+'.'
+                            raise errtxt
+                        
+                        mname=parsed_help[0]
+                        hname=parsed_help[1]
+                        ctext=parsed_help[2]
+                        
+                        if self.help_builtintxt.has_key(ckey):
+                            # Some help has already been registered
+                            # for a function of this name
+                            # Check if module already has help registered.
+                            conflict=0
+                            for idx in range(len(self.help_builtintxt[ckey])):
+                                item=self.help_builtintxt[ckey][idx]
+                                if item[0] == mname:
+                                    txt='Warning: multiple sets of help found for '+\
+                                        'built-in function '+ckey+', module '+mname+'.'+\
+                                        '\nIgnoring all but first.'
+                                    self.showText(txt,'error')
+                                    conflict=1
+
+                            if conflict == 0:
+                                self.help_builtintxt[ckey].append([mname,hname,ctext])
+                        else:
+                            self.help_builtintxt[ckey]=[]
+                            self.help_builtintxt[ckey].append([mname,hname,ctext])
+                    else:
+                        parsed_help=_parse_funchelp_text(ctext)
+                        if parsed_help is None:
+                            errtxt='Invalid helpfile '+helpfilename+\
+                                    ':\nBad entry for function '+ckey+'.'
+                            raise errtxt
+                        
+                        mname=parsed_help[0]
+                        hname=parsed_help[1]
+                        ctext=parsed_help[2]
+                        
+                        if self.help_functxt.has_key(ckey):
+                            # Some help has already been registered
+                            # for a function of this name
+                            # Check if module already has help registered.
+                            conflict=0
+                            for idx in range(len(self.help_functxt[ckey])):
+                                item=self.help_functxt[ckey][idx]
+                                if item[0] == mname:
+                                    txt='Warning: multiple sets of help found for '+\
+                                        'function '+ckey+', module '+mname+'.'+\
+                                        '\nIgnoring all but first.'
+                                    self.showText(txt,'error')
+                                    conflict=1
+                                    
+                            if conflict == 0:
+                                self.help_functxt[ckey].append([mname,hname,ctext]) 
+                        else:
+                            self.help_functxt[ckey]=[]
+                            self.help_functxt[ckey].append([mname,hname,ctext])
+
+            if (linetype == 1):                 
+                junk,ckey = string.split(cline,'=',1)
+                ckey=string.strip(ckey)
+                ctext=''
+                ctype='cmd'
+            elif (linetype == 2): 
+                junk,ckey = string.split(cline,'=',1)
+                ckey=string.strip(ckey)
+                ctext=''
+                ctype='fnc'
+            elif (linetype == 3):
+                junk,ckey = string.split(cline,'=',1)
+                ckey=string.strip(ckey)
+                ctext=''
+                ctype='blt'                
+            else:
+                ctext=ctext+cline
+                        
+        return 1
+        
+class PseudoFile:
+# To send stdout to our console
+
+    def __init__(self, shell, name):
+        self.shell = shell
+        self.name = name
+
+    def write(self, s):
+        self.shell.my_write(s, self.name)
+        # self.shell.write(s)
+
+        import gdal
+        gdal.Debug( "stderr", s )
+        
+    def writelines(self, l):
+        map(self.write, l)
+
+    def flush(self):
+        pass
+
+    def isatty(self):
+        return 1
+
+
+# Creates interactive Python Shell
+#
+# inherit - true if parent's python environment be inherited
+#           None if it shouldn't and want a clean environment
+
+class Shell(GtkWindow):
+    def __init__(self, inherit=None, width=550, height = 250, standalone=0,pyshellfile=None):
+        
+        # Main Window, buttons
+        GtkWindow.__init__(self)
+        self.set_title('Python Shell')
+        self.set_border_width(3)
+
+        self.pyshellfile=pyshellfile
+
+
+        if pyshellfile is not None:
+            guicmds=self.load_pyshell_file_from_xml(self.pyshellfile)
+        else:
+            # If pyshellfile is None, default to old appearance.
+            
+            #menucmds=[]
+            #menucmds.append(self.__get_standard_menu_entries())
+            #guicmds=(menucmds,None,None,None)
+            guicmds=(None,None,None,None)
+            
+        # Use a paned window if the history area is present; a normal
+        # vbox if it isn't.       
+        # panel for menu, messages, icons, command line
+        if (guicmds[2] is not None):
+            pane1=GtkVPaned()
+            vbox = GtkVBox(homogeneous=FALSE,spacing=2)
+            self.add(pane1)
+            pane1.add1(vbox)
+        else:
+            vbox=GtkVBox(homogeneous=FALSE,spacing=2)
+            self.add(vbox)
+
+        self.standalone = standalone
+        
+        # Path Preferences
+        # Currently, the module and script paths
+        # are treated the same way- they are added
+        # to the system path, and python will search
+        # all three for commands, modules.
+        self.preferences={}
+
+        import gview
+        import sys
+        
+        mpathstring=""
+        for i in range(1,MAX_MODULE_PATHS+1):
+            mpath = gview.get_preference('pyshell_module_path'+str(i))
+            if mpath is not None:
+                if mpath not in sys.path:
+                    sys.path.append(mpath)
+                if len(mpathstring) == 0:
+                    mpathstring=mpath
+                else:
+                    mpathstring=mpathstring+";"+mpath
+
+        cpathstring=""
+        for i in range(1,MAX_COMMAND_PATHS+1):
+            cpath = gview.get_preference('pyshell_command_path'+str(i))
+            if cpath is not None:
+                if cpath not in sys.path:
+                    sys.path.append(cpath)
+                if len(cpathstring) == 0:
+                    cpathstring=cpath
+                else:
+                    cpathstring=cpathstring+";"+cpath
+        
+        self.preferences['MODULE PATHS']=mpathstring    
+        self.preferences['COMMAND PATHS']=cpathstring
+        
+        self.path_dlg = None
+
+        # Menu
+        if (guicmds[0] is not None):
+            menuf = gvutils.GvMenuFactory()
+            self.menuf = menuf
+            if self.standalone != 0:
+                self.close_cb = self.close
+            else:
+                self.close_cb = self.destroy
+
+            for cmd in guicmds[0]:
+                exec cmd
+            
+            self.add_accel_group(menuf.accelerator)
+            vbox.pack_start(menuf,expand=FALSE)
+        else:
+            self.menuf = None
+
+        # Iconbar
+        if (guicmds[1] is not None) and (len(guicmds[1]) > 0):
+            self.iconbar = GtkToolbar(ORIENTATION_HORIZONTAL,
+                                      TOOLBAR_ICONS)
+            for cmd in guicmds[1]:
+                exec cmd
+            
+            vbox.pack_start(self.iconbar,expand=FALSE)
+        else:
+            self.iconbar = None
+            
+        top_console = GtkHBox(homogeneous=FALSE, spacing=2)
+        vbox.pack_start(top_console, expand=TRUE)
+
+        text = GtkText()
+        text.set_editable(FALSE)
+        top_console.pack_start(text, expand=TRUE)
+
+        # Scrollbars
+        scroll = GtkVScrollbar(adj=text.get_vadjustment())
+        top_console.pack_start(scroll, expand=FALSE)
+
+        #Horizontal separator bar
+        vbox.pack_start(GtkHSeparator(), expand=FALSE)
+
+        # Prompt Text Area
+        prompt = GtkText()
+        prompt.set_usize(550, 50)
+        prompt.set_editable(TRUE)
+        vbox.pack_start(prompt, expand=FALSE)
+
+        if guicmds[2] is not None:
+            vbox2=GtkVBox()
+            pane1.add2(vbox2)
+            #Horizontal separator bar
+            hlabel = GtkLabel('Command History')
+            hlabel.set_justify(JUSTIFY_LEFT)
+            #vbox2.pack_start(GtkHSeparator(), expand=FALSE)
+            histhbox=GtkHBox()
+            vbox2.pack_start(histhbox,expand=FALSE,fill=FALSE)
+            histhbox.pack_start(hlabel,expand=FALSE,fill=FALSE)
+            # History list
+            histbox = GtkScrolledWindow()
+            vbox2.pack_start(histbox)
+            histlist = GtkCList(cols=1)
+
+            # Changed from add_with_viewport to
+            # add to avoid Gtk warnings about
+            # size allocation for long history
+            # lists (> about 65 lines).  Not sure
+            # why this is necessary, but someone
+            # else had fixed the problem that way
+            # according to an email found with google.
+            # histbox.add_with_viewport(histlist)
+            histbox.add(histlist)
+            
+            histlist.set_selection_mode(SELECTION_SINGLE)
+            histlist.connect('button-press-event',self.list_clicked)
+        else:
+            histlist = None
+
+        if guicmds[3] is not None:
+            #Horizontal separator bar
+            # vbox2 is only created if the history area is created
+            # (paned window is only necessary in that case currently).
+            if guicmds[2] is not None:
+                vbox2.pack_start(GtkHSeparator(), expand=FALSE)
+            else:
+                vbox.pack_start(GtkHSeparator(), expand=FALSE)
+            self.status_bar = GtkStatusbar()            
+            self.status_bar.progress_bar = GtkProgressBar()
+            self.status_bar.shell_context = self.status_bar.get_context_id('shell')
+            self.status_bar.pack_start(self.status_bar.progress_bar, expand=FALSE)
+            if guicmds[2] is not None:
+                vbox2.pack_start(self.status_bar, expand=FALSE)
+            else:
+                vbox.pack_start(self.status_bar, expand=FALSE)                
+        else:
+            self.status_bar = None
+
+        # Setup up the size of the dialog
+        totalheight = height
+        if self.menuf is not None:
+            totalheight=totalheight + 50
+        if self.iconbar is not None:
+            totalheight=totalheight + 50
+        if histlist is not None:
+            totalheight=totalheight + 250
+            
+        self.set_usize(width, totalheight)
+        self.set_policy(FALSE, TRUE, TRUE)
+    
+        # Text properties
+        style = text.get_style()
+        self.fg = style.fg[STATE_NORMAL]
+        self.bg = style.white
+        self.font = load_font(
+            "-*-courier-medium-r-normal-*-14-140-*-*-*-*-iso8859-*")        
+
+        vbox.show_all()
+        self.history_list = histlist
+        self.text = text
+        self.prompt = prompt
+
+        # Environment variables
+        self.prompt_state = 0
+        self.history_buffer = []
+        self.history_pos = None
+
+        # watch for key presses such as returns and Ctrl-D
+        self.prompt.connect_after('key-press-event', self.echo)
+
+        # Setup actual python interpreter
+        self.interp = MyInteractiveConsole(text, inherit, self.history_list,
+                                           self.status_bar)
+
+        # Redefine stdout and stderr so they go to our console
+        sys.stdout = PseudoFile(self.interp, 'stdout')
+
+        # May 2003- commented out the stderr pseudofile- general
+        # openev errors probably shouldn't go to python shell.
+        #sys.stderr = PseudoFile(self.interp, 'stderr')
+
+        self.set_prompt()
+        self.interp.my_write( \
+            '      --== Interactive Python Interpreter ==--\n\n',
+            'title')
+
+        # Make sure that function to parse command lines is loaded.
+        self.interp.push('from pyshell import _run_command_line')
+
+        # Standalone property: 0 if shell launched
+        # from OpenEV or similar application; 1 if
+        # shell launched alone.  Shells that are
+        # standalone will connect to the gtk quit event.
+
+        if self.standalone != 0:
+            self.connect('delete-event',self.close)
+    
+    def preferences_cb(self,*args):
+        self.pref_gui_cb()
+
+    def launch_help_cb(self,*args):
+        PyshellHelpDialog()
+
+    def close(self,*args):
+        response = \
+                 GtkExtra.message_box( 'Confirmation',
+                                       'Are you sure you want to exit OpenEV Command Shell?',
+                                       ('Yes', 'No') )
+
+
+        if response == 'Yes':
+            import gview
+            gview.save_preferences() # save path preferences           
+            gview.app.quit()
+
+        return TRUE
+
+    def show_progress(self,percent,msg=None):
+        if self.status_bar is None:
+            return
+
+        self.interp.showProgress(percent,msg)
+
+        # Make sure that progress bar updates
+        # immediately
+        while events_pending():
+            mainiteration()
+
+    def echo(self, text, event, *args):
+        # Watch for Returns - Main processing loop here!!!!
+        if event.keyval == GDK.Return:
+            input = str(self.prompt.get_chars(0, -1))
+
+            # Remove any internal newlines.
+            input = string.replace(input,chr(10),"")
+            
+            # echo command in text dialog
+            self.append_text(input+chr(10))
+
+            # Pick out command, and strip trailing white space (newline)
+            command = string.rstrip(input[4:])
+
+            self.interp.clearFlags()
+            self.prompt_state = self.interp.push(command)
+
+            # Add command to history buffer
+            self.history_buffer.insert(0, command)
+
+            # Reset the prompt if needed, and history position
+            self.set_prompt()
+            self.history_pos = None
+            if self.history_list is not None:
+                self.history_list.unselect_all()
+            
+        # Watch for Ctrl-D
+        elif (event.keyval in (ord('d'), ord('D'))) \
+                 and (event.state & GDK.CONTROL_MASK):
+            input = str(self.prompt.get_chars(0, -1))
+            if len(input) == 4:
+                if self.standalone != 0:
+                    import gview
+                    gview.app.quit()
+                else:
+                    self.destroy()
+
+        # Up Arrow - back in history list
+        elif event.keyval == GDK.Up:
+            if len(self.history_buffer) > 0:
+                self.set_prompt()
+
+                if self.history_pos is None:
+                    self.history_pos = -1
+                    
+                if self.history_pos < len(self.history_buffer) - 1:
+                    self.history_pos = self.history_pos + 1
+                
+                self.prompt.insert(self.font, self.fg, self.bg, self.history_buffer[self.history_pos])
+
+                if self.history_list is not None:
+                    if self.history_pos < self.history_list.rows:
+                        self.history_list.select_row(self.history_pos,0)
+
+
+        # Down Arrow - forward in history list
+        elif event.keyval == GDK.Down:
+            if (self.history_pos is not None) and (self.history_pos > 0):
+                self.set_prompt()
+                self.history_pos = self.history_pos - 1
+                self.prompt.insert(self.font, self.fg, self.bg, self.history_buffer[self.history_pos])
+
+                # select relevant row in history list
+                if self.history_list is not None:
+                    if self.history_pos < self.history_list.rows:
+                        self.history_list.select_row(self.history_pos,0)
+                
+            elif (self.history_pos is not None) and (self.history_pos == 0):
+                self.set_prompt()
+                self.history_pos = None
+                if self.history_list is not None:
+                    self.history_list.unselect_all()
+
+        # Make sure we don't delete the prompt
+        elif (event.keyval == GDK.BackSpace) and (self.prompt.get_position() < 4):
+            self.prompt.insert(self.font, self.fg, self.bg, ' ')
+
+        # Watch user doesn't uses arrow keys to move into prompt
+        if self.prompt.get_position() < 5:
+            self.prompt.set_position(4)
+            
+        return TRUE
+
+    def list_clicked(self, lst, event):
+        if self.history_list is None:
+            return
+        
+        try:
+            row, col = lst.get_selection_info(int(event.x), int(event.y))
+        except:
+            return
+        
+        if event.button == 1:
+            lst.emit_stop_by_name('button-press-event')
+            self.set_prompt()
+            self.prompt.insert(self.font, self.fg, self.bg, lst.get_text(row,col))
+            self.history_list.select_row(row,col)
+            if len(self.history_buffer) >= row:
+                self.history_pos = row
+                
+    def append_text(self, msg):
+        self.interp.my_write( msg, 'command' )
+
+    def set_prompt(self):
+        if self.prompt_state == 0:
+            # Line dealt with in some way - reset prompt
+            self.prompt.delete_text(0, -1)
+            self.prompt.insert(self.font, self.fg, self.bg, '>>> ')
+        elif self.prompt_state == 1:
+            # More input required
+            self.prompt.delete_text(0, -1)
+            self.prompt.insert(self.font, self.fg, self.bg, '... ')
+        else:
+            raise RunTimeError, ' Should not get here - pyshell.py '
+
+    def command(self, line):
+        """ Takes single line commands, doesn't echo line to console, but output will """
+        
+        self.prompt_state = self.interp.push(line)
+
+    def add_command(self, command):
+        self.interp.cmdlist[string.lower(command.Name)] = command
+
+    def add_helpfile(self,helpfilename):
+        self.interp.add_helpfile(helpfilename)
+
+    def get_commands(self):
+        return list(self.interp.cmdlist.values())
+
+    def hide_dialog_cb(self,*args):
+        dialog = args[0]
+        dialog.hide()
+        return TRUE
+
+    def pref_gui_cb(self):
+        if self.path_dlg is None:
+            new_dlg = GtkWindow()
+            new_dlg.connect('delete-event',self.hide_dialog_cb)            
+            new_dlg.set_title('Paths')
+            new_dlg.set_usize(450,100)
+
+            self.path_dlg = new_dlg
+
+            path_keys = ['MODULE PATHS','COMMAND PATHS']
+            nrows = len(self.preferences.keys())+1
+            wtable = GtkTable(nrows,2,FALSE)
+            wtable.set_row_spacings(5)
+            wtable.set_col_spacings(5)
+            wtable.set_border_width(5)
+            new_dlg.add(wtable)
+
+
+            for idx in range(len(path_keys)):
+                startpath=self.preferences[path_keys[idx]]
+                clabel = GtkLabel(path_keys[idx])
+                wtable.attach(clabel, 0,1, idx,idx+1) 
+                centry = GtkEntry(maxlen=350)
+                # replace the path string with
+                # an entry object
+                self.preferences[path_keys[idx]] = centry
+                centry.set_editable(TRUE)
+                centry.set_usize(280, 25)
+                centry.set_text(startpath)
+                wtable.attach(centry, 1,2, idx,idx+1)
+
+            apply_button = GtkButton('Apply')
+            wtable.attach(apply_button, 1,2, nrows-1,nrows)
+            apply_button.connect('clicked',self._update_prefs_cb,'Paths')
+
+            wtable.show()        
+            self.path_dlg.show_all()
+
+        else:
+            self.path_dlg.show_all()
+            self.path_dlg.get_window()._raise()
+
+    def _update_prefs_cb(self,*args):
+        pref_type = args[1]
+        if pref_type == 'Paths':
+            modpaths = self.preferences['MODULE PATHS'].get_text()
+            commandpaths = self.preferences['COMMAND PATHS'].get_text()
+
+            import sys
+            import string
+            import gview
+
+            # Clear old settings to ''
+            # Note: old paths are not popped in case a
+            # path is shared (eg. if the user specifies
+            # OPENEVHOME as a path), since each path is
+            # only added once.
+        
+            for idx in range(1,MAX_MODULE_PATHS+1):
+                if gview.get_preference('pyshell_module_path'+str(idx)) is not None:
+                    gview.set_preference('pyshell_module_path'+str(idx),'')
+                    
+            for idx in range(1,MAX_COMMAND_PATHS+1):
+                if gview.get_preference('pyshell_command_path'+str(idx)) is not None:
+                    gview.set_preference('pyshell_command_path'+str(idx),'')
+
+            # Get new settings: paths are assumed to be separated
+            # by commas.
+            modtokens=string.split(modpaths,";")
+            idx=1
+            
+            for modpath in modtokens:
+                if len(modpath) == 0:
+                    continue
+
+                if (modpath not in sys.path) and (os.path.isdir(modpath)):
+                    sys.path.append(modpath)
+
+                if (os.path.isdir(modpath)):
+                    if idx < MAX_MODULE_PATHS+1:
+                        gview.set_preference('pyshell_module_path'+str(idx),modpath)
+                    else:
+                        gvutils.warning('Only the first'+str(MAX_MODULE_PATHS+1)+\
+                                        'module paths will be loaded from preferences.')
+                    idx=idx+1
+                else:
+                    import gvutils
+                    gvutils.warning(modpath+' does not exist or is not a directory.')
+
+            cmdtokens=string.split(commandpaths,";")
+            idx=1
+            for cmdpath in cmdtokens:
+                if len(cmdpath) == 0:
+                    continue
+
+                if (cmdpath not in sys.path) and (os.path.isdir(cmdpath)):
+                    sys.path.append(cmdpath)
+
+                if (os.path.isdir(cmdpath)):
+                    if idx < MAX_COMMAND_PATHS+1:
+                        gview.set_preference('pyshell_command_path'+str(idx),cmdpath)
+                    else:
+                        gvutils.warning('Only the first'+ str(MAX_COMMAND_PATHS+1)+\
+                                         ' command paths will be loaded from preferences.')
+                    idx=idx+1
+                    
+                else:
+                    import gvutils
+                    gvutils.warning(cmdpath+' does not exist or is not a directory.')
+
+            self.hide_dialog_cb(self.path_dlg)
+
+    def __get_standard_menu_entries(self):
+            # python shell menu entries that are available
+            # without the addition of tools (not exposed
+            # by default).
+            menucmd="self.menuf.add_entries(["+\
+                "('File/Preferences', None, self.preferences_cb ),"+\
+                "('File/Quit', '<control>D', self.close_cb),"+\
+                "('Help/Help',None,self.launch_help_cb)"+\
+                "])"
+            return menucmd
+        
+    def load_pyshell_file_from_xml(self,pyshellfile="DefaultPyshellFile.xml"):
+        import gview
+        import gdal
+    
+        pyshellfile=os.path.join(gview.home_dir,'xmlconfig',pyshellfile)
+        try:
+            raw_xml = open(pyshellfile).read()
+        except:
+            raise AttributeError,"Unable to load " + pyshellfile
+            return
+
+        tree = gdal.ParseXMLString( raw_xml )
+        if tree is None:
+            raise AttributeError,"Problem occured parsing pyshell file " + pyshellfile
+            return
+
+        if tree[1] != 'GViewAppPyshell':
+            raise AttributeError,"Root of %s is not GViewAppPyshell node " % iconfile
+            return
+
+        # Optional parts of python shell: menu, icon bar, history area, progress bar
+        # If not specified in the xml file, they will not be included.  Currently
+        # History area and progress bar are either present or not, with the []
+        # indicating that they should be included.  Later, preferences may or may not
+        # be added to customize them.  Menu and icon customization is similar to
+        # the main OpenEV shell.
+        menucmds=None
+        iconcmds=None
+        historycmds=None
+        progresscmds=None
+        for node in tree[2:]:
+            if node[1] == 'Menu':
+                menucmds=self.parse_menu_xml_node(node)
+            elif node[1] == 'Iconbar':
+                iconcmds=self.parse_icon_xml_node(node)
+            elif node[1] == 'History':
+                historycmds=[]
+            elif node[1] == 'Progress':
+                progresscmds=[]
+            else:
+                txt="Invalid node %s in pyshell file %s.\n" % node[1],pyshellfile
+                txt=txt+"Valid node types are Menu, Iconbar, History, and Progress."
+                raise AttributeError,txt
+
+        # This tuple may be extended at a later date    
+        return (menucmds,iconcmds,historycmds,progresscmds)
+
+    def parse_menu_xml_node(self,menunode):
+        import gview
+        
+        tools_to_include='All'
+        tools_accounted_for=[]
+        menu_list=[]
+        
+        for node in menunode[2:]:
+            if node[1] == 'entry':
+                node_path  = gvutils.XMLFind( node, 'path')
+                if node_path is None:
+                    raise AttributeError,"Invalid menu file format - missing path"
+                 
+                entry_type = gvutils.XMLFindValue( node_path, 'type', '')
+                entry_path = gvutils.XMLFindValue( node, 'path','')
+                
+                if (string.find(entry_path,"/") == -1):
+                    raise AttributeError,"Invalid menu file format - bad path:%s" % entry_path
+                    
+                if (entry_type != ''):
+                    entry_type = "<" + entry_type + ">"
+                path_split=string.split(entry_path,"/")
+                path_split[-1] = entry_type + path_split[-1]
+                entry_path=string.join(path_split,"/")
+
+                entry_accelerator = gvutils.XMLFindValue( node, 'accelerator', 'None')
+                if (entry_accelerator != 'None'):
+                    (key,mod) = string.split(entry_accelerator,'+')
+                    entry_accelerator = "'<" + key + ">" + mod + "'"
+
+                entry_callback = gvutils.XMLFindValue( node, 'callback', 'None')
+                entry= "("                                             \
+                        + string.join((entry_path,entry_accelerator,   \
+                                       entry_callback),",")
+
+                arguments = gvutils.XMLFind( node, 'arguments')
+                if arguments is not None:
+                    args_list = []
+                    args =  gvutils.XMLFind( arguments, 'arg','')
+                    if args is not None:
+                        for arg in args:
+                            args_list.append(gvutils.XMLFindValue( arg, '',''))
+                        entry = entry + "," + string.join(args_list,",")
+
+                entry = entry + ")"
+
+                menu_list.append(entry)
+                            
+            elif node[1] == 'tools':
+                tools_to_include=node[2][1]
+                
+            elif node[1] == 'simpletoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid menu file format - missing tool name"
+
+                if gview.app.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid menu file format- tool "+toolname+" not loaded."
+
+                ctool=gview.app.Tool_List[gview.app.tool_index[toolname]][1]
+                for cpath in ctool.pymenu_entries.entries.keys():
+                    # Ignore default position- overridden by position in file
+                    # Also note: tool callbacks don't have arguments
+                    entry_accelerator=ctool.pymenu_entries.entries[cpath][2]
+                    if entry_accelerator is None:
+                        entry_accelerator=str(None)
+                    else:
+                        entry_accelerator="'"+entry_accelerator+"'"
+                    
+                    entry= "("                                             \
+                            + string.join(("'"+cpath+"'",entry_accelerator,   \
+                           "gview.app.Tool_List[gview.app.tool_index['"+toolname+\
+                           "']][1].pymenu_entries.entries['"+cpath+"'][1]"),",")+")"
+                        
+                    menu_list.append(entry)
+                    
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+
+                    
+            elif node[1] == 'complextoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid menu file format - missing tool name"
+                
+                oldpath  = gvutils.XMLFindValue( node, 'oldpath')
+
+                if oldpath is None:
+                    txt="Invalid menu file format - complex tool entry\nrequires oldpath item."
+                    raise AttributeError,txt
+                oldpath = oldpath[1:-1] # Entries in XML file are surrounded by quotes- get rid of them
+                
+                newpath  = gvutils.XMLFindValue( node, 'newpath')
+                if newpath is None:
+                    txt="Invalid menu file format - complex tool entry\nrequires newpath item."
+                    raise AttributeError,txt
+                newpath = newpath[1:-1] # Entries in XML file are surrounded by quotes- get rid of them
+                
+                if gview.app.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid menu file format- tool "+toolname+" not loaded."
+
+                ctool=gview.app.Tool_List[gview.app.tool_index[toolname]][1]
+                if ctool.pymenu_entries.entries.has_key(oldpath) == 0:
+                    raise AttributeError,'Invalid menu file entry- tool '+toolname+\
+                          ' has no\nmenu entry '+oldpath
+
+                entry_accelerator=gvutils.XMLFindValue( node, 'accelerator' )
+                if entry_accelerator is None:
+                    entry_accelerator=ctool.pymenu_entries.entries[oldpath][2]
+                else:
+                    (key,mod)=string.split(entry_accelerator,'+')
+                    entry_accelerator="<"+key+">"+mod
+                    
+                if entry_accelerator is None:
+                    entry_accelerator=str(None)
+                else:
+                    entry_accelerator="'"+entry_accelerator+"'"
+                    
+                entry= "("                                             \
+                        + string.join(("'"+newpath+"'",entry_accelerator,   \
+                        "gview.app.Tool_List[gview.app.tool_index['"+toolname+\
+                        "']][1].pymenu_entries.entries['"+oldpath+"'][1]"),",") + ")"                
+                        
+                menu_list.append(entry)
+          
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+                    
+
+        if tools_to_include not in ['All','None','Some']:
+            raise AttributeError,"Invalid menu file format- <tool> entry should be All, None, or Some."
+
+        if ((tools_to_include == 'None') and (len(tools_accounted_for) > 0)):
+            txt = "Invalid menu file format- if <tool> entry is None,\nno "
+            txt = txt+"simpletoolentry or complextoolentry items may be specified."
+            raise AttributeError,txt
+
+        remaining_cmds=[]
+        if tools_to_include == 'All':
+            for citem in gview.app.Tool_List:
+                if citem[0] not in tools_accounted_for:
+                    ctool=citem[1]
+                    for centry in ctool.pymenu_entries.entries.keys():
+                        cpos=ctool.pymenu_entries.entries[centry][0]
+                        cpos=max(cpos,0)
+                        accel=ctool.pymenu_entries.entries[centry][2]
+                        if accel is None:
+                            accel=str(None)
+                        else:
+                            accel="'"+accel+"'"
+                            
+                        entry= "self.menuf.insert_entry(" \
+                                + string.join((str(cpos),"'"+centry+"'",accel,   \
+                               "gview.app.Tool_List[gview.app.tool_index['"+citem[0]+\
+                               "']][1].pymenu_entries.entries['"+centry+"'][1]"),",")+")"                
+                        remaining_cmds.append(entry)
+                        
+        # create the menu command to populate the entries
+        menu_cmds=[]
+        menu_cmd =  "self.menuf.add_entries([" + string.join(menu_list,',') + "])"
+        menu_cmds.append(menu_cmd)
+        if len(remaining_cmds) > 0:
+            menu_cmds.extend(remaining_cmds)
+
+        return menu_cmds        
+
+    def parse_icon_xml_node(self,iconnode):
+        import gview
+        
+        tools_to_include = 'All'
+        tools_accounted_for=[]
+        tool_entry_list=[]
+        icon_list=[]
+        for node in iconnode[2:]:
+            if node[1] == 'icon':
+                type = None
+                icon_label = gvutils.XMLFindValue( node, 'label','None')
+                icon_hint = gvutils.XMLFindValue( node, 'hint','None')
+                icon_callback = gvutils.XMLFindValue( node, 'callback','None')
+                icon_help = gvutils.XMLFindValue( node, 'help','None')
+                icon_file = gvutils.XMLFindValue( node, 'xpm','None')
+                # xpm files - need to add path and possible help
+                if (icon_file != 'None'):
+                    type = 'xpm'
+                    icon = "self.add_icon_to_bar("                           \
+                            + string.join((icon_file,icon_label,icon_hint,   \
+                                           icon_callback,icon_help),",")     \
+                            + ")" 
+
+                # pixmap files - not adding path or help 
+                icon_file = gvutils.XMLFindValue( node, 'pixmap','None')
+                if (icon_file!= 'None'):
+                    type = 'pixmap'
+                    icon = "self.iconbar.append_item("                        \
+                            + string.join((icon_label,icon_hint,icon_hint,    \
+                                              icon_file,icon_callback),",")   \
+                            + ")" 
+
+                # widget  
+                icon_file = gvutils.XMLFindValue( node, 'widget','None')
+                if (icon_file!= 'None'):
+                    type = 'widget'
+                    icon_file = gvutils.XMLFindValue( node, 'widget','None')
+                    icon = "self.iconbar.append_widget("                       \
+                            + string.join((icon_file,icon_hint,icon_hint),",") \
+                            + ")" 
+                # none of the above
+                if type is None:
+                    raise AttributeError,"Invalid icon file format - unknown type"
+
+                icon_list.append(icon)
+            elif node[1] == 'tools':
+                tools_to_include=node[2][1]
+            elif node[1] == 'simpletoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid pyshell file format - missing tool name"
+
+                if gview.app.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid pyshell file format- tool "+toolname+" not loaded."
+
+                ctool=gview.app.Tool_List[gview.app.tool_index[toolname]][1]
+                idx=0
+                for centry in ctool.pyicon_entries.entries:
+                    icon_file=centry[0]
+                    
+                    icon_label=centry[1]
+                    if icon_label is not None:
+                        icon_label="'"+icon_label+"'"
+                    else:
+                        icon_label=str(None)
+                    
+                    icon_hint=centry[2]
+                    if icon_hint is not None:
+                        icon_hint="'"+icon_hint+"'"
+                    else:
+                        icon_hint=str(None)
+                    
+                    # Ignore position- it is overridden by this entry's location in the
+                    # xml file
+                    icon_callback=centry[4]
+                    icon_help=centry[5]
+                    if icon_help is not None:
+                        icon_help="'"+icon_help+"'"
+                    else:
+                        icon_help=str(None)
+                     
+                    icon_type=centry[6]
+                    if icon_type == 'xpm':
+                        icon = "self.add_icon_to_bar("                           \
+                                + string.join(("'"+icon_file+"'",\
+                                icon_label,icon_hint,   \
+                                "gview.app.Tool_List[gview.app.tool_index['"+\
+                                toolname+"']][1].pyicon_entries.entries["+\
+                                str(idx)+"][4]",icon_help),",") + ")"
+                        icon_list.append(icon)
+                    else:
+                        raise AttributeError,"Invalid icon type "+icon_type+" in tool "+toolname+"."
+                    idx=idx+1
+                    
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+                    
+            elif node[1] == 'complextoolentry':
+                toolname  = gvutils.XMLFindValue( node, 'name')
+                if toolname is None:
+                    raise AttributeError,"Invalid icon file format - missing tool name."
+                
+                oindex  = gvutils.XMLFindValue( node, 'index')
+
+                if oindex is None:
+                    txt="Invalid icon file format - complex tool entry\n"+\
+                        "requires the index of the icon entry\n"
+                    txt=txt+"to replace (0...number of entries-1).\n"
+                    raise AttributeError,txt
+                try:
+                    oindex=int(oindex)
+                except:
+                    raise AttributeError,"Invalid icon file- icon index to replace must be an integer."
+                
+                if gview.app.tool_index.has_key(toolname) == 0:
+                    raise AttributeError,"Invalid icon file entry- tool "+toolname+" not loaded."
+
+                ctool=gview.app.Tool_List[gview.app.tool_index[toolname]][1]
+                if len(ctool.pyicon_entries.entries) < (oindex+1):
+                    txt='Invalid file file entry- for tool '+toolname+'.\n maximum entry index is '
+                    txt=txt+str(len(ctool.pyicon_entries.entries)-1)+'.' 
+
+                icon_file=gvutils.XMLFindValue( node, 'xpm')
+                icon_hint=gvutils.XMLFindValue( node, 'hint')
+                icon_label=gvutils.XMLFindValue( node, 'label')
+                icon_help=gvutils.XMLFindValue( node, 'help')
+
+                if icon_file is None:
+                    icon_file="'"+ctool.icon_entries.entries[oindex][0]+"'"
+                elif os.path.isfile(icon_file):
+                    if os.name == 'nt':
+                        icon_file="'"+string.replace(icon_file,"\\","\\\\")+"'"
+                    else:
+                        icon_file="'"+icon_file+"'"
+                elif os.path.isfile(os.path.join(gview.home_dir,'tools',icon_file)):
+                    icon_file="'"+os.path.join(gview.home_dir,'tools',icon_file)+"'"
+                    if os.name == 'nt':
+                        icon_file=string.replace(icon_file,"\\","\\\\")
+                elif os.path.isfile(os.path.join(gview.home_dir,'pics',icon_file)):
+                    icon_file="'"+os.path.join(gview.home_dir,'pics',icon_file)+"'"
+                    if os.name == 'nt':
+                        icon_file=string.replace(icon_file,"\\","\\\\")                
+                else:
+                    txt = "Cannot find file "+tempf+'.  Either the full\n'
+                    txt = txt+"path must be specified, or "+tempf+ " must be\n"
+                    txt = txt+"placed in the tools or pics directory."
+                    raise AttributeError,txt
+                
+
+                if icon_label is None:
+                    icon_label=ctool.pyicon_entries.entries[oindex][1]
+                    
+                if icon_label is not None:
+                    icon_label="'"+icon_label+"'" 
+                else:
+                    icon_label=str(None)
+                    
+                if icon_hint is None:
+                    icon_hint=ctool.pyicon_entries.entries[oindex][2]
+
+                if icon_hint is not None:
+                    icon_hint="'"+icon_hint+"'"
+                else:
+                    icon_hint=str(None)
+                    
+                if icon_help is None:
+                    icon_help=ctool.pyicon_entries.entries[oindex][5]
+
+                if icon_help is not None:
+                    icon_help="'"+icon_help+"'"
+                else:
+                    icon_help=str(None)
+
+                icon_callback=ctool.pyicon_entries.entries[oindex][4]
+                icon_type=ctool.pyicon_entries.entries[oindex][6]
+                if icon_type == 'xpm':
+                    icon = "self.add_icon_to_bar("                           \
+                            + string.join((icon_file,icon_label,icon_hint,   \
+                            "gview.app.Tool_List[gview.app.tool_index['"+\
+                            toolname+"']][1].pyicon_entries.entries["+\
+                            str(oindex)+"][4]",icon_help),",") + ")"
+                    icon_list.append(icon)
+                else:
+                    raise AttributeError,"Invalid icon type "+icon_type+" in tool "+toolname+"."
+                
+                if toolname not in tools_accounted_for:
+                    tools_accounted_for.append(toolname)
+
+
+        if tools_to_include not in ['All','None','Some']:
+            raise AttributeError,"Invalid pyshell file format- icon <tool>"+\
+                  " entry should be All, None, or Some."
+
+        if ((tools_to_include == 'None') and (len(tools_accounted_for) > 0)):
+            txt = "Invalid icon file format- if <tool> entry is None,\nno "
+            txt = txt+"simpletoolentry or complextoolentry items may be specified."
+            raise AttributeError,txt
+
+        if tools_to_include == 'All':
+            for citem in gview.app.Tool_List:
+                if citem[0] not in tools_accounted_for:
+                    ctool=citem[1]
+                    idx=0
+                    for centry in ctool.pyicon_entries.entries:
+                        if centry[6] != 'xpm':            
+                            raise AttributeError,"Error loading tool entry for tool "+\
+                                  citem[0]+"- icon type "+centry[6]+" invalid."
+
+                        icon_file="'"+centry[0]+"'"
+                        icon_label=centry[1]
+                        if icon_label is not None:
+                            icon_label="'"+icon_label+"'"
+                        else:
+                            icon_label=str(None)
+
+                        icon_hint=centry[2]    
+                        if icon_hint is not None:
+                            icon_hint="'"+icon_hint+"'"
+                        else:
+                            icon_hint=str(None)
+
+                        icon_help=centry[5]
+                        if icon_help is not None:
+                            icon_help="'"+icon_help+"'"
+                        else:
+                            icon_help=str(None)
+                            
+                        # Default position in icon bar used
+                        pos=centry[3]
+                        icon = "self.add_icon_to_bar(" +\
+                                string.join((icon_file,icon_label,icon_hint,   \
+                                "gview.app.Tool_List[gview.app.tool_index['"+citem[0]+\
+                                "']][1].pyicon_entries.entries["+\
+                                str(idx)+"][4]",icon_help),",") + ")"
+                      
+                        pos=max(pos,0)
+                        if pos > len(icon_list):
+                            icon_list.append(icon)
+                        else:
+                            icon_list.insert(pos,icon)
+                        idx=idx+1
+
+        return icon_list
+    
+    def add_icon_to_bar(self, filename, text, hint_text, cb, help_topic=None):
+        # Next line doesn't overwrite filename if it is already a full
+        # path (os.path.join is intelligent).
+        import gview
+        import gvhtml
+        if os.name == 'nt':
+            filename=string.replace(filename,"\\","\\\\")
+            
+        full_filename = os.path.join(gview.home_dir,'pics',filename)
+        pix, mask = create_pixmap_from_xpm(self,None,full_filename)
+        item = self.iconbar.append_item(text,hint_text, hint_text,
+                                        GtkPixmap(pix,mask), cb )
+        if help_topic is not None:
+            gvhtml.set_help_topic(item, help_topic)
+
+def parse_interpreter_line(line):
+        tokens = string.split( line, None, 1 )
+        
+        # Null input line is considered valid.
+        if len(tokens) == 0:
+            return ('', '')
+
+        if len(tokens) == 1:
+            return (string.lower(tokens[0]), '')
+        else:
+            return (string.lower(tokens[0]), tokens[1])
+
+
+class PyshellHelpDialog(GtkWindow):
+
+    def __init__(self):
+        GtkWindow.__init__(self)
+        self.set_title('Python Shell Help')
+        self.set_usize(500,500)
+        #gvhtml.set_help_topic( self, "pyshell_help.html" );
+
+        self.set_border_width(3)
+        vbox = GtkVPaned()
+        self.notebook = GtkNotebook()
+        self.add( vbox )
+        vbox.add1(self.notebook)
+
+
+        # Create text window for showing help for selected
+        # command/function
+        pixel_scroll = GtkScrolledWindow()
+        pixel_scroll.set_usize(496,250)
+        vbox.add2(pixel_scroll)
+
+        self.help_text = GtkText()
+        self.help_text.set_line_wrap(FALSE)
+        self.help_text.set_word_wrap(FALSE)
+        self.help_text.set_editable(FALSE)
+        pixel_scroll.add(self.help_text)
+        self.help_text.insert_defaults('')
+
+        # Number of columns to put in command and
+        # function CList's.
+        self.ncols_cmd=2
+        self.ncols_func=2
+        self.ncols_builtin=2
+
+        self.create_commandhelp()
+        self.create_functionhelp()
+        self.create_builtinhelp()
+        
+        self.show_all()
+        
+
+    def create_commandhelp(self):
+        self.cpane=GtkVBox(spacing=10)
+        self.cpane.set_border_width(10)
+        self.notebook.append_page(self.cpane,GtkLabel('Commands'))
+        self.cpane_scroll=GtkScrolledWindow()
+        self.cpane_scroll.set_usize(496,250)
+        self.cpane.pack_start(self.cpane_scroll)
+        
+        self.cpane_list=GtkCList(cols=self.ncols_cmd)
+        for idx in range(self.ncols_cmd):
+            self.cpane_list.set_column_width(idx,200)
+
+        self.cpane_list.set_selection_mode(SELECTION_SINGLE)
+        
+        self.cpane_scroll.add(self.cpane_list)
+
+        # Add items to the list based on interpreter
+        import gview
+        self.loaded_cmd_keys=gview.app.shell.interp.cmdlist.keys()
+        self.unloaded_cmd_keys=[]
+        for citem in gview.app.shell.interp.help_cmdtxt.keys():
+            if citem not in self.loaded_cmd_keys:
+                self.unloaded_cmd_keys.append(citem)
+
+        self.total_cmd_keys=[]
+        self.total_cmd_keys.extend(self.loaded_cmd_keys)
+        self.total_cmd_keys.extend(self.unloaded_cmd_keys)
+        self.total_cmd_keys.sort()
+
+        if ((len(self.total_cmd_keys) % self.ncols_cmd) == 0):
+            nrows=len(self.total_cmd_keys)/self.ncols_cmd
+        else:
+            nrows=len(self.total_cmd_keys)/self.ncols_cmd + 1
+
+        for crow in range(nrows):
+            if (crow < nrows-1) or ((len(self.total_cmd_keys) % self.ncols_cmd) == 0):
+                values=[]
+                for idx in range(self.ncols_cmd):
+                    values.append(self.total_cmd_keys[crow*self.ncols_cmd+idx])
+
+                self.cpane_list.append(tuple(values))
+            else:
+                values=[]
+                for idx in range(self.ncols_cmd):
+                    if ((crow*self.ncols_cmd)+idx < len(self.total_cmd_keys)):
+                        values.append(self.total_cmd_keys[crow*self.ncols_cmd+idx])
+                    else:
+                        values.append('')
+      
+                self.cpane_list.append(tuple(values))                
+
+        # Connections
+        self.cpane_list.connect('button-press-event',self.cmdlist_clicked_cb)
+
+    def cmdlist_clicked_cb(self, lst, event):
+
+        # Should also clear old text at this point
+        try:
+            row,col=lst.get_selection_info(int(event.x), int(event.y))
+        except:
+            return
+
+        if event.button == 1:
+            lst.emit_stop_by_name('button-press-event')
+            ckey=lst.get_text(row,col)
+            import gview
+            txt=gview.app.shell.interp.get_command_help(ckey)
+            if ckey in self.loaded_cmd_keys:
+                if txt is not None:
+                    txt='\t\t\t'+ckey+' (command)\n\n'+txt
+                else:
+                    txt='\t\t\t'+ckey+' (command)\n\nNo help available.'
+                    
+            else:
+                if txt is not None:
+                    txt='\t\t\t'+ckey+' (command- not loaded)\n\n'+txt
+                else:
+                    txt='\t\t\t'+ckey+' (command- not loaded)\n\nNo help available.'
+                    
+            self.update_text(txt)
+            
+    def create_functionhelp(self):
+        self.fpane=GtkVBox(spacing=10)
+        self.fpane.set_border_width(10)
+        self.notebook.append_page(self.fpane,GtkLabel('Functions'))
+        self.fpane_scroll=GtkScrolledWindow()
+        self.fpane_scroll.set_usize(396,250)
+        self.fpane.pack_start(self.fpane_scroll)
+        
+        self.fpane_list=GtkCList(cols=self.ncols_func)
+        for idx in range(self.ncols_func):
+            self.fpane_list.set_column_width(idx,200)
+
+        self.fpane_list.set_selection_mode(SELECTION_SINGLE)
+        
+        self.fpane_scroll.add(self.fpane_list)
+
+        # Add items to the list based on interpreter
+        import gview
+
+        # Need numeric to locate ufunc's 
+        import Numeric
+        
+        self.loaded_func_keys=[]
+        for ckey in gview.app.shell.interp.locals.keys():
+            if (type(gview.app.shell.interp.locals[ckey]) == type(launch)):
+                self.loaded_func_keys.append(ckey)
+            if (type(gview.app.shell.interp.locals[ckey]) == type(Numeric.logical_and)):
+                self.loaded_func_keys.append(ckey)
+
+        self.unloaded_func_keys=[]
+        for ckey in gview.app.shell.interp.help_functxt.keys():
+            if ckey not in self.loaded_func_keys:
+                self.unloaded_func_keys.append(ckey)
+
+        self.total_func_keys=[]
+        self.total_func_keys.extend(self.loaded_func_keys)
+        self.total_func_keys.extend(self.unloaded_func_keys)
+        self.total_func_keys.sort()
+
+        if ((len(self.total_func_keys) % self.ncols_func) == 0):
+            nrows=len(self.total_func_keys)/self.ncols_func
+        else:
+            nrows=len(self.total_func_keys)/self.ncols_func + 1
+
+        for crow in range(nrows):
+            if (crow < nrows-1) or ((len(self.total_func_keys) % self.ncols_func) == 0):
+                values=[]
+                for idx in range(self.ncols_func):
+                    values.append(self.total_func_keys[crow*self.ncols_func+idx])
+
+                self.fpane_list.append(tuple(values))
+            else:
+                values=[]
+                for idx in range(self.ncols_func):
+                    if ((crow*self.ncols_func)+idx < len(self.total_func_keys)):
+                        values.append(self.total_func_keys[crow*self.ncols_func+idx])
+                    else:
+                        values.append('')
+      
+                self.fpane_list.append(tuple(values))                
+
+        # Connections
+        self.fpane_list.connect('button-press-event',self.funclist_clicked_cb)
+
+    def funclist_clicked_cb(self, lst, event):
+
+        # Should also clear old text at this point
+        try:
+            row,col=lst.get_selection_info(int(event.x), int(event.y))
+        except:
+            return
+
+        if event.button == 1:
+            lst.emit_stop_by_name('button-press-event')
+            ckey=lst.get_text(row,col)
+            import gview
+            txt=gview.app.shell.interp.get_function_help(ckey)
+            if ckey in self.loaded_func_keys:
+                if txt is not None:
+                    txt='\t\t\t'+ckey+' (function)\n\n'+txt
+                else:
+                    txt='\t\t\t'+ckey+' (function)\n\nNo help available.\n'                    
+            else: 
+                if txt is not None:            
+                    txt='\t\t\t'+ckey+' (function- not loaded)\n\n'+txt
+                else:
+                    txt='\t\t\t'+ckey+' (function- not loaded)\n\nNo help available.\n'
+                    
+            self.update_text(txt)
+            
+    def create_builtinhelp(self):
+        self.bpane=GtkVBox(spacing=10)
+        self.bpane.set_border_width(10)
+        self.notebook.append_page(self.bpane,GtkLabel('Built-in Functions'))
+        self.bpane_scroll=GtkScrolledWindow()
+        self.bpane_scroll.set_usize(396,250)
+        self.bpane.pack_start(self.bpane_scroll)
+        
+        self.bpane_list=GtkCList(cols=self.ncols_builtin)
+        for idx in range(self.ncols_builtin):
+            self.bpane_list.set_column_width(idx,200)
+
+        self.bpane_list.set_selection_mode(SELECTION_SINGLE)
+        
+        self.bpane_scroll.add(self.bpane_list)
+
+        # Add items to the list based on interpreter
+        import gview
+        
+        self.loaded_builtin_keys=[]
+        for ckey in gview.app.shell.interp.locals.keys():
+            if (type(gview.app.shell.interp.locals[ckey]) == type(hasattr)):
+                self.loaded_builtin_keys.append(ckey)
+        for ckey in gview.app.shell.interp.locals['__builtins__'].keys():
+            if (type(gview.app.shell.interp.locals['__builtins__'][ckey]) == type(hasattr)):
+                self.loaded_builtin_keys.append(ckey)
+                                                  
+
+        self.unloaded_builtin_keys=[]
+        for ckey in gview.app.shell.interp.help_builtintxt.keys():
+            if ckey not in self.loaded_builtin_keys:
+                self.unloaded_builtin_keys.append(ckey)
+
+        self.total_builtin_keys=[]
+        self.total_builtin_keys.extend(self.loaded_builtin_keys)
+        self.total_builtin_keys.extend(self.unloaded_builtin_keys)
+        self.total_builtin_keys.sort()
+
+        if ((len(self.total_builtin_keys) % self.ncols_builtin) == 0):
+            nrows=len(self.total_builtin_keys)/self.ncols_builtin
+        else:
+            nrows=len(self.total_builtin_keys)/self.ncols_builtin + 1
+
+        for crow in range(nrows):
+            if (crow < nrows-1) or ((len(self.total_builtin_keys) % self.ncols_builtin) == 0):
+                values=[]
+                for idx in range(self.ncols_builtin):
+                    values.append(self.total_builtin_keys[crow*self.ncols_builtin+idx])
+
+                self.bpane_list.append(tuple(values))
+            else:
+                values=[]
+                for idx in range(self.ncols_builtin):
+                    if ((crow*self.ncols_builtin)+idx < len(self.total_builtin_keys)):
+                        values.append(self.total_builtin_keys[crow*self.ncols_builtin+idx])
+                    else:
+                        values.append('')
+      
+                self.bpane_list.append(tuple(values))                
+
+        # Connections
+        self.bpane_list.connect('button-press-event',self.builtin_clicked_cb)
+
+    def builtin_clicked_cb(self, lst, event):
+
+        # Should also clear old text at this point
+        try:
+            row,col=lst.get_selection_info(int(event.x), int(event.y))
+        except:
+            return
+
+        if event.button == 1:
+            lst.emit_stop_by_name('button-press-event')
+            ckey=lst.get_text(row,col)
+            import gview
+            txt=gview.app.shell.interp.get_builtin_help(ckey)
+            if ckey in self.loaded_builtin_keys:
+                if txt is not None:
+                    txt='\t\t\t'+ckey+' (built-in function)\n\n'+txt
+                else:
+                    txt='\t\t\t'+ckey+' (built-in function)\n\nNo help available.\n'                    
+            else: 
+                if txt is not None:            
+                    txt='\t\t\t'+ckey+' (built-in function- not loaded)\n\n'+txt
+                else:
+                    txt='\t\t\t'+ckey+' (built-in function- not loaded)\n\nNo help available.\n'
+                    
+            self.update_text(txt)
+                 
+    def update_text(self,txt):
+        self.help_text.freeze()
+        del_len=self.help_text.get_length()
+        self.help_text.backward_delete(del_len)
+        self.help_text.insert_defaults(txt+'\0')
+        self.help_text.thaw()
+
+
+def _format_doc(doc_string):
+    """Reformat docstring for help display."""
+    
+    if doc_string is None:
+        return 'None'
+
+    dsplit=string.split(doc_string,'\n')
+    if len(dsplit) == 1:
+        return doc_string
+
+    # if __doc__ has more than one line,
+    # all except first will be indented.
+    # Get rid of this indent.
+
+    indent=0
+    for cline in dsplit[1:]:
+        temp=string.lstrip(cline)
+        if len(temp) > 0:
+            indent=len(cline)-len(temp)
+            break
+
+    new_doc=dsplit[0]
+    for cline in dsplit[1:]:
+        new_doc=string.join([new_doc,cline[indent:]],'\n')
+
+    return new_doc
+    
+
+def _parse_cmdhelp_text(txt):
+    """ Parse command help text into module, group, html, text.
+
+        Returns (Module, Group, Html, help_text) if command
+        help is successfully parsed; None if parsing
+        fails.  Module and Group are required.  Html is
+        an optional reference to an Html file. help_text
+        is the actual help text.
+
+        Command help entries should be of the form:
+
+        COMMAND_NAME=my_command
+        Module: my_module
+        Group: my_group
+        Html: my_html.html
+        
+        documentation...
+
+        The entries must be in that order if all are present,
+        but Html may be omitted.  If Html or documentation
+        are not present, None will be returned in their place.
+    """
+
+    mname=None
+    stxt=string.split(txt,'\n')
+
+    if len(stxt) < 2:
+        return None
+    
+    # first line should be module
+    mline=stxt[0]
+    if ((len(mline) > 7) and (mline[:7] == 'Module:')):
+        mname=string.strip(mline[7:])
+    else:
+        return None
+
+    # second line should be group
+    gline=stxt[1]
+    if ((len(gline) > 6) and (gline[:6] == 'Group:')):
+        gname=string.strip(gline[6:])
+    else:
+        return None
+
+    if len(stxt) == 2:
+        return (mname,gname,None,None)
+
+    # third line MAY be html
+    hline=stxt[2]
+    if ((len(hline) > 5) and (hline[:5] == 'Html:')):
+        hname=string.strip(hline[5:])
+        if len(stxt) > 3:
+            htext=string.join(stxt[3:],'\n')
+        else:
+            htext=None
+
+        return (mname,gname,hname,htext)
+    else:
+        htext=string.join(stxt[2:],'\n')
+        return (mname,gname,None,htext)
+
+
+def _parse_funchelp_text(txt):
+    """ Parse function help text into module, html, text.
+
+        Returns (Module, Html, help_text) if function
+        help is successfully parsed; None if parsing
+        fails.  Module is required.  Html is
+        an optional reference to an Html file. help_text
+        is the actual help text.
+
+        Function help entries should be of the form:
+
+        FUNCTION_NAME=my_function
+        Module: my_module
+        Html: my_html.html
+        
+        documentation...
+
+        or
+
+        BUILTIN_NAME=my_builtin_function
+        Module: my_module
+        Html: my_html.html
+        
+        documentation...
+        
+        The entries must be in that order if all are present,
+        but Html may be omitted.  If Html or documentation
+        are not present, None will be returned in their place.
+    """
+
+    mname=None
+    stxt=string.split(txt,'\n')
+
+    if len(stxt) < 1:
+        return None
+    
+    # first line should be module
+    mline=stxt[0]
+    if ((len(mline) > 7) and (mline[:7] == 'Module:')):
+        mname=string.strip(mline[7:])
+    else:
+        return None
+
+    if len(stxt) == 1:
+        return (mname,None,None)
+
+    # second line MAY be html
+    hline=stxt[1]
+    if ((len(hline) > 5) and (hline[:5] == 'Html:')):
+        hname=string.strip(hline[5:])
+        if len(stxt) > 2:
+            htext=string.join(stxt[2:],'\n')
+        else:
+            htext=None
+
+        return (mname,hname,htext)
+    else:
+        htext=string.join(stxt[1:],'\n')
+        return (mname,None,htext)
+
+    
+    
+def _run_command_line(line):
+    # This part is very hackish, though consistent with
+    # how openev transfers information between modules
+    # in other cases (eg. main app).  In order to allow the
+    # function to change the value of a variable in the
+    # interpreter, use the fact that the shell is
+    # stored globally under the name "gview.app.shell",
+    # and the interpreter is stored as "interp" beneath it.
+    # and update it's "locals" dictionary to fill in the
+    # needed values.
+
+    import gview     
+    import gvcommand
+    cc=gvcommand.CommandContext(line,gview.app.shell.interp.cmdlist,gview.app.shell.interp)
+    return_val = cc.execute()
+    # return_val will be 0 or 1 for simple commands; 0 
+    # for failure, 1 for success.  If the arguments were
+    # modify-in-place, the return value will be a tuple
+    # consisting of (0 or 1, {} [,options]).  Here, 0 or 1
+    # indicates success or failure as before, {} is a
+    # dictionary of variables to set in the command shell
+    # (keyword is the variable name, value is the value)-
+    # this dictionary can be empty.  the options part is
+    # not implemented yet, but might be later on if
+    # more flexibility is needed.  It should always be
+    # optional though.
+
+    if (type(return_val) == type(())) and (len(return_val) > 1):
+
+        for ckey in return_val[1].keys():
+            if ckey is not 'None':
+                gview.app.shell.interp.locals[ckey]=return_val[1][ckey]
+    
+
+if __name__ == '__main__':
+    app = Shell()
+    app.show_all()
+    
+    GtkExtra.debug_main_quit()
+    mainloop()
+


Property changes on: packages/openev/branches/upstream/current/pymod/pyshell.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/scmexpr.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/scmexpr.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/scmexpr.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+import string
+import types
+# get the fastest implementation of StringIO
+try:
+        from cStringIO import StringIO
+except ImportError:
+        from StringIO import StringIO
+
+trans = [' '] * 256
+for i in range(256):
+        if chr(i) in string.letters + string.digits + '_':
+                trans[i] = chr(i)
+        else:
+                trans[i] = '_'
+trans = string.join(trans, '')
+
+def parse(fp):
+        stack = [()]
+        line = fp.readline()
+        while line:
+                while line:
+                        line = string.lstrip(line)
+                        if not line:
+                                break
+                        elif line[0] == '(':
+                                stack.append(())
+                                line = line[1:]
+                        elif line[0] == ')':
+                                closed = stack[-1]
+                                del stack[-1]
+                                stack[-1] = stack[-1] + (closed,)
+                                line = line[1:]
+                        elif line[0] == '"':
+                                pos = string.index(line[1:], '"')
+                                stack[-1] = stack[-1] + (eval(line[:pos+2]),)
+                                line = line[pos+2:]
+                        elif line[0] in string.digits:
+                                str = ""
+                                while line and line[0] in "0123456789+-.":
+                                        str = str + line[0]
+                                        line = line[1:]
+                                stack[-1] = stack[-1] + (string.atof(str),)
+                        elif line[0] == ';':
+                                break
+                        else:
+                                str = ""
+                                while line and line[0] not in " \t();\n":
+                                        str = str + line[0]
+                                        line = line[1:]
+                                stack[-1] = stack[-1] + (str,)
+                line = fp.readline()
+        if len(stack) != 1:
+                raise IOError, "parentheses don't match"
+        return stack[0]
+
+class Parser:
+        def __init__(self, arg):
+                """Argument is either a string, a parse tree, or file object"""
+                if type(arg) == types.StringType:
+                        self.filename = arg
+                        self.parseTree = parse(open(arg))
+                elif type(arg) == types.TupleType:
+                        self.filename = '<none>'
+                        self.parseTree = arg
+                elif type(arg) == types.FileType:
+                        self.filename = arg.name
+                        self.parseTree = parse(arg)
+                else:
+                        raise TypeError, \
+                              'second arg must be string, tuple or file'
+        def startParsing(self, tuples=None):
+                if tuples == None: tuples = self.parseTree
+                for tup in tuples:
+                        self.handle(tup)
+        def handle(self, tup):
+                cmd = string.translate(tup[0], trans)
+                if hasattr(self, cmd):
+                        apply(getattr(self, cmd), tup[1:])
+                else:
+                        self.unknown(tup)
+        def unknown(self, tup):
+                pass
+
+_testString = """; a scheme file
+(define-func gdk_font_load    ; a comment at end of line
+  GdkFont
+  ((string name)))
+
+(define-boxed GdkEvent
+  gdk_event_copy
+  gdk_event_free
+  "sizeof(GdkEvent)")
+"""
+
+if __name__ == '__main__':
+        import sys
+        if sys.argv[1:]:
+                fp = open(sys.argv[1])
+        else:
+                fp = StringIO(_testString)
+        statements = parse(fp)
+        for s in statements:
+                print `s`

Added: packages/openev/branches/upstream/current/pymod/testmain.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/testmain.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/testmain.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+import gtk, gtkmissing, gview
+import layerdlg
+
+def create_view(name, plines, areas, raster):
+    win = gtk.GtkWindow()
+    win.set_title(name)
+    win.set_policy(gtk.TRUE, gtk.TRUE, gtk.FALSE)
+
+    shell = gtk.GtkVBox()
+    win.add(shell)
+    
+    view = gview.GvViewArea()
+    view.size(512,512)
+    shell.pack_start(view, expand=gtk.TRUE)
+    
+    if raster:
+        raster_layer = gview.GvRasterLayer(raster)
+        view.add_layer(raster_layer)
+    view.add_layer(gview.GvPqueryLayer())
+    view.add_layer(gview.GvAreaLayer(areas))
+    view.add_layer(gview.GvLineLayer(plines))
+
+    statusbar = gtk.GtkHBox()
+    shell.pack_start(statusbar, expand=gtk.FALSE)
+    label = gtk.GtkLabel()
+    statusbar.pack_start(label, expand=gtk.FALSE, padding=3)
+    tracker = gview.GvTrackTool(label)
+    tracker.activate(view)
+
+    view.connect('key-press-event', testmain_button_press)
+    
+    win.show_all()
+    view.grab_focus()
+    win.connect('delete-event', gtk.mainquit)
+    gtk.quit_add_destroy(1, win)
+    return view
+
+def testmain_button_press(view, event, *args):
+    global last_group
+    
+    if event.string == 'g':
+        last_group = gview.undo_start_group();
+        
+    if event.string == 'e':
+        gview.undo_end_group(last_group);
+
+    if event.string == 'p':
+        view.print_to_file( 425, 550, "out.tif", "GTiff" )
+        view.print_postscript_to_file( 425, 550, 1, "out.ps" )
+
+class Toolbar(gtk.GtkWindow):
+    def __init__(self):
+        gtk.GtkWindow.__init__(self)
+
+        toolbox = gview.GvToolbox()        
+        toolbox.add_tool("select", gview.GvSelectionTool())
+        toolbox.add_tool("line", gview.GvLineTool())
+        toolbox.add_tool("area", gview.GvAreaTool())
+        toolbox.add_tool("node", gview.GvNodeTool())
+        toolbox.add_tool("point", gview.GvPointTool())
+        toolbox.add_tool("roi", gview.GvRoiTool())
+        
+        toolbar = gtk.GtkToolbar(gtk.ORIENTATION_VERTICAL, gtk.TOOLBAR_TEXT)
+        self.add(toolbar)
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, None,
+                                     'Select', 'Selection tool',
+                                     None, None, self.toggle, "select")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Query Pnt', 'Query point marking tool',
+                                     None, None, self.toggle, "point")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw Line', 'Line drawing tool',
+                                     None, None, self.toggle, "line")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw Area', 'Area drawing tool',
+                                     None, None, self.toggle, "area")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Edit Node', 'Node edit tool',
+                                     None, None, self.toggle, "node")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, but,
+                                     'Draw ROI', 'ROI drawing tool',
+                                     None, None, self.toggle, "roi")
+        but = toolbar.append_element(gtk.TOOLBAR_CHILD_TOGGLEBUTTON, None,
+                                     'Link Views', 'Link views together',
+                                     None, None, self.link)
+        
+        self.connect('delete-event', gtk.mainquit)
+        toolbar.show()
+        self.show()
+        self.toolbox = toolbox
+        self.toolbar = toolbar
+        self.link = gview.GvViewLink()
+        
+    def toggle(self, but, data):
+        self.toolbox.activate_tool(data)
+        
+    def link(self, but):
+        but = gtk.GtkToggleButton(_obj=but)
+        if (but.active):
+            self.link.enable()
+        else:
+            self.link.disable()
+    def add_view(self, view):
+        self.toolbox.activate(view)
+        self.link.register_view(view)
+
+def setup(numwin, fname=None):
+    plines = gview.GvPolylines()
+    plines.set_name("Some lines")
+    gview.undo_register(plines)
+
+    areas = gview.GvAreas()
+    areas.set_name("Some areas")
+    gview.undo_register(areas)
+
+    if fname:
+        raster = gview.GvRaster(fname,sample=gview.SMAverage)
+        raster.set_name(fname)
+    else:
+        raster = None
+            
+    toolbar = Toolbar()
+    ldlg = layerdlg.LayerDlg()
+    ldlg.connect('destroy', gtk.mainquit)
+    ldlg.show()
+
+    for i in range(numwin):
+        name = 'View %d' % (i+1)
+        view = create_view(name, plines, areas, raster)
+        toolbar.add_view(view)
+        ldlg.add_view(name, view)
+
+if __name__ == '__main__':
+    try:
+        import sys
+        numwin = eval(sys.argv[1])
+    except:
+        numwin = 1
+    if len(sys.argv) > 2:
+        raster = sys.argv[2]
+    else:
+        raster = None
+    setup(numwin, raster)
+    gtk.mainloop()


Property changes on: packages/openev/branches/upstream/current/pymod/testmain.py
___________________________________________________________________
Name: svn:executable
   + 

Added: packages/openev/branches/upstream/current/pymod/toolexample.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/toolexample.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/toolexample.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,968 @@
+#
+# Tool designed to be added to gviewapp's self.Tool_List
+# To try: openev -t <full path to toolfile_example.txt>
+# Assumes that gviewapp at least has a toolbar with 
+# roi and poi tools, a layer manager, and view manager.
+
+import gviewapp
+import gtk
+import os
+import gdal, gdalnumeric
+import Numeric
+import string
+import gvsignaler
+
+FALSE = gtk.FALSE
+TRUE = gtk.TRUE
+import gview,gvutils
+
+
+class StoredROIPOI:
+    def __init__(self):
+        self.roi = (1,1,1,1)
+        self.poi = (1,1) # pixel/line in raster coords
+
+        # View/layer used to select poi/roi
+        self.roi_layer = None
+        self.roi_view = None
+        self.poi_layer = None
+        self.poi_view = None
+
+    def update_roi(self,roi_info,roilayer,roiview):
+        self.roi = roi_info
+        self.roi_layer = roilayer
+        self.roi_view = roiview
+
+    def update_poi(self,poi_info,poilayer,poiview):
+        self.poi = poi_info
+        self.poi_layer = poilayer
+        self.poi_view = poiview
+
+
+class GeneralPOITool(gviewapp.Tool_GViewApp):
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.RP_Stored = StoredROIPOI()
+
+        # relegate initialization of dialog, menu
+        # and connections to functions so they 
+        # can be customized
+        self.init_dialog()
+        self.init_menu()
+        self.init_connections()
+
+    def init_dialog(self):
+        self.RP_ToolDlg = General_POIToolDlg()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/POI Analysis Tool",1,self.roipoitool_cb)
+
+    def init_connections(self):
+        self.app.toolbar.poi_tool.connect('poi-changed',self.update_poi_cb)
+        self.RP_ToolDlg.subscribe('re-activated',self.update_dlgpoi_frame)
+        self.RP_ToolDlg.subscribe('poitool-needs-set',self.set_poitool)
+        self.RP_ToolDlg.subscribe('analyze-pressed',self.analyze_cb)
+
+    def roipoitool_cb(self, *args):
+        self.RP_ToolDlg.show_all()
+        self.RP_ToolDlg.make_active()
+        self.RP_ToolDlg.get_window()._raise()
+
+    def update_poi_cb(self, *args):
+        # always store last chosen poi
+        try:
+            poi_info = self.app.toolbar.get_poi()
+        except:
+            # if poi has been disabled (eg. if current selection
+            # mode is roi), leave it at the latest value
+            return       
+
+        # Returns the NAME of the view, and a COPY of the layer
+        [cview, clayer] = self.app.layerdlg.get_selected_layer()
+
+        if (cview is None) or (clayer is None):
+            # poi only makes sense in the context of a view and layer
+            return
+
+        # Get a reference to the active view
+        cur_view = self.app.layerdlg.get_active_view()
+        if (cur_view.get_raw(clayer) == 0):
+            # View is not in row/col coords-- convert
+            [pixel,line] = clayer.view_to_pixel(poi_info[0],poi_info[1])
+            poi_info = (pixel,line)
+
+        self.RP_Stored.update_poi(poi_info,clayer,cview)
+       # if tooldlg is active, update its region display frame
+        if self.RP_ToolDlg.is_active():
+            self.update_dlgpoi_frame()
+
+    def update_dlgpoi_frame(self, *args):
+        if self.RP_ToolDlg.is_active():       
+            poi_info = self.RP_Stored.poi
+            self.RP_ToolDlg.update_poiframe(poi_info)
+            if self.RP_ToolDlg.is_auto_updating():
+                # Automatic analysis is on
+                self.RP_ToolDlg.analyze_cb(self)
+
+        else:
+            return
+
+    def set_poitool(self, *args):
+        self.app.toolbar.poi_button.set_active(gtk.TRUE)
+
+    def analyze_cb(self,*args):
+        line = self.RP_ToolDlg.entry_dict['line'].get_text()
+        pix = self.RP_ToolDlg.entry_dict['pixel'].get_text()
+        print 'In poi tool analyze_cb- POI is: ',line,'L ',pix,'P'
+
+
+class GeneralROITool(gviewapp.Tool_GViewApp):
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.RP_Stored = StoredROIPOI()
+
+        # relegate initialization of dialog, menu
+        # and connections to functions so they 
+        # can be customized
+        self.init_dialog()
+        self.init_menu()
+        self.init_connections()
+
+    def init_dialog(self):
+        self.RP_ToolDlg = General_ROIToolDlg()
+  
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/ROI Analysis Tool",1,self.roipoitool_cb)
+
+    def init_connections(self):
+        self.app.toolbar.roi_tool.connect('roi-changed',self.update_roi_cb)
+        self.RP_ToolDlg.subscribe('re-activated',self.update_dlgroi_frame)
+        self.RP_ToolDlg.subscribe('roitool-needs-set',self.set_roitool)
+        self.RP_ToolDlg.subscribe('analyze-pressed',self.analyze_cb)
+
+    def roipoitool_cb(self, *args):
+        self.RP_ToolDlg.show_all()
+        self.RP_ToolDlg.make_active()
+        self.RP_ToolDlg.get_window()._raise()
+
+    def update_roi_cb(self, *args):
+        # always store last chosen roi
+        try:
+            roi_info = self.app.toolbar.get_roi()
+        except:
+            # if roi has been disabled (eg. if current selection
+            # mode is poi), leave it at the latest value
+            return       
+
+        [cview, clayer] = self.app.layerdlg.get_selected_layer()
+        
+        if (cview is None) or (clayer is None):
+            # roi only makes sense in the context of a view and layer
+            return
+
+        # Get a reference to the active view
+        cur_view = self.app.layerdlg.get_active_view()
+        if (cur_view.get_raw(clayer) == 0):
+            # View is not in row/col coords-- convert
+            # Note that region will not be exactly the one drawn,
+            # since a rectangle is extracted rather than a 
+            # trapezoid.
+
+            [pixel,line] = clayer.view_to_pixel(roi_info[0],roi_info[1])
+            [pixel2,line2] = clayer.view_to_pixel(roi_info[0]+roi_info[2],
+                                                  roi_info[1]+roi_info[3])
+            if pixel > pixel2:
+                temp = pixel
+                pixel = pixel2
+                pixel2 = temp
+
+            if line > line2:
+                temp = line
+                line = line2
+                line2 = temp
+
+            roi_info = (pixel,line,pixel2-pixel,line2-line)
+  
+        self.RP_Stored.update_roi(roi_info,clayer,cview)
+        # if tooldlg is active, update its region display frame
+        if self.RP_ToolDlg.is_active():
+            self.update_dlgroi_frame()
+
+    def update_dlgroi_frame(self, *args):
+        if self.RP_ToolDlg.is_active():       
+            roi_info = self.RP_Stored.roi
+            self.RP_ToolDlg.update_roiframe(roi_info)
+            if self.RP_ToolDlg.is_auto_updating():
+                # Automatic analysis is on
+                self.RP_ToolDlg.analyze_cb(self)
+
+        else:
+            return
+
+    def set_roitool(self, *args):
+        self.app.toolbar.roi_button.set_active(gtk.TRUE)
+
+    def analyze_cb(self,*args):
+        line = self.RP_ToolDlg.entry_dict['start_line'].get_text()
+        pix = self.RP_ToolDlg.entry_dict['start_pix'].get_text()
+        sl =  self.RP_ToolDlg.entry_dict['num_lines'].get_text()
+        sp =  self.RP_ToolDlg.entry_dict['num_pix'].get_text()
+        print 'In roi tool analyze_cb- ROI is: ',line,'L ',pix,'P (',sl,'x',sp,')'
+        
+
+class General_ROIPOIToolDlg(gtk.GtkWindow,gvsignaler.Signaler):
+    # A base class that has 3 frames: a 
+    # top one with a "needs updating" string
+    # and Analyze button; a middle one
+    # that will display either an roi or
+    # a poi; and an ending one with 2 toggles
+    # and a Set Tool button.
+    def __init__(self):
+        gtk.GtkWindow.__init__(self)
+
+        self.button_dict = {}  # store references to buttons/frames/text entries--
+        self.frame_dict = {}   # we may need to access them.
+        self.entry_dict = {}
+
+        self.show_list = []    # list of widgets to show
+
+        self.init_setup_window()
+        self.init_create_gui_panel()        # base widgets/panel
+        self.init_customize_gui_panel()
+
+        # signals
+        self.publish('re-activated')
+        self.publish('roitool-needs-set')
+        self.publish('poitool-needs-set')
+        self.publish('analyze-pressed')
+ 
+        # Basic connections
+        self.button_dict['Analyze'].connect('clicked',self.analyze_cb)
+        self.button_dict['Activate'].connect('toggled',self.activate_toggled)
+        self.button_dict['Auto Update'].connect('toggled',self.auto_update_toggled)
+        self.button_dict['Set Tool'].connect('clicked',self.set_tool_cb)
+
+        # Set default sensitivities
+        self.button_dict['Auto Update'].set_active(gtk.FALSE)
+        self.button_dict['Analyze'].set_sensitive(gtk.TRUE)
+        self.button_dict['Activate'].set_active(gtk.FALSE)
+
+        self.connect('delete-event',self.close) 
+
+
+        for item in self.show_list:
+            item.show()
+
+    def init_setup_window(self):
+        self.set_title('General Tool')
+        self.set_border_width(10)
+        self.set_usize(450,300)
+
+    def init_create_gui_panel(self):
+
+        # Basic Buttons...
+        self.button_dict['Analyze'] = gtk.GtkButton('Analyze')
+        self.show_list.append(self.button_dict['Analyze'])
+
+        self.button_dict['Activate'] = gtk.GtkCheckButton('Activate')
+        self.show_list.append(self.button_dict['Activate'])
+
+        self.button_dict['Auto Update'] = gtk.GtkCheckButton('Auto Update')
+        self.show_list.append(self.button_dict['Auto Update'])
+
+        self.button_dict['Set Tool'] = gtk.GtkButton('Set Tool')
+        self.show_list.append(self.button_dict['Set Tool'])
+
+        # Basic Frames...
+
+        # first frame
+        self.frame_dict['base_frame1'] = gtk.GtkFrame()
+        self.show_list.append(self.frame_dict['base_frame1'])
+
+        hbox1 = gtk.GtkHBox(gtk.TRUE,5)
+        self.show_list.append(hbox1)
+        hbox1.pack_end(self.button_dict['Analyze'],gtk.TRUE,gtk.TRUE,0)
+        self.frame_dict['base_frame1'].add(hbox1)
+
+        # second frame- will contain roi or poi info
+        self.frame_dict['base_frame2'] = gtk.GtkFrame()
+        self.show_list.append(self.frame_dict['base_frame2'])
+
+        # third frame
+        self.frame_dict['base_frame3'] = gtk.GtkFrame();
+        self.show_list.append(self.frame_dict['base_frame3'])
+
+        hbox3 = gtk.GtkHBox(gtk.TRUE,5)
+        self.show_list.append(hbox3)
+        hbox3.pack_start(self.button_dict['Activate'],gtk.TRUE,gtk.TRUE,0)
+        hbox3.pack_start(self.button_dict['Auto Update'],gtk.TRUE,gtk.TRUE,0)
+        hbox3.pack_start(self.button_dict['Set Tool'],gtk.TRUE,gtk.TRUE,0)
+        self.frame_dict['base_frame3'].add(hbox3)
+
+        # Top level panel...
+        self.main_panel = gtk.GtkVBox(gtk.FALSE,5)
+        self.main_panel.pack_start(self.frame_dict['base_frame1'],gtk.FALSE,gtk.FALSE,0)
+        self.main_panel.pack_start(self.frame_dict['base_frame2'],gtk.FALSE,gtk.FALSE,0)
+        self.main_panel.pack_end(self.frame_dict['base_frame3'],gtk.FALSE,gtk.FALSE,0)
+        self.add(self.main_panel)
+        self.show_list.append(self.main_panel)
+ 
+    def init_customize_gui_panel(self):
+        pass
+
+    def analyze_cb(self,*args):
+        self.notify('analyze-pressed')
+
+    def activate_toggled(self,*args):
+        if self.button_dict['Activate'].get_active():
+            self.set_entry_sensitivities(gtk.TRUE)
+            self.button_dict['Auto Update'].set_sensitive(gtk.TRUE)
+            self.button_dict['Auto Update'].set_active(gtk.FALSE)
+            self.button_dict['Analyze'].set_sensitive(gtk.TRUE)
+            self.button_dict['Set Tool'].set_sensitive(gtk.TRUE)
+            self.notify('re-activated')
+        else:
+            self.set_entry_sensitivities(gtk.FALSE)
+            self.button_dict['Auto Update'].set_active(gtk.FALSE)
+            self.button_dict['Auto Update'].set_sensitive(gtk.FALSE)
+            self.button_dict['Analyze'].set_sensitive(gtk.FALSE)
+            self.button_dict['Set Tool'].set_sensitive(gtk.FALSE)
+
+
+    def auto_update_toggled(self,*args):
+        # If auto-update is on, analyze button should not be sensitive
+        if self.button_dict['Auto Update'].get_active():
+            self.button_dict['Analyze'].set_sensitive(gtk.FALSE)
+        else:
+            self.button_dict['Analyze'].set_sensitive(gtk.TRUE)
+
+    def set_tool_cb(self,*args):
+        pass
+
+    def set_entry_sensitivities(self,bool_val):
+        pass
+
+    def is_active(self):
+        if self.button_dict['Activate'].get_active():
+            return gtk.TRUE
+        else:
+            return gtk.FALSE
+
+    def make_active(self):
+        self.set_tool_cb()
+        self.button_dict['Activate'].set_active(gtk.TRUE)
+
+    def is_auto_updating(self):
+        if self.button_dict['Auto Update'].get_active():
+            return gtk.TRUE
+        else:
+            return gtk.FALSE
+
+    def close(self,*args):
+        # If tool is closed, user probably wants to be rid of it...
+        self.button_dict['Activate'].set_active(gtk.FALSE)
+        self.hide()
+        return TRUE
+
+
+class General_ROIToolDlg(General_ROIPOIToolDlg):
+    def __init__(self):
+        General_ROIPOIToolDlg.__init__(self)
+
+    def init_setup_window(self):
+        self.set_title('General ROI Tool')
+        self.set_border_width(10)
+        self.set_usize(450,150)
+
+    def init_customize_gui_panel(self):    
+        # By now, main panel, basic frames, buttons have been created
+        # Middle frame (region display) must be filled in.
+
+        patch_table = gtk.GtkTable(2,4,FALSE)
+        self.show_list.append(patch_table)
+        self.frame_dict['base_frame2'].add(patch_table)
+
+        patch_table.set_border_width(5)
+        patch_table.set_col_spacings(5)
+        patch_table.set_col_spacing(1, 20)
+
+        label1 = gtk.GtkLabel('Start Line: ')
+        label1.set_alignment(0, 0.5)
+        patch_table.attach(label1, 0,1, 0, 1)
+
+        self.entry_dict['start_line'] = gtk.GtkEntry()
+        self.entry_dict['start_line'].set_editable(gtk.FALSE)
+        self.entry_dict['start_line'].set_usize(90, 25)
+        self.entry_dict['start_line'].set_text('1')
+        patch_table.attach(self.entry_dict['start_line'], 1,2, 0,1)
+
+        label2 = gtk.GtkLabel('Start Pixel: ')
+        label2.set_alignment(0, 0.5)
+        patch_table.attach(label2, 2,3,0, 1)
+
+        self.entry_dict['start_pix'] = gtk.GtkEntry()
+        self.entry_dict['start_pix'].set_editable(gtk.FALSE)
+        self.entry_dict['start_pix'].set_usize(90, 25)
+        self.entry_dict['start_pix'].set_text('1')
+        patch_table.attach(self.entry_dict['start_pix'], 3,4, 0,1)
+
+        label3 = gtk.GtkLabel('Num. of Lines: ')
+        label3.set_alignment(0, 0.5)
+        patch_table.attach(label3, 0,1, 1, 2)
+
+        self.entry_dict['num_lines'] = gtk.GtkEntry()
+        self.entry_dict['num_lines'].set_editable(gtk.FALSE)
+        self.entry_dict['num_lines'].set_usize(90, 25)
+        self.entry_dict['num_lines'].set_text('1')
+        patch_table.attach(self.entry_dict['num_lines'], 1,2, 1,2)
+
+        label4 = gtk.GtkLabel('Num. of Pixels: ')
+        label4.set_alignment(0, 0.5)
+        patch_table.attach(label4, 2,3,1, 2)
+
+        self.entry_dict['num_pix'] = gtk.GtkEntry()
+        self.entry_dict['num_pix'].set_editable(gtk.FALSE)
+        self.entry_dict['num_pix'].set_usize(90, 25)
+        self.entry_dict['num_pix'].set_text('1')
+        patch_table.attach(self.entry_dict['num_pix'], 3,4, 1,2)
+
+        # Create tooltips
+        self.tooltips = gtk.GtkTooltips()
+        tip_text = "Re-enable region-of-interest selection mode (" \
+                   "required for this tool's operation)."
+        self.tooltips.set_tip(self.button_dict['Set Tool'],tip_text)
+        tip_text = 'Automatically update every time the region of ' + \
+                   'interest changes.'
+        self.tooltips.set_tip(self.button_dict['Auto Update'],tip_text)
+        tip_text = 'Enable/Disable this tool.'
+        self.tooltips.set_tip(self.button_dict['Activate'],tip_text)
+        tip_text = 'Perform analysis.'
+        self.tooltips.set_tip(self.button_dict['Analyze'],tip_text)
+
+    
+        self.frame_dict['base_frame2'].show_all()
+
+    def set_tool_cb(self,*args):
+        self.notify('roitool-needs-set')
+
+
+    def update_roiframe(self,roi_info):
+        self.entry_dict['start_line'].set_text(str(int(roi_info[1])))
+        self.entry_dict['start_pix'].set_text(str(int(roi_info[0])))
+        self.entry_dict['num_lines'].set_text(str(int(roi_info[3])))
+        self.entry_dict['num_pix'].set_text(str(int(roi_info[2])))
+
+    def set_entry_sensitivities(self,bool_val):
+        self.entry_dict['start_line'].set_sensitive(bool_val)
+        self.entry_dict['start_pix'].set_sensitive(bool_val)
+        self.entry_dict['num_lines'].set_sensitive(bool_val)
+        self.entry_dict['num_pix'].set_sensitive(bool_val)
+
+
+
+class General_POIToolDlg(General_ROIPOIToolDlg):
+    def __init__(self):
+        General_ROIPOIToolDlg.__init__(self)
+
+    def init_setup_window(self):
+        self.set_title('General POI Tool')
+        self.set_border_width(10)
+        self.set_usize(450,120)
+
+    def init_customize_gui_panel(self):    
+        # By now, main panel, basic frames, buttons have been created
+        # Middle frame (region display) must be filled in.
+
+        patch_table = gtk.GtkTable(1,4,FALSE)
+        self.show_list.append(patch_table)
+        self.frame_dict['base_frame2'].add(patch_table)
+
+        patch_table.set_border_width(5)
+        patch_table.set_col_spacings(5)
+        patch_table.set_col_spacing(1, 20)
+
+        label1 = gtk.GtkLabel('Line: ')
+        label1.set_alignment(0, 0.5)
+        patch_table.attach(label1, 0,1, 0, 1)
+
+        self.entry_dict['line'] = gtk.GtkEntry()
+        self.entry_dict['line'].set_editable(gtk.FALSE)
+        self.entry_dict['line'].set_usize(90, 25)
+        self.entry_dict['line'].set_text('1')
+        patch_table.attach(self.entry_dict['line'], 1,2, 0,1)
+
+        label2 = gtk.GtkLabel('Pixel: ')
+        label2.set_alignment(0, 0.5)
+        patch_table.attach(label2, 2,3,0, 1)
+
+        self.entry_dict['pixel'] = gtk.GtkEntry()
+        self.entry_dict['pixel'].set_editable(gtk.FALSE)
+        self.entry_dict['pixel'].set_usize(90, 25)
+        self.entry_dict['pixel'].set_text('1')
+        patch_table.attach(self.entry_dict['pixel'], 3,4, 0,1)
+    
+        # Create tooltips
+        self.tooltips = gtk.GtkTooltips()
+        tip_text = "Re-enable point-of-interest selection mode (" + \
+                   "required for this tool's operation)."
+        self.tooltips.set_tip(self.button_dict['Set Tool'],tip_text)
+        tip_text = 'Automatically update every time the point of ' + \
+                   'interest changes.'
+        self.tooltips.set_tip(self.button_dict['Auto Update'],tip_text)
+        tip_text = 'Enable/Disable this tool.'
+        self.tooltips.set_tip(self.button_dict['Activate'],tip_text)
+        tip_text = 'Perform analysis.'
+        self.tooltips.set_tip(self.button_dict['Analyze'],tip_text)
+
+
+        self.frame_dict['base_frame2'].show_all()
+
+    def set_tool_cb(self,*args):
+        self.notify('poitool-needs-set')
+
+    def update_poiframe(self,poi_info):
+        self.entry_dict['line'].set_text(str(int(poi_info[1])))
+        self.entry_dict['pixel'].set_text(str(int(poi_info[0])))
+
+    def set_entry_sensitivities(self,bool_val):
+        self.entry_dict['line'].set_sensitive(bool_val)
+        self.entry_dict['pixel'].set_sensitive(bool_val)
+
+
+class Pixel_Tool(GeneralPOITool):
+    def __init__(self,app=None):
+        GeneralPOITool.__init__(self,app)
+
+    def init_dialog(self):
+        self.RP_ToolDlg = Pixel_ToolDlg()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Pixel Tool",1,self.roipoitool_cb)
+
+    def analyze_cb(self,*args):
+        # Find the view and layer
+        [cview, clayer] = self.app.layerdlg.get_selected_layer()
+        if (cview is None) or (clayer is None):
+            # analysis only makes sense in the context of a view and layer
+            gvutils.error('No View/Layer selected!')
+            return
+
+        text = self.basic_pixel_analysis(cview,clayer)
+
+        self.RP_ToolDlg.update_pixelinfo_text(text)
+
+    def basic_pixel_analysis(self,cview,clayer):
+        line = self.RP_ToolDlg.entry_dict['line'].get_text()
+        pix = self.RP_ToolDlg.entry_dict['pixel'].get_text()
+        disp_text = 'Pixel Attributes: \n'
+        fname = clayer.get_name()   # layer title == file name
+        disp_text = disp_text + '    Filename: ' + fname + '\n'
+        disp_text = disp_text + '    Line: ' + str(line) 
+        disp_text = disp_text + '  Pixel: ' + str(pix) + '\n'
+        try:
+            gdal_dataset = clayer.get_data().get_dataset()
+        except:
+            disp_text = disp_text + '\n\n<Not a raster layer>\n'
+            return disp_text
+
+        [long,lat]=clayer.get_data().pixel_to_georef(float(pix),float(line))
+        disp_text = disp_text + '    Latitude: ' + str(lat)
+        disp_text = disp_text + '    Longitude: ' + str(long) + '\n'
+  
+        try:
+            value = gdal_dataset.ReadAsArray(int(float(pix)),int(float(line)),1,1)
+        except:
+            disp_text = disp_text + '\n\n<Unable to read data>\n'
+            return disp_text
+
+        disp_text = disp_text +'\n    Value: ' + str(value[0,0].astype(value.typecode()))
+        return disp_text
+ 
+
+       
+
+class Pixel_ToolDlg(General_POIToolDlg):
+    def __init__(self):
+        General_POIToolDlg.__init__(self)
+
+    def init_setup_window(self):
+        self.set_title('Pixel Tool')
+        self.set_border_width(10)
+        self.set_usize(450,450)
+
+    def init_customize_gui_panel(self):
+        # Inherit all the usual stuff...
+        General_POIToolDlg.init_customize_gui_panel(self)
+
+        # Add new frame with pixel info, keeping track of
+        # the frame and text object...
+        self.frame_dict['pixel_info_frame'] = gtk.GtkFrame()
+        self.show_list.append(self.frame_dict['pixel_info_frame'])
+
+        pixel_vbox = gtk.GtkVBox()
+        self.show_list.append(pixel_vbox)
+        self.frame_dict['pixel_info_frame'].add(pixel_vbox)
+
+        pixel_scroll = gtk.GtkScrolledWindow()
+        self.show_list.append(pixel_scroll)
+        pixel_vbox.pack_start(pixel_scroll,expand = gtk.TRUE)
+
+        self.entry_dict['pixel_info_text'] = gtk.GtkText()
+        self.show_list.append(self.entry_dict['pixel_info_text'])
+        self.entry_dict['pixel_info_text'].set_line_wrap(gtk.FALSE)
+        self.entry_dict['pixel_info_text'].set_word_wrap(gtk.FALSE)
+        self.entry_dict['pixel_info_text'].set_editable(gtk.FALSE)
+        pixel_scroll.add(self.entry_dict['pixel_info_text'])
+        self.entry_dict['pixel_info_text'].insert_defaults('')
+        self.main_panel.pack_start(self.frame_dict['pixel_info_frame'],gtk.TRUE,gtk.TRUE,0)        
+
+    def update_pixelinfo_text(self,text):
+        del_len = self.entry_dict['pixel_info_text'].get_length()
+        self.entry_dict['pixel_info_text'].backward_delete(del_len)
+        self.entry_dict['pixel_info_text'].insert_defaults(text)
+
+
+
+class Stats_Tool(GeneralROITool):
+    def __init__(self,app=None):
+        GeneralROITool.__init__(self,app)
+
+        # Information needed for creating target view window
+        self.target_view_title = 'Stats Tool Target Area'
+        self.target_view_window = None
+        self.target_view_layer = None
+        self.target_view_data = None    # Numpy array of data
+        self.target_view_ds = None      # gdal dataset for target_view_data
+
+    def init_dialog(self):
+        self.RP_ToolDlg = Stats_ToolDlg()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Stats Tool",3,self.roipoitool_cb)
+
+    def update_dlgroi_frame(self,*args):
+        # Update frame as in general tool
+        if self.RP_ToolDlg.is_active():       
+            roi_info = self.RP_Stored.roi
+            self.RP_ToolDlg.update_roiframe(roi_info)
+
+        else:
+            return
+
+        self.update_roi_view()
+
+        if self.RP_ToolDlg.is_auto_updating():
+            # Automatic analysis is on
+            self.RP_ToolDlg.analyze_cb(self)
+
+    def update_roi_view(self,*args):
+        # Shouldn't get here if inactive anyway, but just in case...
+        if (self.RP_ToolDlg.is_active() == gtk.FALSE):
+            return
+
+        # Update view based on new frame values
+        line = int(float(self.RP_ToolDlg.entry_dict['start_line'].get_text()))
+        pix = int(float(self.RP_ToolDlg.entry_dict['start_pix'].get_text()))
+        sl =  int(float(self.RP_ToolDlg.entry_dict['num_lines'].get_text()))
+        sp =  int(float(self.RP_ToolDlg.entry_dict['num_pix'].get_text()))
+
+        # Find the view and layer
+        cview = self.app.view_manager.get_active_view_window().title
+        clayer = self.app.sel_manager.get_active_layer()
+        if (cview is None) or (clayer is None):
+            # Target can only be extracted if a view/layer is selected
+            return
+        if (sl == 0 or sp == 0):
+            print "Trying to extract region with zero lines or zero pixels!"
+            return
+        
+        try:
+            filename = clayer.get_parent().get_dataset().GetDescription()
+        except:
+            gvutils.error('Unable to determine filename!')
+            return
+
+        try:
+            target_data = gdalnumeric.LoadFile(filename,pix,line,sp,sl)
+            target_ds = gdalnumeric.OpenArray(target_data)
+            rl_mode_value = clayer.get_mode()
+        except:
+            gvutils.error('Unable to extract data and/or display mode info!')
+            return
+        
+        if self.target_view_window is not None:
+            # Need to delete old layer in target window and put in new one
+            if self.target_view_layer is not None:
+                self.target_view_window.viewarea.remove_layer(self.target_view_layer)
+        # Create new layer
+        self.target_view_data = target_data
+        self.target_view_ds = target_ds
+        self.target_view_layer = \
+             gview.GvRasterLayer(gview.GvRaster(dataset = self.target_view_ds,real=1),
+                                 rl_mode = rl_mode_value)
+
+        if ((rl_mode_value == gview.RLM_RGBA) and (self.target_view_ds.RasterCount > 2)):
+            green_raster = gview.GvRaster(dataset = self.target_view_ds,real=2)
+            blue_raster = gview.GvRaster(dataset = self.target_view_ds,real=3)
+
+            self.target_view_layer.set_source(1,green_raster)
+            self.target_view_layer.set_source(2,blue_raster)
+
+            if self.target_view_ds.RasterCount > 3:
+                band = self.target_view_ds.GetRasterBand(4)
+                if band.GetRasterColorInterpretation() == gdal.GCI_AlphaBand:
+                    self.target_view_layer.blend_mode_set( gview.RL_BLEND_FILTER )
+                    alpha_raster = gview.GvRaster(dataset = self.target_view_ds,real=4) 
+                    self.target_view_layer.set_source(3,alpha_raster)
+
+        self.target_view_layer.set_name(filename)
+        refresh_old_window = 0           # redraw roi first time
+        if self.target_view_window is None:
+            # Need to create a window to display the current target in
+            import gvviewwindow
+            self.target_view_window = gvviewwindow.GvViewWindow(self.app,
+                 title=self.target_view_title,show_menu = 0, show_icons=0,
+                 show_tracker=1,show_scrollbars=1)
+
+            self.target_view_window.connect('destroy',self.close_target_view_cb)
+            refresh_old_window = 1
+
+        self.target_view_window.viewarea.add_layer(self.target_view_layer)
+        self.target_view_window.show()
+
+        # The roi must be redrawn on the overview if a new window was created
+        # (it will have disappeared)
+        # HACK!!! 
+        if refresh_old_window == 1:
+            view_list = self.app.view_manager.get_views()
+            for item in view_list:
+                if (item.title == cview):
+                    self.app.view_manager.set_active_view(item)
+                    item.viewarea.set_active_layer(clayer)
+                    self.app.toolbar.roi_tool.append((pix,line,sp,sl))
+
+    def analyze_cb(self,*args):
+        # Find the view and layer
+        cview = self.app.view_manager.get_active_view_window().title
+        clayer = self.app.sel_manager.get_active_layer()
+        if (cview is None) or (clayer is None):
+            # analysis only makes sense in the context of a view and layer
+            gvutils.error('No View/Layer selected!')
+            return
+
+        text = self.basic_region_analysis(cview,clayer) 
+
+        self.RP_ToolDlg.update_regioninfo_text(text)
+        if (self.RP_ToolDlg.button_dict['Log To File'].get_active() == gtk.TRUE):
+            log_filename = self.RP_ToolDlg.entry_dict['log_file'].get_text()
+            if (len(log_filename) < 2):
+                gvutils.error('Log file not set!')
+            else:
+                try:
+                    logfile = open(log_filename,'a')
+                    logfile.writelines(text + '\n\n')
+                    logfile.close()
+                except:
+                    err_txt = 'Unable to create or append to ' + log_filename + '.'
+                    gvutils.error(err_txt)
+
+    def basic_region_analysis(self,cview,clayer):
+        line = self.RP_ToolDlg.entry_dict['start_line'].get_text()
+        pix = self.RP_ToolDlg.entry_dict['start_pix'].get_text()
+        sl =  self.RP_ToolDlg.entry_dict['num_lines'].get_text()
+        sp =  self.RP_ToolDlg.entry_dict['num_pix'].get_text()
+    
+        number_of_elements = Numeric.multiply.reduce(self.target_view_data.shape)
+        if (self.is_patch_complex() == 1):
+            # Calculate stats for absloute and power
+            max  = Numeric.maximum.reduce(Numeric.ravel(abs(self.target_view_data)))
+            mean = Numeric.add.reduce(Numeric.ravel(1.0*(abs(self.target_view_data))))/ \
+                           (Numeric.multiply.reduce(self.target_view_data.shape)*1.)
+            mean_power = Numeric.add.reduce(Numeric.ravel(Numeric.power(abs(self.target_view_data),2)))/ \
+                           (Numeric.multiply.reduce(self.target_view_data.shape)*1.)
+            min  = Numeric.minimum.reduce(Numeric.ravel(abs(self.target_view_data)))
+            if(number_of_elements > 1):
+                var  = (Numeric.add.reduce(Numeric.power(Numeric.ravel(abs(self.target_view_data)) \
+                         -mean,2))) /(Numeric.multiply.reduce(self.target_view_data.shape)-1.)
+                var_power =  (Numeric.add.reduce(Numeric.power(Numeric.ravel(Numeric.power(abs(self.target_view_data),2)) \
+                         -mean_power,2))) /(Numeric.multiply.reduce(self.target_view_data.shape)-1.)
+            else:
+                var_power = 0
+                var   = 0
+        else:
+            max  = Numeric.maximum.reduce(Numeric.ravel(self.target_view_data))
+            mean = Numeric.add.reduce(Numeric.ravel(1.0*self.target_view_data))/ \
+                           (Numeric.multiply.reduce(self.target_view_data.shape)*1.)
+            mean_power = Numeric.add.reduce(Numeric.ravel(Numeric.power(self.target_view_data,2)))/ \
+                           (Numeric.multiply.reduce(self.target_view_data.shape)*1.)
+            min  = Numeric.minimum.reduce(Numeric.ravel(self.target_view_data))
+            if(number_of_elements > 1):
+                var  = (Numeric.add.reduce(Numeric.power(Numeric.ravel(self.target_view_data)-mean,2))) / \
+                       (Numeric.multiply.reduce(self.target_view_data.shape)-1.)
+                var_power = (Numeric.add.reduce(Numeric.power(Numeric.ravel(Numeric.power(self.target_view_data,2))-mean_power,2))) / \
+                       (Numeric.multiply.reduce(self.target_view_data.shape)-1.)
+            else:
+                var_power = 0
+                var   = 0
+
+                
+        disp_text = 'Region Attributes: \n'
+        fname = clayer.get_parent().get_dataset().GetDescription()
+        disp_text = disp_text + '\tFilename: ' + fname + '\n'
+        disp_text = disp_text + '\tLines: ' + str(int(float(line))) + ' to '
+        disp_text = disp_text + str(int(float(line))+int(float(sl))-1) + '\n'
+        disp_text = disp_text + '\tPixels: ' + str(int(float(pix))) + ' to ' 
+        disp_text = disp_text + str(int(float(pix))+int(float(sp))-1)+'\n'
+        disp_text = disp_text + '\tRegion Maximum: ' + str(max) +  '\n'
+        disp_text = disp_text + '\tRegion Mean: ' + str(mean) +  '\n'
+        disp_text = disp_text + '\tRegion Minimum: ' + str(min) +  '\n'
+        disp_text = disp_text + '\tRegion Variance: ' + str(var) +  '\n'
+        disp_text = disp_text + '\tPower Statistics: '  +  '\n'
+        disp_text = disp_text + '\t\tMean Power: ' + str(mean_power) +  '\n'
+        disp_text = disp_text + '\t\tPower Variance: ' + str(var_power) +  '\n'
+        disp_text = disp_text + '\n'
+        return disp_text
+
+    def close_target_view_cb(self,*args):
+        self.target_view_layer = None
+        self.target_view_window = None
+        self.target_view_ds = None
+
+    def is_patch_complex(self):
+        data_typecode = self.target_view_data.typecode()
+        if (data_typecode == Numeric.Complex):
+            return 1
+        elif (data_typecode == Numeric.Complex0):
+            return 1
+        elif (data_typecode == Numeric.Complex8):
+            return 1  
+        elif (data_typecode == Numeric.Complex16):
+            return 1
+        elif (data_typecode == Numeric.Complex32):
+            return 1  
+        elif (data_typecode == Numeric.Complex64):
+            return 1
+        else:
+            return 0        
+
+
+class Stats_ToolDlg(General_ROIToolDlg):
+    def __init__(self):
+        General_ROIToolDlg.__init__(self)
+
+    def init_setup_window(self):
+        self.set_title('Stats Tool')
+        self.set_border_width(10)
+        self.set_usize(450,650)
+
+    def init_customize_gui_panel(self):
+        # Inherit all the usual stuff...
+        General_ROIToolDlg.init_customize_gui_panel(self)
+
+        # Add new frame with pixel info, keeping track of
+        # the frame and text object...
+        self.frame_dict['region_info_frame'] = gtk.GtkFrame()
+        self.show_list.append(self.frame_dict['region_info_frame'])
+
+        pixel_vbox = gtk.GtkVBox()
+        self.show_list.append(pixel_vbox)
+        self.frame_dict['region_info_frame'].add(pixel_vbox)
+
+        pixel_scroll = gtk.GtkScrolledWindow()
+        self.show_list.append(pixel_scroll)
+        pixel_vbox.pack_start(pixel_scroll,expand = gtk.TRUE)
+
+        self.entry_dict['region_info_text'] = gtk.GtkText()
+        self.show_list.append(self.entry_dict['region_info_text'])
+        self.entry_dict['region_info_text'].set_line_wrap(gtk.FALSE)
+        self.entry_dict['region_info_text'].set_word_wrap(gtk.FALSE)
+        self.entry_dict['region_info_text'].set_editable(gtk.FALSE)
+        pixel_scroll.add(self.entry_dict['region_info_text'])
+        self.entry_dict['region_info_text'].insert_defaults('')
+
+
+        # Add a frame with the log file options
+        self.frame_dict['log_frame']=gtk.GtkFrame()
+        self.show_list.append(self.frame_dict['log_frame'])
+
+        log_table = gtk.GtkTable(2,4,gtk.FALSE)
+        self.show_list.append(log_table)
+        self.frame_dict['log_frame'].add(log_table)
+        
+        log_table.set_border_width(5)
+        log_table.set_col_spacings(5)
+        log_table.set_col_spacing(1, 20)
+
+        self.button_dict['Log To File'] = gtk.GtkCheckButton('Log To File')
+        self.show_list.append(self.button_dict['Log To File'])
+        log_table.attach(self.button_dict['Log To File'], 0,1, 0, 1)
+
+        self.button_dict['Select Log'] = gtk.GtkButton('Select Log')
+        self.show_list.append(self.button_dict['Select Log'])
+        log_table.attach(self.button_dict['Select Log'], 3,4, 0, 1)
+
+        log_label = gtk.GtkLabel('Log File (full path): ')
+        log_label.set_alignment(0, 0.5)
+        log_table.attach(log_label, 0,1, 1, 2)
+
+        self.entry_dict['log_file'] = gtk.GtkEntry()
+        self.entry_dict['log_file'].set_editable(gtk.TRUE)
+        self.entry_dict['log_file'].set_usize(400,25)
+        self.entry_dict['log_file'].set_text('')
+        log_table.attach(self.entry_dict['log_file'], 1,4, 1,2)
+
+        self.main_panel.pack_start(self.frame_dict['region_info_frame'],gtk.TRUE,gtk.TRUE,0)   
+        self.main_panel.pack_start(self.frame_dict['log_frame'],gtk.FALSE,gtk.FALSE,0)      
+
+        # Customized connections
+        self.button_dict['Select Log'].connect('clicked',self.select_log_cb)
+
+        # Set default sensitivities for customized tool
+        self.button_dict['Log To File'].set_active(gtk.FALSE)
+        self.button_dict['Log To File'].set_sensitive(gtk.TRUE)
+        self.button_dict['Select Log'].set_sensitive(gtk.TRUE)
+
+    def update_regioninfo_text(self,text):
+        del_len = self.entry_dict['region_info_text'].get_length()
+        self.entry_dict['region_info_text'].backward_delete(del_len)
+        self.entry_dict['region_info_text'].insert_defaults(text)
+
+    def update_logfile_text(self,filepath,*args):
+        self.entry_dict['log_file'].set_text(filepath)
+
+    def set_entry_sensitivities(self,bool_val):
+        self.entry_dict['start_line'].set_sensitive(bool_val)
+        self.entry_dict['start_pix'].set_sensitive(bool_val)
+        self.entry_dict['num_lines'].set_sensitive(bool_val)
+        self.entry_dict['num_pix'].set_sensitive(bool_val)
+        self.entry_dict['log_file'].set_sensitive(bool_val)
+
+    def select_log_cb(self,*args):
+        import pgufilesel
+        pgufilesel.SimpleFileSelect( self.update_logfile_text, 
+                                     None,
+                                     'Select Log File',
+                                     help_topic = 'files.html' )
+
+    def activate_toggled(self,*args):
+        if self.button_dict['Activate'].get_active():
+            self.set_entry_sensitivities(gtk.TRUE)
+            self.button_dict['Auto Update'].set_sensitive(gtk.TRUE)
+            self.button_dict['Auto Update'].set_active(gtk.FALSE)
+            self.button_dict['Analyze'].set_sensitive(gtk.TRUE)
+            self.button_dict['Set Tool'].set_sensitive(gtk.TRUE)
+            self.button_dict['Log To File'].set_sensitive(gtk.TRUE)
+            self.button_dict['Log To File'].set_active(gtk.FALSE)
+            self.button_dict['Select Log'].set_sensitive(gtk.TRUE)
+            self.notify('re-activated')
+        else:
+            self.set_entry_sensitivities(gtk.FALSE)
+            self.button_dict['Auto Update'].set_active(gtk.FALSE)
+            self.button_dict['Auto Update'].set_sensitive(gtk.FALSE)
+            self.button_dict['Analyze'].set_sensitive(gtk.FALSE)
+            self.button_dict['Set Tool'].set_sensitive(gtk.FALSE)
+            self.button_dict['Log To File'].set_sensitive(gtk.FALSE)
+            self.button_dict['Log To File'].set_active(gtk.FALSE)
+            self.button_dict['Select Log'].set_sensitive(gtk.FALSE)
+       
+

Added: packages/openev/branches/upstream/current/pymod/toolfile_example.txt
===================================================================
--- packages/openev/branches/upstream/current/pymod/toolfile_example.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/toolfile_example.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,7 @@
+# Example tool file for openev extension
+
+MODULE_NAME = toolexample
+TOOL_NAME = Stats_Tool
+TOOL_NAME=GeneralPOITool
+TOOL_NAME=GeneralROITool
+TOOL_NAME = Pixel_Tool

Added: packages/openev/branches/upstream/current/pymod/vecplot.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/vecplot.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/vecplot.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,3721 @@
+#! /usr/bin/env python
+###############################################################################
+# $Id: vecplot.py,v 1.7 2004/06/27 20:18:14 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  GvViewArea-based plotting.
+# Author:   Gillian Walter <gillian.walter at atlantis-scientific.com>
+#
+# Developed by Atlantis Scientific Inc. (www.atlantis-scientific.com) for
+# DRDC Ottawa
+#
+###############################################################################
+# Copyright (c) Her majesty the Queen in right of Canada as represented
+# by the Minister of National Defence, 2003.
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: vecplot.py,v $
+#  Revision 1.7  2004/06/27 20:18:14  gmwalter
+#  Fixed xspc/xtics bug in vecplot, added
+#  character-based field width option to
+#  pgugrid.py.
+#
+#  Revision 1.6  2004/03/01 17:29:51  gmwalter
+#  Bug fix for interactive plot merging, add see-all
+#  function for setting extents properly.
+#
+#  Revision 1.5  2004/02/12 22:36:08  gmwalter
+#  Add functions for easily creating a line from three
+#  lists of nodes (x,y,z), avoiding python-level for
+#  loop.
+#
+#  Revision 1.4  2004/01/16 21:32:52  gmwalter
+#  Added in rough 3d support to see what
+#  problems arise.
+#
+#  Revision 1.3  2004/01/13 14:55:00  gmwalter
+#  Add some functionality for modifying plots.
+#
+#  Revision 1.2  2004/01/09 22:28:33  gmwalter
+#  A few bug fixes.
+#
+#  Revision 1.1  2004/01/07 20:41:48  gmwalter
+#  Initial check-in of plotting and
+#  annotation layer modules developed
+#  for DRDC Ottawa.
+#
+#
+
+# - For 2D or 3D plotting
+#
+# - Layers:
+#       - data
+#       - xlabel/ylabel/title (each is movable)
+#       - legend
+#       - Grid/tics/axis (optional grid, display of x/y values, etc.)
+#
+# Problems: 3D legend? Might want to have ability to adjust the 3D plot
+#           without legend, "sink" it to a raster (using print), then
+#           put the legend layer on top.
+#
+# Eventually: contours, mesh, etc.
+
+#######################################
+# IMMEDIATE TO-DO:
+# 1) Fix up plot object/view area/window relationships and decide
+#    on appropriate API
+#
+# 2) Labels/Axis- should be able to reset without discarding when
+#                 pxmin/pxmax or borders reset.  Labels need not
+#                 be updated when data max/mins are reset, though
+#                 axis must be (discontinuities, spacing).
+
+
+#
+# NOTE: To avoid roundoff errors in getting data from interactive plots,
+#       make sure openev is compiled with GV_USE_DOUBLE_PRECISION_COORD
+#       (otherwise rounding could lead to points that are on the
+#       plot boundary not being shown)
+#
+# LATER TO-DO:
+#
+# 1) Line symbols at nodes
+#
+
+import gview
+import Numeric
+import gtk
+import GtkExtra
+import os
+import string
+import gvogrfs
+import gvsignaler
+
+#########################################################################
+# Class hierarchy (not completely implemented yet)
+#
+# Classes meant to be used externally:
+#
+# GvPlotWindow: window to store a GvSimplePlot, GvMPlotTable, or GvMPlotLayout
+# GvPlot: a GvViewArea that stores one or more plot class instances, and.
+#         provides an interface for using the internal plot classes.
+#         Each plot class instance is assigned a set of extents within
+#         the GvPlot which it uses in creating its layers.
+#         GvSimplePlot is a simple implementation of this class for the
+#         2D cartesian case.  The general case needs more thought.
+# GvMPlotTable: A gtk table containing multiple GvPlot instances
+# GvMPlotLayout: A gtk layout containing multiple GvPlot instances
+#
+# The GvMPlotTable/GvMPlotLayout classes should only be required if the
+# user wishes to have 2-D and 3-D plots side-by-side or embedded in
+# each other, or for displaying multiple 3-D plots (so that zooming/panning
+# rotation can be done independently in each 3-D plot).  They have not
+# been implemented yet (3-D hasn't been either).
+#
+# Classes/functions meant to be used internally:
+#
+# plot classes:
+# 
+# gvplot_2Ddata_cartesian- class for doing 2-D cartesian plots.  Axis can
+#                          be linear or log, and can contain one or more
+#                          discontinuities.  More than one array of data
+#                          can be plotted.
+#
+# Each plot class is characterized by one or more data arrays, one
+# set of axis (top/left/bottom/right), one set of data and view extents and
+# function for converting from data coordinates to view coordinates, one
+# set of labels, one border layer (2 area shapes: a background for the border
+# and a background for the data frame), and one legend.
+#
+# If data is to be plotted using two separate ranges and two sets of
+# axis are required (eg. velocity over top of acceleration), use two
+# plot classes within the same GvPlot using the same plot extents, and don't
+# add a border layer to the top plot.
+#
+# supporting classes used by plot classes (internal):
+#
+# gvplot_array_cartesian- store arrays after getting rid of inf's and nan's,
+#                         and store min/max values of the arrays.
+#
+# gvplot_layer_defaults- store layer legend labels (also used in the layer
+#                        name) and properties.
+#
+# gvplot_axis- store axis extents and display, tic information.
+#
+# functions for creating layers:
+# Create2DBorderLayer
+# CreateArrayDataLayer, CreateGridDataLayer
+# CreateAxisLayer
+# CreateLabelLayer
+# CreateLegendLayer
+#
+# Layers that form a single plot, created by plot classes (from bottom up):
+# Border layer
+# Data layer(s)- one or more
+# Axis layer
+# Label layer
+# Legend layer
+
+GVPLOT_DATA_LAYER='Data' # plotted data
+GVPLOT_LABEL_LAYER='Labels' # x/y/z labels, title, other annotation
+GVPLOT_LEGEND_LAYER='Legend' # Legend
+GVPLOT_AXIS_LAYER='Axis' # Axis lines and Axis tic-marks/values
+GVPLOT_BORDER_LAYER='Border' # Backdrop to plot and border
+
+# Recognized types of axis
+GVPLOT_AXISTYPE_LINEAR='Linear'
+GVPLOT_AXISTYPE_LOG='Log'
+
+# Axis display options
+GVPLOT_AXIS_NOTSHOWN=0  # Not displayed
+GVPLOT_AXIS_NOTICS=1    # Show axis, but no tics
+GVPLOT_AXIS_VTICS=2     # Show only major tics, without labels
+GVPLOT_AXIS_VTICSLABELS=3 # Show only major tics, with labels
+GVPLOT_AXIS_VTICSLABELSTICS=4 # Show major tics with labels and minor tics
+
+DEFAULT_EDGE_PADDING=0.15
+
+        
+if os.name == 'nt':
+    DEFAULT_FONT="-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-*-*"
+else:
+    DEFAULT_FONT="-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+
+
+
+
+# plot classes
+
+class gvplot_2Ddata_cartesian:
+    """ Store an accumulation of _gvplot_arraydata instances,
+        along with plot-window specific information.
+    """
+    def __init__(self,plot_label,pxmin=0.0,pymin=0.0,pxmax=1.0,pymax=1.0):
+        """ plot_label- text label to prepend to layer names for this plot
+            pxmin/pxmax/pymin/pymax- extents that plot should cover in
+                                     main view window.
+        """
+        ###############################################################
+        # Properties that will be common to other 2-D plot classes    #
+        ###############################################################
+        
+        # Data: one or more gvplot_array_cartesian objects to plot.
+        #       Each will be plotted as a separate line layer.  Each item
+        #       in array_layer_defaults stores the label (for legend)
+        #       and layer properties of the corresponding array_data
+        #       item (eg. '_line_width', '_line_color','_gv_ogrfs_line',
+        #       '_gl_antialias')
+        self.array_data=[]      # Data to plot
+        self.array_layer_defaults=[]  # Data plotting properties
+                             
+        
+        # Axis: zero or more axis/lines to create.  These are all put
+        #       together in a single layer.  Layer-wide properties
+        #       are stored in axis_layer_properties- use this to
+        #       set the colours/widths of all axis/lines at once.
+        #       Individual axis properties (start/end points and
+        #       discontinuity locations in plot coordinates, locations
+        #       of major and minor tics also in plot coordinates,
+        #       labels for major tics, formats for labels, colours
+        #       if desired) are stored in axis_data.  Individual
+        #       properties override layer-wide properties.
+        #       By default, 4 keys are created: top, bottom, left,
+        #       and right.  More can be added.
+        self.axis_data={}
+        self.axis_data['top']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['bottom']=gvplot_axis(GVPLOT_AXIS_VTICSLABELS)
+        self.axis_data['bottom'].vtics_properties['_gv_ogrfs']=\
+            'LABEL(c:#000000FF,'+\
+            'f:"'+DEFAULT_FONT+'",t:{label},a:0.0,s:1.0,dx:-10.0,dy:15.0);'+\
+            'SYMBOL(c:#000000FF,id:ogr-sym-10,a:0.0,s:1.0)'
+        self.axis_data['left']=gvplot_axis(GVPLOT_AXIS_VTICSLABELS)
+        self.axis_data['left'].vtics_properties['_gv_ogrfs']=\
+            'LABEL(c:#000000FF,'+\
+            'f:"'+DEFAULT_FONT+'",t:{label},a:0.0,s:1.0,dx:-40.0,dy:5.0);'+\
+            'SYMBOL(c:#000000FF,id:ogr-sym-10,a:270.0,s:1.0)'
+        self.axis_data['right']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_layer_properties={}
+
+        # axis defaults
+        self.axis_layer_properties['_gl_antialias']='1'
+        self.axis_layer_properties['_line_color']='0.0 0.0 0.0 1.0'
+
+        # Extents that plot covers in main view area (plot coordinates)
+        self.pxmin=pxmin
+        self.pxmax=pxmax
+        self.pymin=pymin
+        self.pymax=pymax
+        self.plot_label=plot_label
+        
+        # left/right/top/bottom border padding
+        self.lborder=DEFAULT_EDGE_PADDING
+        self.rborder=DEFAULT_EDGE_PADDING
+        self.tborder=DEFAULT_EDGE_PADDING
+        self.bborder=DEFAULT_EDGE_PADDING
+
+        # border backgrounds (inner- behind plotted data;
+        # outer- around edge of data).  Border layer is only
+        # created if show_inner_border or show_outer_border
+        # is set to 1.
+        self.show_inner_border=0
+        self.show_outer_border=0
+        self.inner_border_props={}
+        self.outer_border_props={}
+        self.outer_border_props['_area_edge_color']='1.0 1.0 1.0 1.0'
+        self.outer_border_props['_area_edge_width']='1.0'
+        self.outer_border_props['_area_fill_color']='1.0 1.0 1.0 1.0'
+        self.inner_border_props['_area_edge_color']='1.0 1.0 1.0 1.0'
+        self.inner_border_props['_area_edge_width']='1.0'
+        self.inner_border_props['_area_fill_color']='1.0 1.0 1.0 1.0'
+        
+
+        # Label parameters:
+        #     label_data- a dictionary containing several lists, each
+        #                 with an (x,y) tuple of plot coordinates, and
+        #                 a text string or None.
+        #     properties- properties to set on the layer (usually just
+        #                 contains a key/value pair for _gv_ogrfs,
+        #                 which contains display info).  Text for labels
+        #                 is taken from the 'label' property set for
+        #                 the shape.
+        self.label_data={}
+        self.label_layer_properties={}
+
+        # defaults
+        self.label_layer_properties['_gv_ogrfs_point']=\
+             'LABEL(c:#000000FF,f:"'+DEFAULT_FONT+'",t:{label})'
+        
+        x0=self.pxmin+(self.lborder/5.0)*(self.pxmax-self.pxmin)
+        xm=(self.pxmax+self.pxmin)/2.0+self.lborder-self.rborder
+        xf=self.pxmax-(self.rborder/5.0)*(self.pxmax-self.pxmin)
+        y0=self.pymin+(self.bborder/4.0)*(self.pymax-self.pymin)
+        ym=(self.pymax+self.pymin)/2.0+self.bborder-self.tborder
+        yf=self.pymax-(self.tborder/2.0)*(self.pymax-self.pymin)
+
+        self.label_data['xlabel']=[(xm,y0,0),'xlabel']
+        self.label_data['ylabel']=[(x0,ym,0),None]
+        self.label_data['title']=[(xm,yf,0),None]
+        
+        # Legend parameters:
+        #    show_legend- 1 if legend should be created; 0 otherwise
+        #    location- an x,y tuple that specifies where the top left
+        #              corner of the legend should fall as a fraction
+        #              of pxmax-pxmin, pymax-pymin (plot extents):
+        #              each value should be in the range 0-1.
+        
+        self.show_legend=0
+        self.legend_location=None
+
+
+        ########################
+        # Specific properties. #
+        ########################
+        
+        # Each min/max pair corresponds to
+        # a continous range in the plot axis
+        # NOTE: In the log axis case, these still
+        # correspond to the non-logged min/max's;
+        # logarithms are taken as the data layer
+        # created- ie. for a log plot that ranges
+        # from 10^0 to 10^2, the values here would
+        # be 1 and 100.
+        self.xmins=[]
+        self.xmaxs=[]
+        self.xloc=None   # Major xtic locations (data coordinates)
+        self.xfmt="%g"   # Formatting string for tic labels
+        
+        self.ymins=[]
+        self.ymaxs=[]
+        self.yloc=None   # Major ytic locations (data coordinates)
+        self.yfmt="%g"
+        
+        self.xaxistype=GVPLOT_AXISTYPE_LINEAR
+        self.yaxistype=GVPLOT_AXISTYPE_LINEAR
+
+        #################################################
+        # Layers
+        #################################################
+        self.BorderLayer=None
+        self.DataLayers=[]
+        self.AxisLayer=None
+        self.LabelLayer=None
+        self.LegendLayer=None
+
+    def create_layers(self):
+        """ Create layers """
+        self.BorderLayer=Create2DBorderLayer(self)
+        if self.BorderLayer is not None:
+            ll=[self.BorderLayer]
+        else:
+            ll=[]
+
+        self.DataLayers=[]    
+        for idx in range(len(self.array_data)):
+            self.DataLayers.append(CreateArrayDataLayer(self,idx))
+        ll.extend(self.DataLayers)
+        
+        self.AxisLayer=CreateAxisLayer(self)
+        if ll is not None:
+            ll.append(self.AxisLayer)
+            
+        self.LabelLayer=CreateLabelLayer(self)
+        if self.LabelLayer is not None:
+            ll.append(self.LabelLayer)
+            
+        self.LegendLayer=Create2DLegendLayer(self)
+        if self.LegendLayer is not None:
+            ll.append(self.LegendLayer)
+        
+        return ll
+
+    def get_layer(self,ltype=GVPLOT_DATA_LAYER,idx=0):
+        """ Get the layer of type ltype.  If ltype is 'Data',
+            index idx is also specified to indicate which
+            data layer to return.  If layer is not present,
+            return None.
+        """
+        if ltype is GVPLOT_DATA_LAYER:
+            if len(self.DataLayers) > idx:
+                return self.DataLayers[idx]
+            else:
+                return None
+        elif ltype is GVPLOT_AXIS_LAYER:
+            return self.AxisLayer
+        elif ltype is GVPLOT_BORDER_LAYER:
+            return self.BorderLayer
+        elif ltype is GVPLOT_LABEL_LAYER:
+            return self.LabelLayer
+        elif ltype is GVPLOT_LEGEND_LAYER:
+            return self.LegendLayer
+        else:
+            raise RuntimeError,'get_layer: Invalid layer type'
+
+    def create_layer(self,ltype=GVPLOT_DATA_LAYER,idx=0):
+        """ Create or update the layer of type ltype.  If ltype is 'Data',
+            index idx is also specified to indicate which
+            data layer to return.  If layer is not present,
+            return None.
+        """
+        if ltype is GVPLOT_DATA_LAYER:
+            if len(self.DataLayers) > idx:
+                self.DataLayers[idx]=CreateArrayDataLayer(self,idx)
+            elif len(self.DataLayers) == idx:
+                self.DataLayers.append(CreateArrayDataLayer(self,idx))
+            else:
+                raise RuntimeError,'create_layer: invalid data layer index'
+        elif ltype is GVPLOT_AXIS_LAYER:
+            self.AxisLayer=CreateAxisLayer(self)
+            return self.AxisLayer
+        elif ltype is GVPLOT_BORDER_LAYER:
+            self.BorderLayer=Create2DBorderLayer(self)
+            return self.BorderLayer
+        elif ltype is GVPLOT_LABEL_LAYER:
+            self.LabelLayer=CreateLabelLayer(self)
+            return self.LabelLayer
+        elif ltype is GVPLOT_LEGEND_LAYER:
+            self.LegendLayer=Create2DLegendLayer(self)
+            return self.LegendLayer
+        else:
+            raise RuntimeError,'create_layer: Invalid layer type'
+        
+            
+
+    def get_layers(self):
+        """ Return all layers that have been created, but don't
+            create new ones.
+        """
+        if self.BorderLayer is not None:
+            ll=[self.BorderLayer]
+        else:
+            ll=[]
+            
+        ll.extend(self.DataLayers)
+        if self.AxisLayer is not None:
+            ll.append(self.AxisLayer)
+        
+        if self.LabelLayer is not None:
+            ll.append(self.LabelLayer)
+            
+        if self.LegendLayer is not None:
+            ll.append(self.LegendLayer)
+        
+        return ll
+
+    def update_data_from_plot(self,index):
+        """ Update the underlying array data based on the
+            modifications to the current plot.  This will
+            discard any data outside of the viewing
+            ranges.
+        """
+        newx,newy=self.get_data_from_plot(index)
+        self.array_data[index]=gvplot_array_cartesian(newx,newy)
+
+    def merge_data_from_plot(self,index):
+        """ Update the underlying array data based on the
+            modifications to the current plot.  This will
+            try to merge the plot with the underlying data
+            (data from the original array that lies outside
+            the current viewing range is included).
+        """
+        newx,newy=self.get_data_from_plot(index)
+        # find data that was out of range and not plotted
+        xold=self.array_data[index].xarr
+        yold=self.array_data[index].yarr
+        outx,outy,outz=GetOutlierData(xold,self.xmins,self.xmaxs,
+                                      yold,self.ymins,self.ymaxs)
+        newx.extend(outx)
+        newy.extend(outy)
+        newx2,newy2=MakeContiguousXY(newx,newy)
+        self.array_data[index]=gvplot_array_cartesian(newx2,newy2)
+        
+
+    
+    def get_data_from_plot(self,index):
+        """ Get (potentially modified) data from the the data layer
+            corresponding to array_data[index].
+        """
+        if index > len(self.DataLayers)-1:
+            return None
+
+        l=self.DataLayers[index]
+        shps=l.get_parent()
+        xlists=[]
+        ylists=[]
+        arrxlists=[]
+        arrylists=[]
+        idx=0
+        for shp in shps:
+            xlists.append([])
+            ylists.append([])
+            for cnodeidx in range(shp.get_nodes()):
+                cnode=shp.get_node(cnodeidx)
+                xlists[idx].append(cnode[0])
+                ylists[idx].append(cnode[1])
+            xdata,ydata,ok=self.get_xyposition(Numeric.array(xlists[idx]),
+                                               Numeric.array(ylists[idx]))
+            arrxlists.append(xdata)
+            arrylists.append(ydata)
+            idx=idx+1
+            
+        return (arrxlists,arrylists)
+
+    def set_extents(self,xmin=None,xmax=None,xspc=None,nxtics=0,
+                    ymin=None,ymax=None,yspc=None,nytics=0,nice=1):
+        """ Set data extents for plot.  In each case below, entering
+            a value of None indicates that the plot should decide on
+            sensible min/max's.  If xmin is an array, xmax must be
+            an array of the same length, and vice versa (same for
+            ymin/ymax).  Each xmin/xmax pair represents one continuous
+            range of values to plot.
+            
+            xmin- minimum in x direction (None, single value, or array)
+            ymin- minimum in y direction (None, single value, or array)
+            xmax- maximum in x direction (None, single value, or array)
+            ymax- maximum in y direction (None, single value, or array)
+            xspc- approximate tic spacing in x direction (None or single value)
+            yspc- approximate tic spacing in y direction (None or single value)
+            nxtics- number of minor x tics per major x tic (defaults to 0)
+            nytics- number of minor y tics per major y tic (defaults to 0)
+            nice- 1 if plot is allowed to alter entered values slightly
+                  to make them look 'nice'- eg. 0.015999 can be rounded
+                  to 0.16, etc.; 0 if plot must not alter the values.
+                  Defaults to 1.
+            """
+        
+        if len(self.array_data) > 0:
+            if xmin is None:
+                xmin=self.array_data[0].xmin
+                for item in self.array_data[1:]:
+                    xmin=min([xmin,item.xmin])
+
+            if xmax is None:
+                xmax=self.array_data[0].xmax
+                for item in self.array_data[1:]:
+                    xmax=max([xmax,item.xmax])
+
+            if ymin is None:         
+                ymin=self.array_data[0].ymin
+                for item in self.array_data[1:]:
+                    ymin=min([ymin,item.ymin])
+
+            if ymax is None:     
+                ymax=self.array_data[0].ymax
+                for item in self.array_data[1:]:
+                    ymax=max([ymax,item.ymax])
+
+            if nice == 1:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                self.xmins=dxmin
+                self.xmaxs=dxmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+            
+                dymin,dymax,dylocs,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                     self.yaxistype)
+                self.ymins=dymin
+                self.ymaxs=dymax
+                self.ylocs=dylocs
+                self.yfmt=fmt
+            else:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                if type(xmin) not in [type([]),type((1,))]:
+                    self.xmins=[xmin]
+                else:
+                    self.xmins=xmin
+                if type(xmax) not in [type([]),type((1,))]:
+                    self.xmaxs=[xmax]
+                else:
+                    self.xmaxs=xmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+                    
+                dymin,dymax,dyspc,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                    self.yaxistype)
+                if type(ymin) not in [type([]),type((1,))]:
+                    self.ymins=[ymin]
+                else:
+                    self.ymins=ymin
+                    
+                if type(ymax) not in [type([]),type((1,))]:
+                    self.ymaxs=[ymax]
+                else:
+                    self.ymaxs=ymax
+                    
+                self.ylocs=dylocs
+                self.yfmt=fmt
+        else:
+            if ((xmin is None) or (xmax is None) or (ymin is None) or
+                (ymax is None)):
+                raise RuntimeError,'set_extents: if no data has been set,'+\
+                      'then\nxmin/xmax/ymin/ymax must all be specified.'
+
+            if nice == 1:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                self.xmins=dxmin
+                self.xmaxs=dxmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+            
+                dymin,dymax,dylocs,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                     self.yaxistype)
+                self.ymins=dymin
+                self.ymaxs=dymax
+                self.ylocs=dylocs
+                self.yfmt=fmt
+            else:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                # TO DO: ADD A CHECK HERE AND MAKE SURE THEY ARE LISTS!
+                self.xmins=xmin
+                self.xmaxs=xmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+                    
+                dymin,dymax,dyspc,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                    self.yaxistype)
+                self.ymins=list(dymin)
+                self.ymaxs=list(dymax)
+                self.ylocs=list(dylocs)
+                self.yfmt=fmt
+                
+        self.set_axis()
+        
+    def set_axis(self):
+        """ Set the default axis """
+
+        new_axis_data={}
+        xbreaks=GetAxisBreaks(self.xmins,self.xmaxs,self.xaxistype)
+        ybreaks=GetAxisBreaks(self.ymins,self.ymaxs,self.yaxistype)
+        ext=self.get_plot_extents(include_border=0)
+        pxlocs,xok=DataToPlot1D(ext[0],ext[3],self.xmins,self.xmaxs,
+                            self.xlocs,self.xaxistype)
+        pylocs,yok=DataToPlot1D(ext[1],ext[4],self.ymins,self.ymaxs,
+                            self.ylocs,self.yaxistype)
+
+        toplocs=[]
+        bottomlocs=[]
+        leftlocs=[]
+        rightlocs=[]
+        ext=self.get_plot_extents(include_border=0)
+        
+        for item in pxlocs:
+            toplocs.append((item,ext[4]))
+            bottomlocs.append((item,ext[1]))
+        for item in pylocs:
+            leftlocs.append((ext[0],item))
+            rightlocs.append((ext[3],item))
+    
+        # TO DO: add minor tics    
+        new_axis_data['top']=self.axis_data['top']
+        new_axis_data['top'].set_data(toplocs,list(self.xlocs),self.xfmt,
+                                      None,
+                                      (ext[0],ext[4],0),
+                                      (ext[3],ext[4],0),
+                                      xbreaks)
+        new_axis_data['bottom']=self.axis_data['bottom']
+        new_axis_data['bottom'].set_data(bottomlocs,list(self.xlocs),self.xfmt,
+                                      None,
+                                      (ext[0],ext[1],0),
+                                      (ext[3],ext[1],0),
+                                      xbreaks)
+        new_axis_data['left']=self.axis_data['left']
+        new_axis_data['left'].set_data(leftlocs,list(self.ylocs),self.yfmt,
+                                      None,
+                                      (ext[0],ext[1],0),
+                                      (ext[0],ext[4],0),
+                                      ybreaks)
+        new_axis_data['right']=self.axis_data['right']
+        new_axis_data['right'].set_data(rightlocs,list(self.ylocs),self.yfmt,
+                                      None,
+                                      (ext[3],ext[1],0),
+                                      (ext[3],ext[4],0),
+                                      ybreaks)
+        
+        self.axis_data=new_axis_data
+
+    #########################################################################
+    # Versions of the following functions will be required of most or       #
+    # all plot classes.                                                     #
+    #########################################################################
+
+    def add_data(self,xarr,yarr,label='',properties=None, interactive=0):
+        """ Add an array to the data to plot """
+        self.array_data.append(gvplot_array_cartesian(xarr,yarr))
+        self.array_layer_defaults.append(
+            gvplot_layer_defaults(label,properties,interactive))
+        return len(self.array_data)-1
+
+    def set_labels(self,xlabel=None,ylabel=None,title=None):
+        """ Set labels. """
+        self.label_data['xlabel'][1]=xlabel
+        self.label_data['ylabel'][1]=ylabel
+        self.label_data['title'][1]=title
+    
+    def set_plot_extents(self,pxmin,pxmax,pymin,pymax):
+        """ Set/Reset the extents covered by this plot in the view.
+            Update labels, axis, data info.
+        """
+        
+        oldpxmin=self.pxmin
+        oldpxmax=self.pxmax
+        oldpymin=self.pymin
+        oldpymax=self.pymax
+        
+        self.pxmin=pxmin
+        self.pxmax=pxmax
+        self.pymin=pymin
+        self.pymax=pymax
+
+        # Update label positions
+        for ckey in self.label_data.keys():
+            xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.label_data[ckey][0][0],
+                              GVPLOT_AXISTYPE_LINEAR)
+            ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.label_data[ckey][0][1],
+                              GVPLOT_AXISTYPE_LINEAR)
+            self.label_data[ckey][0]=(xnew,ynew,0)
+
+        # Update axis information            
+        for ckey in self.axis_data.keys():
+            for idx in range(len(self.axis_data[key].vtics)):
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].vtics[idx][0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].vtics[idx][1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].vtics[idx]=(xnew,ynew,0)
+                
+            for idx in range(len(self.axis_data[key].tics)):
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].tics[idx][0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].tics[idx][1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].tics[idx]=(xnew,ynew,0)
+
+            if self.axis_data[ckey].start is not None:
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].start[0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].start[1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].start=(xnew,ynew,0)
+                
+            if self.axis_data[ckey].end is not None:
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].end[0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].end[1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].end=(xnew,ynew,0)
+
+            if self.axis_data[ckey].breaks is not None:
+                for idx in range(len(breaks)):
+                    xnew1=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                        self.axis_data[ckey].breaks[idx][0][0],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    ynew1=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                        self.axis_data[ckey].breaks[idx][0][1],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    xnew2=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                        self.axis_data[ckey].breaks[idx][1][0],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    ynew2=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                        self.axis_data[ckey].breaks[idx][1][1],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    self.axis_data[ckey].breaks[idx]=((xnew1,ynew1,0),
+                                                      (xnew2,ynew2,0))
+                
+        if len(self.xmins) == 0:
+            return
+        
+        self.reset_axis()
+
+    def get_extents(self):
+        """ Get data extents: (xmins, xmaxs, ymins, ymaxs, zmins, zmaxs). """
+        return (self.xmins,self.xmaxs,self.ymins,
+                self.ymaxs,[0],[0])
+
+    def get_plot_extents(self,include_border=1):
+        """ Get plot extents with (include_border=1) or
+            without (include_border=0) borders.
+            (xmin,ymin,zmin,xmax,ymax,zmax).  For
+            this class, zmin=zmax=0 always.  The third
+            dimension is included because the CreateAxisLayer
+            function will be shared with the 3d case.
+        """
+        if include_border == 1:
+            return (self.pxmin,self.pymin,0,self.pxmax,self.pymax,0)
+        
+        dxmin=self.pxmin+(self.pxmax-self.pxmin)*self.lborder
+        dxmax=self.pxmax-(self.pxmax-self.pxmin)*self.rborder
+        dymin=self.pymin+(self.pymax-self.pymin)*self.bborder
+        dymax=self.pymax-(self.pymax-self.pymin)*self.tborder
+        
+        return (dxmin,dymin,0,dxmax,dymax,0)
+        
+        
+    def get_xyposition(self,x,y):
+        """ Get the data position corresponding to GvViewPlot position x,y.
+            x and y may be single values or same-length 1-D arrays.
+        """
+
+        # Get plot extents minus borders.
+        dxmin=self.pxmin+(self.pxmax-self.pxmin)*self.lborder
+        dxmax=self.pxmax-(self.pxmax-self.pxmin)*self.rborder
+        dymin=self.pymin+(self.pymax-self.pymin)*self.bborder
+        dymax=self.pymax-(self.pymax-self.pymin)*self.tborder
+
+        if type(x) == type(Numeric.array([])):
+            x=Numeric.ravel(x)
+
+        if type(y) == type(Numeric.array([])):
+            y=Numeric.ravel(y)
+            
+        x,xok=PlotToData1D(dxmin,dxmax,self.xmins,self.xmaxs,x,self.xaxistype)
+        y,yok=PlotToData1D(dymin,dymax,self.ymins,self.ymaxs,y,self.yaxistype)
+
+        okarr=Numeric.where(xok==0,0,yok)
+
+        return (x,y,okarr)
+
+    def get_plotposition(self,x,y):
+        """ Get the GvViewPlot position corresponding to data position x,y.
+            x and y may be single values or same-length 1-D arrays.
+        """
+
+        # Get plot extents minus borders.
+        dxmin=self.pxmin+(self.pxmax-self.pxmin)*self.lborder
+        dxmax=self.pxmax-(self.pxmax-self.pxmin)*self.rborder
+        dymin=self.pymin+(self.pymax-self.pymin)*self.bborder
+        dymax=self.pymax-(self.pymax-self.pymin)*self.tborder
+
+        if type(x) == type(Numeric.array([])):
+            x=Numeric.ravel(x)
+
+        if type(y) == type(Numeric.array([])):
+            y=Numeric.ravel(y)
+            
+        x,xok=DataToPlot1D(dxmin,dxmax,self.xmins,self.xmaxs,x,self.xaxistype)
+        y,yok=DataToPlot1D(dymin,dymax,self.ymins,self.ymaxs,y,self.yaxistype)
+
+        okarr=Numeric.where(xok==0,0,yok)
+
+        return (x,y,okarr)        
+
+    def set_arraylayer_properties(self,idx,props):
+        """ Set the display properties for the idxth data layer.
+            idx- index of data
+            props- a dictionary of properties to set (keys) and their
+                   values.
+
+            An error will be raised if there is no idx'th data array
+            to plot.
+            
+            Example properties:
+            _line_color='1.0 0.0 0.0 1.0'
+            _line_width='1.0'
+            _gl_antialias='1' (turn on display antialiasing)
+        """
+        self.array_layer_defaults[idx]=props
+
+    def set_axislayer_properties(self,props):
+        """ Set the display properties of the axis layer
+        
+            Example properties:
+            _line_color='1.0 0.0 0.0 1.0'
+            _line_width='1.0'
+            _gl_antialias='1' (turn on display antialiasing)
+            
+        """
+        self.axis_layer_properties=props
+
+    def set_label_color(self,color):
+        """ Set label layer color to color. """
+        
+        import gvogrfs
+        cstr2=gvogrfs.gv_to_ogr_color(color)
+        if len(cstr2) < 9:
+            cstr2=cstr2+'FF'
+        if self.label_layer_properties.has_key('_gv_ogrfs_point'):
+            ogrfs=gvogrfs.OGRFeatureStyle(
+                self.label_layer_properties['_gv_ogrfs_point'])
+            if ogrfs.has_part('LABEL'):
+                part=ogrfs.get_part('LABEL')
+                part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                ogrfs.remove_part('LABEL')
+                ogrfs.add_part(part)
+            if ogrfs.has_part('SYMBOL'):
+                part=ogrfs.get_part('SYMBOL')
+                part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                ogrfs.remove_part('SYMBOL')
+                ogrfs.add_part(part)
+            self.label_layer_properties['_gv_ogrfs_point']=ogrfs.unparse()
+             
+    def set_axis_color(self,color):
+        """ Set all axis and axis label colours to color.
+            color is a tuple of 4 values, each between
+            0 and 1, representing red/green/blue/alpha
+            components.
+        """
+        cstr1=str(color[0])+' '+str(color[1])+' '+str(color[2])+\
+               ' '+str(color[3])
+        self.axis_layer_properties['_line_color']=cstr1
+
+        import gvogrfs
+        cstr2=gvogrfs.gv_to_ogr_color(color)
+        if len(cstr2) < 9:
+            cstr2=cstr2+'FF'
+        for caxis in self.axis_data.values():
+            caxis.line_properties['_line_color']=cstr1
+            if caxis.vtics_properties.has_key('_gv_ogrfs'):
+                ogrfs=gvogrfs.OGRFeatureStyle(
+                    caxis.vtics_properties['_gv_ogrfs'])
+                if ogrfs.has_part('LABEL'):
+                    part=ogrfs.get_part('LABEL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('LABEL')
+                    ogrfs.add_part(part)
+                if ogrfs.has_part('SYMBOL'):
+                    part=ogrfs.get_part('SYMBOL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('SYMBOL')
+                    ogrfs.add_part(part)
+                caxis.vtics_properties['_gv_ogrfs']=ogrfs.unparse()
+
+            if caxis.tics_properties.has_key('_gv_ogrfs'):
+                ogrfs=gvogrfs.OGRFeatureStyle(
+                    caxis.tics_properties['_gv_ogrfs'])
+                if ogrfs.has_part('LABEL'):
+                    part=ogrfs.get_part('LABEL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('LABEL')
+                    ogrfs.add_part(part)
+                if ogrfs.has_part('SYMBOL'):
+                    part=ogrfs.get_part('SYMBOL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('SYMBOL')
+                    ogrfs.add_part(part)
+                caxis.tics_properties['_gv_ogrfs']=ogrfs.unparse()
+  
+
+    def set_axis_properties(self,key,lprops,vprops,tprops):
+        """ Set the properties of the shapes forming the axis
+            referenced by 'key'.  Default keys are 'top, 'bottom',
+            'left','right'.  An error will be raised if the axis
+            dictionary doesn't contain key.
+
+            Parameters:
+                key- key to index axis
+                lprops- line properties (if different from layer default)
+                vprops- vtic properties
+                tprops- tic properties
+                
+            Example properties: (defaults for left axis shown here)
+            _gv_ogrfs='LABEL(c:#000000FF,'+\
+            'f:"-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-*-*",
+            t:{label},a:0.0,s:1.0,dx:-35.0,dy:5.0);'+\
+            'SYMBOL(c:#000000FF,id:ogr-sym-10,a:270.0,s:1.0)'
+
+            Note: the label property is always used to get text
+                  in CreateAxisLayer, so the t:{label} portion
+                  of this should be left alone.
+        """
+           
+        self.axis_data[key].line_properties=lprops
+        self.axis_data[key].vtics_properties=vprops
+        self.axis_data[key].tics_properties=tprops
+        
+
+    def set_border_padding(self,top,bottom,left,right):
+        """ Set border padding as a fraction of the plot
+            extents (0-1).
+            top- top padding
+            bottom- bottom
+            left- left
+            right- right
+        """
+        self.tborder=top
+        self.bborder=bottom
+        self.lborder=left
+        self.rborder=right
+
+    def set_borderlayer_properties(self,show_outer,show_inner,
+                                   outer_props,inner_props):
+        """ Set border layer properties:
+            show_outer- 1 to create outer border, 0 to not create it
+            show_inner- 1 to create inner border, 0 to not create it
+            outer_props- dictionary of outer border properties
+                         (None to leave at defaults)
+            inner_props- dictionary of inner border properties
+                         (None to leave at defaults)
+
+            Example properties:
+
+            outer_props['_area_edge_color']='1.0 1.0 1.0 1.0'
+            outer_props['_area_edge_width']='1.0'
+            outer_props['_area_fill_color']='1.0 1.0 1.0 1.0'
+        
+        """
+        self.show_outer_border=show_outer
+        if outer_props is not None:
+            self.outer_border_props=outer_props
+            
+        self.show_inner_border=show_inner
+        if inner_props is not None:
+            self.inner_border_props=inner_props
+
+    def set_labellayer_properties(self,label_props):
+        """ Set label layer properties:
+            Example:
+            _gv_ogrfs_point='LABEL(c:#000000FF,
+            f:"-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-*-*",t:{label})'
+
+            Note: the label property is always used to get text
+                  in CreateLabelLayer, so the t:{label} portion
+                  of this should be left alone.            
+        """
+        self.label_layer_properties=label_props
+
+    def set_legend(self,show=1,location=None):
+        """ Set legend status:
+            show- 1 (show) or 0 (hide)
+            location- offset as a fraction of
+                      current plot extents
+                      (xoffset,yoffset); if None,
+                      default will be used.
+        """
+        self.show_legend=show
+        if location is not None:
+            self.legend_location=location
+        else:
+            self.legend_location=(self.lborder+0.05,
+                                  1.0-self.tborder-0.1)
+            
+    def add_axis(self,key,properties,start_tuple,end_tuple,breaks=None,
+                 type=GVPLOT_AXIS_NOTICS):
+        """ Add an axis (line):
+            key- key to reference the axis
+            properties- axis properties
+            start_tuple- an (x,y) tuple in data coordinates representing
+                         the start point of the line.
+            end_tuple- an (x,y) tuple in data coordinates representing
+                       the start point of the line.
+            breaks- breaks in the axis/line (defaults to None, must be
+                    a list of start/end tuples if present).  Also in
+                    data coordinates.
+            type- type of axis (0-4: not shown, shown but no tics, shown
+                  with major tics but no labels, shown with major tics
+                  and labels, shown with major tics and labels and minor
+                  tics).
+                         
+        """
+        pass
+    
+
+class gvplot_3Ddata_cartesiangrid:
+    """ Store an accumulation of _gvplot_arraydata instances,
+        along with plot-window specific information.
+    """
+    def __init__(self,plot_label,pxmin=0.0,pymin=0.0,pzmin=0.0,pxmax=1.0,
+                 pymax=1.0,pzmax=1.0):
+        """ plot_label- text label to prepend to layer names for this plot
+            pxmin/pxmax/pymin/pymax/pzmin/pzmax- extents that plot should
+            cover in main view window.
+        """
+        self.array_data=[]      # Data to plot
+        self.array_layer_defaults=[]  # Data plotting properties
+                             
+        
+        # Axis: zero or more axis/lines to create.  These are all put
+        #       together in a single layer.  Layer-wide properties
+        #       are stored in axis_layer_properties- use this to
+        #       set the colours/widths of all axis/lines at once.
+        #       Individual axis properties (start/end points and
+        #       discontinuity locations in plot coordinates, locations
+        #       of major and minor tics also in plot coordinates,
+        #       labels for major tics, formats for labels, colours
+        #       if desired) are stored in axis_data.  Individual
+        #       properties override layer-wide properties.
+        #       By default, 4 keys are created: top, bottom, left,
+        #       and right.  More can be added.
+        self.axis_data={}
+        self.axis_data['topleft']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['topright']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['topfront']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['topback']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['bottomleft']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['bottomright']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['bottomfront']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['bottomback']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['leftfront']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['leftback']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['rightfront']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        self.axis_data['rightback']=gvplot_axis(GVPLOT_AXIS_NOTICS)
+        
+        # axis defaults
+        self.axis_layer_properties={}
+        self.axis_layer_properties['_gl_antialias']='1'
+        self.axis_layer_properties['_line_color']='0.0 0.0 0.0 1.0'
+
+        # Extents that plot covers in main view area (plot coordinates)
+        self.pxmin=pxmin
+        self.pxmax=pxmax
+        self.pymin=pymin
+        self.pymax=pymax
+        self.pzmin=pzmin
+        self.pzmax=pzmax
+        self.plot_label=plot_label
+        
+        # left/right/top/bottom border padding
+        self.lborder=DEFAULT_EDGE_PADDING
+        self.rborder=DEFAULT_EDGE_PADDING
+        self.tborder=DEFAULT_EDGE_PADDING
+        self.bborder=DEFAULT_EDGE_PADDING
+        self.fborder=DEFAULT_EDGE_PADDING
+        self.bkborder=DEFAULT_EDGE_PADDING # back
+
+
+        # Label parameters:
+        #     label_data- a dictionary containing several lists, each
+        #                 with an (x,y,z) tuple of plot coordinates, and
+        #                 a text string or None.
+        #     properties- properties to set on the layer (usually just
+        #                 contains a key/value pair for _gv_ogrfs,
+        #                 which contains display info).  Text for labels
+        #                 is taken from the 'label' property set for
+        #                 the shape.
+        self.label_data={}
+        self.label_layer_properties={}
+
+        # defaults
+        self.label_layer_properties['_gv_ogrfs_point']=\
+             'LABEL(c:#000000FF,f:"'+DEFAULT_FONT+'",t:{label})'
+        
+        x0=self.pxmin+(self.lborder/5.0)*(self.pxmax-self.pxmin)
+        xm=(self.pxmax+self.pxmin)/2.0+self.lborder-self.rborder
+        xf=self.pxmax-(self.rborder/5.0)*(self.pxmax-self.pxmin)
+        y0=self.pymin+(self.fborder/4.0)*(self.pymax-self.pymin)
+        ym=(self.pymax+self.pymin)/2.0+self.fborder-self.bkborder
+        yf=self.pymax-(self.bkborder/2.0)*(self.pymax-self.pymin)
+        z0=self.pzmin+(self.bborder/4.0)*(self.pzmax-self.pzmin)
+        zm=(self.pzmax+self.pzmin)/2.0+self.bborder-self.tborder
+        zf=self.pzmax-(self.tborder/2.0)*(self.pzmax-self.pzmin)
+
+        self.label_data['xlabel']=[(xm,y0,z0),None]
+        self.label_data['ylabel']=[(x0,ym,z0),None]
+        self.label_data['zlabel']=[(x0,y0,zm),None]
+        self.label_data['title']=[(xm,ym,zf),None]
+
+
+        ########################
+        # Specific properties. #
+        ########################
+        
+        # Each min/max pair corresponds to
+        # a continous range in the plot axis
+        # NOTE: In the log axis case, these still
+        # correspond to the non-logged min/max's;
+        # logarithms are taken as the data layer
+        # created- ie. for a log plot that ranges
+        # from 10^0 to 10^2, the values here would
+        # be 1 and 100.
+        self.xmins=[]
+        self.xmaxs=[]
+        self.xloc=None   # Major xtic locations (data coordinates)
+        self.xfmt="%g"   # Formatting string for tic labels
+        
+        self.ymins=[]
+        self.ymaxs=[]
+        self.yloc=None   # Major ytic locations (data coordinates)
+        self.yfmt="%g"
+
+        self.zmins=[]
+        self.zmaxs=[]
+        self.zloc=None   # Major ztic locations (data coordinates)
+        self.zfmt="%g"
+        
+        self.xaxistype=GVPLOT_AXISTYPE_LINEAR
+        self.yaxistype=GVPLOT_AXISTYPE_LINEAR
+        self.zaxistype=GVPLOT_AXISTYPE_LINEAR
+
+        #################################################
+        # Layers
+        #################################################
+        self.DataLayers=[]
+        self.AxisLayer=None
+        self.LabelLayer=None
+
+    def create_layers(self):
+        """ Create layers """
+        
+        ll=[]
+        self.DataLayers=[]
+        for idx in range(len(self.array_data)):
+            self.DataLayers.append(CreateGridArrayDataLayer(self,idx))
+        ll.extend(self.DataLayers)
+        
+        self.AxisLayer=CreateAxisLayer(self)
+        if ll is not None:
+            ll.append(self.AxisLayer)
+            
+        self.LabelLayer=CreateLabelLayer(self)
+        if self.LabelLayer is not None:
+            ll.append(self.LabelLayer)
+            
+        return ll
+
+    def get_layer(self,ltype=GVPLOT_DATA_LAYER,idx=0):
+        """ Get the layer of type ltype.  If ltype is 'Data',
+            index idx is also specified to indicate which
+            data layer to return.  If layer is not present,
+            return None.
+        """
+        if ltype is GVPLOT_DATA_LAYER:
+            if len(self.DataLayers) > idx:
+                return self.DataLayers[idx]
+            else:
+                return None
+        elif ltype is GVPLOT_AXIS_LAYER:
+            return self.AxisLayer
+        elif ltype is GVPLOT_LABEL_LAYER:
+            return self.LabelLayer
+        else:
+            raise RuntimeError,'get_layer: Invalid layer type'
+
+    def create_layer(self,ltype=GVPLOT_DATA_LAYER,idx=0):
+        """ Create or update the layer of type ltype.  If ltype is 'Data',
+            index idx is also specified to indicate which
+            data layer to return.  If layer is not present,
+            return None.
+        """
+        if ltype is GVPLOT_DATA_LAYER:
+            if len(self.DataLayers) > idx:
+                self.DataLayers[idx]=CreateGridArrayDataLayer(self,idx)
+            elif len(self.DataLayers) == idx:
+                self.DataLayers.append(CreateGridArrayDataLayer(self,idx))
+            else:
+                raise RuntimeError,'create_layer: invalid data layer index'
+        elif ltype is GVPLOT_AXIS_LAYER:
+            self.AxisLayer=CreateAxisLayer(self)
+            return self.AxisLayer
+        elif ltype is GVPLOT_LABEL_LAYER:
+            self.LabelLayer=CreateLabelLayer(self)
+            return self.LabelLayer
+        else:
+            raise RuntimeError,'create_layer: Invalid layer type'
+        
+            
+
+    def get_layers(self):
+        """ Return all layers that have been created, but don't
+            create new ones.
+        """
+            
+        ll.extend(self.DataLayers)
+        if self.AxisLayer is not None:
+            ll.append(self.AxisLayer)
+        
+        if self.LabelLayer is not None:
+            ll.append(self.LabelLayer)
+        
+        return ll
+
+    def set_extents(self,xmin=None,xmax=None,xspc=None,nxtics=0,
+                    ymin=None,ymax=None,yspc=None,nytics=0,
+                    zmin=None,zmax=None,zspc=None,nztics=0,nice=1):
+        """ Set data extents for plot.  In each case below, entering
+            a value of None indicates that the plot should decide on
+            sensible min/max's.  If xmin is an array, xmax must be
+            an array of the same length, and vice versa (same for
+            ymin/ymax).  Each xmin/xmax pair represents one continuous
+            range of values to plot.
+            
+            xmin- minimum in x direction (None, single value, or array)
+            ymin- minimum in y direction (None, single value, or array)
+            zmin- minimum in z direction (None, single value, or array)
+            xmax- maximum in x direction (None, single value, or array)
+            ymax- maximum in y direction (None, single value, or array)
+            zmax- maximum in z direction (None, single value, or array)
+            xspc- approximate tic spacing in x direction (None or single value)
+            yspc- approximate tic spacing in y direction (None or single value)
+            zspc- approximate tic spacing in y direction (None or single value)
+            nxtics- number of minor x tics per major x tic (defaults to 0)
+            nytics- number of minor y tics per major y tic (defaults to 0)
+            nztics- number of minor y tics per major y tic (defaults to 0)
+            nice- 1 if plot is allowed to alter entered values slightly
+                  to make them look 'nice'- eg. 0.015999 can be rounded
+                  to 0.16, etc.; 0 if plot must not alter the values.
+                  Defaults to 1.
+            """
+        
+        if len(self.array_data) > 0:
+            if xmin is None:
+                xmin=self.array_data[0].xmin
+                for item in self.array_data[1:]:
+                    xmin=min([xmin,item.xmin])
+
+            if xmax is None:
+                xmax=self.array_data[0].xmax
+                for item in self.array_data[1:]:
+                    xmax=max([xmax,item.xmax])
+
+            if ymin is None:         
+                ymin=self.array_data[0].ymin
+                for item in self.array_data[1:]:
+                    ymin=min([ymin,item.ymin])
+
+            if ymax is None:     
+                ymax=self.array_data[0].ymax
+                for item in self.array_data[1:]:
+                    ymax=max([ymax,item.ymax])
+
+            if zmin is None:         
+                zmin=self.array_data[0].zmin
+                for item in self.array_data[1:]:
+                    zmin=min([zmin,item.zmin])
+
+            if zmax is None:     
+                zmax=self.array_data[0].zmax
+                for item in self.array_data[1:]:
+                    zmax=max([zmax,item.zmax])
+                    
+            if nice == 1:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                self.xmins=dxmin
+                self.xmaxs=dxmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+            
+                dymin,dymax,dylocs,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                     self.yaxistype)
+                self.ymins=dymin
+                self.ymaxs=dymax
+                self.ylocs=dylocs
+                self.yfmt=fmt
+                
+                dzmin,dzmax,dzlocs,fmt=GetNiceMinMax(zmin,zmax,zspc,
+                                                     self.zaxistype)
+                self.zmins=dzmin
+                self.zmaxs=dzmax
+                self.zlocs=dzlocs
+                self.zfmt=fmt
+            else:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                if type(xmin) not in [type([]),type((1,))]:
+                    self.xmins=[xmin]
+                else:
+                    self.xmins=xmin
+                if type(xmax) not in [type([]),type((1,))]:
+                    self.xmaxs=[xmax]
+                else:
+                    self.xmaxs=xmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+                    
+                dymin,dymax,dyspc,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                    self.yaxistype)
+                if type(ymin) not in [type([]),type((1,))]:
+                    self.ymins=[ymin]
+                else:
+                    self.ymins=ymin
+                    
+                if type(ymax) not in [type([]),type((1,))]:
+                    self.ymaxs=[ymax]
+                else:
+                    self.ymaxs=ymax
+                    
+                self.ylocs=dylocs
+                self.yfmt=fmt
+                    
+                dzmin,dzmax,dzspc,fmt=GetNiceMinMax(zmin,zmax,zspc,
+                                                    self.zaxistype)
+                if type(zmin) not in [type([]),type((1,))]:
+                    self.zmins=[zmin]
+                else:
+                    self.zmins=zmin
+                    
+                if type(zmax) not in [type([]),type((1,))]:
+                    self.zmaxs=[zmax]
+                else:
+                    self.zmaxs=zmax
+                    
+                self.zlocs=dzlocs
+                self.zfmt=fmt                
+        else:
+            if ((xmin is None) or (xmax is None) or (ymin is None) or
+                (ymax is None) or (zmin is None) or (zmax is None)):
+                raise RuntimeError,'set_extents: if no data has been set,'+\
+                  'then\nxmin/xmax/ymin/ymax/zmin/zmax must all be specified.'
+
+            if nice == 1:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                self.xmins=dxmin
+                self.xmaxs=dxmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+            
+                dymin,dymax,dylocs,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                     self.yaxistype)
+                self.ymins=dymin
+                self.ymaxs=dymax
+                self.ylocs=dylocs
+                self.yfmt=fmt
+            
+                dzmin,dzmax,dzlocs,fmt=GetNiceMinMax(zmin,zmax,zspc,
+                                                     self.zaxistype)
+                self.zmins=dzmin
+                self.zmaxs=dzmax
+                self.zlocs=dzlocs
+                self.zfmt=fmt
+            else:
+                dxmin,dxmax,dxlocs,fmt=GetNiceMinMax(xmin,xmax,xspc,
+                                                     self.xaxistype)
+                # TO DO: ADD A CHECK HERE AND MAKE SURE THEY ARE LISTS!
+                self.xmins=xmin
+                self.xmaxs=xmax
+                self.xlocs=dxlocs
+                self.xfmt=fmt
+                    
+                dymin,dymax,dyspc,fmt=GetNiceMinMax(ymin,ymax,yspc,
+                                                    self.yaxistype)
+                self.ymins=list(dymin)
+                self.ymaxs=list(dymax)
+                self.ylocs=list(dylocs)
+                self.yfmt=fmt
+
+                dzmin,dzmax,dzspc,fmt=GetNiceMinMax(zmin,zmax,zspc,
+                                                    self.zaxistype)
+                self.zmins=list(dzmin)
+                self.zmaxs=list(dzmax)
+                self.zlocs=list(dzlocs)
+                self.zfmt=fmt
+                                
+        self.set_axis()
+        
+    def set_axis(self):
+        """ Set the default axis """
+
+        new_axis_data={}
+        xbreaks=GetAxisBreaks(self.xmins,self.xmaxs,self.xaxistype)
+        ybreaks=GetAxisBreaks(self.ymins,self.ymaxs,self.yaxistype)
+        zbreaks=GetAxisBreaks(self.zmins,self.zmaxs,self.zaxistype)
+        ext=self.get_plot_extents(include_border=0)
+        pxlocs,xok=DataToPlot1D(ext[0],ext[3],self.xmins,self.xmaxs,
+                            self.xlocs,self.xaxistype)
+        pylocs,yok=DataToPlot1D(ext[1],ext[4],self.ymins,self.ymaxs,
+                            self.ylocs,self.yaxistype)
+        pzlocs,zok=DataToPlot1D(ext[2],ext[5],self.zmins,self.zmaxs,
+                            self.zlocs,self.zaxistype)
+
+        bottomlocs=[]
+        toplocs=[]
+        leftlocs=[]
+        rightlocs=[]
+        frontlocs=[]
+        backlocs=[]
+        ext=self.get_plot_extents(include_border=0)
+        
+        for item in pxlocs:
+            backlocs.append((item,ext[4]))
+            frontlocs.append((item,ext[1]))
+        for item in pylocs:
+            leftlocs.append((ext[0],item))
+            rightlocs.append((ext[3],item))
+        for item in pzlocs:
+            bottomlocs.append((ext[2],item))
+            toplocs.append((ext[5],item))
+    
+        # TO DO: add minor tics    
+        new_axis_data['topleft']=self.axis_data['topleft']
+        new_axis_data['topleft'].set_data(toplocs,list(self.xlocs),self.xfmt,
+                                      None,
+                                      (ext[0],ext[1],ext[5]),
+                                      (ext[0],ext[4],ext[5]),
+                                      xbreaks) 
+        new_axis_data['topright']=self.axis_data['topright']
+        new_axis_data['topright'].set_data(toplocs,list(self.xlocs),self.xfmt,
+                                      None,
+                                      (ext[3],ext[1],ext[5]),
+                                      (ext[3],ext[4],ext[5]),
+                                      xbreaks) 
+        new_axis_data['topfront']=self.axis_data['topfront']
+        new_axis_data['topfront'].set_data(toplocs,list(self.xlocs),self.xfmt,
+                                      None,
+                                      (ext[0],ext[1],ext[5]),
+                                      (ext[3],ext[1],ext[5]),
+                                      ybreaks) 
+        new_axis_data['topback']=self.axis_data['topback']
+        new_axis_data['topback'].set_data(toplocs,list(self.xlocs),self.xfmt,
+                                      None,
+                                      (ext[0],ext[4],ext[5]),
+                                      (ext[3],ext[4],ext[5]),
+                                      ybreaks)
+        new_axis_data['bottomleft']=self.axis_data['bottomleft']
+        new_axis_data['bottomleft'].set_data(bottomlocs,list(self.xlocs),
+                                             self.xfmt,
+                                      None,
+                                      (ext[0],ext[1],ext[2]),
+                                      (ext[0],ext[4],ext[2]),
+                                      xbreaks)
+        new_axis_data['bottomright']=self.axis_data['bottomright']
+        new_axis_data['bottomright'].set_data(bottomlocs,list(self.xlocs),
+                                              self.xfmt,
+                                      None,
+                                      (ext[3],ext[1],ext[2]),
+                                      (ext[3],ext[4],ext[2]),
+                                      xbreaks)
+        new_axis_data['bottomfront']=self.axis_data['bottomfront']
+        new_axis_data['bottomfront'].set_data(bottomlocs,list(self.xlocs),
+                                              self.xfmt,
+                                      None,
+                                      (ext[0],ext[1],ext[2]),
+                                      (ext[3],ext[1],ext[2]),
+                                      xbreaks)
+        new_axis_data['bottomback']=self.axis_data['bottomback']
+        new_axis_data['bottomback'].set_data(bottomlocs,list(self.xlocs),
+                                             self.xfmt,
+                                      None,
+                                      (ext[0],ext[4],ext[2]),
+                                      (ext[3],ext[4],ext[2]),
+                                      xbreaks)
+        new_axis_data['leftfront']=self.axis_data['leftfront']
+        new_axis_data['leftfront'].set_data(leftlocs,list(self.ylocs),
+                                            self.yfmt,
+                                      None,
+                                      (ext[0],ext[1],ext[2]),
+                                      (ext[0],ext[1],ext[5]),
+                                      ybreaks)
+        new_axis_data['leftback']=self.axis_data['leftback']
+        new_axis_data['leftback'].set_data(leftlocs,list(self.ylocs),self.yfmt,
+                                      None,
+                                      (ext[0],ext[4],ext[2]),
+                                      (ext[0],ext[4],ext[5]),
+                                      ybreaks)
+        new_axis_data['rightfront']=self.axis_data['rightfront']
+        new_axis_data['rightfront'].set_data(rightlocs,list(self.ylocs),
+                                             self.yfmt,
+                                      None,
+                                      (ext[3],ext[1],ext[2]),
+                                      (ext[3],ext[1],ext[5]),
+                                      ybreaks)
+        new_axis_data['rightback']=self.axis_data['rightback']
+        new_axis_data['rightback'].set_data(rightlocs,list(self.ylocs),
+                                            self.yfmt,
+                                      None,
+                                      (ext[3],ext[4],ext[2]),
+                                      (ext[3],ext[4],ext[5]),
+                                      ybreaks)
+        
+        self.axis_data=new_axis_data
+
+    #########################################################################
+    # Versions of the following functions will be required of most or       #
+    # all plot classes.                                                     #
+    #########################################################################
+
+    def add_data(self,xarr,yarr,zarr,label='',properties=None):
+        """ Add an array to the data to plot """
+        self.array_data.append(gvplot_grid_cartesian(xarr,yarr,zarr))
+        self.array_layer_defaults.append(
+            gvplot_layer_defaults(label,properties,interactive=0))
+        return len(self.array_data)-1
+
+    def set_labels(self,xlabel=None,ylabel=None,zlabel=None,title=None):
+        """ Set labels. """
+        self.label_data['xlabel'][1]=xlabel
+        self.label_data['ylabel'][1]=ylabel
+        self.label_data['zlabel'][1]=zlabel
+        self.label_data['title'][1]=title
+    
+    def set_plot_extents(self,pxmin,pxmax,pymin,pymax,pzmin,pzmax):
+        """ Set/Reset the extents covered by this plot in the view.
+            Update labels, axis, data info.
+        """
+        
+        oldpxmin=self.pxmin
+        oldpxmax=self.pxmax
+        oldpymin=self.pymin
+        oldpymax=self.pymax
+        oldpzmin=self.pzmin
+        oldpzmax=self.pzmax
+        
+        self.pxmin=pxmin
+        self.pxmax=pxmax
+        self.pymin=pymin
+        self.pymax=pymax
+        self.pzmin=pzmin
+        self.pzmax=pzmax
+
+        # Update label positions
+        for ckey in self.label_data.keys():
+            xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.label_data[ckey][0][0],
+                              GVPLOT_AXISTYPE_LINEAR)
+            ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.label_data[ckey][0][1],
+                              GVPLOT_AXISTYPE_LINEAR)
+            znew=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                              self.label_data[ckey][0][2],
+                              GVPLOT_AXISTYPE_LINEAR)
+            self.label_data[ckey][0]=(xnew,ynew,znew)
+
+        # Update axis information            
+        for ckey in self.axis_data.keys():
+            for idx in range(len(self.axis_data[key].vtics)):
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].vtics[idx][0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].vtics[idx][1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                znew=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                              self.axis_data[ckey].vtics[idx][2],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].vtics[idx]=(xnew,ynew,znew)
+                
+            for idx in range(len(self.axis_data[key].tics)):
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].tics[idx][0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].tics[idx][1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                znew=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                              self.axis_data[ckey].tics[idx][2],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].tics[idx]=(xnew,ynew,znew)
+
+            if self.axis_data[ckey].start is not None:
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].start[0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].start[1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                znew=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                              self.axis_data[ckey].start[2],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].start=(xnew,ynew,znew)
+                
+            if self.axis_data[ckey].end is not None:
+                xnew=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                              self.axis_data[ckey].end[0],
+                              GVPLOT_AXISTYPE_LINEAR)
+                ynew=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                              self.axis_data[ckey].end[1],
+                              GVPLOT_AXISTYPE_LINEAR)
+                znew=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                              self.axis_data[ckey].end[2],
+                              GVPLOT_AXISTYPE_LINEAR)
+                self.axis_data[ckey].end=(xnew,ynew,znew)
+
+            if self.axis_data[ckey].breaks is not None:
+                for idx in range(len(breaks)):
+                    xnew1=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                        self.axis_data[ckey].breaks[idx][0][0],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    ynew1=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                        self.axis_data[ckey].breaks[idx][0][1],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    znew1=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                        self.axis_data[ckey].breaks[idx][0][2],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    xnew2=DataToPlot1D(pxmin,pxmax,oldpxmin,oldpxmax,
+                        self.axis_data[ckey].breaks[idx][1][0],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    ynew2=DataToPlot1D(pymin,pymax,oldpymin,oldpymax,
+                        self.axis_data[ckey].breaks[idx][1][1],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    znew2=DataToPlot1D(pzmin,pzmax,oldpzmin,oldpzmax,
+                        self.axis_data[ckey].breaks[idx][1][2],
+                        GVPLOT_AXISTYPE_LINEAR)
+                    self.axis_data[ckey].breaks[idx]=((xnew1,ynew1,znew1),
+                                                      (xnew2,ynew2,znew2))
+                
+        if len(self.xmins) == 0:
+            return
+        
+        self.reset_axis()
+
+    def get_extents(self):
+        """ Get data extents: (xmins, xmaxs, ymins, ymaxs, zmins, zmaxs). """
+        return (self.xmins,self.xmaxs,self.ymins,
+                self.ymaxs,self.zmins,self.zmaxs)
+
+    def get_plot_extents(self,include_border=1):
+        """ Get plot extents with (include_border=1) or
+            without (include_border=0) borders.
+            (xmin,ymin,zmin,xmax,ymax,zmax).
+        """
+        if include_border == 1:
+            return (self.pxmin,self.pymin,self.pzmin,self.pxmax,self.pymax,
+                    self.pzmax)
+        
+        dxmin=self.pxmin+(self.pxmax-self.pxmin)*self.lborder
+        dxmax=self.pxmax-(self.pxmax-self.pxmin)*self.rborder
+        dymin=self.pymin+(self.pymax-self.pymin)*self.fborder
+        dymax=self.pymax-(self.pymax-self.pymin)*self.bkborder
+        dzmin=self.pzmin+(self.pzmax-self.pzmin)*self.bborder
+        dzmax=self.pzmax-(self.pzmax-self.pzmin)*self.tborder
+        
+        return (dxmin,dymin,dzmin,dxmax,dymax,dzmax)
+        
+        
+    def get_xyposition(self,x,y,z):
+        """ Get the data position corresponding to GvViewPlot position x,y,z.
+            x, y and z may be single values or same-size arrays.
+        """
+
+        # Get plot extents minus borders.
+        dxmin=self.pxmin+(self.pxmax-self.pxmin)*self.lborder
+        dxmax=self.pxmax-(self.pxmax-self.pxmin)*self.rborder
+        dymin=self.pymin+(self.pymax-self.pymin)*self.fborder
+        dymax=self.pymax-(self.pymax-self.pymin)*self.bkborder
+        dzmin=self.pzmin+(self.pzmax-self.pzmin)*self.bborder
+        dzmax=self.pzmax-(self.pzmax-self.pzmin)*self.tborder
+
+        arrshp=None
+        if type(x) == type(Numeric.array([])):
+            arrshp=x.shape
+            x=Numeric.ravel(x)
+
+        if type(y) == type(Numeric.array([])):
+            y=Numeric.ravel(y)
+
+        if type(y) == type(Numeric.array([])):
+            z=Numeric.ravel(z)
+            
+        x,xok=PlotToData1D(dxmin,dxmax,self.xmins,self.xmaxs,x,self.xaxistype)
+        y,yok=PlotToData1D(dymin,dymax,self.ymins,self.ymaxs,y,self.yaxistype)
+        z,zok=PlotToData1D(dzmin,dzmax,self.zmins,self.zmaxs,z,self.zaxistype)
+
+        okarr=Numeric.where(xok==0,0,yok)
+        okarr=Numeric.where(zok==0,0,okarr)
+
+        if arrshp is not None:
+            x=Numeric.reshape(x,arrshp)
+            y=Numeric.reshape(y,arrshp)
+            z=Numeric.reshape(z,arrshp)
+            okarr=Numeric.reshape(okarr,arrshp)
+
+        return (x,y,z,okarr)
+
+    def get_plotposition(self,x,y,z):
+        """ Get the GvViewPlot position corresponding to data position x,y.
+            x and y may be single values or same-length 1-D arrays.
+        """
+
+        # Get plot extents minus borders.
+
+        dxmin=self.pxmin+(self.pxmax-self.pxmin)*self.lborder
+        dxmax=self.pxmax-(self.pxmax-self.pxmin)*self.rborder
+        dymin=self.pymin+(self.pymax-self.pymin)*self.fborder
+        dymax=self.pymax-(self.pymax-self.pymin)*self.bkborder
+        dzmin=self.pzmin+(self.pzmax-self.pzmin)*self.bborder
+        dzmax=self.pzmax-(self.pzmax-self.pzmin)*self.tborder
+
+        arrshp=None
+        if type(x) == type(Numeric.array([])):
+            arrshp=x.shape
+            x=Numeric.ravel(x)
+
+        if type(y) == type(Numeric.array([])):
+            y=Numeric.ravel(y)
+
+        if type(y) == type(Numeric.array([])):
+            z=Numeric.ravel(z)
+            
+        x,xok=DataToPlot1D(dxmin,dxmax,self.xmins,self.xmaxs,x,self.xaxistype)
+        y,yok=DataToPlot1D(dymin,dymax,self.ymins,self.ymaxs,y,self.yaxistype)
+        z,zok=DataToPlot1D(dzmin,dzmax,self.zmins,self.zmaxs,z,self.zaxistype)
+
+        okarr=Numeric.where(xok==0,0,yok)
+        okarr=Numeric.where(zok==0,0,okarr)
+
+        if arrshp is not None:
+            x=Numeric.reshape(x,arrshp)
+            y=Numeric.reshape(y,arrshp)
+            z=Numeric.reshape(z,arrshp)
+            okarr=Numeric.reshape(okarr,arrshp)
+
+
+        return (x,y,z,okarr)        
+
+    def set_arraylayer_properties(self,idx,props):
+        """ Set the display properties for the idxth data layer.
+            idx- index of data
+            props- a dictionary of properties to set (keys) and their
+                   values.
+
+            An error will be raised if there is no idx'th data array
+            to plot.
+            
+            Example properties:
+            _area_fill_color='1.0 0.0 0.0 1.0'
+            _area_edge_color='1.0'
+            _gl_antialias='1' (turn on display antialiasing)
+        """
+        self.array_layer_defaults[idx]=props
+
+    def set_axislayer_properties(self,props):
+        """ Set the display properties of the axis layer
+        
+            Example properties:
+            _line_color='1.0 0.0 0.0 1.0'
+            _line_width='1.0'
+            _gl_antialias='1' (turn on display antialiasing)
+            
+        """
+        self.axis_layer_properties=props
+
+    def set_label_color(self,color):
+        """ Set label layer color to color. """
+        
+        import gvogrfs
+        cstr2=gvogrfs.gv_to_ogr_color(color)
+        if len(cstr2) < 9:
+            cstr2=cstr2+'FF'
+        if self.label_layer_properties.has_key('_gv_ogrfs_point'):
+            ogrfs=gvogrfs.OGRFeatureStyle(
+                self.label_layer_properties['_gv_ogrfs_point'])
+            if ogrfs.has_part('LABEL'):
+                part=ogrfs.get_part('LABEL')
+                part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                ogrfs.remove_part('LABEL')
+                ogrfs.add_part(part)
+            if ogrfs.has_part('SYMBOL'):
+                part=ogrfs.get_part('SYMBOL')
+                part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                ogrfs.remove_part('SYMBOL')
+                ogrfs.add_part(part)
+            self.label_layer_properties['_gv_ogrfs_point']=ogrfs.unparse()
+             
+    def set_axis_color(self,color):
+        """ Set all axis and axis label colours to color.
+            color is a tuple of 4 values, each between
+            0 and 1, representing red/green/blue/alpha
+            components.
+        """
+        cstr1=str(color[0])+' '+str(color[1])+' '+str(color[2])+\
+               ' '+str(color[3])
+        self.axis_layer_properties['_line_color']=cstr1
+
+        import gvogrfs
+        cstr2=gvogrfs.gv_to_ogr_color(color)
+        if len(cstr2) < 9:
+            cstr2=cstr2+'FF'
+        for caxis in self.axis_data.values():
+            caxis.line_properties['_line_color']=cstr1
+            if caxis.vtics_properties.has_key('_gv_ogrfs'):
+                ogrfs=gvogrfs.OGRFeatureStyle(
+                    caxis.vtics_properties['_gv_ogrfs'])
+                if ogrfs.has_part('LABEL'):
+                    part=ogrfs.get_part('LABEL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('LABEL')
+                    ogrfs.add_part(part)
+                if ogrfs.has_part('SYMBOL'):
+                    part=ogrfs.get_part('SYMBOL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('SYMBOL')
+                    ogrfs.add_part(part)
+                caxis.vtics_properties['_gv_ogrfs']=ogrfs.unparse()
+
+            if caxis.tics_properties.has_key('_gv_ogrfs'):
+                ogrfs=gvogrfs.OGRFeatureStyle(
+                    caxis.tics_properties['_gv_ogrfs'])
+                if ogrfs.has_part('LABEL'):
+                    part=ogrfs.get_part('LABEL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('LABEL')
+                    ogrfs.add_part(part)
+                if ogrfs.has_part('SYMBOL'):
+                    part=ogrfs.get_part('SYMBOL')
+                    part.set_parm(gvogrfs.OGRFeatureStyleParam('c:'+cstr2))
+                    ogrfs.remove_part('SYMBOL')
+                    ogrfs.add_part(part)
+                caxis.tics_properties['_gv_ogrfs']=ogrfs.unparse()
+  
+
+    def set_axis_properties(self,key,lprops,vprops,tprops):
+        """ Set the properties of the shapes forming the axis
+            referenced by 'key'.  Default keys are 'top, 'bottom',
+            'left','right'.  An error will be raised if the axis
+            dictionary doesn't contain key.
+
+            Parameters:
+                key- key to index axis
+                lprops- line properties (if different from layer default)
+                vprops- vtic properties
+                tprops- tic properties
+                
+            Example properties: (defaults for left axis shown here)
+            _gv_ogrfs='LABEL(c:#000000FF,'+\
+            'f:"-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-*-*",
+            t:{label},a:0.0,s:1.0,dx:-35.0,dy:5.0);'+\
+            'SYMBOL(c:#000000FF,id:ogr-sym-10,a:270.0,s:1.0)'
+
+            Note: the label property is always used to get text
+                  in CreateAxisLayer, so the t:{label} portion
+                  of this should be left alone.
+        """
+           
+        self.axis_data[key].line_properties=lprops
+        self.axis_data[key].vtics_properties=vprops
+        self.axis_data[key].tics_properties=tprops
+        
+
+    def set_border_padding(self,top,bottom,left,right,front,back):
+        """ Set border padding as a fraction of the plot
+            extents (0-1).
+            top- top padding
+            bottom- bottom
+            left- left
+            right- right
+            front- front
+            back- back
+        """
+        self.tborder=top
+        self.bborder=bottom
+        self.lborder=left
+        self.rborder=right
+        self.fborder=front
+        self.bkborder=back
+
+    def set_labellayer_properties(self,label_props):
+        """ Set label layer properties:
+            Example:
+            _gv_ogrfs_point='LABEL(c:#000000FF,
+            f:"-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-*-*",t:{label})'
+
+            Note: the label property is always used to get text
+                  in CreateLabelLayer, so the t:{label} portion
+                  of this should be left alone.            
+        """
+        self.label_layer_properties=label_props
+
+    def add_axis(self,key,properties,start_tuple,end_tuple,breaks=None,
+                 type=GVPLOT_AXIS_NOTICS):
+        """ Add an axis (line):
+            key- key to reference the axis
+            properties- axis properties
+            start_tuple- an (x,y) tuple in data coordinates representing
+                         the start point of the line.
+            end_tuple- an (x,y) tuple in data coordinates representing
+                       the start point of the line.
+            breaks- breaks in the axis/line (defaults to None, must be
+                    a list of start/end tuples if present).  Also in
+                    data coordinates.
+            type- type of axis (0-4: not shown, shown but no tics, shown
+                  with major tics but no labels, shown with major tics
+                  and labels, shown with major tics and labels and minor
+                  tics).
+                         
+        """
+        pass
+    
+
+class gvplot_layer_defaults:
+    def __init__(self,label="",properties=None,interactive=0):
+        """ Store display information for a layer. """
+
+        # default display properties for point/line layers
+        if properties is not None:
+            self.properties=properties
+        else:
+            self.properties={}
+            self.properties['_gl_antialias']='1'
+            self.properties['_line_width']='1'
+            self.properties['_line_color']='1.0 0.0 0.0 1.0'
+            self.properties['_gl_antialias']='1'
+            
+        self.label=label
+
+        # interactive: 1 if user should be able to edit
+        #              the layer; 0 otherwise.
+        self.interactive=interactive
+
+
+# supporting classes for storing plot class components
+
+class gvplot_array_cartesian:
+    """ Store arrays and related information.
+        Discard nan's and inf's and calculate max/mins
+        for plotting convenience.
+        xarr- array or list of arrays
+        yarr- array or list of arrays
+        zarr- array or list of arrays
+
+        xarr, yarr, zarr must be same size.
+    """
+    def __init__(self,xarr,yarr,zarr=None):
+
+
+        if type(xarr) == type(Numeric.array([1,2])):
+            # Below:
+            # Data points must have x, y, and z (if present)
+            # not equal to nan or inf to be included.
+            # This isn't implemented properly yet though
+            # because of problems on some platforms with nan/inf
+            okarr=Numeric.ones(xarr.shape)
+
+            # nt and irix both don't seem to have a notion
+            # of nan and inf, so leave out the checks for
+            # them...
+            #if os.name != 'nt':
+            #    okarr=Numeric.where(xarr == float('inf'),0,okarr)
+            #    okarr=Numeric.where(xarr == float('nan'),0,okarr)
+            #    okarr=Numeric.where(yarr == float('inf'),0,okarr)
+            #    okarr=Numeric.where(yarr == float('nan'),0,okarr)
+
+            #    if zarr is not None:
+            #        okarr=Numeric.where(zarr == float('inf'),0,okarr)
+            #        okarr=Numeric.where(zarr == float('nan'),0,okarr)
+
+            self.xarr=Numeric.compress(okarr==1,xarr)
+            self.yarr=Numeric.compress(okarr==1,yarr)
+        
+            if zarr is not None:
+                self.zarr=Numeric.compress(okarr==1,zarr)
+        
+            self.xmin=min(self.xarr)
+            self.xmax=max(self.xarr)
+            self.ymin=min(self.yarr)
+            self.ymax=max(self.yarr)
+        
+            if zarr is not None:
+                self.zmin=min(self.zarr)
+                self.zmax=max(self.zarr)
+            else:
+                self.zarr=None
+                self.zmin=None
+                self.zmax=None
+        elif type(xarr) == type([1,2]):
+            self.xarr=xarr
+            self.yarr=yarr
+            self.zarr=zarr
+            # If nan/inf problems get sorted out, add checking
+            # code below...
+            self.xmin=min(self.xarr[0])
+            self.xmax=max(self.xarr[0])
+            self.ymin=min(self.yarr[0])
+            self.ymin=max(self.yarr[0])
+            if zarr is not None:
+                self.zmin=min(self.zarr[0])
+                self.zmax=max(self.zarr[0])
+                
+            for idx in range(1,len(xarr)):
+                self.xmin=min(min(self.xarr[idx]),self.xmin)
+                self.xmax=max(max(self.xarr[idx]),self.xmax)
+                self.ymin=min(min(self.yarr[idx]),self.ymin)
+                self.ymax=max(max(self.yarr[idx]),self.ymax)
+                if zarr is not None:
+                    self.zmin=min(min(self.zarr[idx]),self.zmin)
+                    self.zmax=max(max(self.zarr[idx]),self.zmax)
+                
+
+
+class gvplot_grid_cartesian:
+    """ Store arrays and related information.
+        Discard nan's and inf's and calculate max/mins
+        for plotting convenience.
+        xarr- 2D array or list of arrays
+        yarr- 2D array or list of arrays
+        zarr- 2D array or list of arrays
+
+        xarr, yarr, zarr must be same size.
+    """
+    def __init__(self,xarr,yarr,zarr=None):
+
+        if type(xarr) == type(Numeric.array([1,2])):
+            self.xarr=xarr
+            self.yarr=yarr
+            self.zarr=zarr
+        
+            self.xmin=min(Numeric.ravel(self.xarr))
+            self.xmax=max(Numeric.ravel(self.xarr))
+            self.ymin=min(Numeric.ravel(self.yarr))
+            self.ymax=max(Numeric.ravel(self.yarr))
+        
+            if zarr is not None:
+                self.zmin=min(Numeric.ravel(self.zarr))
+                self.zmax=max(Numeric.ravel(self.zarr))
+            else:
+                self.zarr=None
+                self.zmin=None
+                self.zmax=None
+                
+        elif type(xarr) == type([1,2]):
+            self.xarr=xarr
+            self.yarr=yarr
+            self.zarr=zarr
+ 
+            self.xmin=min(Numeric.ravel(self.xarr[0]))
+            self.xmax=max(Numeric.ravel(self.xarr[0]))
+            self.ymin=min(Numeric.ravel(self.yarr[0]))
+            self.ymin=max(Numeric.ravel(self.yarr[0]))
+            if zarr is not None:
+                self.zmin=min(Numeric.ravel(self.zarr[0]))
+                self.zmax=max(Numeric.ravel(self.zarr[0]))
+                
+            for idx in range(1,len(xarr)):
+                self.xmin=min(min(Numeric.ravel(self.xarr[idx])),self.xmin)
+                self.xmax=max(max(Numeric.ravel(self.xarr[idx])),self.xmax)
+                self.ymin=min(min(Numeric.ravel(self.yarr[idx])),self.ymin)
+                self.ymax=max(max(Numeric.ravel(self.yarr[idx])),self.ymax)
+                if zarr is not None:
+                    self.zmin=min(Numeric.ravel(min(self.zarr[idx])),self.zmin)
+                    self.zmax=max(Numeric.ravel(max(self.zarr[idx])),self.zmax)
+                
+
+
+class gvplot_axis:
+    """ Axis/line properties. """
+    def __init__(self,displaytype=GVPLOT_AXIS_VTICSLABELS,tics_per_vtic=4):
+        # Display defaults, set before plotting.  Can
+        # be overridden through function calls.  These
+        # are not erased when data max/mins are reset.
+
+        self.display_type=displaytype # whether or not to show axis, tics, etc.
+        self.tics_per_vtic=tics_per_vtic
+
+        self.vtics_properties={}
+        self.tics_properties={}
+        
+        # Tic label/format defaults, except for label text
+        # (determined by plotting functions).
+        self.vtics_properties['_gv_ogrfs']='LABEL(c:#000000FF,'+\
+            'f:"'+DEFAULT_FONT+'",t:{label},a:0.0,s:1.0,dx:0.0,dy:0.0);'+\
+            'SYMBOL(c:#000000FF,id:ogr-sym-10,a:0.0,s:1.0)'
+        self.tics_properties['_gv_ogrfs']=\
+                     'SYMBOL(c:#000000FF,id:ogr-sym-10,a:0.0,s:0.5)'
+
+        # Axis line properties (color, etc) if different from overall
+        # axis layer default
+        self.line_properties={}
+        
+        # Properties calculated by plotting object
+        
+        # major tics (bigger, may have values)
+        self.vtics=[]   # xyz locations in view coords.
+        self.vtics_labels=[] # text for labels
+        self.vtics_fmt=None  # format for labels eg. "%6f"
+
+        # minor tics
+        self.tics=[]
+
+        # start and end points of line in
+        # view area coordinates
+        self.start=None
+        self.end=None
+
+        # discontinuities: each a tuple of 2 values
+        # (start,end) where (0 <start < end < 1),and
+        # breaks[idx][1] < breaks[idx+1][0].  Start
+        # and end correspond to fractions of the distance
+        # along the axis.
+        self.breaks=None 
+
+    def set_data(self,vtics,vtics_labels,vtics_fmt,tics,start,end,breaks):
+        """ Set the tic data. """
+        self.vtics=vtics
+        self.vtics_labels=vtics_labels
+        self.vtics_fmt=vtics_fmt
+        self.tics=tics
+        self.start=start
+        self.end=end
+        self.breaks=breaks
+
+
+#######################################################################
+# Layer creation functions                                            #
+#######################################################################
+
+def CreateAxisLayer(plot_data,name=None):
+    """ Create Axis and lines (including tics and axis numbering) """
+    
+    if name is None:
+        name=plot_data.plot_label+' '+GVPLOT_AXIS_LAYER
+        
+    shapes=gview.GvShapes(name=name)
+    layer=gview.GvShapesLayer(shapes=shapes)
+    for caxis in plot_data.axis_data.values():
+        if caxis.display_type == GVPLOT_AXIS_NOTSHOWN:
+            continue
+        x0,y0,z0=caxis.start
+        xf,yf,zf=caxis.end
+        if caxis.breaks is None:
+            shp=gview.GvShape(type=gview.GVSHAPE_LINE)
+            shp.set_node(x0,y0,z0,0)
+            shp.set_node(xf,yf,zf,1)
+            
+            shapes.append(shp)
+        else:
+            last_x=x0
+            last_y=y0
+            last_z=z0
+            for btuple in caxis.breaks:
+                shp=gview.GvShape(type=gview.GVSHAPE_LINE)
+                shp.set_node(last_x,last_y,last_z,0)
+                shp.set_node(x0 +(btuple[0]*(xf-x0)),
+                             yf +(btuple[0]*(yf-y0)),
+                             zf +(btuple[0]*(zf-z0)),1)
+                last_x = x0 + btuple[1]*(xf-x0)
+                last_y = y0 + btuple[1]*(yf-y0)
+                last_z = z0 + btuple[1]*(zf-z0)
+                shapes.append(shp)
+                
+        if caxis.display_type == GVPLOT_AXIS_NOTICS:
+            continue
+        
+        for idx in range(len(caxis.vtics)):
+            shp=gview.GvShape(type=gview.GVSHAPE_POINT)
+            shp.set_node(caxis.vtics[idx][0],caxis.vtics[idx][1])
+            if caxis.display_type != GVPLOT_AXIS_VTICS:
+                shp.set_property('label',
+                     caxis.vtics_fmt % caxis.vtics_labels[idx])
+            for ckey in caxis.vtics_properties.keys():
+                shp.set_property(ckey,
+                  caxis.vtics_properties[ckey])
+            shapes.append(shp)
+            
+        if caxis.display_type != GVPLOT_AXIS_VTICSLABELSTICS:
+            continue
+        
+        for idx in range(len(caxis.tics)):
+            shp=gview.GvShape(type=gview.GVSHAPE_POINT)
+            shp.set_node(caxis.tics[idx][0],caxis.tics[idx][1])
+            for ckey in caxis.tics_properties.keys():
+                shp.set_property(ckey,
+                  caxis.tics_properties[ckey])
+            shapes.append(shp)    
+
+    if len(shapes) == 0:
+        return None
+
+    for ckey in plot_data.axis_layer_properties.keys():
+        layer.set_property(ckey,plot_data.axis_layer_properties[ckey])
+        
+    return layer
+
+        
+def Create2DBorderLayer(plot_data,name=None):
+    """ Create the plot background. """
+
+    if ((plot_data.show_inner_border == 0) and
+        (plot_data.show_outer_border == 0)):
+        return None
+    
+    if name is None:
+        name=plot_data.plot_label+' '+GVPLOT_BORDER_LAYER
+        
+    shapes=gview.GvShapes(name=name)
+    layer=gview.GvShapesLayer(shapes=shapes)
+    inner=plot_data.get_plot_extents(include_border=0)
+    outer=plot_data.get_plot_extents(include_border=1)
+
+    # shapes: outer rings clockwise; inner rings
+    #         counterclockwise.
+    if plot_data.show_outer_border == 1:
+        nshp=gview.GvShape(type=gview.GVSHAPE_AREA)
+        nshp.set_node(outer[0],outer[3],0.0,0,0)
+        nshp.set_node(outer[2],outer[3],0.0,1,0)
+        nshp.set_node(outer[2],outer[1],0.0,2,0)
+        nshp.set_node(outer[0],outer[1],0.0,3,0)
+        nshp.set_node(outer[0],outer[3],0.0,4,0)
+        nshp.set_node(inner[0],inner[3],0.0,0,1)
+        nshp.set_node(inner[0],inner[1],0.0,1,1)
+        nshp.set_node(inner[2],inner[1],0.0,2,1)
+        nshp.set_node(inner[2],inner[3],0.0,3,1)
+        nshp.set_node(inner[0],inner[3],0.0,4,1)        
+        for cprop in plot_data.outer_border_props.keys():
+            nshp.set_property(cprop,
+                          plot_data.outer_border_props[cprop])
+        shapes.append(nshp)
+
+    if plot_data.show_inner_border == 1:
+        nshp=gview.GvShape(type=gview.GVSHAPE_AREA)
+        nshp.set_node(inner[0],inner[3],0.0,0,0)
+        nshp.set_node(inner[2],inner[3],0.0,1,0)
+        nshp.set_node(inner[2],inner[1],0.0,2,0)
+        nshp.set_node(inner[0],inner[1],0.0,3,0)
+        nshp.set_node(inner[0],inner[3],0.0,4,0)
+        for cprop in plot_data.inner_border_props.keys():
+            nshp.set_property(cprop,
+                          plot_data.inner_border_props[cprop])
+        shapes.append(nshp)
+
+    return layer    
+
+def CreateLabelLayer(plot_data,name=None):
+    """ Create annotation (label) layer. """
+    
+    if name is None:
+        name=plot_data.plot_label+' '+GVPLOT_LABEL_LAYER
+        
+    shapes=gview.GvShapes(name=name)
+    layer=gview.GvShapesLayer(shapes=shapes)
+    for item in plot_data.label_data.values():
+        if item[1] is None:
+            continue
+
+        shp=gview.GvShape(type=gview.GVSHAPE_POINT)
+        shp.set_node(item[0][0],item[0][1],item[0][2])
+        shp.set_property('label',item[1])
+        shapes.append(shp)
+
+    if len(shapes) == 0:
+        return None
+
+    for prop in plot_data.label_layer_properties.keys():
+        layer.set_property(prop,
+                    plot_data.label_layer_properties[prop])
+
+    return layer
+
+
+def Create2DLegendLayer(plot_data,name=None):
+    """ Create a legend layer. """
+
+    if plot_data.show_legend == 0:
+        return None
+    
+    if name is None:
+        name=plot_data.plot_label+' '+GVPLOT_LEGEND_LAYER
+            
+    shapes=gview.GvShapes(name=name)
+    layer=gview.GvShapesLayer(shapes=shapes)
+    ext=plot_data.get_plot_extents(include_border=1)
+    
+    xrng=ext[3]-ext[0]
+    yrng=ext[4]-ext[1]
+    x0 = plot_data.legend_location[0]*xrng+ext[0]
+    y0 = plot_data.legend_location[1]*yrng+ext[1]
+
+    idx=0
+    for item in plot_data.array_layer_defaults:
+        try:
+            color=item.properties['_line_color']
+        except:
+            continue
+        
+        tmp=string.split(color)
+        rgba=[]
+        for tmp2 in tmp:
+            rgba.append(float(tmp2))
+
+        cstr=gvogrfs.gv_to_ogr_color(rgba)
+            
+        if (item.label is not None) and (len(item.label) > 0):
+            dshp=gview.GvShape(type=gview.GVSHAPE_POINT)
+            dshp.set_node(x0,y0)
+            dshp.set_property('_gv_ogrfs','LABEL(c:'+cstr+\
+              ',t:"'+item.label+'",f:"'+DEFAULT_FONT+'")')
+            shapes.append(dshp)
+
+            
+        y0=y0-0.1*yrng                
+        idx=idx+1
+
+    return layer
+
+def CreateGridArrayDataLayer(plot_data,index,name=None):
+    """ Create a 3D grid data layer.
+        plot_data- plot object (eg. gvplot_grid_cartesian)
+        index- index to array to plot
+        name- layer name (if set to None, the layer name
+              will be 'Data: '+plot_data.array_layer_defaults.label)
+    """
+   
+    if index > len(plot_data.array_data)-1:
+        raise RuntimeError,'Create Data Layer: invalid data index!'
+
+    if name is None:
+        if plot_data.array_layer_defaults[index].label is not None:
+            name=plot_data.plot_label+GVPLOT_DATA_LAYER+' '+\
+              plot_data.array_layer_defaults[index].label
+        else:
+            name=plot_data.plot_label+GVPLOT_DATA_LAYER+' '+str(index)
+            
+    shapes=gview.GvShapes(name=name)
+    layer=gview.GvShapesLayer(shapes=shapes) 
+    
+    if type(plot_data.array_data[index].xarr) == type([]):
+        xinlist=plot_data.array_data[index].xarr
+        yinlist=plot_data.array_data[index].yarr
+        zinlist=plot_data.array_data[index].zarr
+    else:
+        xinlist=[plot_data.array_data[index].xarr]
+        yinlist=[plot_data.array_data[index].yarr]
+        zinlist=[plot_data.array_data[index].zarr]
+        
+    ext=plot_data.get_plot_extents(include_border=0)
+    okinlist=[]
+    
+    for idx in range(len(xinlist)):
+        xarr,xok=DataToPlot1D(ext[0],ext[3],
+                              plot_data.xmins,plot_data.xmaxs,
+                              xinlist[idx],
+                              plot_data.xaxistype)
+
+        yarr,yok=DataToPlot1D(ext[1],ext[4],
+                              plot_data.ymins,plot_data.ymaxs,
+                              yinlist[idx],
+                              plot_data.yaxistype)
+
+        okarr=Numeric.where(yok == 0,0,xok)
+    
+        zarr,zok=DataToPlot1D(ext[2],ext[5],
+                              plot_data.zmins,plot_data.zmaxs,
+                              zinlist[idx],
+                              plot_data.zaxistype)
+        okarr=Numeric.where(zok == 0,0,okarr)
+
+        for idx in range(xarr.shape[0]-1):
+            for idx2 in range(xarr.shape[1]-1):
+                if (okarr[idx,idx2]+okarr[idx,idx2+1]+okarr[idx+1,idx2]+
+                    okarr[idx+1,idx2+1] == 4):
+                    ashp=gview.GvShape(type=gview.GVSHAPE_AREA)
+                    ashp.add_node(xarr[idx,idx2],yarr[idx,idx2],
+                                  zarr[idx,idx2])
+                    ashp.add_node(xarr[idx+1,idx2],yarr[idx+1,idx2],
+                                  zarr[idx+1,idx2])
+                    ashp.add_node(xarr[idx+1,idx2+1],yarr[idx+1,idx2+1],
+                                  zarr[idx+1,idx2+1])
+                    ashp.add_node(xarr[idx,idx2],yarr[idx,idx2],
+                                  zarr[idx,idx2])
+                    shapes.append(ashp)
+                    ashp=gview.GvShape(type=gview.GVSHAPE_AREA)
+                    ashp.add_node(xarr[idx,idx2],yarr[idx,idx2],
+                                  zarr[idx,idx2])
+                    ashp.add_node(xarr[idx,idx2+1],yarr[idx,idx2+1],
+                                  zarr[idx,idx2+1])
+                    ashp.add_node(xarr[idx+1,idx2+1],yarr[idx+1,idx2+1],
+                                  zarr[idx+1,idx2+1])
+                    ashp.add_node(xarr[idx,idx2],yarr[idx,idx2],
+                                  zarr[idx,idx2])
+                    shapes.append(ashp)
+                    #lshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+                    #lshp.add_node(xarr[idx,idx2],yarr[idx,idx2],
+                    #              zarr[idx,idx2])
+                    #lshp.add_node(xarr[idx+1,idx2],yarr[idx+1,idx2],
+                    #              zarr[idx+1,idx2])
+                    #lshp.add_node(xarr[idx+1,idx2+1],yarr[idx+1,idx2+1],
+                    #              zarr[idx+1,idx2+1])
+                    #lshp.add_node(xarr[idx,idx2+1],yarr[idx,idx2+1],
+                    #              zarr[idx,idx2+1])
+                    #lshp.add_node(xarr[idx,idx2],yarr[idx,idx2],
+                    #              zarr[idx,idx2])
+                    #shapes.append(lshp)
+    
+    for ckey in plot_data.array_layer_defaults[index].properties.keys():
+        layer.set_property(ckey,
+              plot_data.array_layer_defaults[index].properties[ckey])
+
+    return layer
+            
+
+def CreateArrayDataLayer(plot_data,index,name=None):
+    """ Create a data layer.
+        plot_data- plot object (eg. gvplot_2Ddata_cartesian)
+        index- index to array to plot
+        name- layer name (if set to None, the layer name
+              will be 'Data: '+plot_data.array_layer_defaults.label)
+    """
+   
+    if index > len(plot_data.array_data)-1:
+        raise RuntimeError,'Create Data Layer: invalid data index!'
+
+    if name is None:
+        if plot_data.array_layer_defaults[index].label is not None:
+            name=plot_data.plot_label+GVPLOT_DATA_LAYER+' '+\
+              plot_data.array_layer_defaults[index].label
+        else:
+            name=plot_data.plot_label+GVPLOT_DATA_LAYER+' '+str(index)
+            
+    shapes=gview.GvShapes(name=name)
+    layer=gview.GvShapesLayer(shapes=shapes) 
+
+    ext=plot_data.get_plot_extents(include_border=0)
+    okinlist=[]
+    
+    if type(plot_data.array_data[index].xarr) == type([]):
+        xinlist=plot_data.array_data[index].xarr
+        yinlist=plot_data.array_data[index].yarr
+        if plot_data.array_data[index].zarr is not None:
+            zinlist=plot_data.array_data[index].zarr
+    else:
+        xinlist=[plot_data.array_data[index].xarr]
+        yinlist=[plot_data.array_data[index].yarr]
+        if plot_data.array_data[index].zarr is not None:
+            zinlist=[plot_data.array_data[index].zarr]
+
+    for idx in range(len(xinlist)):
+        xarr,xok=DataToPlot1D(ext[0],ext[3],
+                              plot_data.xmins,plot_data.xmaxs,
+                              xinlist[idx],
+                              plot_data.xaxistype)
+
+        yarr,yok=DataToPlot1D(ext[1],ext[4],
+                              plot_data.ymins,plot_data.ymaxs,
+                              yinlist[idx],
+                              plot_data.yaxistype)
+
+        okarr=Numeric.where(yok == 0,0,xok)
+    
+        if plot_data.array_data[index].zarr is not None:
+            zarr,zok=DataToPlot1D(ext[2],ext[5],
+                                plot_data.zmins,plot_data.zmaxs,
+                                zinlist[idx],
+                                plot_data.zaxistype)
+            okarr=Numeric.where(zok == 0,0,okarr)
+        else:
+            zarr=Numeric.zeros(Numeric.shape(xarr),Numeric.Float64)
+
+        # NOTE: taking the for-loop down to the c-level didn't
+        # speed up the plot time.  The render time seems
+        # to be the limiting factor.
+        newshps=gview.gv_shapes_lines_for_vecplot(xarr,yarr,zarr,okarr)
+        for idx in range(len(newshps)):
+            shapes.append(newshps[idx].copy())
+
+        #lshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        #last_valid=0
+        #for idx2 in range(len(xarr)):
+        #    if okarr[idx2] == 1:
+        #        lshp.add_node(xarr[idx2],yarr[idx2],zarr[idx2],0)
+        #        last_valid=1
+        #    else:
+        #        if last_valid == 1:
+        #            shapes.append(lshp)
+        #            lshp=gview.GvShape(type=gview.GVSHAPE_LINE)
+        #            last_valid=0
+              
+    #if last_valid == 1:
+    #    shapes.append(lshp)
+    
+    for ckey in plot_data.array_layer_defaults[index].properties.keys():
+        layer.set_property(ckey,
+              plot_data.array_layer_defaults[index].properties[ckey])
+
+    return layer
+
+
+# PLOT class (GvPlot)- stores one or more plots
+class GvSimplePlot(gview.GvViewArea):
+    """ View area wrapper designed for plotting.
+        NOTE: window containing GvSimplePlot view area must
+        have its show_all function called for the first time
+        ATER the GvSimplePlot widget is inserted, but
+        BEFORE plot is called.  If these two conditions
+        don't hold, then the configure event that
+        sets up the plot area's internal c-level
+        size parameters sets them to invalid values,
+        and the plot extents will be set incorrectly
+        (plot will be a teeny blob in the center).
+        Calling show_all before and after the widget
+        is inserted also will not work (the second
+        call doesn't seem to do anything).
+    """
+    def __init__(self, _obj=None, bgcolor=(1.0,1.0,1.0,1.0)):
+        gview.GvViewArea.__init__(self,_obj)
+        self.set_background_color(bgcolor)
+        self.set_border_padding()
+        self.bgcolor=bgcolor
+        self.mode='2D'
+
+        # plot information
+        self.plots=[]
+
+
+    def set_border_padding(self,top=DEFAULT_EDGE_PADDING,
+                         bottom=DEFAULT_EDGE_PADDING,
+                         left=DEFAULT_EDGE_PADDING,
+                         right=DEFAULT_EDGE_PADDING):
+        """ Set border padding for plot as a fraction of view
+            area (top,bottom,left, right).
+        """
+        self.tborder=top
+        self.bborder=bottom
+        self.lborder=left
+        self.rborder=right
+        
+    def oplot(self, yarr=None, xarr=None, xmin=None,xmax=None,xspc=None,
+              ymin=None, ymax=None, yspc=None,datalabel=None,
+              color=(0.0,0.0,1.0,1.0),drawstyle='_', reset_extents=1,
+              with_legend=0,interactive=0):
+        """ Plot overtop of an existing simple 2-D plot """
+
+        if yarr is None:
+            print 'Usage: oplot(yarr,[,xarr][,xmin=n][,xmax=n][,xspc=n]'
+            print '            [,ymin=n][,ymax=n][yspc=n][,datalabel=text]'
+            print '            [,color=4-tuple][,drawstyle=text]'
+            print '            [,reset_extents=0 or 1][,with_legend=0 or 1])'
+            print ''
+            print ' yarr -- 1-D array of y values to plot'
+            print ' xarr -- x values (optional): if present, must be same'
+            print '         length as yarr.'
+            print ' xmin -- minimum x (may be rounded for nice labels)'
+            print ' xmax -- maximum x (may be rounded for nice labels)'
+            print ' xspc -- # x tics (may be rounded for nice labels)'
+            print ' ymin -- minimum y (may be rounded for nice labels)'
+            print ' ymax -- maximum y (may be rounded for nice labels)'
+            print ' yspc -- # ytics (may be rounded for nice labels)'
+            print ' datalabel -- data label: add a legend (label datalabel) '
+            print ' color -- a tuple of rgba values, each ranging from '
+            print '           0.0-1.0 (color to draw data line).'
+            print " drawstyle -- linestyle.  Currently only '_' is supported. "
+            print ' reset_extents -- reset extents (1) or leave extents as '
+            print '                  they are (0).  xmin/xmax/ymin/ymax will'
+            print '                  be ignored if this is 0.'
+            print ' with_legend -- 1 to add a legend, 0 otherwise.  Do not '
+            print '                add until all datasets have been put on.'
+            print ' interactive -- 0 if data in plot is not user-editable;'
+            print '                1 if it is.  Defaults to 0.'
+            print ''
+            return
+
+        if len(self.plots) == 0:
+            raise RuntimeError,'No plot to plot over.'
+
+        try:
+            dshape = Numeric.shape( yarr )
+        except:
+            raise ValueError,"data argument to plot() does not appear to be "+\
+                          "a NumPy array"
+
+        dim = len(dshape)
+
+        if dim == 2 and (dshape[0] == 1):
+            yarr=Numeric.reshape(yarr,(dshape[1],))
+        elif dim == 2 and (dshape[1] == 1):
+            yarr=Numeric.reshape(yarr,(dshape[0],))
+        elif dim != 1:
+            raise ValueError,\
+                  "data argument dimension or shape is not supported."
+    
+        if xarr is None:
+            xarr=Numeric.array(range(len(yarr)))
+
+        cstr=str(color[0])+' '+str(color[1])+' '+str(color[2])+\
+              ' '+str(color[3])
+        props={}
+        props['_line_color']=cstr
+        props['_gl_antialias']='1'
+        idx=self.plots[0].add_data(xarr,yarr,datalabel,props,interactive)
+        if with_legend == 1:
+            self.plots[0].set_legend()
+        if reset_extents == 1:
+            self.plots[0].set_extents(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax,
+                                      xspc=xspc,yspc=yspc) 
+            self.clear_layers()
+            ll=self.plots[0].create_layers()
+            for item in ll:
+                self.add_layer(item)        
+        else:
+            self.plots[0].create_layer(ltype=GVPLOT_DATA_LAYER,idx=idx)
+            # Clear the layers from the view then re-add them so that
+            # they are in the right order.
+            self.clear_layers()
+            ll=self.plots[0].get_layers()
+            for item in ll:
+                self.add_layer(item)        
+
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())
+        self.fit_extents(0.0,0.0,1.0,ysize/xsize)
+        
+    def plot(self,yarr=None,xarr=None, xlabel=None, ylabel=None, xmin=None,
+             xmax=None,xspc=None,ymin=None, ymax=None, yspc=None,
+             title=None,datalabel=None,
+             color=(1.0,0.0,0.0,1.0),drawstyle='_',interactive=0):
+        """ Simple 2-D plot """
+        
+        if yarr is None:
+            print 'Usage: plot(yarr,[,xarr][, xlabel=text] [,ylabel=text]'
+            print '                 [,xmin=n][, xmax=n][, xspc=n][, ymin=n]'
+            print '                 [, ymax=n][, yspc=n]'
+            print '                 [title=text] [datalabel=text]'
+            print ''
+            print ' yarr -- 1-D array of values to plot'
+            print ' xarr -- x values (optional): if present, must be same'
+            print '         length as yarr.'
+            print ' xlabel -- text for x label '
+            print ' xmin -- minimum x (may be rounded for nice labels)'
+            print ' xmax -- maximum x (may be rounded for nice labels)'
+            print ' xspc -- x spacing (if not present, default will be used)'
+            print ' ylabel -- text for y label '
+            print ' ymin -- minimum y (may be rounded for nice labels)'
+            print ' ymax -- maximum y (may be rounded for nice labels)'
+            print ' yspc -- y spacing (if not present, default will be used)'
+            print ' title -- title text'
+            print ' datalabel -- data label: add a legend (label datalabel) '
+            print ' color -- a tuple of rgba values, each ranging from '
+            print '           0.0-1.0 (color to draw data line).'
+            print " drawstyle -- linestyle.  Currently only '_' is supported. "
+            print ' interactive -- 0 if data in plot is not user-editable;'
+            print '                1 if it is.  Defaults to 0.'
+            print ''
+            return
+            
+        try:
+            dshape = Numeric.shape( yarr )
+        except:
+            raise ValueError,"data argument to plot() does not appear to be "+\
+                          "a NumPy array"
+
+        dim = len(dshape)
+
+        if dim == 2 and (dshape[0] == 1):
+            yarr=Numeric.reshape(yarr,(dshape[1],))
+        elif dim == 2 and (dshape[1] == 1):
+            yarr=Numeric.reshape(yarr,(dshape[0],))
+        elif dim != 1:
+            raise ValueError,\
+                  "data argument dimension or shape is not supported."
+    
+        if xarr is None:
+            xarr=Numeric.array(range(len(yarr)))
+
+        self.clear()
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())
+        self.plots.append(gvplot_2Ddata_cartesian('1: ',0.0,0.0,1.0,
+                                                  ysize/xsize))
+        self.plots[0].set_border_padding(self.tborder,self.bborder,
+                                         self.lborder,self.rborder)
+        cstr=str(color[0])+' '+str(color[1])+' '+str(color[2])+\
+              ' '+str(color[3])
+        props={}
+        props['_line_color']=cstr
+        props['_gl_antialias']='1'
+        self.plots[0].add_data(xarr,yarr,datalabel,props,interactive)
+        self.plots[0].set_extents(xmin=xmin,xmax=xmax,ymin=ymin,
+                                  ymax=ymax,xspc=xspc,yspc=yspc)
+
+        self.plots[0].set_labels(xlabel,ylabel,title)
+        
+        if self.bgcolor == (0.0,0.0,0.0,1.0):
+            self.plots[0].set_axis_color((1.0,1.0,1.0,1.0))
+            self.plots[0].set_label_color((1.0,1.0,1.0,1.0))
+            
+        ll=self.plots[0].create_layers()
+        for item in ll:
+            self.add_layer(item)
+
+        self.fit_extents(0.0,0.0,1.0,ysize/xsize)
+
+    def seeall(self):
+        """ Set extents so that whole plot is shown in view. """
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())
+        self.fit_extents(0.0,0.0,1.0,ysize/xsize)
+        
+    def plot3D(self,xarr,yarr,zarr):
+        """ Simple 3-D plot """
+        self.set_mode(gview.MODE_3D)
+
+        props={}
+        props['_area_edge_color']='0.0 0.0 0.0 1.0'
+        props['_area_fill_color']='0.0 0.0 1.0 1.0'
+        #props['_line_color']='0.0 0.0 0.0 1.0'
+        props['_gl_antialias']='1'
+
+        self.clear()
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())
+        self.plots.append(gvplot_3Ddata_cartesiangrid('1: ',0.0,0.0,0.0,
+                                                  1.0,1.0,ysize/xsize))
+        self.plots[0].add_data(xarr,yarr,zarr,'',props)
+        self.plots[0].set_extents()
+        #self.plots[0].set_labels(xlabel,ylabel,title)
+        
+        self.plots[0].set_axis_color((0.0,0.0,0.0,1.0))
+        self.plots[0].set_label_color((0.0,0.0,0.0,1.0))
+            
+        ll=self.plots[0].create_layers()
+        for item in ll:
+            self.add_layer(item)
+
+        self.set_3d_view_look_at((1.2,-0.3,0.5),(-10.0,20.0))
+
+    def set_plot(self,plot):
+        """ Function to allow user to set plot class directly """
+        self.clear()
+        self.plots=[]
+        self.plots.append(plot)
+
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())
+        self.plot.set_plot_extents(0.0,0.0,1.0,ysize/xsize)
+        
+        ll=self.plots[0].create_layers()
+        for item in ll:
+            self.add_layer(item)        
+
+        
+    def begin_plot(self,xlabel=None,ylabel=None,title=None):
+        """ Begin constructing a plot (use to construct a plot
+            without displaying at each iteration).
+        """
+
+        self.clear()
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())
+        self.plots.append(gvplot_2Ddata_cartesian('1: ',0.0,0.0,1.0,
+                                                  ysize/xsize))
+        self.plots[0].set_border_padding(self.tborder,self.bborder,
+                                         self.lborder,self.rborder)
+
+        self.plots[0].set_labels(xlabel,ylabel,title)
+        
+        if self.bgcolor == (0.0,0.0,0.0,1.0):
+            self.plots[0].set_axis_color((1.0,1.0,1.0,1.0))
+            self.plots[0].set_label_color((1.0,1.0,1.0,1.0))
+
+    def shift_label(self,label_key,xytuple,plot_idx=0):
+        """ Shift label referenced by label_key by the
+            amounts in xytuple for the plot_idxth plot.
+            label_key- eg. 'xlabel','ylabel','title'
+            xytuple- (x,y), where x and y are fractions
+                     of the plot extents
+            plot_idx- plot index of plot whose labels are
+                      to be adjusted.
+        """
+        ext=self.plots[plot_idx].get_plot_extents(include_border=1)
+        xrng=ext[3]-ext[0]
+        yrng=ext[4]-ext[1]
+        xold=self.plots[plot_idx].label_data[label_key][0][0]
+        yold=self.plots[plot_idx].label_data[label_key][0][1]
+        
+        self.plots[plot_idx].label_data[label_key][0]=(xold+xytuple[0]*xrng,
+                                               yold+xytuple[1]*yrng,
+                                               0)
+
+    def add_data(self, xarr=None, yarr=None,datalabel=None,
+              color=(0.0,0.0,1.0,1.0),drawstyle='_', interactive=0):
+            
+        try:
+            dshape = Numeric.shape( xarr )
+            dshape = Numeric.shape( yarr )
+        except:
+            raise ValueError,"data argument to plot() does not appear to be "+\
+                          "a NumPy array"
+
+        dim = len(dshape)
+
+        if dim == 2 and (dshape[0] == 1):
+            yarr=Numeric.reshape(yarr,(dshape[1],))
+        elif dim == 2 and (dshape[1] == 1):
+            yarr=Numeric.reshape(yarr,(dshape[0],))
+        elif dim != 1:
+            raise ValueError,\
+                  "data argument dimension or shape is not supported."
+    
+        if xarr is None:
+            xarr=Numeric.array(range(len(yarr)))
+
+        cstr=str(color[0])+' '+str(color[1])+' '+str(color[2])+\
+              ' '+str(color[3])
+        props={}
+        props['_line_color']=cstr
+        props['_gl_antialias']='1'
+        self.plots[0].add_data(xarr,yarr,datalabel,props,interactive)
+                    
+
+    def end_plot(self,xmin=None,xmax=None,ymin=None,ymax=None,
+                 xtics=None,ytics=None,with_legend=0):
+        
+        self.plots[0].set_extents(xmin=xmin,xmax=xmax,ymin=ymin,
+                                  ymax=ymax)
+        xspc=None
+        yspc=None
+        if xtics is not None:
+            # Currently this will only work for continuous
+            # plots.
+            xmin=self.plots[0].xmins[0]
+            xmax=self.plots[0].xmaxs[len(self.plots[0].xmaxs)-1]
+            xspc=(xmax-xmin)/(xtics-1)
+            rord=Numeric.log10(abs(xspc))
+            nrord=rord % 1
+            spc=pow(10,Numeric.floor(rord))
+            min_diff=abs(xspc-spc)
+            for i in [1,2,5]:
+                nspc=i*pow(10,Numeric.floor(rord))
+                if abs(nspc-spc) < min_diff:
+                    min_diff=abs(xspc-nspc)
+                    spc=nspc
+            xspc=spc
+            
+        if ytics is not None:
+            # Currently this will only work for continuous
+            # plots.
+            ymin=self.plots[0].ymins[0]
+            ymax=self.plots[0].ymaxs[len(self.plots[0].ymaxs)-1]
+            yspc=(ymax-ymin)/(ytics-1)
+            rord=Numeric.log10(abs(yspc))
+            nrord=rord % 1
+            spc=pow(10,Numeric.floor(rord))
+            min_diff=abs(yspc-spc)
+            for i in [1,2,5]:
+                nspc=i*pow(10,Numeric.floor(rord))
+                if abs(nspc-spc) < min_diff:
+                    min_diff=abs(yspc-nspc)
+                    spc=nspc
+            yspc=spc
+            
+        self.plots[0].set_extents(xmin=xmin,xmax=xmax,ymin=ymin,
+                                  ymax=ymax,xspc=xspc,yspc=yspc)
+
+        if with_legend == 1:
+            self.plots[0].set_legend()
+            
+        ll=self.plots[0].create_layers()
+        for item in ll:
+            self.add_layer(item)
+
+        xsize=float(self.get_width())
+        ysize=float(self.get_height())            
+        self.fit_extents(0.0,0.0,1.0,ysize/xsize)
+
+    def get_xy_position(self,x,y,plotidx=0):
+        """ Get the data position corresponding to GvViewPlot position x,y
+            for the plot with index plotidx.
+            Input:
+               x- x value(s)
+               y- y value(s)
+               plotidx- index of plot to use in conversion, if more than
+                        one plot is present in the view area.
+                        
+            x and y may be single values or same-length 1-D arrays.
+
+            Output:
+                x- data x position
+                y- data y position
+                okarr- array indicating where the conversion is valid
+                       (ie. within plot bounds)
+        """
+        x,y,okarr=self.plots[plotidx].get_xyposition(x,y)
+
+        return (x,y,okarr)
+
+    def get_plotposition(self,x,y,plotidx=0):
+        """ Get the GvViewPlot position corresponding to data position x,y
+            for plot plotidx.
+            
+            Input:
+               x- x value(s)
+               y- y value(s)
+               plotidx- index of plot to use in conversion, if more than
+                        one plot is present in the view area.
+                        
+            x and y may be single values or same-length 1-D arrays.
+
+            Output:
+                x- plot x position
+                y- plot y position
+                okarr- array indicating where the conversion is valid
+                       (ie. within plot bounds)
+        """
+        x,y,okarr=self.plots[plotidx].get_plotposition(x,y)
+
+        return (x,y,okarr)
+    
+    def clear_layers(self):
+        """ Remove all layers. """
+
+        for clayer in self.list_layers():
+            self.remove_layer(clayer)
+
+    def clear(self):
+        """ Clear all layers AND plot information. """
+
+        for clayer in self.list_layers():
+            self.remove_layer(clayer)
+            
+        self.plots=[]
+
+
+class GvSimplePlotWindow(gtk.GtkWindow):
+    
+    def __init__(self,bgcolor=(1.0,1.0,1.0,1.0)):
+        gtk.GtkWindow.__init__(self)
+        self.plotarea=GvSimplePlot(bgcolor=bgcolor)
+        #self.plotarea.fit_extents(0.0,0.0,1.0,0.84)
+        self.set_policy(gtk.TRUE,gtk.TRUE,gtk.TRUE)
+        shell = gtk.GtkVBox(spacing=0)
+        self.add(shell)
+        self.set_usize(650,545)
+
+        # Print menu
+        menuf = GtkExtra.MenuFactory()
+        self.menuf = menuf
+        menuf.add_entries([
+                 ('File/Print', None, self.print_cb)])
+
+        shell.pack_start(menuf, expand=gtk.FALSE)
+
+        shell.pack_start( self.plotarea )
+
+        self.show_all()
+
+        #self.viewarea.fit_extents(0, self.ysize, self.xsize, -self.ysize )
+
+    def print_cb(self, *args):
+        import gvprint
+        pd = gvprint.GvPrintDialog( self.plotarea )
+
+    def plot(self,yarr=None,xarr=None, xlabel=None, ylabel=None, xmin=None,
+             xmax=None,ymin=None, ymax=None, xspc=None,yspc=None,
+             title=None,datalabel=None,
+             color=(1.0,0.0,0.0,1.0),drawstyle='_'):
+        self.plotarea.plot(yarr,xarr,xlabel,ylabel,xmin,xmax,xspc,
+                           ymin,ymax,yspc,title,
+                           datalabel,color,drawstyle)
+
+    def oplot(self,yarr=None,xarr=None, xmin=None,
+             xmax=None, xspc=None,ymin=None, ymax=None,
+              yspc=None, datalabel=None,
+             color=(1.0,0.0,0.0,1.0),drawstyle='_',reset_extents=1,
+              with_legend=0):
+        self.plotarea.oplot(yarr,xarr,xmin,xmax,xspc,ymin,ymax,yspc,
+                           datalabel,color,drawstyle,reset_extents,with_legend)
+
+def DataToPlot1D(pmin,pmax,dmins,dmaxs,dpos,axistype=GVPLOT_AXISTYPE_LINEAR):
+    """ Transform from data to plot coordinates.
+        Input:
+            pmin- plot minimum position not including borders (single value)
+            pmax- plot maximum position not including borders (single value)
+            dmins- data range minima (a list or tuple of at least length 1)
+            dmaxs- data range maxima (a list or tuple of length(dmins))
+            dpos- data positions to transform
+                  (single value or 1-D array)
+            axistype- type of axis (eg. linear or log).
+
+        The dmin-dmax ranges must not be overlapping.
+
+        Output:
+            ppos- plot position (float or array)
+            okarr- integer or array of integers indicating where data
+                   is within the plot bounds (1's) or invalid (0's)
+    """
+    if axistype not in [GVPLOT_AXISTYPE_LINEAR,GVPLOT_AXISTYPE_LOG]:
+        raise RuntimeError,'DataToPlot1D: unknown axis type!'
+
+    ptype=type(dpos)
+    if ptype != type(Numeric.array([])):
+        dpos=Numeric.array(dpos,Numeric.Float64)
+
+    # First check for length 1 case and do quick
+    # transform and return if it is; otherwise, continue.
+        
+    if len(dmins) == 1:
+        # Only one continuous range
+        if axistype == GVPLOT_AXISTYPE_LINEAR:
+            ppos=((float(pmax)-float(pmin))/
+                  (float(dmaxs[0])-float(dmins[0])))*\
+                  (dpos-float(dmins[0]))+float(pmin)
+            
+            okarr=Numeric.where(ppos >= pmin,1,0)
+            okarr=Numeric.where(ppos <= pmax,okarr,0)            
+            
+        elif axistype == GVPLOT_AXISTYPE_LOG:
+            # Make sure logs don't choke
+            okarr=Numeric.where(dpos > 0,1,0)
+            dpos=Numeric.where(okarr == 0,1,dpos)
+            
+            ppos=(((float(pmax)-float(pmin))/float(Numeric.log10(dmaxs[0])-
+                                          Numeric.log10(dmins[0])))*\
+                  (Numeric.log10(dpos)-Numeric.log10(dmins[0])))+pmin
+            okarr=Numeric.where(ppos >= pmin,okarr,0)
+            okarr=Numeric.where(ppos <= pmax,okarr,0)
+
+        if ptype not in [type(Numeric.array([])),type((1,)),type([])]:
+            okarr=okarr[0]
+
+        return (ppos,okarr)
+            
+    dmaxs=Numeric.array(dmaxs,Numeric.Float64)
+    dmins=Numeric.array(dmins,Numeric.Float64)
+    
+    if axistype == GVPLOT_AXISTYPE_LINEAR:    
+        dwidtharr=Numeric.ravel(dmaxs-dmins)
+        dwidth=Numeric.sum(dwidtharr)
+        dcsum=Numeric.cumsum(dwidtharr)
+        drng=Numeric.array(list(dcsum/dwidth).insert(0,0.0))
+        prng=pmin+(drng*(pmax-pmin)) # plot range endpoints
+
+        ppos=Numeric.zeros((len(dpos),),Numeric.Float64)
+        okarr=Numeric.zeros((len(dpos),),Numeric.Float64)
+        
+        for idx in range(len(dmins)):
+            sc=Numeric.where(dpos >= dmins[idx],1,0)
+            sc=Numeric.where(dpos <= dmaxs[idx],1,sc)
+            okarr=Numeric.where(sc == 1,1,okarr)
+            sc=sc.astype(Numeric.Float64)
+
+            ppos=ppos+(sc*(prng[idx]+((prng[idx+1]-prng[idx])*
+                     (dpos-dmins[idx])/(dmaxs[idx+1]-dmins[idx]))))
+    
+    elif axistype == GVPLOT_AXISTYPE_LOG:
+        dmaxs=Numeric.log10(dmaxs)
+        dmins=Numeric.log10(dmins)
+
+        # Make sure logs don't choke
+        okarrmask=Numeric.where(dpos > 0,1,0)
+        dpos=Numeric.where(okarrmask == 0,1,dpos)
+        
+        dpos=Numeric.log10(dpos)
+        
+        dwidtharr=Numeric.ravel(dmaxs-dmins)
+        dwidth=Numeric.sum(dwidtharr)
+        dcsum=Numeric.cumsum(dwidtharr)
+        drng=Numeric.array(list(dcsum/dwidth).insert(0,0.0))
+        prng=pmin+(drng*(pmax-pmin)) # plot range endpoints
+
+        ppos=Numeric.zeros((len(dpos),),Numeric.Float64)
+        okarr=Numeric.zeros((len(dpos),),Numeric.Float64)
+        
+        for idx in range(len(dmins)):
+            sc=Numeric.where(dpos >= dmins[idx],1,0)
+            sc=Numeric.where(dpos <= dmaxs[idx],1,sc)
+            okarr=Numeric.where(sc == 1,1,okarr)
+            sc=sc.astype(Numeric.Float64)
+
+            ppos=ppos+(sc*(prng[idx]+((prng[idx+1]-prng[idx])*
+                     (dpos-dmins[idx])/(dmaxs[idx+1]-dmins[idx]))))
+
+        okarr=okarr*okarrmask
+        
+    if ptype not in [type(Numeric.array([])),type((1,)),type([])]:
+        okarr=okarr[0]
+
+    return (ppos,okarr)
+
+def PlotToData1D(pmin,pmax,dmins,dmaxs,ppos,axistype=GVPLOT_AXISTYPE_LINEAR):
+    """ Transform from plot to data coordinates.
+        Input:
+            pmin- plot minimum position not including borders (single value)
+            pmax- plot maximum position not including borders (single value)
+            dmins- data range minima (a list or tuple of at least length 1)
+            dmaxs- data range maxima (a list or tuple of length(dmins))
+            ppos- plot positions to transform
+                  (single value or 1-D array)
+            axistype- type of axis (eg. linear or log).
+
+        The dmin-dmax ranges must not be overlapping.
+
+        Output:
+            dpos- data position (float or array)
+            okarr- integer or array of integers indicating where plot
+                   position is within bounds (1's) or invalid (0's).
+    """
+    if axistype not in [GVPLOT_AXISTYPE_LINEAR,GVPLOT_AXISTYPE_LOG]:
+        raise RuntimeError,'PlotToData1D: unknown axis type!'
+
+    dtype=type(ppos)
+    if dtype != type(Numeric.array([])):
+        ppos=Numeric.array(ppos,Numeric.Float64)
+
+    okarr=Numeric.where(ppos >= pmin,1,0)
+    okarr=Numeric.where(ppos <= pmax,okarr,0)
+     
+    # First check for length 1 case and do quick
+    # transform and return if it is; otherwise, continue.    
+    if len(dmins) == 1:
+        # Only one continuous range
+        if axistype == GVPLOT_AXISTYPE_LINEAR:
+            dpos=((float(dmaxs[0]-dmins[0])/float(pmax-pmin))*\
+                  (ppos-pmin))+dmins[0]
+            
+        elif axistype == GVPLOT_AXISTYPE_LOG:
+            dpos=Numeric.power(10,((((Numeric.log10(float(dmaxs[0]))-
+                   Numeric.log10(float(dmins[0])))/
+                  float(pmax-pmin))*(ppos-pmin))+
+                  Numeric.log10(float(dmins[0]))))
+   
+        if dtype not in [type(Numeric.array([])),type((1,)),type([])]:
+            okarr=okarr[0]
+
+        return (dpos,okarr)
+            
+
+    dmaxs=Numeric.array(dmaxs,Numeric.Float64)
+    dmins=Numeric.array(dmins,Numeric.Float64)
+    
+    if axistype == GVPLOT_AXISTYPE_LINEAR:    
+        dwidtharr=Numeric.ravel(dmaxs-dmins)
+        dwidth=Numeric.sum(dwidtharr)
+        dcsum=Numeric.cumsum(dwidtharr)
+        drng=Numeric.array(list(dcsum/dwidth).insert(0,0.0))
+        prng=pmin+(drng*(pmax-pmin)) # plot range endpoints
+        pfrac=(float(ppos)-float(pmin))/(float(pmax)-float(pmin))
+        dpos=Numeric.zeros((len(ppos),),Numeric.Float64)
+        for idx in range(len(dmins)):
+            # If ppos is between dmins[idx] and dmaxs[idx],
+            # sc will be 1; otherwise it will be 0
+            sc=(Numeric.sign(pfrac-drng[idx])+
+                Numeric.sign(drng[idx+1]-pfrac))
+            # exact boundary of 2 ranges- use lower.
+            sc=Numeric.where(sc == -1,1,sc)
+            sc=Numeric.where(sc == 1,0,sc)
+            sc=sc.astype(Numeric.Float64)/2.0
+            dpos=dpos+(sc*(dmins[idx]+((dmaxs[idx+1]-dmins[idx])*
+                        (ppos-prng[idx])/(prng[idx+1]-prng[idx]))))
+ 
+    elif axistype == GVPLOT_AXISTYPE_LOG:
+        dmaxs=Numeric.log10(dmaxs)
+        dmins=Numeric.log10(dmins)
+        dwidtharr=Numeric.ravel(dmaxs-dmins)
+        dwidth=Numeric.sum(dwidtharr)
+        dcsum=Numeric.cumsum(dwidtharr)
+        drng=Numeric.array(list(dcsum/dwidth).insert(0,0.0))
+        prng=pmin+(drng*(pmax-pmin)) # plot range endpoints
+        pfrac=(float(ppos)-float(pmin))/(float(pmax)-float(pmin))
+        dpos=Numeric.zeros((len(ppos),),Numeric.Float64)
+        for idx in range(len(dmins)):
+            # If ppos is between dmins[idx] and dmaxs[idx],
+            # sc will be 1; otherwise it will be 0
+            sc=(Numeric.sign(pfrac-drng[idx])+
+                Numeric.sign(drng[idx+1]-pfrac))
+            # exact boundary of 2 ranges- use lower.
+            sc=Numeric.where(sc == -1,1,sc)
+            sc=Numeric.where(sc == 1,0,sc)
+            sc=sc.astype(Numeric.Float64)/2.0
+            dpos=dpos+(sc*(dmins[idx]+((dmaxs[idx+1]-dmins[idx])*
+                        (ppos-prng[idx])/(prng[idx+1]-prng[idx]))))
+            
+        dpos=Numeric.power(10,dpos)
+
+
+    if dtype not in [type(Numeric.array([])),type((1,)),type([])]:
+        okarr=okarr[0]
+
+    return (dpos,okarr)
+
+def MakeContiguousXY(xarr,yarr):
+    """ Arrange array data by ascending x. """
+    longxlist=[]
+    longylist=[]
+    for idx in range(len(xarr)):
+        longxlist.extend(xarr[idx])
+        longylist.extend(yarr[idx])
+    lxarr=Numeric.array(longxlist) 
+    lyarr=Numeric.array(longylist)   
+    ind=Numeric.argsort(lxarr)
+    nxarr=Numeric.take(lxarr,ind)
+    nyarr=Numeric.take(lyarr,ind)
+    
+    return(nxarr,nyarr)
+
+def GetOutlierData(xarr,xmins,xmaxs,yarr,ymins,ymaxs,
+                   zarr=None,zmins=None,zmaxs=None):
+    """ Get data that lies outside of the current plot boundaries.
+        xarr, yarr, (optional zarr)- array, or list of arrays
+
+        xmins/xmaxs- x boundaries (single value or list of values)
+        ymins/ymaxs- y boundaries
+        zmins/zmaxs (optional)- z boundaries
+
+        Note: boundaries and data arrays are in data coordinates,
+              not plot coordinates (xmins/xmaxs etc. are the plot
+              boundaries converted to data coordinates).
+    """
+    if type(xarr) == type(Numeric.array([1,2])):
+        xarr=[xarr]
+        yarr=[yarr]
+        if zarr is not None:
+            zarr=[zarr]
+            
+    if type(xmins) not in [type([]),type((1,))]:
+        xmins=[xmins]
+        xmaxs=[xmaxs]
+            
+    if type(ymins) not in [type([]),type((1,))]:
+        ymins=[ymins]
+        ymaxs=[ymaxs]
+
+    if zarr is not None:        
+        if type(zmins) not in [type([]),type((1,))]:
+            zmins=[zmins]
+            zmaxs=[zmaxs]
+
+    nxarr=[]
+    nyarr=[]
+    nzarr=None
+    if zarr is not None:
+        nzarr=[]
+    else:
+        nzarr=None
+        
+    for idx in range(len(xarr)):
+        tx=xarr[idx]
+        ty=yarr[idx]
+        if zarr is not None:
+            tz=zarr[idx]
+        xok=Numeric.zeros(Numeric.shape(tx))
+        for xidx in range(len(xmins)):
+           xok=Numeric.where((tx >= xmins[xidx]) & (tx <= xmaxs[xidx]),1,xok)
+        yok=Numeric.zeros(Numeric.shape(ty))
+        for yidx in range(len(ymins)):
+           yok=Numeric.where((ty >= ymins[yidx]) & (ty <= ymaxs[yidx]),1,yok)
+        if zarr is not None:
+            for zidx in range(len(zmins)):
+               zok=Numeric.where((tz >= zmins[zidx]) & (tz <= zmaxs[zidx]),
+                                 1,zok)
+        outlier=Numeric.where(xok == 0,1,0)
+        outlier=Numeric.where(yok == 0,1,outlier)
+        if zarr is not None:
+            outlier=Numeric.where(zok == 0,1,outlier)
+
+        nxarr.append(Numeric.compress(outlier == 1,tx))
+        nyarr.append(Numeric.compress(outlier == 1,ty))
+        if zarr is not None:              
+            nzarr.append(Numeric.compress(outlier == 1,tz))
+
+    return (nxarr,nyarr,nzarr)
+
+def GetAxisBreaks(mins,maxs,axis_type=GVPLOT_AXISTYPE_LINEAR,bwidth=0.1):
+    """ Get the axis discontinuity info.
+        mins- axis range minima.
+        maxs- axis range maxima.
+        axis_type- type of axis (linear or log)
+        bwidth- break width as a percentage of axis length.
+    """
+    if len(mins) == 1:
+        return None
+
+    maxs=Numeric.array(maxs)
+    mins=Numeric.array(mins)
+    
+    if axis_type == GVPLOT_AXISTYPE_LOG:
+        maxs=Numeric.log10(maxs)
+        mins=Numeric.log10(mins)
+        
+    dwidtharr=Numeric.ravel(maxs-mins)
+    dwidth=Numeric.sum(dwidtharr)
+    dcsum=Numeric.cumsum(dwidtharr)
+    drng=Numeric.array(list(dcsum/dwidth))
+    drng=drng[:len(drng)-1]
+    bstarts=drng-(bwidth/2.0)
+    bends=drng+(bwidth/2.0)
+    bstarts=Numeric.where(bstarts < 0.0,0.0,bstarts)
+    bends=Numeric.where(bends > 1.0,1.0,bends)
+    diff=bstarts[1:]-bends[:-1]
+    bstarts[1:]=Numeric.where(diff < 0,bstarts[1:]+diff/2.0,bstarts[1:])
+    bends[:-1]=Numeric.where(diff < 0,bends[:-1]-diff/2.0,bends[:-1])
+
+    breaks=[]
+    for idx in range(len(bstarts)):
+        breaks.append((bstarts[idx],bends[idx]))
+
+    return breaks
+
+
+def GetNiceMinMax(mins,maxs,pref_spacing,axis_type=GVPLOT_AXISTYPE_LINEAR):
+    """ Get 'nice' min/max values that enclose the given range,
+        and return the nice min/max along with a list of data
+        offsets for major tics, and a formatting string for the
+        labels.
+    """
+    if type(mins) not in [type([]),type((1,))]:
+        mins=[mins]
+    if type(maxs) not in [type([]),type((1,))]:
+        maxs=[maxs]
+        
+    if axis_type == GVPLOT_AXISTYPE_LINEAR:
+        dmins=[]
+        dmaxs=[]
+        labels=[]
+        for idx in range(len(mins)):
+            tmin,tmax,offsets,fmt=GetNiceLinearMinMax(mins[idx],
+                                           maxs[idx],pref_spacing)
+            dmins.append(tmin)
+            dmaxs.append(tmax)
+            labels.extend(offsets)
+
+    return (dmins,dmaxs,labels,fmt)
+
+def GetNiceLinearMinMax(minval,maxval,pref_spacing=None):
+    """ Linear axis case:
+        Get 'nice' max/min values that enclose the given range,
+        and return the nice min/max along with a list of data
+        offsets for major tics, and a formatting string for the
+        labels.
+    """
+    if abs(maxval-minval) > 0:
+        rord=Numeric.log10(abs(maxval-minval))
+    else:
+        maxval=maxval*1.1
+        minval=minval*0.9
+        if maxval == 0:
+            maxval=1.0
+            minval=-1.0
+        rord=Numeric.log10(abs(maxval-minval))
+        
+    nrord=rord % 1
+
+    # format
+    if rord > 1:
+        fmt="%d"
+    else:
+        prec=int(abs(rord))+2
+        fmt="%."+str(prec)+"f"
+         
+    if pref_spacing is None:
+        if nrord < Numeric.log10(2):
+            spc=0.2*pow(10,Numeric.floor(rord))
+        elif nrord < Numeric.log10(5):
+            spc=0.5*pow(10,Numeric.floor(rord))
+        else:
+            spc=pow(10,Numeric.floor(rord))
+    else:
+        spc=pref_spacing
+
+    tmp1=(abs(minval) % spc)/spc
+    new_min=minval - (minval % spc)
+
+    new_max=Numeric.floor((maxval-new_min)/spc)*spc+new_min
+
+    if maxval-new_max > (float(spc)/10000.0):
+        new_max=new_max+spc
+
+    label_offsets=[]
+    for label_val in Numeric.arange(new_min,new_max+spc/100.0,spc):
+        label_offsets.append(label_val)
+        
+    return (new_min,new_max,label_offsets,fmt)
+    
+
+class GvSimpleSettingsVBox(gtk.GtkVBox,gvsignaler.Signaler):
+    """ Class to hold a number of plot-related settings.
+        Inputs:
+            plotarea- GvSimplePlot to act on
+            plot_num- index to plot within plotarea to apply changes to
+            include_list- tuple of which tables to include:
+                          0- rescale table
+                          1- Apply button
+                          
+            withx,xminlabel,xmaxlabel,
+            withy,yminlabel,ymaxlabel,
+            withz,zminlabel,zmaxlabel- rescale table settings.
+
+            applylabel- apply button label
+
+        Sends out a 'plot-updated' signal when plot has been
+        changed.
+    """
+    def __init__(self,plotarea=None,plot_num=0,include_list=(0,1),
+                 withx=1,xminlabel='X Min',xmaxlabel='X Max',
+                 withy=1,yminlabel='Y Min',ymaxlabel='Y Max',
+                 withz=0,zminlabel='Z Min',zmaxlabel='Z Max',
+                 applylabel='Apply'
+                 ):
+        gtk.GtkVBox.__init__(self)
+        self.set_border_width(5)
+        self.set_spacing(5)
+        self.plotarea=plotarea
+        self.plot_num=plot_num
+        self.rescale_table=None
+        self.apply_button=None
+        for item in include_list:
+            if item == 0:
+                self.rescale_table=GvSimpleRescaleTable(
+                    withx=withx,xminlabel=xminlabel,xmaxlabel=xmaxlabel,
+                    withy=withy,yminlabel=yminlabel,ymaxlabel=ymaxlabel,
+                    withz=withz,zminlabel=zminlabel,zmaxlabel=zmaxlabel)
+                self.pack_start(self.rescale_table)
+            elif item == 1:
+                self.apply_button=gtk.GtkButton(applylabel)
+                self.pack_start(self.apply_button,expand=gtk.FALSE)
+
+        if self.apply_button is not None:
+            self.apply_button.connect("clicked",self.apply_cb)
+
+        self.publish('plot-updated')
+
+    def apply_cb(self,*args):
+        """ Update plot. """
+        # If plot was interactive, draw changes back in.
+        for idx in range(len(self.plotarea.plots)):
+            cplot=self.plotarea.plots[idx]
+            for idx2 in range(len(cplot.array_data)):
+                if cplot.array_layer_defaults[idx2].interactive == 1:
+                    cplot.merge_data_from_plot(idx2)
+                    
+        if self.rescale_table is not None:
+            ext=self.rescale_table.get_settings()      
+            self.plotarea.plots[self.plot_num].set_extents(xmin=ext[0],
+                  xmax=ext[1],ymin=ext[2],ymax=ext[3])
+        ll=self.plotarea.plots[self.plot_num].create_layers()
+        self.plotarea.clear_layers()
+        for item in ll:
+            self.plotarea.add_layer(item)
+        xsize=float(self.plotarea.get_width())
+        ysize=float(self.plotarea.get_height())            
+        self.plotarea.fit_extents(0.0,0.0,1.0,ysize/xsize)
+        self.notify('plot-updated')
+
+        
+class GvSimpleRescaleTable(gtk.GtkTable):
+    """ Class to provide a table for rescaling a GvSimplePlot.
+        Inputs:
+            withx- 1 to include x in table, 0 to not include
+            xmin- minimum x (None to start from defaults)
+            xmax- maximum x
+            withy- 1 to include y in table, 0 to not include
+            ymin- minimum y
+            ymax- maximum y
+            withz- 1 to include y in table, 0 to not include
+            zmin- minimum y
+            zmax- maximum y
+    """
+    def __init__(self,withx=1,xmin=None,xmax=None,
+                 withy=1,ymin=None,ymax=None,
+                 withz=1,zmin=None,zmax=None,xminlabel='X Min',
+                 xmaxlabel='X Max',yminlabel='Y Min', ymaxlabel='Y Max',
+                 zminlabel='Z Min',zmaxlabel='Z Max'):
+        numrows=withx+withy+withz
+        gtk.GtkTable.__init__(self,numrows,5)
+        self.set_row_spacings(5)
+        self.set_col_spacings(5)
+        self.xminentry=None
+        self.xmaxentry=None
+        self.yminentry=None
+        self.ymaxentry=None
+        self.zminentry=None
+        self.zmaxentry=None
+        c_row=0
+        if withx == 1:
+            self.attach(gtk.GtkLabel(xminlabel),0,1,0,1)
+            self.xminentry=gtk.GtkEntry()
+            self.xminentry.set_editable(gtk.TRUE)
+            if xmin is not None:
+                self.xminentry.set_text(str(xmin))
+            else:
+                self.xminentry.set_text('')
+            self.attach(self.xminentry,1,2,0,1)
+            self.attach(gtk.GtkLabel(xmaxlabel),2,3,0,1)
+            self.xmaxentry=gtk.GtkEntry()
+            self.xmaxentry.set_editable(gtk.TRUE)
+            if xmax is not None:
+                self.xmaxentry.set_text(str(xmax))
+            else:
+                self.xmaxentry.set_text('')
+            self.attach(self.xmaxentry,3,4,0,1)
+            c_row=c_row+1
+            
+        if withy == 1:
+            self.attach(gtk.GtkLabel(yminlabel),0,1,c_row,c_row+1)
+            self.yminentry=gtk.GtkEntry()
+            self.yminentry.set_editable(gtk.TRUE)
+            if ymin is not None:
+                self.yminentry.set_text(str(ymin))
+            else:
+                self.yminentry.set_text('')
+            self.attach(self.yminentry,1,2,c_row,c_row+1)
+            self.attach(gtk.GtkLabel(ymaxlabel),2,3,c_row,c_row+1)
+            self.ymaxentry=gtk.GtkEntry()
+            self.ymaxentry.set_editable(gtk.TRUE)
+            if ymax is not None:
+                self.ymaxentry.set_text(str(ymax))
+            else:
+                self.ymaxentry.set_text('')
+            self.attach(self.ymaxentry,3,4,c_row,c_row+1)
+            c_row=c_row+1
+            
+        if withz == 1:
+            self.attach(gtk.GtkLabel(zminlabel),0,1,c_row,c_row+1)
+            self.zminentry=gtk.GtkEntry()
+            self.zminentry.set_editable(gtk.TRUE)
+            if zmin is not None:
+                self.zminentry.set_text(str(zmin))
+            else:
+                self.zminentry.set_text('')
+            self.attach(self.zminentry,1,2,c_row,c_row+1)
+            self.attach(gtk.GtkLabel(zmaxlabel),2,3,c_row,c_row+1)
+            self.zmaxentry=gtk.GtkEntry()
+            self.zmaxentry.set_editable(gtk.TRUE)
+            if zmax is not None:
+                self.zmaxentry.set_text(str(zmax))
+            else:
+                self.zmaxentry.set_text('')
+            self.attach(self.zmaxentry,3,4,c_row,c_row+1)
+            c_row=c_row+1
+
+    def get_settings(self):
+        """ Return the current settings as a
+            (xmin,xmax,ymin,ymax,zmin,zmax) tuple.
+            If any aren't present or aren't entered,
+            set them to None.
+        """
+        try:
+            xmin=float(self.xminentry.get_text())
+        except:
+            xmin=None
+        try:
+            xmax=float(self.xmaxentry.get_text())
+        except:
+            xmax=None
+        try:
+            ymin=float(self.yminentry.get_text())
+        except:
+            ymin=None
+        try:
+            ymax=float(self.ymaxentry.get_text())
+        except:
+            ymax=None
+        try:
+            zmin=float(self.zminentry.get_text())
+        except:
+            zmin=None
+        try:
+            zmax=float(self.zmaxentry.get_text())
+        except:
+            zmax=None
+            
+        return (xmin,xmax,ymin,ymax,zmin,zmax)
+
+
+    def set_settings(self, xmin = None, xmax = None, ymin = None, ymax = None, zmin = None, zmax = None ):
+        """ Sets the value xmin,xmax,ymin,ymax,zmin,zmax
+            in the table entries.
+            A value that is None, has its entry left blank.
+        """
+        
+        if xmin is not None and self.xminentry is not None:
+            self.xminentry.set_text(str(xmin))
+            
+        if xmax is not None and self.xmaxentry is not None:
+            self.xmaxentry.set_text(str(xmax))
+
+        if ymin is not None and self.yminentry is not None:
+            self.yminentry.set_text(str(ymin))
+
+        if ymax is not None and self.ymaxentry is not None:
+            self.ymaxentry.set_text(str(ymax))
+
+        if zmin is not None and self.zminentry is not None:
+            self.zminentry.set_text(str(zmin))
+
+        if zmax is not None and self.zmaxentry is not None:
+            self.zmaxentry.set_text(str(zmax))
+
+        return
+
+    
+            
+if __name__ == '__main__':
+    yarr=Numeric.arange(20000,typecode='f')/1000.0
+    yarr2=Numeric.arange(20000,typecode='f')/2000.0
+    yarr3=Numeric.arange(20000,typecode='f')/1500.0
+    yarr=Numeric.sin(yarr)
+    yarr2=2*Numeric.sin(yarr2)
+    yarr3=1.5*Numeric.sin(yarr3)
+    win=GvSimplePlotWindow()
+    win.connect('delete-event',gtk.mainquit)
+    win.show()
+    win.plot(yarr,xlabel='x',ylabel='y',title='y=sin(x)')
+    win_settings=gtk.GtkWindow()
+    win_settings.add(GvSimpleSettingsVBox(win.plotarea))
+    win_settings.show_all()
+    win2=GvSimplePlotWindow(bgcolor=(0.0,0.0,0.0,1.0))
+    win2.show()
+    win2.plot(yarr,xlabel='x',ylabel='y',title='y=sin(x)')
+    
+    win3=GvSimplePlotWindow()
+    win3.show()
+    win3.plot(yarr,xmax=5000)
+    win3.oplot(yarr2,color=(0.0,0.0,1.0,1.0),reset_extents=0)
+    win3.oplot(yarr3,color=(0.0,1.0,0.0,1.0),reset_extents=0)
+    
+    win4=GvSimplePlotWindow()
+    win4.show()
+    win4.plot(yarr,xmin=17010,xmax=19031)
+    win4.oplot(yarr2,color=(0.0,0.0,1.0,1.0),datalabel='hi',reset_extents=0)
+    win4.oplot(yarr3,color=(0.0,1.0,0.0,1.0),xmin=17010,xmax=19031,
+               with_legend=1)
+
+    xg=Numeric.zeros((20,20),Numeric.Float16)
+    yg=Numeric.zeros((20,20),Numeric.Float16)
+    zg=Numeric.zeros((20,20),Numeric.Float16)
+    for idx1 in range(20):
+        for idx2 in range(20):
+            xg[idx1,idx2]=idx1
+            yg[idx1,idx2]=idx2
+    zg=Numeric.sin(xg*Numeric.pi/10)*Numeric.sin(yg*Numeric.pi/10)
+    win5=GvSimplePlotWindow()
+    win5.show()
+    win5.plotarea.plot3D(xg,yg,zg)
+    
+    gtk.mainloop()
+    
+ 
+    

Added: packages/openev/branches/upstream/current/pymod/vrtutils.py
===================================================================
--- packages/openev/branches/upstream/current/pymod/vrtutils.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/pymod/vrtutils.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1225 @@
+###############################################################################
+# $Id: vrtutils.py,v 1.17 2005/07/07 21:36:06 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Utilities for creating vrt files.
+# Author:   Gillian Walter, gwalter at atlsci.com
+#
+###############################################################################
+# Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: vrtutils.py,v $
+#  Revision 1.17  2005/07/07 21:36:06  gmwalter
+#  Use string to join lines- faster.
+#
+#  Revision 1.16  2005/01/12 20:00:13  gmwalter
+#  Add option to serialize default geotransform.
+#
+#  Revision 1.15  2005/01/04 17:17:20  gmwalter
+#  Add option to adjust geocoding for SrcRect != DstRect
+#  added to serializeGCPs, serializeGeoTransform.
+#
+#  Revision 1.14  2004/11/25 20:53:18  gmwalter
+#  Avoid core dump when default geotransform set.
+#
+#  Revision 1.13  2004/10/28 18:32:33  gmwalter
+#  Add band descriptions, minor fixes.
+#
+#  Revision 1.12  2004/10/15 20:45:23  gmwalter
+#  Fix typo.
+#
+#  Revision 1.11  2004/10/15 17:12:28  gmwalter
+#  Added a function.
+#
+#  Revision 1.10  2004/08/20 15:57:17  gmwalter
+#  Various fixes to export tool, added
+#  raw band creation to vrt utilities.
+#
+#  Revision 1.9  2004/07/07 23:13:21  gmwalter
+#  Update default geotransform->gcp grid to have
+#  16 control points instead of 4.
+#
+#  Revision 1.8  2003/09/15 19:11:09  gmwalter
+#  Bug fix.
+#
+#  Revision 1.7  2003/09/15 18:53:19  gmwalter
+#  Added some functionality for combining bands from different files,
+#  reprojecting geocoding information.
+#
+#  Revision 1.6  2003/04/09 14:46:13  gmwalter
+#  Fixed typo in datatype setting.
+#
+#  Revision 1.5  2003/02/26 22:53:15  gmwalter
+#  Add geocoding information preference.
+#
+#  Revision 1.4  2003/01/28 14:31:39  warmerda
+#  added log
+#
+#
+import gdal       
+import Numeric
+import osr
+import os
+import string
+
+class VRTCreationOptions:
+    def __init__(self,num_dstbands):
+        # num_dstbands: number of bands in destination vrt
+        self.band_opts={}
+        self.geocode_preference=None
+        self.reproj=None
+        
+        for cband in range(1,num_dstbands+1):
+            self.band_opts[str(cband)]={}
+            self.band_opts[str(cband)]['band']=str(cband)
+            self.band_opts[str(cband)]['SourceBand']=str(cband)
+            
+    def set_geopref(self,geocode_pref=None):
+        # Set the geocode preference
+        # (None or 'geotransform' or 'gcps')
+        if geocode_pref is None:
+            self.geocode_preference=None            
+        elif geocode_pref == 'geotransform':
+            self.geocode_preference='geotransform'
+        elif geocode_pref == 'gcps':
+            self.geocode_preference = 'gcps'
+        else:
+            txt="Invalid geocoding preference- options are None,"
+            txt=txt+"'geotransform',or 'gcp'.  Resetting to None." 
+            print txt
+            self.geocode_preference = None
+
+    def get_geopref(self):
+        return self.geocode_preference
+
+    def set_reproj(self,proj):
+        """ Reproject gcps or geotransform to projection proj (a WKT string)
+            during serialization.
+        """
+        self.reproj=proj
+        
+    def set_src_window(self,src_tuple,band_list=None):
+        if band_list is None:
+            # Window all bands
+            for ckey in self.band_opts.keys():
+                self.band_opts[ckey]['SrcRect']=src_tuple
+        else:
+            for cband in band_list:
+                self.band_opts[str(cband)]['SrcRect']=src_tuple
+            
+    def set_dst_window(self,dst_tuple,band_list=None):
+        if band_list is None:
+            # Window all bands
+            for ckey in self.band_opts.keys():
+                self.band_opts[ckey]['DstRect']=dst_tuple
+        else:
+            for cband in band_list:
+                self.band_opts[str(cband)]['DstRect']=dst_tuple
+
+    def set_datatype(self,data_type,band_list=None):
+        if band_list is None:
+            # Set datatype in all bands
+            for ckey in self.band_opts.keys():
+                self.band_opts[ckey]['DataType']=gdal.GetDataTypeName(data_type)
+        else:
+            for cband in band_list:
+                self.band_opts[str(cband)]['DataType']=gdal.GetDataTypeName(data_type)
+
+    def set_color_interp(self,color_interp,band_list=None):
+        if band_list is None:
+            # Window all bands
+            for ckey in self.band_opts.keys():
+                self.band_opts[ckey]['ColorInterp']=color_interp
+        else:
+            for cband in band_list:
+                self.band_opts[str(cband)]['ColorInterp']=color_interp
+        
+
+    def set_scaling(self,scale_tuple,band_list=None):
+        srcmin=scale_tuple[0]
+        srcmax=scale_tuple[1]
+        dstmin=scale_tuple[2]
+        dstmax=scale_tuple[3]
+        srcdiff=float(srcmax-srcmin)
+        dstdiff=float(dstmax-dstmin)
+        if abs(srcdiff) > 0.0:
+            ratio=dstdiff/srcdiff
+        else:
+            print 'Warning- no dynamic range for source. Ratio defaulting to 1.'
+            ratio=1.0
+
+        offset=dstmin-(srcmin*ratio)    
+        if band_list is None:
+            # Window all bands
+            for ckey in self.band_opts.keys():
+                self.band_opts[ckey]['ScaleRatio']=ratio                
+                self.band_opts[ckey]['ScaleOffset']=offset
+        else:
+            for cband in band_list:
+                self.band_opts[str(cband)]['ScaleRatio']=ratio                
+                self.band_opts[str(cband)]['ScaleOffset']=offset
+        
+
+    def get_opts(self):
+        return self.band_opts
+
+def serializeMetadata(indataset=None, dict=None):
+    """ Serialize metadata.
+    
+        Inputs:
+        
+            indataset- gdal dataset to extract metadata from
+                       (None if not using)
+                       
+            dict- dictionary to use for metadata (None
+                  if not using)
+
+        Values in dict will override values in indataset.
+    """
+    
+    if indataset is not None:
+        metadict=indataset.GetMetadata()
+        if dict is not None:
+            for item in dict.keys():
+                metadict[item] = dict[item]
+    else:
+        metadict=dict
+        
+    if len (metadict) > 0:
+        metabase=[gdal.CXT_Element,'Metadata']
+        for ckey in metadict.keys():
+            mdibase=[gdal.CXT_Element,'MDI']
+            mdibase.append([gdal.CXT_Attribute,'key',[gdal.CXT_Text,ckey]])
+            mdibase.append([gdal.CXT_Text,metadict[ckey]])
+            metabase.append(mdibase)
+    else:
+        metabase=None
+        
+    return metabase
+
+
+def serializeGCPs(indataset=None, vrt_options=None,with_Z=0,gcplist=None,
+                  projection_attr_txt=None,reproj=None,srcrect=None,
+                  dstrect=None):
+    """ This function can be used to serialize gcps with or
+        without an associated gdal dataset.
+
+        Inputs:
+            indataset- a gdal dataset
+            vrt_options- a VRTCreationOptions object
+            with_Z- flag to indicate whether Z values should be included
+            gcplist- a gcplist or None.  If it is None, indataset
+                     will be searched.
+            projection_attr_text- projection of the gcps.  If it is
+                      None, indataset will be searched.
+            reproj- projection of output gcps.  Only used if vrt_options
+                    is None (otherwise it looks in vrt_options for reproj).
+            srcrect, dstrect- source and destination rectangles to use to
+                              adjust gcps (optional- default to None).
+                              Only used if vrt_options is None (otherwise
+                              function looks at the vrt options for the first
+                              band and checks that for SrcRect, DstRect).
+                              GCPs are updated so that the new pixel and
+                              line values of each gcp correspond to the
+                              destination rectangle as opposed to the source
+                              rectangle.
+
+        Examples:
+            serializeGCPs(dataset,vrtopts)
+            serializeGCPs(gcplist=gcps,projection_attr_txt='GEOGCS...',
+                          reproj='PROJCS...')
+    """
+            
+    if gcplist is None:
+        gcplist=indataset.GetGCPs()
+
+    if projection_attr_txt is None:
+        projection_attr_txt=indataset.GetGCPProjection()
+        
+    srtrans=None
+    if vrt_options is not None:
+        reproj=vrt_options.reproj
+        if reproj == projection_attr_txt:
+            reproj=None
+        
+    if (reproj is not None):
+        sr1=osr.SpatialReference()
+        sr2=osr.SpatialReference()
+        try:
+            sr1.ImportFromWkt(reproj)
+            try:
+                sr2.ImportFromWkt(projection_attr_txt)
+                srtrans=osr.CoordinateTransformation(sr2,sr1)
+            except:
+                srtrans=None
+                print 'Warning: unable to reproject gcps- invalid source projection string'
+        except:
+            srtrans=None
+            print 'Warning: unable to reproject gcps- invalid destination projection string'
+            
+    if len(gcplist) > 0:
+        gcpbase=[gdal.CXT_Element,'GCPList']
+        if (srtrans is not None):
+            gcpbase.append([gdal.CXT_Attribute,'Projection',
+                            [gdal.CXT_Text,reproj]])
+        else:
+            gcpbase.append([gdal.CXT_Attribute,'Projection',
+                            [gdal.CXT_Text,projection_attr_txt]])
+        if (vrt_options is None) and ((srcrect is None) or (dstrect is None)):
+            for gcp in gcplist:
+                if srtrans is not None:
+                    ngcp=srtrans.TransformPoint(gcp.GCPX,gcp.GCPY,gcp.GCPZ)
+                    gcp.GCPX=ngcp[0]
+                    gcp.GCPY=ngcp[1]
+                    gcp.GCPZ=ngcp[2]
+                gcpbase.append(gcp.serialize(with_Z))    
+        else:
+            if vrt_options is not None:
+                bopts=vrt_options.get_opts()[vrt_options.get_opts().keys()[0]]
+                if((bopts.has_key('SrcRect')) and
+                   (bopts.has_key('DstRect'))):
+                    srcrect = bopts['SrcRect']
+                    dstrect = bopts['DstRect']
+                else:
+                    srcrect = None
+                    dstrect = None
+                    
+            if (srcrect is None) or (dstrect is None):
+                for gcp in gcplist:
+                    if srtrans is not None:
+                        ngcp=srtrans.TransformPoint(gcp.GCPX,gcp.GCPY,gcp.GCPZ)
+                        gcp.GCPX=ngcp[0]
+                        gcp.GCPY=ngcp[1]
+                        gcp.GCPZ=ngcp[2]
+                    gcpbase.append(gcp.serialize(with_Z))    
+            else:
+                (spix,sline,xsize,ysize)=srcrect
+                (dpix,dline,dxsize,dysize)=dstrect
+                for gcp in gcplist:
+                    gcpbase2=[gdal.CXT_Element,'GCP']
+                    if srtrans is not None:
+                        ngcp=srtrans.TransformPoint(gcp.GCPX,gcp.GCPY,gcp.GCPZ)
+                        gx=ngcp[0]
+                        gy=ngcp[1]
+                        gz=ngcp[2]
+                    else:
+                        gx=gcp.GCPX
+                        gy=gcp.GCPY
+                        gz=gcp.GCPZ
+                    gp=(gcp.GCPPixel+dpix-spix)*dxsize/xsize
+                    gl=(gcp.GCPLine+dline-sline)*dysize/ysize
+                    gcpbase2.append([gdal.CXT_Attribute,'Id',
+                                     [gdal.CXT_Text,gcp.Id]])
+                    pixval = '%0.15E' % gp       
+                    lineval = '%0.15E' % gl
+                    xval = '%0.15E' % gx
+                    yval = '%0.15E' % gy
+                    zval = '%0.15E' % gz
+                    gcpbase2.append([gdal.CXT_Attribute,'Pixel',
+                                     [gdal.CXT_Text,pixval]])
+                    gcpbase2.append([gdal.CXT_Attribute,'Line',
+                                     [gdal.CXT_Text,lineval]])
+                    gcpbase2.append([gdal.CXT_Attribute,'X',
+                                     [gdal.CXT_Text,xval]])
+                    gcpbase2.append([gdal.CXT_Attribute,'Y',
+                                     [gdal.CXT_Text,yval]])
+                    if with_Z:
+                        gcpbase2.append([gdal.CXT_Attribute,'Z',
+                                         [gdal.CXT_Text,yval]])
+                    gcpbase.append(gcpbase2)
+    else:
+        gcpbase=None
+        
+    return gcpbase
+
+
+def GeoTransformToGCPs(gt,num_pixels,num_lines,grid=2):
+    """ Form a gcp list from a geotransform. If grid=0, just use 4
+        corners.  If grid=1, split each dimension once.  If grid=2,
+        split twice, etc:
+
+        grid=0             grid=1                grid=2
+
+        *           *      *     *     *         *   *   *   *
+        
+                                                 *   *   *   *    
+                           *     *     *
+                                                 *   *   *   *
+                                               
+        *           *      *     *     *         *   *   *   *
+
+        This function is meant to be used to convert a geotransform
+        to gcp's so that the geocoded information can be reprojected.
+
+        Inputs: gt- geotransform to convert to gcps
+                num_pixels- number of pixels in the dataset
+                num_lines- number of lines in the dataset
+                grid- see above.  Defaults to 2.
+        
+    """
+    
+    gcp_list=[]
+
+    parr=Numeric.arange(0.0,num_pixels+1.0,num_pixels/(grid+1.0))
+    larr=Numeric.arange(0.0,num_lines+1.0,num_lines/(grid+1.0))
+
+    for idx in range(len(parr)*len(larr)):
+        cgcp=gdal.GCP()
+        pix=parr[idx % len(parr)]
+        line=larr[idx/len(larr)]
+        cgcp.Id=str(idx)
+        cgcp.GCPX=gt[0]+(pix*gt[1])+(line*gt[2])
+        cgcp.GCPY=gt[3]+(pix*gt[4])+(line*gt[5])
+        cgcp.GCPZ=0.0
+        cgcp.GCPPixel=pix
+        cgcp.GCPLine=line
+        
+        gcp_list.append(cgcp)
+
+    return gcp_list
+
+def serializeGeoTransform(indataset=None,vrt_options=None,geotransform=None,
+                          srcrect=None,dstrect=None,force_geo=0):
+    """
+        Returns a serialized geotransform, or None if the input
+        geotransform is the default (0.0,1.0,0.0,0.0,0.0,1.0).
+
+        Inputs:
+
+        indataset- an input GDAL dataset to get the geotranform
+                   from if the geotransform input isn't specified.
+                   
+        vrtoptions- options for indataset, specifying source and
+                    destination rectangles (optional)
+                    
+        geotransform- a geotransform (6-valued tuple).  Overrides
+                      the geotransform in indataset, if present.
+    
+        srcrect, dstrect-  arguments used to shift
+        and scale the transform in case of cropping or resolution
+        changes.  The srcrect and dstrect arguments must both be
+        tuples of the form (xstart,ystart,xsize,ysize).  If only
+        one is specified, it will be ignored. srcrect and dstrect
+        are IGNORED if vrt_options is not None.
+
+        force_geo- Set to 1 to force the geotransform to be serialized
+                   even if the input geotransform is just the
+                   default.  Is 0 (off) by default.
+    """
+    if geotransform is None:
+        gt=indataset.GetGeoTransform()
+    else:
+        gt=geotransform
+        
+    default_geo=(0.0,1.0,0.0,0.0,0.0,1.0)
+    # Don't add anything if the transform
+    # is the default values, unless force_geo
+    # is set to 1.
+    usegeo=force_geo
+    gbase=None
+
+    for i in range(6):
+        if gt[i] != default_geo[i]:
+            usegeo=1
+
+    if usegeo==1:
+        if (vrt_options is None) and ((srcrect is None) or (dstrect is None)):
+            geo_text='  %0.22E, %0.22E, %0.22E, %0.22E, %0.22E, %0.22E' % (gt[0], gt[1], gt[2], gt[3], gt[4], gt[5])
+        else:
+            if vrt_options is not None:
+                bopts=vrt_options.get_opts()[vrt_options.get_opts().keys()[0]]
+                if ((bopts.has_key('SrcRect')) and
+                    (bopts.has_key('DstRect'))):
+                    srcrect = bopts['SrcRect']
+                    dstrect = bopts['DstRect']
+                else:
+                    srcrect=None
+                    dstrect=None
+            if (srcrect is None) or (dstrect is None):
+                geo_text='  %0.22E, %0.22E, %0.22E, %0.22E, %0.22E, %0.22E' % (gt[0], gt[1], gt[2], gt[3], gt[4], gt[5])
+            else:    
+                # Geotransform should be updated to reflect the window
+                # All the bands should have the same windowing/overview options,
+                # so just look at first one
+                
+                (spix,sline,xsize,ysize)=srcrect
+                (dpix,dline,dxsize,dysize)=dstrect
+                gt0=gt[0]+gt[1]*(spix-dpix)+gt[2]*(sline-dline)
+                gt3=gt[3]+gt[4]*(spix-dpix)+gt[5]*(sline-dline)
+                # floor- if xsize/ysize/dxsize/dysize are non-integer,
+                # gdal will truncate, so account for that here.
+                gt1=float(gt[1])*Numeric.floor(xsize)/Numeric.floor(dxsize)
+                gt2=float(gt[2])*Numeric.floor(ysize)/Numeric.floor(dysize)
+                gt4=float(gt[4])*Numeric.floor(xsize)/Numeric.floor(dxsize)
+                gt5=float(gt[5])*Numeric.floor(ysize)/Numeric.floor(dysize)
+                geo_text='  %0.22E, %0.22E, %0.22E, %0.22E, %0.22E, %0.22E' % (gt0, gt1, gt2, gt3, gt4, gt5)
+  
+        gbase=[gdal.CXT_Element,'GeoTransform',[gdal.CXT_Text,geo_text]]
+    else:
+        gbase=None
+
+    return gbase
+
+def serializeCombinedDatasets(indatasetlist,vrt_options_list=None,
+                              band_lists=None):
+    """ Combines bands from several datasets into one.  Uses
+        metadata and georeferencing from the FIRST one,
+        and determines raster size from first one.
+        indatasetlist- a list of gdal datasets
+        vrt_options_list- a single vrt_options instance applicable
+                          to all the datasets, or else a list
+                          of vrt_options (same length as
+                          indatasetlist).
+        band_lists- list of lists of bands, or None (use all bands).
+    """
+    
+    if vrt_options_list is None:
+        band_opts=[]
+        for ds in indatasetlist:
+            band_opts.append({})
+    else:
+        band_opts=[]
+        if type(vrt_options_list) == type(list):
+            if len(vrt_options_list) != len(indatasetlist):
+                raise 'VRT option list length does not match dataset list '+\
+                      'length!'
+            for opts in vrt_options_list:
+                band_opts.append(opts.get_opts())
+        else:
+            for ds in indatasetlist:
+                band_opts.append(vrt_options_list.get_opts())
+        
+    # band opts: A dictionary of band option dictionaries,
+    # indexed by the str(band number)
+    base=[gdal.CXT_Element,'VRTDataset']
+    if vrt_options_list is None:
+        base.append([gdal.CXT_Attribute,'rasterXSize',
+                     [gdal.CXT_Text,str(indatasetlist[0].RasterXSize)]])
+        base.append([gdal.CXT_Attribute,'rasterYSize',
+                     [gdal.CXT_Text,str(indatasetlist[0].RasterYSize)]])
+    else:
+        bopts=band_opts[0]
+        if bopts.has_key('DstRect'):
+            # For now, get dataset size form first band
+            (dpix,dline,dxsize,dysize)=bopts['DstRect']
+            base.append([gdal.CXT_Attribute,'rasterXSize',
+                         [gdal.CXT_Text,str(dxsize)]])
+            base.append([gdal.CXT_Attribute,'rasterYSize',
+                         [gdal.CXT_Text,str(dysize)]])
+        else:
+            base.append([gdal.CXT_Attribute,'rasterXSize',
+                         [gdal.CXT_Text,str(indatasetlist[0].RasterXSize)]])
+            base.append([gdal.CXT_Attribute,'rasterYSize',
+                         [gdal.CXT_Text,str(indatasetlist[0].RasterYSize)]])
+
+    mbase=serializeMetadata(indatasetlist[0])
+    if mbase is not None:
+        base.append(mbase)
+
+    if vrt_options_list is None:
+        geopref=None
+        vrt_options=None
+    elif type(vrt_options_list) == type([]):
+        geopref=vrt_options_list[0].get_geopref()
+        vrt_options=vrt_options_list[0]
+    else:
+        geopref=vrt_options_list.get_geopref()
+        vrt_options=vrt_options_list
+        
+    if ((geopref == 'gcps') or (geopref is None)):
+        # If preference is gcps or none, copy any available gcp information
+        gcpbase=serializeGCPs(indatasetlist[0],vrt_options)
+        if gcpbase is not None:
+            base.append(gcpbase)
+
+        if ((gcpbase is None) and (geopref == 'gcps')):
+            print 'Warning- No gcp information to transfer.'
+        
+    if ((geopref == 'geotransform') or (geopref is None)):
+        # If preference is geotransform or none, copy any available
+        # geotransform information.  If geopref is None and
+        # vrt indicates that the user wishes to reproject georeferencing,
+        # convert to gcps and serialize them.
+
+        srs_text=indatasetlist[0].GetProjection()
+        if vrt_options is not None:
+            reproj=vrt_options.reproj
+            if reproj == srs_text:
+                reproj=None
+        else:
+            reproj=None
+            
+        if ((reproj is not None) and (srs_text is not None) and
+            (len(srs_text) > 0) and (gcpbase is None)):
+            
+            if geopref is None:
+                gcps=GeoTransformToGCPs(indatasetlist[0].GetGeoTransform(),
+                                        indatasetlist[0].RasterXSize,
+                                        indatasetlist[0].RasterYSize)
+                gcpbase=serializeGCPs(indatasetlist[0],vrt_options,
+                         gcplist=gcps,projection_attr_txt=srs_text)
+                
+                if gcpbase is not None:
+                    base.append(gcpbase)
+            else:
+                print 'Warning- reprojection of a geotransform to a '+\
+                      '\n        new geotransform not supported.'
+        elif (reproj is None):
+            if ((srs_text is not None) and (len(srs_text) > 0)):
+                prjbase=[gdal.CXT_Element,'SRS',[gdal.CXT_Text,srs_text]]      
+                base.append(prjbase)
+           
+            gbase=serializeGeoTransform(indatasetlist[0],vrt_options)
+            if gbase is not None:
+                base.append(gbase)
+
+            if ((gbase is None) and (geopref == 'geotransform')):
+                print 'Warning- No geotransform information to transfer.'
+        elif gcpbase is None:
+            print 'Warning- No reprojectable geotransform information found.'
+    
+    for idx in range(len(indatasetlist)):
+        indataset=indatasetlist[idx]
+        if band_lists is None:
+            band_list=None
+        else:
+            band_list=band_lists[idx]
+            
+        if band_list is None:
+            for cband in range(1,indataset.RasterCount+1):
+                if band_opts[idx].has_key(str(cband)):
+                    bbase=serializeBand(indataset,
+                                        opt_dict=band_opts[idx][str(cband)])
+                    base.append(bbase)
+                else:
+                    opt_dict={}
+                    opt_dict['band']=str(cband)
+                    opt_dict['SourceBand']=str(cband)
+                    bbase=serializeBand(indataset,opt_dict=opt_dict)
+                    base.append(bbase)
+        else:
+            for cband in band_list:
+                if band_opts.has_key(str(cband)):
+                    bbase=serializeBand(indataset,
+                                        opt_dict=band_opts[idx][str(cband)])
+                    base.append(bbase)
+                else:
+                    opt_dict={}
+                    opt_dict['band']=str(cband)
+                    opt_dict['SourceBand']=str(cband)
+                    bbase=serializeBand(indataset,opt_dict=opt_dict)
+                    base.append(bbase)
+        
+    return base
+                    
+    
+def serializeDataset(indataset,vrt_options=None,band_list=None):
+    if vrt_options is None:
+        band_opts={}
+    else:
+        band_opts=vrt_options.get_opts()
+        
+    # band opts: A dictionary of band option dictionaries,
+    # indexed by the str(band number)
+    base=[gdal.CXT_Element,'VRTDataset']
+    if vrt_options is None:
+        base.append([gdal.CXT_Attribute,'rasterXSize',
+                     [gdal.CXT_Text,str(indataset.RasterXSize)]])
+        base.append([gdal.CXT_Attribute,'rasterYSize',
+                     [gdal.CXT_Text,str(indataset.RasterYSize)]])
+    else:
+        bopts=band_opts[band_opts.keys()[0]]
+        if bopts.has_key('DstRect'):
+            # For now, get dataset size form first band
+            (dpix,dline,dxsize,dysize)=bopts['DstRect']
+            base.append([gdal.CXT_Attribute,'rasterXSize',
+                         [gdal.CXT_Text,str(dxsize)]])
+            base.append([gdal.CXT_Attribute,'rasterYSize',
+                         [gdal.CXT_Text,str(dysize)]])
+        else:
+            base.append([gdal.CXT_Attribute,'rasterXSize',
+                         [gdal.CXT_Text,str(indataset.RasterXSize)]])
+            base.append([gdal.CXT_Attribute,'rasterYSize',
+                         [gdal.CXT_Text,str(indataset.RasterYSize)]])
+
+    mbase=serializeMetadata(indataset)
+    if mbase is not None:
+        base.append(mbase)
+
+    geopref=None
+    if vrt_options is not None:
+        geopref=vrt_options.get_geopref()
+
+    gcpbase=None    
+    if ((geopref == 'gcps') or (geopref is None)):
+        # If preference is gcps or none, copy any available gcp information
+        gcpbase=serializeGCPs(indataset,vrt_options)
+        if gcpbase is not None:
+            base.append(gcpbase)
+
+        if ((gcpbase is None) and (geopref == 'gcps')):
+            print 'Warning- No gcp information to transfer.'
+        
+    if ((geopref == 'geotransform') or (geopref is None)):
+        # If preference is geotransform or none, copy any available
+        # geotransform information.  If geopref is None and
+        # vrt indicates that the user wishes to reproject georeferencing,
+        # convert to gcps and serialize them.
+
+        srs_text=indataset.GetProjection()
+        if vrt_options is not None:
+            reproj=vrt_options.reproj
+            if reproj == srs_text:
+                reproj=None
+        else:
+            reproj=None
+            
+        if ((reproj is not None) and (srs_text is not None) and
+            (len(srs_text) > 0) and (gcpbase is None)):
+            
+            if geopref is None:
+                gcps=GeoTransformToGCPs(indataset.GetGeoTransform(),
+                                        indataset.RasterXSize,
+                                        indataset.RasterYSize)
+                gcpbase=serializeGCPs(indataset,vrt_options,gcplist=gcps,
+                                      projection_attr_txt=srs_text)
+                
+                if gcpbase is not None:
+                    base.append(gcpbase)
+            else:
+                print 'Warning- reprojection of a geotransform to a '+\
+                      '\n        new geotransform not supported.'
+        elif (reproj is None):
+            if ((srs_text is not None) and (len(srs_text) > 0)):
+                prjbase=[gdal.CXT_Element,'SRS',[gdal.CXT_Text,srs_text]]      
+                base.append(prjbase)
+           
+            gbase=serializeGeoTransform(indataset,vrt_options)
+            if gbase is not None:
+                base.append(gbase)
+
+            if ((gbase is None) and (geopref == 'geotransform')):
+                print 'Warning- No geotransform information to transfer.'
+        elif gcpbase is None:
+            print 'Warning- No reprojectable geotransform information found.'
+         
+
+    if band_list is None:
+        for cband in range(1,indataset.RasterCount+1):
+            if band_opts.has_key(str(cband)):
+                bbase=serializeBand(indataset,opt_dict=band_opts[str(cband)])
+                base.append(bbase)
+            else:
+                opt_dict={}
+                opt_dict['band']=str(cband)
+                opt_dict['SourceBand']=str(cband)
+                bbase=serializeBand(indataset,opt_dict=opt_dict)
+                base.append(bbase)
+    else:
+        for cband in band_list:
+            if band_opts.has_key(str(cband)):
+                bbase=serializeBand(indataset,opt_dict=band_opts[str(cband)])
+                base.append(bbase)
+            else:
+                opt_dict={}
+                opt_dict['band']=str(cband)
+                opt_dict['SourceBand']=str(cband)
+                bbase=serializeBand(indataset,opt_dict=opt_dict)
+                base.append(bbase)
+        
+    return base
+
+def serializeRawBand(SourceFilename, band, DataType, ByteOrder,
+                      ImageOffset, PixelOffset, LineOffset,Description=None):
+    """ Serialize a raw (flat binary) raster band.
+        Inputs:
+        
+        SourceFilename- filename of a gdal dataset (a string)
+                    
+        band- band number that the serialized band will correspond
+              to in the output dataset (an integer).
+
+        DataType- data type (a string).  May be 'Byte', 'UInt16',
+                  'Int16', 'UInt32', 'Int32', 'Float32', 'Float64',
+                  'CInt16', 'CInt32', 'CFloat32', 'CFloat64'.
+                  
+        ByteOrder- MSB or LSB (string)
+        
+        ImageOffset- offset to first pixel of band (int)
+                
+        PixelOffset- offset between successive pixels in the input (int)
+
+        LineOffset- offset between successive lines in the input (int)
+
+        Description- OPTIONAL description for the band (a string)
+    """
+    base=[gdal.CXT_Element,'VRTRasterBand']
+        
+    base.append([gdal.CXT_Attribute,'dataType',
+                     [gdal.CXT_Text,DataType]])
+        
+    base.append([gdal.CXT_Attribute,'band',[gdal.CXT_Text,str(band)]])
+
+    base.append([gdal.CXT_Attribute,'subClass',
+                 [gdal.CXT_Text,"VRTRawRasterBand"]])
+
+    if Description is not None:
+        base.append([gdal.CXT_Element,'Description',
+                       [gdal.CXT_Text,Description]])
+        
+    base.append([gdal.CXT_Element,'SourceFilename',
+                       [gdal.CXT_Text,SourceFilename]])
+    base[len(base)-1].append([gdal.CXT_Attribute,'relativeToVRT',
+                    [gdal.CXT_Text,GetRelativeToVRT(SourceFilename)]])
+
+    base.append([gdal.CXT_Element,'ByteOrder',
+                       [gdal.CXT_Text,ByteOrder]])
+
+    base.append([gdal.CXT_Element,'ImageOffset',
+                       [gdal.CXT_Text,str(ImageOffset)]])
+
+    base.append([gdal.CXT_Element,'PixelOffset',
+                       [gdal.CXT_Text,str(PixelOffset)]])
+
+    base.append([gdal.CXT_Element,'LineOffset',
+                       [gdal.CXT_Text,str(LineOffset)]])
+
+    return base
+    
+def serializeBand(indataset=None,opt_dict={}):
+    """ Serialize a raster band.
+        Inputs:
+            indataset- dataset to take default values from for
+                       items that are not specified in opt_dict.
+                       Set to None if not needed.
+                       
+            opt_dict- dictionary to take values from.
+
+        opt_dict will be searched for the following keys:
+
+        SourceFilename- filename of a gdal dataset (a string)
+        
+        SourceBand- an integer indicating the band from SourceFilename
+                    to use.  1 if not specified.
+                    
+        band- band number that the serialized band will correspond
+              to in the output dataset (an integer).  1 if not specified.
+
+        DataType- data type (a string).  May be 'Byte', 'UInt16',
+                  'Int16', 'UInt32', 'Int32', 'Float32', 'Float64',
+                  'CInt16', 'CInt32', 'CFloat32', 'CFloat64'
+
+        Description- OPTIONAL description to associate with the band (string).
+
+        ColorInterp- OPTIONAL colour interpretation (a string).  One of
+                     'Gray', 'Red', 'Green', 'Blue', 'Alpha', 'Undefined', or
+                     'Palette'.  If palette is specified, then opt_dict
+                     must also have a key 'Palette' with a value that
+                     is a gdal ColorTable object.
+
+        NoDataValue- OPTIONAL no data value.  Floating point or integer.
+
+        ScaleOffset, ScaleRatio- OPTIONAL scaling offset and ratio for
+                     rescaling the input bands (floating point numbers):
+                     outband = ScaleOffset + (ScaleRatio*inband)
+
+        SrcRect- a tuple of four integers specifying the extents of the
+                 source to use (xoffset, yoffset, xsize, ysize)
+
+        DstRect- a tuple of four integers specifying the extents that
+                 SrcRect will correspond to in the output file.
+                     
+    """
+    base=[gdal.CXT_Element,'VRTRasterBand']
+
+    if opt_dict.has_key('SourceBand'):
+        inband=int(opt_dict['SourceBand'])
+    else:
+        inband=1
+    
+    if opt_dict.has_key('band'):    
+        outband=int(opt_dict['band'])
+    else:
+        outband=1
+     
+    if opt_dict.has_key('DataType'):
+        base.append([gdal.CXT_Attribute,'dataType',
+                     [gdal.CXT_Text,opt_dict['DataType']]])        
+    elif indataset is not None:
+        base.append([gdal.CXT_Attribute,'dataType',
+                     [gdal.CXT_Text,
+                      gdal.GetDataTypeName(
+            indataset.GetRasterBand(inband).DataType)]])
+    else:
+        base.append([gdal.CXT_Attribute,'dataType',[gdal.CXT_Text,'Byte']])
+
+        
+    base.append([gdal.CXT_Attribute,'band',[gdal.CXT_Text,str(outband)]])
+
+    if opt_dict.has_key('Description'):
+        base.append([gdal.CXT_Element,'Description',
+                         [gdal.CXT_Text,opt_dict['Description']]])
+    elif indataset is not None:
+        desc = indataset.GetRasterBand(inband).GetDescription()
+        if len(desc) > 0:
+            base.append([gdal.CXT_Element,'Description',
+                         [gdal.CXT_Text,desc]])
+        
+    if opt_dict.has_key('ColorInterp'):
+        if opt_dict['ColorInterp'] != 'Undefined':
+            base.append([gdal.CXT_Element,'ColorInterp',
+                         [gdal.CXT_Text,opt_dict['ColorInterp']]])
+            if opt_dict['ColorInterp'] == 'Palette':
+                base.append(opt_dict['Palette'].serialize())
+            
+    elif indataset is not None:
+        cinterp=indataset.GetRasterBand(inband).GetRasterColorInterpretation()
+        if cinterp != gdal.GCI_Undefined:
+            cinterpname=gdal.GetColorInterpretationName(cinterp)
+            base.append([gdal.CXT_Element,'ColorInterp',
+                         [gdal.CXT_Text,cinterpname]])
+            if cinterpname=='Palette':
+                ct = indataset.GetRasterBand(inband).GetRasterColorTable()
+                base.append(ct.serialize())
+
+    if opt_dict.has_key('NoDataValue'):
+        base.append([gdal.CXT_Element,'NoDataValue',
+                         [gdal.CXT_Text,str(opt_dict['NoDataValue'])]])
+    elif indataset is not None:
+        nodata_val=indataset.GetRasterBand(inband).GetNoDataValue()
+        if ((nodata_val is not None) and (nodata_val != '')):
+            base.append([gdal.CXT_Element,'NoDataValue',
+                         [gdal.CXT_Text,str(nodata_val)]])
+                
+    if opt_dict.has_key('ScaleOffset') or opt_dict.has_key('ScaleRatio'):
+        ssbase=[gdal.CXT_Element,'ComplexSource']
+    else:
+        ssbase=[gdal.CXT_Element,'SimpleSource']
+
+    if opt_dict.has_key('SourceFilename'):
+        ssbase.append([gdal.CXT_Element,'SourceFilename',
+                       [gdal.CXT_Text,opt_dict['SourceFilename']]])
+        ssbase[len(ssbase)-1].append([gdal.CXT_Attribute,'relativeToVRT',
+                 [gdal.CXT_Text,GetRelativeToVRT(opt_dict['SourceFilename'])]])
+    elif indataset is not None:
+        ssbase.append([gdal.CXT_Element,'SourceFilename',
+                       [gdal.CXT_Text,indataset.GetDescription()]])
+        ssbase[len(ssbase)-1].append([gdal.CXT_Attribute,'relativeToVRT',
+                 [gdal.CXT_Text,GetRelativeToVRT(indataset.GetDescription())]])
+    else:
+        ssbase.append([gdal.CXT_Element,'SourceFilename',
+                       [gdal.CXT_Text,'']])
+        ssbase[len(ssbase)-1].append([gdal.CXT_Attribute,
+                                      'relativeToVRT',[gdal.CXT_Text,'0']])
+
+    ssbase.append([gdal.CXT_Element,'SourceBand',[gdal.CXT_Text,str(inband)]])
+
+    srcwinbase=[gdal.CXT_Element,'SrcRect']    
+
+    if opt_dict.has_key('SrcRect'):
+        xoff=str(opt_dict['SrcRect'][0])
+        yoff=str(opt_dict['SrcRect'][1])
+        xsize=str(opt_dict['SrcRect'][2])
+        ysize=str(opt_dict['SrcRect'][3])
+    elif indataset is not None:
+        xoff='0'
+        yoff='0'
+        xsize=str(indataset.GetRasterBand(inband).XSize)
+        ysize=str(indataset.GetRasterBand(inband).YSize)
+    else:
+        xoff='0'
+        yoff='0'
+        xsize='0'
+        ysize='0'
+        
+    srcwinbase.append([gdal.CXT_Attribute,'xOff',[gdal.CXT_Text,xoff]])        
+    srcwinbase.append([gdal.CXT_Attribute,'yOff',[gdal.CXT_Text,yoff]])        
+    srcwinbase.append([gdal.CXT_Attribute,'xSize',[gdal.CXT_Text,xsize]])        
+    srcwinbase.append([gdal.CXT_Attribute,'ySize',[gdal.CXT_Text,ysize]])        
+    ssbase.append(srcwinbase)
+    
+    dstwinbase=[gdal.CXT_Element,'DstRect']
+    if opt_dict.has_key('DstRect'):
+        x2off=str(opt_dict['DstRect'][0])
+        y2off=str(opt_dict['DstRect'][1])
+        x2size=str(opt_dict['DstRect'][2])
+        y2size=str(opt_dict['DstRect'][3])
+    elif indataset is not None:
+        x2off='0'
+        y2off='0'
+        x2size=str(indataset.GetRasterBand(inband).XSize)
+        y2size=str(indataset.GetRasterBand(inband).YSize)
+    else:
+        x2off='0'
+        y2off='0'
+        x2size='0'
+        y2size='0'
+
+    dstwinbase.append([gdal.CXT_Attribute,'xOff',[gdal.CXT_Text,x2off]])        
+    dstwinbase.append([gdal.CXT_Attribute,'yOff',[gdal.CXT_Text,y2off]])        
+    dstwinbase.append([gdal.CXT_Attribute,'xSize',[gdal.CXT_Text,x2size]])        
+    dstwinbase.append([gdal.CXT_Attribute,'ySize',[gdal.CXT_Text,y2size]])        
+    ssbase.append(dstwinbase)
+
+    if opt_dict.has_key('ScaleOffset'):
+        ssbase.append([gdal.CXT_Element,'ScaleOffset',[gdal.CXT_Text,str(opt_dict['ScaleOffset'])]])
+
+    if opt_dict.has_key('ScaleRatio'):
+        ssbase.append([gdal.CXT_Element,'ScaleRatio',[gdal.CXT_Text,str(opt_dict['ScaleRatio'])]])
+        
+    base.append(ssbase)
+
+    return base
+
+
+def GetSimilarFiles(filename):
+    """ Looks in the directory of filename for files with the same size
+        and extension, and returns a list containing their full paths.
+    """
+
+    import os
+    import glob
+    
+    fdir=os.path.dirname(filename)
+    froot,fext=os.path.splitext(filename)
+    flist=glob.glob(os.path.join(fdir,'*'+fext))
+
+    fsize=os.path.getsize(filename)
+    slist=[]
+    for item in flist:
+        if os.path.getsize(item) == fsize:
+            slist.append(item)
+
+    return slist
+
+
+def GetRelativeToVRT(path):
+    """ Returns '0' if path is absolute, '1' if it is
+        relative to vrt.
+    """
+    if path[0] == '<':
+        # input path is an in-memory vrt file
+        return "0"
+    
+    if os.path.isabs(path):
+        return "0"
+    else:
+        return "1"
+
+class VRTDatasetConstructor:
+    """ Class to use for creating vrt datasets from scratch at
+        the python level.
+
+        Initial inputs:
+            pixels- number of pixels in dataset
+            lines- number of lines in dataset
+    """
+    def __init__(self,pixels,lines):
+        self.base=[gdal.CXT_Element,'VRTDataset']
+        self.base.append([gdal.CXT_Attribute,'rasterXSize',
+                     [gdal.CXT_Text,str(pixels)]])
+        self.base.append([gdal.CXT_Attribute,'rasterYSize',
+                     [gdal.CXT_Text,str(lines)]])
+        self.band_idx=1
+        self.xsize=pixels
+        self.ysize=lines
+
+    def AddSimpleBand(self, SourceFilename, SourceBand, DataType,
+                      SrcRect=None, DstRect=None, ColorInterp='Undefined',
+                      colortable=None, NoDataValue=None,
+                      ScaleOffset=None,ScaleRatio=None,
+                      Description=None):
+        """ Add a simple raster band
+
+        SourceFilename- filename of a gdal dataset (a string)
+        
+        SourceBand- an integer indicating the band from SourceFilename
+                    to use.
+
+        DataType- data type (a string).  May be 'Byte', 'UInt16',
+                  'Int16', 'UInt32', 'Int32', 'Float32', 'Float64',
+                  'CInt16', 'CInt32', 'CFloat32', 'CFloat64'
+
+        SrcRect- a tuple of four integers specifying the extents of the
+                 source to use (xoffset, yoffset, xsize, ysize).
+                 Defaults to (0, 0, xsize, ysize) for the dataset.
+
+        DstRect- a tuple of four integers specifying the extents that
+                 SrcRect will correspond to in the output file.
+                 Defaults to (0, 0, xsize, ysize) for the dataset.
+                 
+        ColorInterp- OPTIONAL colour interpretation (a string).  One of
+                     'Gray', 'Red', 'Green', 'Blue', 'Alpha', 'Undefined', or
+                     'Palette'.  If 'Palette' is specified, then colortable
+                     must also be specified.
+
+        colortable- GDAL colortable object (only used if ColorInterp
+                    is set to 'Palette').
+                    
+        NoDataValue- OPTIONAL no data value.  Floating point or integer.
+
+        ScaleOffset, ScaleRatio- OPTIONAL scaling offset and ratio for
+                     rescaling the input bands (floating point numbers):
+                     outband = ScaleOffset + (ScaleRatio*inband)
+
+        Description- OPTIONAL description for the band (a string).
+       
+        """
+        opt_dict={'SourceFilename':SourceFilename,'SourceBand':SourceBand,
+                  'DataType':DataType,'ColorInterp':ColorInterp}
+
+        if ColorInterp == 'Palette':
+            if colortable is None:
+                raise 'Colour table not specified!'
+            opt_dict['Palette']=colortable
+        
+        if SrcRect is not None:
+            opt_dict['SrcRect']=SrcRect
+        else:
+            opt_dict['SrcRect']=(0,0,self.xsize,self.ysize)
+
+        if DstRect is not None:
+            opt_dict['DstRect']=DstRect
+        else:
+            opt_dict['DstRect']=(0,0,self.xsize,self.ysize)
+
+        if ScaleOffset is not None:
+            opt_dict['ScaleOffset']=ScaleOffset
+
+        if ScaleRatio is not None:
+            opt_dict['ScaleRatio']=ScaleRatio
+
+        if NoDataValue is not None:
+            opt_dict['NoDataValue']=NoDataValue
+
+        if Description is not None:
+            opt_dict['Description']=Description
+
+        opt_dict['band']=self.band_idx
+        
+        bbase=serializeBand(None,opt_dict)
+        
+        self.base.append(bbase)
+        self.band_idx=self.band_idx+1
+
+    def AddRawBand(self, SourceFilename, DataType, ByteOrder,
+                      ImageOffset, PixelOffset, LineOffset,
+                      Description=None):
+        """ Add a flat binary source raster band.
+            Inputs:
+                SourceFilename- path to source file name (string)
+                
+                DataType- datatype (string).  One of:
+                                Byte        Float64
+                                UInt16      CInt16
+                                Int16       CInt32
+                                UInt32      CFloat32
+                                Int32       CFloat64
+                                Float32
+
+                ByteOrder- MSB or LSB (string)
+                
+                ImageOffset- offset to first pixel of band (int)
+                
+                PixelOffset- offset between successive pixels in the input (int)
+
+                LineOffset- offset between successive lines in the input (int)
+
+                Description- OPTIONAL description for the band (a string). 
+        """
+        bbase=serializeRawBand(SourceFilename,self.band_idx, DataType,
+                               ByteOrder,ImageOffset,PixelOffset,
+                               LineOffset,Description)
+        self.base.append(bbase)
+        self.band_idx=self.band_idx+1
+
+    def AddMetadata(self, metadict):
+        """ Add metadata from a dictionary """
+        if len(metadict.keys()) < 1:
+            return
+        
+        mbase=serializeMetadata(dict=metadict)
+        self.base.append(mbase)
+
+    def SetSRS(self, projection):
+        """ Set projection information for geotransform (a WKT string)"""
+        prjbase=[gdal.CXT_Element,'SRS',[gdal.CXT_Text,projection]]      
+        self.base.append(prjbase)
+
+    def SetGeoTransform(self, gt, srcrect=None, dstrect=None, force_geo=0):
+        """ Add a geotransform (input is a tuple of 6 numbers)
+            
+            Optional srcrect and dstrect arguments are used to shift
+            and scale the transform in case of cropping or resolution
+            changes.  The srcrect and dstrect arguments must both be
+            tuples of the form (xstart,ystart,xsize,ysize).  If only
+            one is specified, it will be ignored.
+
+            force_geo- Set to 1 to force the geotransform to be serialized
+                       even if the input geotransform is just the
+                       default.  Is 0 (off) by default.
+         """
+        gbase=serializeGeoTransform(geotransform=gt, srcrect=srcrect,
+                                    dstrect=dstrect, force_geo=force_geo)
+        if gbase is not None:
+            self.base.append(gbase)
+
+    def SetGCPs(self, gcps, projection='', reprojection=None, srcrect=None,
+                dstrect=None):
+        """ Add gcps from a list of GDAL GCP objects.
+            Optional projection argument should be a WKT string.
+            Optional reprojection argument should also be a WKT
+            string, and should only be specified if the projection
+            argument is also specified.
+            
+            Optional srcrect and dstrect arguments are used to shift
+            and scale the gcps in case of cropping or resolution
+            changes.  The srcrect and dstrect arguments must both be
+            tuples of the form (xstart,ystart,xsize,ysize).  If only
+            one is specified, it will be ignored.
+        """
+        gcpbase=serializeGCPs(gcplist=gcps,projection_attr_txt=projection,
+                              reproj=reprojection, srcrect=srcrect,
+                              dstrect=dstrect)
+        self.base.append(gcpbase)
+
+    def GetVRTLines(self):
+        """ Return lines suitable for writing to a vrt file. """
+        return gdal.SerializeXMLTree(self.base)
+
+    def GetVRTString(self):
+        """ Return a vrt string that can be opened as a gdal dataset. """
+        lines = self.GetVRTLines()
+        vrtstr = string.join(lines,'')
+        return vrtstr
+
+
+if __name__ ==  '__main__':
+    import string
+    
+    ds=VRTDatasetConstructor(2000,2000)
+    ds.AddSimpleBand('reltest.tif',2,'Float32')
+    ds.AddSimpleBand('/data/abstest.tif',1,'Byte',
+                     SrcRect=(1000,2000,4000,4000),
+                     ScaleOffset=2,ScaleRatio=3)
+    ds.AddRawBand('rawtest.x00','CFloat32','MSB',0,8,16000)
+    for item in string.split(ds.GetVRTLines(),'\n'):
+        print item
+        
+

Added: packages/openev/branches/upstream/current/ramps/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/ramps/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,23 @@
+/blue_green_ramp.txt/1.2/Mon Nov  4 20:12:02 2002//
+/blue_ramp.txt/1.2/Mon Nov  4 20:12:02 2002//
+/blue_white_red_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/brown_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/cyan_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/discrete_1.txt/1.1/Tue Nov  5 14:58:38 2002//
+/discrete_2.txt/1.1/Tue Nov  5 14:58:38 2002//
+/gray_ramp.txt/1.2/Mon Nov  4 20:12:02 2002//
+/green_ramp.txt/1.2/Mon Nov  4 20:12:02 2002//
+/green_yellow_red_ramp.txt/1.3/Mon Nov  4 20:47:23 2002//
+/lavender_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/orange_brown_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/orange_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/pink_red_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/purple_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/real_blue_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/real_red_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/red_blue_ramp.txt/1.2/Mon Nov  4 20:12:02 2002//
+/red_green_ramp.txt/1.2/Mon Nov  4 20:12:02 2002//
+/red_ramp.txt/1.2/Mon Nov  4 20:12:03 2002//
+/rose_red_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+/yellow_ramp.txt/1.1/Wed Aug 13 14:03:55 2003//
+D

Added: packages/openev/branches/upstream/current/ramps/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/ramps/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/ramps

Added: packages/openev/branches/upstream/current/ramps/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/ramps/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/ramps/blue_green_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/blue_green_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/blue_green_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Green-Blue Ramp
+0
+0.0 (0.0, 1.0, 0.0, 1.0)
+1.0 (0.0, 0.0, 1.0, 1.0)
\ No newline at end of file

Added: packages/openev/branches/upstream/current/ramps/blue_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/blue_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/blue_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Blue Ramp
+0
+0.0 (0.0, 0.0, 0.2, 1.0)
+1.0 (0.0, 0.0, 0.9, 1.0)

Added: packages/openev/branches/upstream/current/ramps/blue_white_red_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/blue_white_red_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/blue_white_red_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,5 @@
+Blue-White-Red Ramp
+0
+0.0 (0.0, 0.0, 1.0, 1.0)
+0.5 (1.0, 1.0, 1.0, 1.0)
+1.0 (1.0, 0.0, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/brown_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/brown_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/brown_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Brown Ramp
+0
+0.0 (1.0, 0.9, 0.85, 1.0)
+1.0 (0.5, 0.2, 0.1, 1.0)

Added: packages/openev/branches/upstream/current/ramps/cyan_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/cyan_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/cyan_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,5 @@
+Cyan Ramp
+0
+0.0 (0.8, 1.0, 1.0, 1.0)
+0.5 (0.0, 1.0, 1.0, 1.0)
+1.0 (0.0, 0.55, 0.55, 1.0)

Added: packages/openev/branches/upstream/current/ramps/discrete_1.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/discrete_1.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/discrete_1.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,14 @@
+Discrete Color Sample 1
+1
+(1.0, 0.0, 0.0, 1.0)
+(1.0, 0.5, 0.0, 1.0)
+(1.0, 1.0, 0.0, 1.0)
+(0.5, 1.0, 0.0, 1.0)
+(0.0, 1.0, 0.0, 1.0)
+(0.0, 1.0, 0.5, 1.0)
+(0.0, 1.0, 1.0, 1.0)
+(0.0, 0.5, 1.0, 1.0)
+(0.0, 0.0, 1.0, 1.0)
+(0.5, 0.0, 1.0, 1.0)
+(1.0, 0.0, 1.0, 1.0)
+(1.0, 0.0, 0.5, 1.0)
\ No newline at end of file

Added: packages/openev/branches/upstream/current/ramps/discrete_2.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/discrete_2.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/discrete_2.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,14 @@
+Discrete Color Sample 2
+1
+(0.5, 0.0, 0.0, 1.0)
+(0.5, 0.25, 0.0, 1.0)
+(0.5, 0.5, 0.0, 1.0)
+(0.25, 0.5, 0.0, 1.0)
+(0.0, 0.5, 0.0, 1.0)
+(0.0, 0.5, 0.25, 1.0)
+(0.0, 0.5, 0.5, 1.0)
+(0.0, 0.25, 0.5, 1.0)
+(0.0, 0.0, 0.5, 1.0)
+(0.25, 0.0, 0.5, 1.0)
+(0.5, 0.0, 0.5, 1.0)
+(0.5, 0.0, 0.25, 1.0)
\ No newline at end of file

Added: packages/openev/branches/upstream/current/ramps/gray_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/gray_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/gray_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Gray Ramp
+0
+0.0 (0.9, 0.9, 0.9, 1.0)
+1.0 (0.2, 0.2, 0.2, 1.0)

Added: packages/openev/branches/upstream/current/ramps/green_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/green_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/green_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Green Ramp
+0
+0.0 (0.0, 0.2, 0.0, 1.0)
+1.0 (0.0, 0.9, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/green_yellow_red_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/green_yellow_red_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/green_yellow_red_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,5 @@
+Green-Yellow-Red
+0
+0.0 (0.0, 1.0, 0.0, 1.0)
+0.5 (1.0, 1.0, 0.0, 1.0)
+1.0 (1.0, 0.0, 0.0, 1.0)
\ No newline at end of file

Added: packages/openev/branches/upstream/current/ramps/lavender_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/lavender_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/lavender_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Lavender Ramp
+0
+0.0 (1.0, 0.9, 1.0, 1.0)
+1.0 (0.5, 0.0, 0.5, 1.0)

Added: packages/openev/branches/upstream/current/ramps/orange_brown_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/orange_brown_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/orange_brown_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,5 @@
+Orange-Brown Ramp
+0
+0.0 (1.0, 0.93, 0.87, 1.0)
+0.5 (0.94, 0.53, 0.13, 1.0)
+1.0 (0.5, 0.22, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/orange_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/orange_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/orange_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Orange Ramp
+0
+0.0 (1.0, 0.9, 0.85, 1.0)
+1.0 (1.0, 0.25, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/pink_red_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/pink_red_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/pink_red_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Pink Red Ramp
+0
+0.0 (1.0, 0.9, 0.9, 1.0)
+1.0 (0.6, 0.0, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/purple_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/purple_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/purple_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Purple Ramp
+0
+0.0 (1.0, 0.9, 1.0, 1.0)
+1.0 (0.3, 0.0, 0.3, 1.0)

Added: packages/openev/branches/upstream/current/ramps/real_blue_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/real_blue_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/real_blue_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,5 @@
+Real Blue Ramp
+0
+0.0 (0.9, 0.9, 1.0, 1.0)
+0.5 (0.0, 0.0, 1.0, 1.0)
+1.0 (0.0, 0.0, 0.4, 1.0)

Added: packages/openev/branches/upstream/current/ramps/real_red_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/real_red_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/real_red_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,5 @@
+Real Red Ramp
+0
+0.0 (1.0, 0.9, 0.9, 1.0)
+0.5 (1.0, 0.0, 0.0, 1.0)
+1.0 (0.4, 0.0, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/red_blue_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/red_blue_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/red_blue_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Red-Blue Ramp
+0
+0.0 (0.9, 0.0, 0.0, 1.0)
+1.0 (0.0, 0.0, 0.9, 1.0)

Added: packages/openev/branches/upstream/current/ramps/red_green_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/red_green_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/red_green_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Red-Green Ramp
+0
+0.0 (0.9, 0.0, 0.0, 1.0)
+1.0 (0.0, 0.9, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/red_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/red_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/red_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Red Ramp
+0
+0.0 (0.9, 0.0, 0.0, 1.0)
+1.0 (0.2, 0.0, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/rose_red_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/rose_red_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/rose_red_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Rose Red Ramp
+0
+0.0 (1.0, 0.6, 0.6, 1.0)
+1.0 (0.4, 0.0, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/ramps/yellow_ramp.txt
===================================================================
--- packages/openev/branches/upstream/current/ramps/yellow_ramp.txt	                        (rev 0)
+++ packages/openev/branches/upstream/current/ramps/yellow_ramp.txt	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+Yellow Ramp
+0
+0.0 (1.0, 1.0, 0.9, 1.0)
+1.0 (0.8, 0.8, 0.0, 1.0)

Added: packages/openev/branches/upstream/current/shapefil.h
===================================================================
--- packages/openev/branches/upstream/current/shapefil.h	                        (rev 0)
+++ packages/openev/branches/upstream/current/shapefil.h	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,475 @@
+#ifndef _SHAPEFILE_H_INCLUDED
+#define _SHAPEFILE_H_INCLUDED
+
+/******************************************************************************
+ * $Id: shapefil.h,v 1.25 2002/05/07 13:46:30 warmerda Exp $
+ *
+ * Project:  Shapelib
+ * Purpose:  Primary include file for Shapelib.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * This software is available under the following "MIT Style" license,
+ * or at the option of the licensee under the LGPL (see LICENSE.LGPL).  This
+ * option is discussed in more detail in shapelib.html.
+ *
+ * --
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: shapefil.h,v $
+ * Revision 1.25  2002/05/07 13:46:30  warmerda
+ * added DBFWriteAttributeDirectly().
+ *
+ * Revision 1.24  2002/04/10 16:59:54  warmerda
+ * added SHPRewindObject
+ *
+ * Revision 1.23  2002/01/15 14:36:07  warmerda
+ * updated email address
+ *
+ * Revision 1.22  2002/01/15 14:32:00  warmerda
+ * try to improve SHPAPI_CALL docs
+ *
+ * Revision 1.21  2001/11/01 16:29:55  warmerda
+ * move pabyRec into SHPInfo for thread safety
+ *
+ * Revision 1.20  2001/07/20 13:06:02  warmerda
+ * fixed SHPAPI attribute for SHPTreeFindLikelyShapes
+ *
+ * Revision 1.19  2001/05/31 19:20:13  warmerda
+ * added DBFGetFieldIndex()
+ *
+ * Revision 1.18  2001/05/31 18:15:40  warmerda
+ * Added support for NULL fields in DBF files
+ *
+ * Revision 1.17  2001/05/23 13:36:52  warmerda
+ * added use of SHPAPI_CALL
+ *
+ * Revision 1.16  2000/09/25 14:15:59  warmerda
+ * added DBFGetNativeFieldType()
+ *
+ * Revision 1.15  2000/02/16 16:03:51  warmerda
+ * added null shape support
+ *
+ * Revision 1.14  1999/11/05 14:12:05  warmerda
+ * updated license terms
+ *
+ * Revision 1.13  1999/06/02 18:24:21  warmerda
+ * added trimming code
+ *
+ * Revision 1.12  1999/06/02 17:56:12  warmerda
+ * added quad'' subnode support for trees
+ *
+ * Revision 1.11  1999/05/18 19:11:11  warmerda
+ * Added example searching capability
+ *
+ * Revision 1.10  1999/05/18 17:49:38  warmerda
+ * added initial quadtree support
+ *
+ * Revision 1.9  1999/05/11 03:19:28  warmerda
+ * added new Tuple api, and improved extension handling - add from candrsn
+ *
+ * Revision 1.8  1999/03/23 17:22:27  warmerda
+ * Added extern "C" protection for C++ users of shapefil.h.
+ *
+ * Revision 1.7  1998/12/31 15:31:07  warmerda
+ * Added the TRIM_DBF_WHITESPACE and DISABLE_MULTIPATCH_MEASURE options.
+ *
+ * Revision 1.6  1998/12/03 15:48:15  warmerda
+ * Added SHPCalculateExtents().
+ *
+ * Revision 1.5  1998/11/09 20:57:16  warmerda
+ * Altered SHPGetInfo() call.
+ *
+ * Revision 1.4  1998/11/09 20:19:33  warmerda
+ * Added 3D support, and use of SHPObject.
+ *
+ * Revision 1.3  1995/08/23 02:24:05  warmerda
+ * Added support for reading bounds.
+ *
+ * Revision 1.2  1995/08/04  03:17:39  warmerda
+ * Added header.
+ *
+ */
+
+#include <stdio.h>
+
+#ifdef USE_DBMALLOC
+#include <dbmalloc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/************************************************************************/
+/*                        Configuration options.                        */
+/************************************************************************/
+
+/* -------------------------------------------------------------------- */
+/*      Should the DBFReadStringAttribute() strip leading and           */
+/*      trailing white space?                                           */
+/* -------------------------------------------------------------------- */
+#define TRIM_DBF_WHITESPACE
+
+/* -------------------------------------------------------------------- */
+/*      Should we write measure values to the Multipatch object?        */
+/*      Reportedly ArcView crashes if we do write it, so for now it     */
+/*      is disabled.                                                    */
+/* -------------------------------------------------------------------- */
+#define DISABLE_MULTIPATCH_MEASURE
+
+/* -------------------------------------------------------------------- */
+/*      SHPAPI_CALL                                                     */
+/*                                                                      */
+/*      The following two macros are present to allow forcing           */
+/*      various calling conventions on the Shapelib API.                */
+/*                                                                      */
+/*      To force __stdcall conventions (needed to call Shapelib         */
+/*      from Visual Basic and/or Dephi I believe) the makefile could    */
+/*      be modified to define:                                          */
+/*                                                                      */
+/*        /DSHPAPI_CALL=__stdcall                                       */
+/*                                                                      */
+/*      If it is desired to force export of the Shapelib API without    */
+/*      using the shapelib.def file, use the following definition.      */
+/*                                                                      */
+/*        /DSHAPELIB_DLLEXPORT                                          */
+/*                                                                      */
+/*      To get both at once it will be necessary to hack this           */
+/*      include file to define:                                         */
+/*                                                                      */
+/*        #define SHPAPI_CALL __declspec(dllexport) __stdcall           */
+/*        #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall        */
+/*                                                                      */
+/*      The complexity of the situtation is partly caused by the        */
+/*      peculiar requirement of Visual C++ that __stdcall appear        */
+/*      after any "*"'s in the return value of a function while the     */
+/*      __declspec(dllexport) must appear before them.                  */
+/* -------------------------------------------------------------------- */
+
+#ifdef SHAPELIB_DLLEXPORT
+#  define SHPAPI_CALL __declspec(dllexport)
+#  define SHPAPI_CALL1(x)  __declspec(dllexport) x
+#endif
+
+#ifndef SHPAPI_CALL
+#  define SHPAPI_CALL
+#endif
+
+#ifndef SHPAPI_CALL1
+#  define SHPAPI_CALL1(x)      x SHPAPI_CALL
+#endif
+    
+/************************************************************************/
+/*                             SHP Support.                             */
+/************************************************************************/
+typedef	struct
+{
+    FILE        *fpSHP;
+    FILE	*fpSHX;
+
+    int		nShapeType;				/* SHPT_* */
+    
+    int		nFileSize;				/* SHP file */
+
+    int         nRecords;
+    int		nMaxRecords;
+    int		*panRecOffset;
+    int		*panRecSize;
+
+    double	adBoundsMin[4];
+    double	adBoundsMax[4];
+
+    int		bUpdated;
+
+    unsigned char *pabyRec;
+    int         nBufSize;
+} SHPInfo;
+
+typedef SHPInfo * SHPHandle;
+
+/* -------------------------------------------------------------------- */
+/*      Shape types (nSHPType)                                          */
+/* -------------------------------------------------------------------- */
+#define SHPT_NULL	0
+#define SHPT_POINT	1
+#define SHPT_ARC	3
+#define SHPT_POLYGON	5
+#define SHPT_MULTIPOINT	8
+#define SHPT_POINTZ	11
+#define SHPT_ARCZ	13
+#define SHPT_POLYGONZ	15
+#define SHPT_MULTIPOINTZ 18
+#define SHPT_POINTM	21
+#define SHPT_ARCM	23
+#define SHPT_POLYGONM	25
+#define SHPT_MULTIPOINTM 28
+#define SHPT_MULTIPATCH 31
+
+
+/* -------------------------------------------------------------------- */
+/*      Part types - everything but SHPT_MULTIPATCH just uses           */
+/*      SHPP_RING.                                                      */
+/* -------------------------------------------------------------------- */
+
+#define SHPP_TRISTRIP	0
+#define SHPP_TRIFAN	1
+#define SHPP_OUTERRING	2
+#define SHPP_INNERRING	3
+#define SHPP_FIRSTRING	4
+#define SHPP_RING	5
+
+/* -------------------------------------------------------------------- */
+/*      SHPObject - represents on shape (without attributes) read       */
+/*      from the .shp file.                                             */
+/* -------------------------------------------------------------------- */
+typedef struct
+{
+    int		nSHPType;
+
+    int		nShapeId; /* -1 is unknown/unassigned */
+
+    int		nParts;
+    int		*panPartStart;
+    int		*panPartType;
+    
+    int		nVertices;
+    double	*padfX;
+    double	*padfY;
+    double	*padfZ;
+    double	*padfM;
+
+    double	dfXMin;
+    double	dfYMin;
+    double	dfZMin;
+    double	dfMMin;
+
+    double	dfXMax;
+    double	dfYMax;
+    double	dfZMax;
+    double	dfMMax;
+} SHPObject;
+
+/* -------------------------------------------------------------------- */
+/*      SHP API Prototypes                                              */
+/* -------------------------------------------------------------------- */
+SHPHandle SHPAPI_CALL
+      SHPOpen( const char * pszShapeFile, const char * pszAccess );
+SHPHandle SHPAPI_CALL
+      SHPCreate( const char * pszShapeFile, int nShapeType );
+void SHPAPI_CALL
+      SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType,
+                  double * padfMinBound, double * padfMaxBound );
+
+SHPObject SHPAPI_CALL1(*)
+      SHPReadObject( SHPHandle hSHP, int iShape );
+int SHPAPI_CALL
+      SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject );
+
+void SHPAPI_CALL
+      SHPDestroyObject( SHPObject * psObject );
+void SHPAPI_CALL
+      SHPComputeExtents( SHPObject * psObject );
+SHPObject SHPAPI_CALL1(*)
+      SHPCreateObject( int nSHPType, int nShapeId,
+                       int nParts, int * panPartStart, int * panPartType,
+                       int nVertices, double * padfX, double * padfY,
+                       double * padfZ, double * padfM );
+SHPObject SHPAPI_CALL1(*)
+      SHPCreateSimpleObject( int nSHPType, int nVertices,
+                             double * padfX, double * padfY, double * padfZ );
+
+int SHPAPI_CALL
+      SHPRewindObject( SHPHandle hSHP, SHPObject * psObject );
+
+void SHPAPI_CALL
+      SHPClose( SHPHandle hSHP );
+
+const char SHPAPI_CALL1(*)
+      SHPTypeName( int nSHPType );
+const char SHPAPI_CALL1(*)
+      SHPPartTypeName( int nPartType );
+
+/* -------------------------------------------------------------------- */
+/*      Shape quadtree indexing API.                                    */
+/* -------------------------------------------------------------------- */
+
+/* this can be two or four for binary or quad tree */
+#define MAX_SUBNODE	4
+
+typedef struct shape_tree_node
+{
+    /* region covered by this node */
+    double	adfBoundsMin[4];
+    double	adfBoundsMax[4];
+
+    /* list of shapes stored at this node.  The papsShapeObj pointers
+       or the whole list can be NULL */
+    int		nShapeCount;
+    int		*panShapeIds;
+    SHPObject   **papsShapeObj;
+
+    int		nSubNodes;
+    struct shape_tree_node *apsSubNode[MAX_SUBNODE];
+    
+} SHPTreeNode;
+
+typedef struct
+{
+    SHPHandle   hSHP;
+    
+    int		nMaxDepth;
+    int		nDimension;
+    
+    SHPTreeNode	*psRoot;
+} SHPTree;
+
+SHPTree SHPAPI_CALL1(*)
+      SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth,
+                     double *padfBoundsMin, double *padfBoundsMax );
+void    SHPAPI_CALL
+      SHPDestroyTree( SHPTree * hTree );
+
+int	SHPAPI_CALL
+      SHPWriteTree( SHPTree *hTree, const char * pszFilename );
+SHPTree SHPAPI_CALL
+      SHPReadTree( const char * pszFilename );
+
+int	SHPAPI_CALL
+      SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject );
+int	SHPAPI_CALL
+      SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject );
+int	SHPAPI_CALL
+      SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId );
+
+void 	SHPAPI_CALL
+      SHPTreeTrimExtraNodes( SHPTree * hTree );
+
+int    SHPAPI_CALL1(*)
+      SHPTreeFindLikelyShapes( SHPTree * hTree,
+                               double * padfBoundsMin,
+                               double * padfBoundsMax,
+                               int * );
+int     SHPAPI_CALL
+      SHPCheckBoundsOverlap( double *, double *, double *, double *, int );
+
+/************************************************************************/
+/*                             DBF Support.                             */
+/************************************************************************/
+typedef	struct
+{
+    FILE	*fp;
+
+    int         nRecords;
+
+    int		nRecordLength;
+    int		nHeaderLength;
+    int		nFields;
+    int		*panFieldOffset;
+    int		*panFieldSize;
+    int		*panFieldDecimals;
+    char	*pachFieldType;
+
+    char	*pszHeader;
+
+    int		nCurrentRecord;
+    int		bCurrentRecordModified;
+    char	*pszCurrentRecord;
+    
+    int		bNoHeader;
+    int		bUpdated;
+} DBFInfo;
+
+typedef DBFInfo * DBFHandle;
+
+typedef enum {
+  FTString,
+  FTInteger,
+  FTDouble,
+  FTInvalid
+} DBFFieldType;
+
+#define XBASE_FLDHDR_SZ       32
+
+DBFHandle SHPAPI_CALL
+      DBFOpen( const char * pszDBFFile, const char * pszAccess );
+DBFHandle SHPAPI_CALL
+      DBFCreate( const char * pszDBFFile );
+
+int	SHPAPI_CALL
+      DBFGetFieldCount( DBFHandle psDBF );
+int	SHPAPI_CALL
+      DBFGetRecordCount( DBFHandle psDBF );
+int	SHPAPI_CALL
+      DBFAddField( DBFHandle hDBF, const char * pszFieldName,
+                   DBFFieldType eType, int nWidth, int nDecimals );
+
+DBFFieldType SHPAPI_CALL
+      DBFGetFieldInfo( DBFHandle psDBF, int iField, 
+                       char * pszFieldName, int * pnWidth, int * pnDecimals );
+
+int SHPAPI_CALL
+      DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName);
+
+int 	SHPAPI_CALL
+      DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField );
+double 	SHPAPI_CALL
+      DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField );
+const char SHPAPI_CALL1(*)
+      DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField );
+int     SHPAPI_CALL
+      DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField );
+
+int SHPAPI_CALL
+      DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, 
+                                int nFieldValue );
+int SHPAPI_CALL
+      DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField,
+                               double dFieldValue );
+int SHPAPI_CALL
+      DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField,
+                               const char * pszFieldValue );
+int SHPAPI_CALL
+     DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField );
+
+int SHPAPI_CALL
+     DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
+                               void * pValue );
+const char SHPAPI_CALL1(*)
+      DBFReadTuple(DBFHandle psDBF, int hEntity );
+int SHPAPI_CALL
+      DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple );
+
+DBFHandle SHPAPI_CALL
+      DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename );
+ 
+void	SHPAPI_CALL
+      DBFClose( DBFHandle hDBF );
+char    SHPAPI_CALL
+      DBFGetNativeFieldType( DBFHandle hDBF, int iField );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ndef _SHAPEFILE_H_INCLUDED */

Added: packages/openev/branches/upstream/current/shpopen.c
===================================================================
--- packages/openev/branches/upstream/current/shpopen.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/shpopen.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1866 @@
+/******************************************************************************
+ * $Id: shpopen.c,v 1.39 2002/08/26 06:46:56 warmerda Exp $
+ *
+ * Project:  Shapelib
+ * Purpose:  Implementation of core Shapefile read/write functions.
+ * Author:   Frank Warmerdam, warmerdam at pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, 2001, Frank Warmerdam
+ *
+ * This software is available under the following "MIT Style" license,
+ * or at the option of the licensee under the LGPL (see LICENSE.LGPL).  This
+ * option is discussed in more detail in shapelib.html.
+ *
+ * --
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: shpopen.c,v $
+ * Revision 1.39  2002/08/26 06:46:56  warmerda
+ * avoid c++ comments
+ *
+ * Revision 1.38  2002/05/07 16:43:39  warmerda
+ * Removed debugging printf.
+ *
+ * Revision 1.37  2002/04/10 17:35:22  warmerda
+ * fixed bug in ring reversal code
+ *
+ * Revision 1.36  2002/04/10 16:59:54  warmerda
+ * added SHPRewindObject
+ *
+ * Revision 1.35  2001/12/07 15:10:44  warmerda
+ * fix if .shx fails to open
+ *
+ * Revision 1.34  2001/11/01 16:29:55  warmerda
+ * move pabyRec into SHPInfo for thread safety
+ *
+ * Revision 1.33  2001/07/03 12:18:15  warmerda
+ * Improved cleanup if SHX not found, provied by Riccardo Cohen.
+ *
+ * Revision 1.32  2001/06/22 01:58:07  warmerda
+ * be more careful about establishing initial bounds in face of NULL shapes
+ *
+ * Revision 1.31  2001/05/31 19:35:29  warmerda
+ * added support for writing null shapes
+ *
+ * Revision 1.30  2001/05/28 12:46:29  warmerda
+ * Add some checking on reasonableness of record count when opening.
+ *
+ * Revision 1.29  2001/05/23 13:36:52  warmerda
+ * added use of SHPAPI_CALL
+ *
+ * Revision 1.28  2001/02/06 22:25:06  warmerda
+ * fixed memory leaks when SHPOpen() fails
+ *
+ * Revision 1.27  2000/07/18 15:21:33  warmerda
+ * added better enforcement of -1 for append in SHPWriteObject
+ *
+ * Revision 1.26  2000/02/16 16:03:51  warmerda
+ * added null shape support
+ *
+ * Revision 1.25  1999/12/15 13:47:07  warmerda
+ * Fixed record size settings in .shp file (was 4 words too long)
+ * Added stdlib.h.
+ *
+ * Revision 1.24  1999/11/05 14:12:04  warmerda
+ * updated license terms
+ *
+ * Revision 1.23  1999/07/27 00:53:46  warmerda
+ * added support for rewriting shapes
+ *
+ * Revision 1.22  1999/06/11 19:19:11  warmerda
+ * Cleanup pabyRec static buffer on SHPClose().
+ *
+ * Revision 1.21  1999/06/02 14:57:56  kshih
+ * Remove unused variables
+ *
+ * Revision 1.20  1999/04/19 21:04:17  warmerda
+ * Fixed syntax error.
+ *
+ * Revision 1.19  1999/04/19 21:01:57  warmerda
+ * Force access string to binary in SHPOpen().
+ *
+ * Revision 1.18  1999/04/01 18:48:07  warmerda
+ * Try upper case extensions if lower case doesn't work.
+ *
+ * Revision 1.17  1998/12/31 15:29:39  warmerda
+ * Disable writing measure values to multipatch objects if
+ * DISABLE_MULTIPATCH_MEASURE is defined.
+ *
+ * Revision 1.16  1998/12/16 05:14:33  warmerda
+ * Added support to write MULTIPATCH.  Fixed reading Z coordinate of
+ * MULTIPATCH. Fixed record size written for all feature types.
+ *
+ * Revision 1.15  1998/12/03 16:35:29  warmerda
+ * r+b is proper binary access string, not rb+.
+ *
+ * Revision 1.14  1998/12/03 15:47:56  warmerda
+ * Fixed setting of nVertices in SHPCreateObject().
+ *
+ * Revision 1.13  1998/12/03 15:33:54  warmerda
+ * Made SHPCalculateExtents() separately callable.
+ *
+ * Revision 1.12  1998/11/11 20:01:50  warmerda
+ * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
+ *
+ * Revision 1.11  1998/11/09 20:56:44  warmerda
+ * Fixed up handling of file wide bounds.
+ *
+ * Revision 1.10  1998/11/09 20:18:51  warmerda
+ * Converted to support 3D shapefiles, and use of SHPObject.
+ *
+ * Revision 1.9  1998/02/24 15:09:05  warmerda
+ * Fixed memory leak.
+ *
+ * Revision 1.8  1997/12/04 15:40:29  warmerda
+ * Fixed byte swapping of record number, and record length fields in the
+ * .shp file.
+ *
+ * Revision 1.7  1995/10/21 03:15:58  warmerda
+ * Added support for binary file access, the magic cookie 9997
+ * and tried to improve the int32 selection logic for 16bit systems.
+ *
+ * Revision 1.6  1995/09/04  04:19:41  warmerda
+ * Added fix for file bounds.
+ *
+ * Revision 1.5  1995/08/25  15:16:44  warmerda
+ * Fixed a couple of problems with big endian systems ... one with bounds
+ * and the other with multipart polygons.
+ *
+ * Revision 1.4  1995/08/24  18:10:17  warmerda
+ * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc()
+ * functions (such as on the Sun).
+ *
+ * Revision 1.3  1995/08/23  02:23:15  warmerda
+ * Added support for reading bounds, and fixed up problems in setting the
+ * file wide bounds.
+ *
+ * Revision 1.2  1995/08/04  03:16:57  warmerda
+ * Added header.
+ *
+ */
+
+static char rcsid[] = 
+  "$Id: shpopen.c,v 1.39 2002/08/26 06:46:56 warmerda Exp $";
+
+#include "shapefil.h"
+
+#include <math.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned char uchar;
+
+#if UINT_MAX == 65535
+typedef long	      int32;
+#else
+typedef int	      int32;
+#endif
+
+#ifndef FALSE
+#  define FALSE		0
+#  define TRUE		1
+#endif
+
+#define ByteCopy( a, b, c )	memcpy( b, a, c )
+#ifndef MAX
+#  define MIN(a,b)      ((a<b) ? a : b)
+#  define MAX(a,b)      ((a>b) ? a : b)
+#endif
+
+static int 	bBigEndian;
+
+
+/************************************************************************/
+/*                              SwapWord()                              */
+/*                                                                      */
+/*      Swap a 2, 4 or 8 byte word.                                     */
+/************************************************************************/
+
+static void	SwapWord( int length, void * wordP )
+
+{
+    int		i;
+    uchar	temp;
+
+    for( i=0; i < length/2; i++ )
+    {
+	temp = ((uchar *) wordP)[i];
+	((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
+	((uchar *) wordP)[length-i-1] = temp;
+    }
+}
+
+/************************************************************************/
+/*                             SfRealloc()                              */
+/*                                                                      */
+/*      A realloc cover function that will access a NULL pointer as     */
+/*      a valid input.                                                  */
+/************************************************************************/
+
+static void * SfRealloc( void * pMem, int nNewSize )
+
+{
+    if( pMem == NULL )
+        return( (void *) malloc(nNewSize) );
+    else
+        return( (void *) realloc(pMem,nNewSize) );
+}
+
+/************************************************************************/
+/*                          SHPWriteHeader()                            */
+/*                                                                      */
+/*      Write out a header for the .shp and .shx files as well as the	*/
+/*	contents of the index (.shx) file.				*/
+/************************************************************************/
+
+static void SHPWriteHeader( SHPHandle psSHP )
+
+{
+    uchar     	abyHeader[100];
+    int		i;
+    int32	i32;
+    double	dValue;
+    int32	*panSHX;
+
+/* -------------------------------------------------------------------- */
+/*      Prepare header block for .shp file.                             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < 100; i++ )
+      abyHeader[i] = 0;
+
+    abyHeader[2] = 0x27;				/* magic cookie */
+    abyHeader[3] = 0x0a;
+
+    i32 = psSHP->nFileSize/2;				/* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    i32 = 1000;						/* version */
+    ByteCopy( &i32, abyHeader+28, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+28 );
+    
+    i32 = psSHP->nShapeType;				/* shape type */
+    ByteCopy( &i32, abyHeader+32, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+32 );
+
+    dValue = psSHP->adBoundsMin[0];			/* set bounds */
+    ByteCopy( &dValue, abyHeader+36, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+36 );
+
+    dValue = psSHP->adBoundsMin[1];
+    ByteCopy( &dValue, abyHeader+44, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+44 );
+
+    dValue = psSHP->adBoundsMax[0];
+    ByteCopy( &dValue, abyHeader+52, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+52 );
+
+    dValue = psSHP->adBoundsMax[1];
+    ByteCopy( &dValue, abyHeader+60, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+60 );
+
+    dValue = psSHP->adBoundsMin[2];			/* z */
+    ByteCopy( &dValue, abyHeader+68, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+68 );
+
+    dValue = psSHP->adBoundsMax[2];
+    ByteCopy( &dValue, abyHeader+76, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+76 );
+
+    dValue = psSHP->adBoundsMin[3];			/* m */
+    ByteCopy( &dValue, abyHeader+84, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+84 );
+
+    dValue = psSHP->adBoundsMax[3];
+    ByteCopy( &dValue, abyHeader+92, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+92 );
+
+/* -------------------------------------------------------------------- */
+/*      Write .shp file header.                                         */
+/* -------------------------------------------------------------------- */
+    fseek( psSHP->fpSHP, 0, 0 );
+    fwrite( abyHeader, 100, 1, psSHP->fpSHP );
+
+/* -------------------------------------------------------------------- */
+/*      Prepare, and write .shx file header.                            */
+/* -------------------------------------------------------------------- */
+    i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2;   /* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    fseek( psSHP->fpSHX, 0, 0 );
+    fwrite( abyHeader, 100, 1, psSHP->fpSHX );
+
+/* -------------------------------------------------------------------- */
+/*      Write out the .shx contents.                                    */
+/* -------------------------------------------------------------------- */
+    panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);
+
+    for( i = 0; i < psSHP->nRecords; i++ )
+    {
+	panSHX[i*2  ] = psSHP->panRecOffset[i]/2;
+	panSHX[i*2+1] = psSHP->panRecSize[i]/2;
+	if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
+	if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
+    }
+
+    fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX );
+
+    free( panSHX );
+}
+
+/************************************************************************/
+/*                              SHPOpen()                               */
+/*                                                                      */
+/*      Open the .shp and .shx files based on the basename of the       */
+/*      files or either file name.                                      */
+/************************************************************************/
+   
+SHPHandle SHPAPI_CALL
+SHPOpen( const char * pszLayer, const char * pszAccess )
+
+{
+    char		*pszFullname, *pszBasename;
+    SHPHandle		psSHP;
+    
+    uchar		*pabyBuf;
+    int			i;
+    double		dValue;
+    
+/* -------------------------------------------------------------------- */
+/*      Ensure the access string is one of the legal ones.  We          */
+/*      ensure the result string indicates binary to avoid common       */
+/*      problems on Windows.                                            */
+/* -------------------------------------------------------------------- */
+    if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0
+        || strcmp(pszAccess,"r+") == 0 )
+        pszAccess = "r+b";
+    else
+        pszAccess = "rb";
+    
+/* -------------------------------------------------------------------- */
+/*	Establish the byte order on this machine.			*/
+/* -------------------------------------------------------------------- */
+    i = 1;
+    if( *((uchar *) &i) == 1 )
+        bBigEndian = FALSE;
+    else
+        bBigEndian = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*	Initialize the info structure.					*/
+/* -------------------------------------------------------------------- */
+    psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);
+
+    psSHP->bUpdated = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*	Compute the base (layer) name.  If there is any extension	*/
+/*	on the passed in filename we will strip it off.			*/
+/* -------------------------------------------------------------------- */
+    pszBasename = (char *) malloc(strlen(pszLayer)+5);
+    strcpy( pszBasename, pszLayer );
+    for( i = strlen(pszBasename)-1; 
+	 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+	       && pszBasename[i] != '\\';
+	 i-- ) {}
+
+    if( pszBasename[i] == '.' )
+        pszBasename[i] = '\0';
+
+/* -------------------------------------------------------------------- */
+/*	Open the .shp and .shx files.  Note that files pulled from	*/
+/*	a PC to Unix with upper case filenames won't work!		*/
+/* -------------------------------------------------------------------- */
+    pszFullname = (char *) malloc(strlen(pszBasename) + 5);
+    sprintf( pszFullname, "%s.shp", pszBasename );
+    psSHP->fpSHP = fopen(pszFullname, pszAccess );
+    if( psSHP->fpSHP == NULL )
+    {
+        sprintf( pszFullname, "%s.SHP", pszBasename );
+        psSHP->fpSHP = fopen(pszFullname, pszAccess );
+    }
+    
+    if( psSHP->fpSHP == NULL )
+    {
+        free( psSHP );
+        free( pszBasename );
+        free( pszFullname );
+        return( NULL );
+    }
+
+    sprintf( pszFullname, "%s.shx", pszBasename );
+    psSHP->fpSHX = fopen(pszFullname, pszAccess );
+    if( psSHP->fpSHX == NULL )
+    {
+        sprintf( pszFullname, "%s.SHX", pszBasename );
+        psSHP->fpSHX = fopen(pszFullname, pszAccess );
+    }
+    
+    if( psSHP->fpSHX == NULL )
+    {
+        fclose( psSHP->fpSHP );
+        free( psSHP );
+        free( pszBasename );
+        free( pszFullname );
+        return( NULL );
+    }
+
+    free( pszFullname );
+    free( pszBasename );
+
+/* -------------------------------------------------------------------- */
+/*  Read the file size from the SHP file.				*/
+/* -------------------------------------------------------------------- */
+    pabyBuf = (uchar *) malloc(100);
+    fread( pabyBuf, 100, 1, psSHP->fpSHP );
+
+    psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
+			+ pabyBuf[25] * 256 * 256
+			+ pabyBuf[26] * 256
+			+ pabyBuf[27]) * 2;
+
+/* -------------------------------------------------------------------- */
+/*  Read SHX file Header info                                           */
+/* -------------------------------------------------------------------- */
+    fread( pabyBuf, 100, 1, psSHP->fpSHX );
+
+    if( pabyBuf[0] != 0 
+        || pabyBuf[1] != 0 
+        || pabyBuf[2] != 0x27 
+        || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
+    {
+	fclose( psSHP->fpSHP );
+	fclose( psSHP->fpSHX );
+	free( psSHP );
+
+	return( NULL );
+    }
+
+    psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256
+      + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
+    psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;
+
+    psSHP->nShapeType = pabyBuf[32];
+
+    if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
+    {
+        /* this header appears to be corrupt.  Give up. */
+	fclose( psSHP->fpSHP );
+	fclose( psSHP->fpSHX );
+	free( psSHP );
+
+	return( NULL );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the bounds.                                                */
+/* -------------------------------------------------------------------- */
+    if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
+    memcpy( &dValue, pabyBuf+36, 8 );
+    psSHP->adBoundsMin[0] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
+    memcpy( &dValue, pabyBuf+44, 8 );
+    psSHP->adBoundsMin[1] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
+    memcpy( &dValue, pabyBuf+52, 8 );
+    psSHP->adBoundsMax[0] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
+    memcpy( &dValue, pabyBuf+60, 8 );
+    psSHP->adBoundsMax[1] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+68 );		/* z */
+    memcpy( &dValue, pabyBuf+68, 8 );
+    psSHP->adBoundsMin[2] = dValue;
+    
+    if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
+    memcpy( &dValue, pabyBuf+76, 8 );
+    psSHP->adBoundsMax[2] = dValue;
+    
+    if( bBigEndian ) SwapWord( 8, pabyBuf+84 );		/* z */
+    memcpy( &dValue, pabyBuf+84, 8 );
+    psSHP->adBoundsMin[3] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
+    memcpy( &dValue, pabyBuf+92, 8 );
+    psSHP->adBoundsMax[3] = dValue;
+
+    free( pabyBuf );
+
+/* -------------------------------------------------------------------- */
+/*	Read the .shx file to get the offsets to each record in 	*/
+/*	the .shp file.							*/
+/* -------------------------------------------------------------------- */
+    psSHP->nMaxRecords = psSHP->nRecords;
+
+    psSHP->panRecOffset =
+        (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
+    psSHP->panRecSize =
+        (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
+
+    pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
+    fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX );
+
+    for( i = 0; i < psSHP->nRecords; i++ )
+    {
+	int32		nOffset, nLength;
+
+	memcpy( &nOffset, pabyBuf + i * 8, 4 );
+	if( !bBigEndian ) SwapWord( 4, &nOffset );
+
+	memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
+	if( !bBigEndian ) SwapWord( 4, &nLength );
+
+	psSHP->panRecOffset[i] = nOffset*2;
+	psSHP->panRecSize[i] = nLength*2;
+    }
+    free( pabyBuf );
+
+    return( psSHP );
+}
+
+/************************************************************************/
+/*                              SHPClose()                              */
+/*								       	*/
+/*	Close the .shp and .shx files.					*/
+/************************************************************************/
+
+void SHPAPI_CALL
+SHPClose(SHPHandle psSHP )
+
+{
+/* -------------------------------------------------------------------- */
+/*	Update the header if we have modified anything.			*/
+/* -------------------------------------------------------------------- */
+    if( psSHP->bUpdated )
+    {
+	SHPWriteHeader( psSHP );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Free all resources, and close files.                            */
+/* -------------------------------------------------------------------- */
+    free( psSHP->panRecOffset );
+    free( psSHP->panRecSize );
+
+    fclose( psSHP->fpSHX );
+    fclose( psSHP->fpSHP );
+
+    if( psSHP->pabyRec != NULL )
+    {
+        free( psSHP->pabyRec );
+    }
+    
+    free( psSHP );
+}
+
+/************************************************************************/
+/*                             SHPGetInfo()                             */
+/*                                                                      */
+/*      Fetch general information about the shape file.                 */
+/************************************************************************/
+
+void SHPAPI_CALL
+SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
+           double * padfMinBound, double * padfMaxBound )
+
+{
+    int		i;
+    
+    if( pnEntities != NULL )
+        *pnEntities = psSHP->nRecords;
+
+    if( pnShapeType != NULL )
+        *pnShapeType = psSHP->nShapeType;
+
+    for( i = 0; i < 4; i++ )
+    {
+        if( padfMinBound != NULL )
+            padfMinBound[i] = psSHP->adBoundsMin[i];
+        if( padfMaxBound != NULL )
+            padfMaxBound[i] = psSHP->adBoundsMax[i];
+    }
+}
+
+/************************************************************************/
+/*                             SHPCreate()                              */
+/*                                                                      */
+/*      Create a new shape file and return a handle to the open         */
+/*      shape file with read/write access.                              */
+/************************************************************************/
+
+SHPHandle SHPAPI_CALL
+SHPCreate( const char * pszLayer, int nShapeType )
+
+{
+    char	*pszBasename, *pszFullname;
+    int		i;
+    FILE	*fpSHP, *fpSHX;
+    uchar     	abyHeader[100];
+    int32	i32;
+    double	dValue;
+    
+/* -------------------------------------------------------------------- */
+/*      Establish the byte order on this system.                        */
+/* -------------------------------------------------------------------- */
+    i = 1;
+    if( *((uchar *) &i) == 1 )
+        bBigEndian = FALSE;
+    else
+        bBigEndian = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*	Compute the base (layer) name.  If there is any extension	*/
+/*	on the passed in filename we will strip it off.			*/
+/* -------------------------------------------------------------------- */
+    pszBasename = (char *) malloc(strlen(pszLayer)+5);
+    strcpy( pszBasename, pszLayer );
+    for( i = strlen(pszBasename)-1; 
+	 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+	       && pszBasename[i] != '\\';
+	 i-- ) {}
+
+    if( pszBasename[i] == '.' )
+        pszBasename[i] = '\0';
+
+/* -------------------------------------------------------------------- */
+/*      Open the two files so we can write their headers.               */
+/* -------------------------------------------------------------------- */
+    pszFullname = (char *) malloc(strlen(pszBasename) + 5);
+    sprintf( pszFullname, "%s.shp", pszBasename );
+    fpSHP = fopen(pszFullname, "wb" );
+    if( fpSHP == NULL )
+        return( NULL );
+
+    sprintf( pszFullname, "%s.shx", pszBasename );
+    fpSHX = fopen(pszFullname, "wb" );
+    if( fpSHX == NULL )
+        return( NULL );
+
+    free( pszFullname );
+    free( pszBasename );
+
+/* -------------------------------------------------------------------- */
+/*      Prepare header block for .shp file.                             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < 100; i++ )
+      abyHeader[i] = 0;
+
+    abyHeader[2] = 0x27;				/* magic cookie */
+    abyHeader[3] = 0x0a;
+
+    i32 = 50;						/* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    i32 = 1000;						/* version */
+    ByteCopy( &i32, abyHeader+28, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+28 );
+    
+    i32 = nShapeType;					/* shape type */
+    ByteCopy( &i32, abyHeader+32, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+32 );
+
+    dValue = 0.0;					/* set bounds */
+    ByteCopy( &dValue, abyHeader+36, 8 );
+    ByteCopy( &dValue, abyHeader+44, 8 );
+    ByteCopy( &dValue, abyHeader+52, 8 );
+    ByteCopy( &dValue, abyHeader+60, 8 );
+
+/* -------------------------------------------------------------------- */
+/*      Write .shp file header.                                         */
+/* -------------------------------------------------------------------- */
+    fwrite( abyHeader, 100, 1, fpSHP );
+
+/* -------------------------------------------------------------------- */
+/*      Prepare, and write .shx file header.                            */
+/* -------------------------------------------------------------------- */
+    i32 = 50;						/* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    fwrite( abyHeader, 100, 1, fpSHX );
+
+/* -------------------------------------------------------------------- */
+/*      Close the files, and then open them as regular existing files.  */
+/* -------------------------------------------------------------------- */
+    fclose( fpSHP );
+    fclose( fpSHX );
+
+    return( SHPOpen( pszLayer, "r+b" ) );
+}
+
+/************************************************************************/
+/*                           _SHPSetBounds()                            */
+/*                                                                      */
+/*      Compute a bounds rectangle for a shape, and set it into the     */
+/*      indicated location in the record.                               */
+/************************************************************************/
+
+static void	_SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
+
+{
+    ByteCopy( &(psShape->dfXMin), pabyRec +  0, 8 );
+    ByteCopy( &(psShape->dfYMin), pabyRec +  8, 8 );
+    ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 );
+    ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 );
+
+    if( bBigEndian )
+    {
+        SwapWord( 8, pabyRec + 0 );
+        SwapWord( 8, pabyRec + 8 );
+        SwapWord( 8, pabyRec + 16 );
+        SwapWord( 8, pabyRec + 24 );
+    }
+}
+
+/************************************************************************/
+/*                         SHPComputeExtents()                          */
+/*                                                                      */
+/*      Recompute the extents of a shape.  Automatically done by        */
+/*      SHPCreateObject().                                              */
+/************************************************************************/
+
+void SHPAPI_CALL
+SHPComputeExtents( SHPObject * psObject )
+
+{
+    int		i;
+    
+/* -------------------------------------------------------------------- */
+/*      Build extents for this object.                                  */
+/* -------------------------------------------------------------------- */
+    if( psObject->nVertices > 0 )
+    {
+        psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
+        psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
+        psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
+        psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
+    }
+    
+    for( i = 0; i < psObject->nVertices; i++ )
+    {
+        psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
+        psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
+        psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
+        psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
+
+        psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
+        psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
+        psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
+        psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
+    }
+}
+
+/************************************************************************/
+/*                          SHPCreateObject()                           */
+/*                                                                      */
+/*      Create a shape object.  It should be freed with                 */
+/*      SHPDestroyObject().                                             */
+/************************************************************************/
+
+SHPObject SHPAPI_CALL1(*)
+SHPCreateObject( int nSHPType, int nShapeId, int nParts,
+                 int * panPartStart, int * panPartType,
+                 int nVertices, double * padfX, double * padfY,
+                 double * padfZ, double * padfM )
+
+{
+    SHPObject	*psObject;
+    int		i, bHasM, bHasZ;
+
+    psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
+    psObject->nSHPType = nSHPType;
+    psObject->nShapeId = nShapeId;
+
+/* -------------------------------------------------------------------- */
+/*	Establish whether this shape type has M, and Z values.		*/
+/* -------------------------------------------------------------------- */
+    if( nSHPType == SHPT_ARCM
+        || nSHPType == SHPT_POINTM
+        || nSHPType == SHPT_POLYGONM
+        || nSHPType == SHPT_MULTIPOINTM )
+    {
+        bHasM = TRUE;
+        bHasZ = FALSE;
+    }
+    else if( nSHPType == SHPT_ARCZ
+             || nSHPType == SHPT_POINTZ
+             || nSHPType == SHPT_POLYGONZ
+             || nSHPType == SHPT_MULTIPOINTZ
+             || nSHPType == SHPT_MULTIPATCH )
+    {
+        bHasM = TRUE;
+        bHasZ = TRUE;
+    }
+    else
+    {
+        bHasM = FALSE;
+        bHasZ = FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Capture parts.  Note that part type is optional, and            */
+/*      defaults to ring.                                               */
+/* -------------------------------------------------------------------- */
+    if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
+        || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
+        || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
+        || nSHPType == SHPT_MULTIPATCH )
+    {
+        psObject->nParts = MAX(1,nParts);
+
+        psObject->panPartStart = (int *)
+            malloc(sizeof(int) * psObject->nParts);
+        psObject->panPartType = (int *)
+            malloc(sizeof(int) * psObject->nParts);
+
+        psObject->panPartStart[0] = 0;
+        psObject->panPartType[0] = SHPP_RING;
+        
+        for( i = 0; i < nParts; i++ )
+        {
+            psObject->panPartStart[i] = panPartStart[i];
+            if( panPartType != NULL )
+                psObject->panPartType[i] = panPartType[i];
+            else
+                psObject->panPartType[i] = SHPP_RING;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Capture vertices.  Note that Z and M are optional, but X and    */
+/*      Y are not.                                                      */
+/* -------------------------------------------------------------------- */
+    if( nVertices > 0 )
+    {
+        psObject->padfX = (double *) calloc(sizeof(double),nVertices);
+        psObject->padfY = (double *) calloc(sizeof(double),nVertices);
+        psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
+        psObject->padfM = (double *) calloc(sizeof(double),nVertices);
+
+        assert( padfX != NULL );
+        assert( padfY != NULL );
+    
+        for( i = 0; i < nVertices; i++ )
+        {
+            psObject->padfX[i] = padfX[i];
+            psObject->padfY[i] = padfY[i];
+            if( padfZ != NULL && bHasZ )
+                psObject->padfZ[i] = padfZ[i];
+            if( padfM != NULL && bHasM )
+                psObject->padfM[i] = padfM[i];
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute the extents.                                            */
+/* -------------------------------------------------------------------- */
+    psObject->nVertices = nVertices;
+    SHPComputeExtents( psObject );
+
+    return( psObject );
+}
+
+/************************************************************************/
+/*                       SHPCreateSimpleObject()                        */
+/*                                                                      */
+/*      Create a simple (common) shape object.  Destroy with            */
+/*      SHPDestroyObject().                                             */
+/************************************************************************/
+
+SHPObject SHPAPI_CALL1(*)
+SHPCreateSimpleObject( int nSHPType, int nVertices,
+                       double * padfX, double * padfY,
+                       double * padfZ )
+
+{
+    return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL,
+                             nVertices, padfX, padfY, padfZ, NULL ) );
+}
+                                  
+/************************************************************************/
+/*                           SHPWriteObject()                           */
+/*                                                                      */
+/*      Write out the vertices of a new structure.  Note that it is     */
+/*      only possible to write vertices at the end of the file.         */
+/************************************************************************/
+
+int SHPAPI_CALL
+SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
+		      
+{
+    int	       	nRecordOffset, i, nRecordSize;
+    uchar	*pabyRec;
+    int32	i32;
+
+    psSHP->bUpdated = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Ensure that shape object matches the type of the file it is     */
+/*      being written to.                                               */
+/* -------------------------------------------------------------------- */
+    assert( psObject->nSHPType == psSHP->nShapeType 
+            || psObject->nSHPType == SHPT_NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Ensure that -1 is used for appends.  Either blow an             */
+/*      assertion, or if they are disabled, set the shapeid to -1       */
+/*      for appends.                                                    */
+/* -------------------------------------------------------------------- */
+    assert( nShapeId == -1 
+            || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );
+
+    if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
+        nShapeId = -1;
+
+/* -------------------------------------------------------------------- */
+/*      Add the new entity to the in memory index.                      */
+/* -------------------------------------------------------------------- */
+    if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
+    {
+	psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
+
+	psSHP->panRecOffset = (int *) 
+            SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
+	psSHP->panRecSize = (int *) 
+            SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize record.                                              */
+/* -------------------------------------------------------------------- */
+    pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) 
+			       + psObject->nParts * 8 + 128);
+    
+/* -------------------------------------------------------------------- */
+/*  Extract vertices for a Polygon or Arc.				*/
+/* -------------------------------------------------------------------- */
+    if( psObject->nSHPType == SHPT_POLYGON
+        || psObject->nSHPType == SHPT_POLYGONZ
+        || psObject->nSHPType == SHPT_POLYGONM
+        || psObject->nSHPType == SHPT_ARC 
+        || psObject->nSHPType == SHPT_ARCZ
+        || psObject->nSHPType == SHPT_ARCM
+        || psObject->nSHPType == SHPT_MULTIPATCH )
+    {
+	int32		nPoints, nParts;
+	int    		i;
+
+	nPoints = psObject->nVertices;
+	nParts = psObject->nParts;
+
+	_SHPSetBounds( pabyRec + 12, psObject );
+
+	if( bBigEndian ) SwapWord( 4, &nPoints );
+	if( bBigEndian ) SwapWord( 4, &nParts );
+
+	ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
+	ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
+
+        nRecordSize = 52;
+
+        /*
+         * Write part start positions.
+         */
+	ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
+                  4 * psObject->nParts );
+	for( i = 0; i < psObject->nParts; i++ )
+	{
+	    if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
+            nRecordSize += 4;
+	}
+
+        /*
+         * Write multipatch part types if needed.
+         */
+        if( psObject->nSHPType == SHPT_MULTIPATCH )
+        {
+            memcpy( pabyRec + nRecordSize, psObject->panPartType,
+                    4*psObject->nParts );
+            for( i = 0; i < psObject->nParts; i++ )
+            {
+                if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
+                nRecordSize += 4;
+            }
+        }
+
+        /*
+         * Write the (x,y) vertex values.
+         */
+	for( i = 0; i < psObject->nVertices; i++ )
+	{
+	    ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
+	    ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
+
+	    if( bBigEndian )
+                SwapWord( 8, pabyRec + nRecordSize );
+            
+	    if( bBigEndian )
+                SwapWord( 8, pabyRec + nRecordSize + 8 );
+
+            nRecordSize += 2 * 8;
+	}
+
+        /*
+         * Write the Z coordinates (if any).
+         */
+        if( psObject->nSHPType == SHPT_POLYGONZ
+            || psObject->nSHPType == SHPT_ARCZ
+            || psObject->nSHPType == SHPT_MULTIPATCH )
+        {
+            ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+            
+            ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+
+            for( i = 0; i < psObject->nVertices; i++ )
+            {
+                ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
+                if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+                nRecordSize += 8;
+            }
+        }
+
+        /*
+         * Write the M values, if any.
+         */
+        if( psObject->nSHPType == SHPT_POLYGONM
+            || psObject->nSHPType == SHPT_ARCM
+#ifndef DISABLE_MULTIPATCH_MEASURE            
+            || psObject->nSHPType == SHPT_MULTIPATCH
+#endif            
+            || psObject->nSHPType == SHPT_POLYGONZ
+            || psObject->nSHPType == SHPT_ARCZ )
+        {
+            ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+            
+            ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+
+            for( i = 0; i < psObject->nVertices; i++ )
+            {
+                ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
+                if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+                nRecordSize += 8;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Extract vertices for a MultiPoint.					*/
+/* -------------------------------------------------------------------- */
+    else if( psObject->nSHPType == SHPT_MULTIPOINT
+             || psObject->nSHPType == SHPT_MULTIPOINTZ
+             || psObject->nSHPType == SHPT_MULTIPOINTM )
+    {
+	int32		nPoints;
+	int    		i;
+
+	nPoints = psObject->nVertices;
+
+        _SHPSetBounds( pabyRec + 12, psObject );
+
+	if( bBigEndian ) SwapWord( 4, &nPoints );
+	ByteCopy( &nPoints, pabyRec + 44, 4 );
+	
+	for( i = 0; i < psObject->nVertices; i++ )
+	{
+	    ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
+	    ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
+
+	    if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
+	    if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
+	}
+
+	nRecordSize = 48 + 16 * psObject->nVertices;
+
+        if( psObject->nSHPType == SHPT_MULTIPOINTZ )
+        {
+            ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+
+            ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+            
+            for( i = 0; i < psObject->nVertices; i++ )
+            {
+                ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
+                if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+                nRecordSize += 8;
+            }
+        }
+
+        if( psObject->nSHPType == SHPT_MULTIPOINTZ
+            || psObject->nSHPType == SHPT_MULTIPOINTM )
+        {
+            ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+
+            ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+            
+            for( i = 0; i < psObject->nVertices; i++ )
+            {
+                ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
+                if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+                nRecordSize += 8;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write point.							*/
+/* -------------------------------------------------------------------- */
+    else if( psObject->nSHPType == SHPT_POINT
+             || psObject->nSHPType == SHPT_POINTZ
+             || psObject->nSHPType == SHPT_POINTM )
+    {
+	ByteCopy( psObject->padfX, pabyRec + 12, 8 );
+	ByteCopy( psObject->padfY, pabyRec + 20, 8 );
+
+	if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
+	if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
+
+        nRecordSize = 28;
+        
+        if( psObject->nSHPType == SHPT_POINTZ )
+        {
+            ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+        }
+        
+        if( psObject->nSHPType == SHPT_POINTZ
+            || psObject->nSHPType == SHPT_POINTM )
+        {
+            ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Not much to do for null geometries.                             */
+/* -------------------------------------------------------------------- */
+    else if( psObject->nSHPType == SHPT_NULL )
+    {
+        nRecordSize = 12;
+    }
+
+    else
+    {
+        /* unknown type */
+        assert( FALSE );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Establish where we are going to put this record. If we are      */
+/*      rewriting and existing record, and it will fit, then put it     */
+/*      back where the original came from.  Otherwise write at the end. */
+/* -------------------------------------------------------------------- */
+    if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
+    {
+        if( nShapeId == -1 )
+            nShapeId = psSHP->nRecords++;
+
+        psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
+        psSHP->panRecSize[nShapeId] = nRecordSize-8;
+        psSHP->nFileSize += nRecordSize;
+    }
+    else
+    {
+        nRecordOffset = psSHP->panRecOffset[nShapeId];
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Set the shape type, record number, and record size.             */
+/* -------------------------------------------------------------------- */
+    i32 = nShapeId+1;					/* record # */
+    if( !bBigEndian ) SwapWord( 4, &i32 );
+    ByteCopy( &i32, pabyRec, 4 );
+
+    i32 = (nRecordSize-8)/2;				/* record size */
+    if( !bBigEndian ) SwapWord( 4, &i32 );
+    ByteCopy( &i32, pabyRec + 4, 4 );
+
+    i32 = psObject->nSHPType;				/* shape type */
+    if( bBigEndian ) SwapWord( 4, &i32 );
+    ByteCopy( &i32, pabyRec + 8, 4 );
+
+/* -------------------------------------------------------------------- */
+/*      Write out record.                                               */
+/* -------------------------------------------------------------------- */
+    if( fseek( psSHP->fpSHP, nRecordOffset, 0 ) != 0
+        || fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
+    {
+        printf( "Error in fseek() or fwrite().\n" );
+        free( pabyRec );
+        return -1;
+    }
+    
+    free( pabyRec );
+
+/* -------------------------------------------------------------------- */
+/*	Expand file wide bounds based on this shape.			*/
+/* -------------------------------------------------------------------- */
+    if( psSHP->adBoundsMin[0] == 0.0
+        && psSHP->adBoundsMax[0] == 0.0
+        && psSHP->adBoundsMin[1] == 0.0
+        && psSHP->adBoundsMax[1] == 0.0 
+        && psObject->nSHPType != SHPT_NULL )
+    {
+        psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
+        psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
+        psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
+        psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
+    }
+
+    for( i = 0; i < psObject->nVertices; i++ )
+    {
+	psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
+	psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
+	psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
+	psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
+	psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
+	psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
+	psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
+	psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
+    }
+
+    return( nShapeId  );
+}
+
+/************************************************************************/
+/*                          SHPReadObject()                             */
+/*                                                                      */
+/*      Read the vertices, parts, and other non-attribute information	*/
+/*	for one shape.							*/
+/************************************************************************/
+
+SHPObject SHPAPI_CALL1(*)
+SHPReadObject( SHPHandle psSHP, int hEntity )
+
+{
+    SHPObject		*psShape;
+
+/* -------------------------------------------------------------------- */
+/*      Validate the record/entity number.                              */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity >= psSHP->nRecords )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Ensure our record buffer is large enough.                       */
+/* -------------------------------------------------------------------- */
+    if( psSHP->panRecSize[hEntity]+8 > psSHP->nBufSize )
+    {
+	psSHP->nBufSize = psSHP->panRecSize[hEntity]+8;
+	psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,psSHP->nBufSize);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the record.                                                */
+/* -------------------------------------------------------------------- */
+    fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 );
+    fread( psSHP->pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP );
+
+/* -------------------------------------------------------------------- */
+/*	Allocate and minimally initialize the object.			*/
+/* -------------------------------------------------------------------- */
+    psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
+    psShape->nShapeId = hEntity;
+
+    memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
+    if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );
+
+/* ==================================================================== */
+/*  Extract vertices for a Polygon or Arc.				*/
+/* ==================================================================== */
+    if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC
+        || psShape->nSHPType == SHPT_POLYGONZ
+        || psShape->nSHPType == SHPT_POLYGONM
+        || psShape->nSHPType == SHPT_ARCZ
+        || psShape->nSHPType == SHPT_ARCM
+        || psShape->nSHPType == SHPT_MULTIPATCH )
+    {
+	int32		nPoints, nParts;
+	int    		i, nOffset;
+
+/* -------------------------------------------------------------------- */
+/*	Get the X/Y bounds.						*/
+/* -------------------------------------------------------------------- */
+        memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 +  4, 8 );
+        memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
+        memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
+        memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
+
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
+
+/* -------------------------------------------------------------------- */
+/*      Extract part/point count, and build vertex and part arrays      */
+/*      to proper size.                                                 */
+/* -------------------------------------------------------------------- */
+	memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
+	memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
+
+	if( bBigEndian ) SwapWord( 4, &nPoints );
+	if( bBigEndian ) SwapWord( 4, &nParts );
+
+	psShape->nVertices = nPoints;
+        psShape->padfX = (double *) calloc(nPoints,sizeof(double));
+        psShape->padfY = (double *) calloc(nPoints,sizeof(double));
+        psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
+        psShape->padfM = (double *) calloc(nPoints,sizeof(double));
+
+	psShape->nParts = nParts;
+        psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
+        psShape->panPartType = (int *) calloc(nParts,sizeof(int));
+
+        for( i = 0; i < nParts; i++ )
+            psShape->panPartType[i] = SHPP_RING;
+
+/* -------------------------------------------------------------------- */
+/*      Copy out the part array from the record.                        */
+/* -------------------------------------------------------------------- */
+	memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
+	for( i = 0; i < nParts; i++ )
+	{
+	    if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
+	}
+
+	nOffset = 44 + 8 + 4*nParts;
+
+/* -------------------------------------------------------------------- */
+/*      If this is a multipatch, we will also have parts types.         */
+/* -------------------------------------------------------------------- */
+        if( psShape->nSHPType == SHPT_MULTIPATCH )
+        {
+            memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
+            for( i = 0; i < nParts; i++ )
+            {
+                if( bBigEndian ) SwapWord( 4, psShape->panPartType+i );
+            }
+
+            nOffset += 4*nParts;
+        }
+        
+/* -------------------------------------------------------------------- */
+/*      Copy out the vertices from the record.                          */
+/* -------------------------------------------------------------------- */
+	for( i = 0; i < nPoints; i++ )
+	{
+	    memcpy(psShape->padfX + i,
+		   psSHP->pabyRec + nOffset + i * 16,
+		   8 );
+
+	    memcpy(psShape->padfY + i,
+		   psSHP->pabyRec + nOffset + i * 16 + 8,
+		   8 );
+
+	    if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
+	    if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
+	}
+
+        nOffset += 16*nPoints;
+        
+/* -------------------------------------------------------------------- */
+/*      If we have a Z coordinate, collect that now.                    */
+/* -------------------------------------------------------------------- */
+        if( psShape->nSHPType == SHPT_POLYGONZ
+            || psShape->nSHPType == SHPT_ARCZ
+            || psShape->nSHPType == SHPT_MULTIPATCH )
+        {
+            memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
+            
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
+            
+            for( i = 0; i < nPoints; i++ )
+            {
+                memcpy( psShape->padfZ + i,
+                        psSHP->pabyRec + nOffset + 16 + i*8, 8 );
+                if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
+            }
+
+            nOffset += 16 + 8*nPoints;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      If we have a M measure value, then read it now.  We assume      */
+/*      that the measure can be present for any shape if the size is    */
+/*      big enough, but really it will only occur for the Z shapes      */
+/*      (options), and the M shapes.                                    */
+/* -------------------------------------------------------------------- */
+        if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
+        {
+            memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
+            
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
+            
+            for( i = 0; i < nPoints; i++ )
+            {
+                memcpy( psShape->padfM + i,
+                        psSHP->pabyRec + nOffset + 16 + i*8, 8 );
+                if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
+            }
+        }
+        
+    }
+
+/* ==================================================================== */
+/*  Extract vertices for a MultiPoint.					*/
+/* ==================================================================== */
+    else if( psShape->nSHPType == SHPT_MULTIPOINT
+             || psShape->nSHPType == SHPT_MULTIPOINTM
+             || psShape->nSHPType == SHPT_MULTIPOINTZ )
+    {
+	int32		nPoints;
+	int    		i, nOffset;
+
+	memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
+	if( bBigEndian ) SwapWord( 4, &nPoints );
+
+	psShape->nVertices = nPoints;
+        psShape->padfX = (double *) calloc(nPoints,sizeof(double));
+        psShape->padfY = (double *) calloc(nPoints,sizeof(double));
+        psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
+        psShape->padfM = (double *) calloc(nPoints,sizeof(double));
+
+	for( i = 0; i < nPoints; i++ )
+	{
+	    memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
+	    memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
+
+	    if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
+	    if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
+	}
+
+        nOffset = 48 + 16*nPoints;
+        
+/* -------------------------------------------------------------------- */
+/*	Get the X/Y bounds.						*/
+/* -------------------------------------------------------------------- */
+        memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 +  4, 8 );
+        memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
+        memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
+        memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
+
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
+	if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
+
+/* -------------------------------------------------------------------- */
+/*      If we have a Z coordinate, collect that now.                    */
+/* -------------------------------------------------------------------- */
+        if( psShape->nSHPType == SHPT_MULTIPOINTZ )
+        {
+            memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
+            
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
+            
+            for( i = 0; i < nPoints; i++ )
+            {
+                memcpy( psShape->padfZ + i,
+                        psSHP->pabyRec + nOffset + 16 + i*8, 8 );
+                if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
+            }
+
+            nOffset += 16 + 8*nPoints;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      If we have a M measure value, then read it now.  We assume      */
+/*      that the measure can be present for any shape if the size is    */
+/*      big enough, but really it will only occur for the Z shapes      */
+/*      (options), and the M shapes.                                    */
+/* -------------------------------------------------------------------- */
+        if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
+        {
+            memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
+            
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
+            if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
+            
+            for( i = 0; i < nPoints; i++ )
+            {
+                memcpy( psShape->padfM + i,
+                        psSHP->pabyRec + nOffset + 16 + i*8, 8 );
+                if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
+            }
+        }
+    }
+
+/* ==================================================================== */
+/*      Extract vertices for a point.                                   */
+/* ==================================================================== */
+    else if( psShape->nSHPType == SHPT_POINT
+             || psShape->nSHPType == SHPT_POINTM
+             || psShape->nSHPType == SHPT_POINTZ )
+    {
+        int	nOffset;
+        
+	psShape->nVertices = 1;
+        psShape->padfX = (double *) calloc(1,sizeof(double));
+        psShape->padfY = (double *) calloc(1,sizeof(double));
+        psShape->padfZ = (double *) calloc(1,sizeof(double));
+        psShape->padfM = (double *) calloc(1,sizeof(double));
+
+	memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
+	memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
+
+	if( bBigEndian ) SwapWord( 8, psShape->padfX );
+	if( bBigEndian ) SwapWord( 8, psShape->padfY );
+
+        nOffset = 20 + 8;
+        
+/* -------------------------------------------------------------------- */
+/*      If we have a Z coordinate, collect that now.                    */
+/* -------------------------------------------------------------------- */
+        if( psShape->nSHPType == SHPT_POINTZ )
+        {
+            memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );
+        
+            if( bBigEndian ) SwapWord( 8, psShape->padfZ );
+            
+            nOffset += 8;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      If we have a M measure value, then read it now.  We assume      */
+/*      that the measure can be present for any shape if the size is    */
+/*      big enough, but really it will only occur for the Z shapes      */
+/*      (options), and the M shapes.                                    */
+/* -------------------------------------------------------------------- */
+        if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 )
+        {
+            memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
+        
+            if( bBigEndian ) SwapWord( 8, psShape->padfM );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Since no extents are supplied in the record, we will apply      */
+/*      them from the single vertex.                                    */
+/* -------------------------------------------------------------------- */
+        psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
+        psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
+        psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
+        psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
+    }
+
+    return( psShape );
+}
+
+/************************************************************************/
+/*                            SHPTypeName()                             */
+/************************************************************************/
+
+const char SHPAPI_CALL1(*)
+SHPTypeName( int nSHPType )
+
+{
+    switch( nSHPType )
+    {
+      case SHPT_NULL:
+        return "NullShape";
+
+      case SHPT_POINT:
+        return "Point";
+
+      case SHPT_ARC:
+        return "Arc";
+
+      case SHPT_POLYGON:
+        return "Polygon";
+
+      case SHPT_MULTIPOINT:
+        return "MultiPoint";
+        
+      case SHPT_POINTZ:
+        return "PointZ";
+
+      case SHPT_ARCZ:
+        return "ArcZ";
+
+      case SHPT_POLYGONZ:
+        return "PolygonZ";
+
+      case SHPT_MULTIPOINTZ:
+        return "MultiPointZ";
+        
+      case SHPT_POINTM:
+        return "PointM";
+
+      case SHPT_ARCM:
+        return "ArcM";
+
+      case SHPT_POLYGONM:
+        return "PolygonM";
+
+      case SHPT_MULTIPOINTM:
+        return "MultiPointM";
+
+      case SHPT_MULTIPATCH:
+        return "MultiPatch";
+
+      default:
+        return "UnknownShapeType";
+    }
+}
+
+/************************************************************************/
+/*                          SHPPartTypeName()                           */
+/************************************************************************/
+
+const char SHPAPI_CALL1(*)
+SHPPartTypeName( int nPartType )
+
+{
+    switch( nPartType )
+    {
+      case SHPP_TRISTRIP:
+        return "TriangleStrip";
+        
+      case SHPP_TRIFAN:
+        return "TriangleFan";
+
+      case SHPP_OUTERRING:
+        return "OuterRing";
+
+      case SHPP_INNERRING:
+        return "InnerRing";
+
+      case SHPP_FIRSTRING:
+        return "FirstRing";
+
+      case SHPP_RING:
+        return "Ring";
+
+      default:
+        return "UnknownPartType";
+    }
+}
+
+/************************************************************************/
+/*                          SHPDestroyObject()                          */
+/************************************************************************/
+
+void SHPAPI_CALL
+SHPDestroyObject( SHPObject * psShape )
+
+{
+    if( psShape == NULL )
+        return;
+    
+    if( psShape->padfX != NULL )
+        free( psShape->padfX );
+    if( psShape->padfY != NULL )
+        free( psShape->padfY );
+    if( psShape->padfZ != NULL )
+        free( psShape->padfZ );
+    if( psShape->padfM != NULL )
+        free( psShape->padfM );
+
+    if( psShape->panPartStart != NULL )
+        free( psShape->panPartStart );
+    if( psShape->panPartType != NULL )
+        free( psShape->panPartType );
+
+    free( psShape );
+}
+
+/************************************************************************/
+/*                          SHPRewindObject()                           */
+/*                                                                      */
+/*      Reset the winding of polygon objects to adhere to the           */
+/*      specification.                                                  */
+/************************************************************************/
+
+int SHPAPI_CALL
+SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
+
+{
+    int  iOpRing, bAltered = 0;
+
+/* -------------------------------------------------------------------- */
+/*      Do nothing if this is not a polygon object.                     */
+/* -------------------------------------------------------------------- */
+    if( psObject->nSHPType != SHPT_POLYGON
+        && psObject->nSHPType != SHPT_POLYGONZ
+        && psObject->nSHPType != SHPT_POLYGONM )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Process each of the rings.                                      */
+/* -------------------------------------------------------------------- */
+    for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
+    {
+        int      bInner, iVert, nVertCount, nVertStart, iCheckRing;
+        double   dfSum, dfTestX, dfTestY;
+
+/* -------------------------------------------------------------------- */
+/*      Determine if this ring is an inner ring or an outer ring        */
+/*      relative to all the other rings.  For now we assume the         */
+/*      first ring is outer and all others are inner, but eventually    */
+/*      we need to fix this to handle multiple island polygons and      */
+/*      unordered sets of rings.                                        */
+/* -------------------------------------------------------------------- */
+        dfTestX = psObject->padfX[psObject->panPartStart[iOpRing]];
+        dfTestY = psObject->padfY[psObject->panPartStart[iOpRing]];
+
+        bInner = FALSE;
+        for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
+        {
+            int iEdge;
+
+            if( iCheckRing == iOpRing )
+                continue;
+            
+            nVertStart = psObject->panPartStart[iCheckRing];
+
+            if( iCheckRing == psObject->nParts-1 )
+                nVertCount = psObject->nVertices 
+                    - psObject->panPartStart[iCheckRing];
+            else
+                nVertCount = psObject->panPartStart[iCheckRing+1] 
+                    - psObject->panPartStart[iCheckRing];
+
+            for( iEdge = 0; iEdge < nVertCount; iEdge++ )
+            {
+                int iNext;
+
+                if( iEdge < nVertCount-1 )
+                    iNext = iEdge+1;
+                else
+                    iNext = 0;
+
+                if( (psObject->padfY[iEdge+nVertStart] < dfTestY 
+                     && psObject->padfY[iNext+nVertStart] >= dfTestY)
+                    || (psObject->padfY[iNext+nVertStart] < dfTestY 
+                        && psObject->padfY[iEdge+nVertStart] >= dfTestY) )
+                {
+                    if( psObject->padfX[iEdge+nVertStart] 
+                        + (dfTestY - psObject->padfY[iEdge+nVertStart])
+                           / (psObject->padfY[iNext+nVertStart]
+                              - psObject->padfY[iEdge+nVertStart])
+                           * (psObject->padfX[iNext+nVertStart]
+                              - psObject->padfX[iEdge+nVertStart]) < dfTestX )
+                        bInner = !bInner;
+                }
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Determine the current order of this ring so we will know if     */
+/*      it has to be reversed.                                          */
+/* -------------------------------------------------------------------- */
+        nVertStart = psObject->panPartStart[iOpRing];
+
+        if( iOpRing == psObject->nParts-1 )
+            nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
+        else
+            nVertCount = psObject->panPartStart[iOpRing+1] 
+                - psObject->panPartStart[iOpRing];
+
+        dfSum = 0.0;
+        for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ )
+        {
+            dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1]
+                - psObject->padfY[iVert] * psObject->padfX[iVert+1];
+        }
+
+        dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart]
+               - psObject->padfY[iVert] * psObject->padfX[nVertStart];
+
+/* -------------------------------------------------------------------- */
+/*      Reverse if necessary.                                           */
+/* -------------------------------------------------------------------- */
+        if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
+        {
+            int   i;
+
+            bAltered++;
+            for( i = 0; i < nVertCount/2; i++ )
+            {
+                double dfSaved;
+
+                /* Swap X */
+                dfSaved = psObject->padfX[nVertStart+i];
+                psObject->padfX[nVertStart+i] = 
+                    psObject->padfX[nVertStart+nVertCount-i-1];
+                psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;
+
+                /* Swap Y */
+                dfSaved = psObject->padfY[nVertStart+i];
+                psObject->padfY[nVertStart+i] = 
+                    psObject->padfY[nVertStart+nVertCount-i-1];
+                psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;
+
+                /* Swap Z */
+                if( psObject->padfZ )
+                {
+                    dfSaved = psObject->padfZ[nVertStart+i];
+                    psObject->padfZ[nVertStart+i] = 
+                        psObject->padfZ[nVertStart+nVertCount-i-1];
+                    psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
+                }
+
+                /* Swap M */
+                if( psObject->padfM )
+                {
+                    dfSaved = psObject->padfM[nVertStart+i];
+                    psObject->padfM[nVertStart+i] = 
+                        psObject->padfM[nVertStart+nVertCount-i-1];
+                    psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;
+                }
+            }
+        }
+    }
+
+    return bAltered;
+}

Added: packages/openev/branches/upstream/current/symbols/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/symbols/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,10 @@
+/circle.xml/1.1/Tue Apr  8 12:02:16 2003//
+/circle_filled.xml/1.1/Tue Apr  8 12:02:16 2003//
+/cross.xml/1.1/Tue Apr  8 12:02:16 2003//
+/dash.xml/1.1/Tue Apr  8 12:02:16 2003//
+/square.xml/1.2/Wed Apr  9 13:35:17 2003//
+/square_filled.xml/1.2/Wed Apr  9 13:35:17 2003//
+/triangle.xml/1.2/Wed Apr  9 13:35:17 2003//
+/triangle_filled.xml/1.2/Wed Apr  9 13:35:18 2003//
+/x.xml/1.2/Wed Apr  9 13:35:18 2003//
+D

Added: packages/openev/branches/upstream/current/symbols/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/symbols/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/symbols

Added: packages/openev/branches/upstream/current/symbols/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/symbols/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/symbols/circle.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/circle.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/circle.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,13 @@
+<GvShape type="2">
+  <rings>
+    <ring>-3,0,0 -2.6,1.5,0 -2.2,2.04,0 -1.8,2.4,0 -1.4,2.65,0 -1,2.83,0
+	  -0.6,2.94,0 -0.2,2.99,0 0.2,2.99,0 0.6,2.94,0 1,2.83,0 1.4,2.65,0
+	  1.8,2.4,0 2.2,2.04,0 2.6,1.5,0 3,0.0,0.0
+	  2.6,-1.5,0  2.2,-2.04,0 1.8,-2.4,0 1.4,-2.65,0 1,-2.83,0 0.6,-2.94,0
+	  0.2,-2.99,0 -0.2,-2.99,0 -0.6,-2.94,0 1,-2.83,0 -1.4,-2.65,0
+	  -1.8,-2.4,0 -2.2,-2.04,0 -2.6,-1.5,0 -3,0,0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/circle_filled.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/circle_filled.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/circle_filled.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,13 @@
+<GvShape type="3">
+  <rings>
+    <ring>-3,0,0 -2.6,1.5,0 -2.2,2.04,0 -1.8,2.4,0 -1.4,2.65,0 -1,2.83,0
+	  -0.6,2.94,0 -0.2,2.99,0 0.2,2.99,0 0.6,2.94,0 1,2.83,0 1.4,2.65,0
+	  1.8,2.4,0 2.2,2.04,0 2.6,1.5,0 3,0.0,0.0
+	  2.6,-1.5,0  2.2,-2.04,0 1.8,-2.4,0 1.4,-2.65,0 1,-2.83,0 0.6,-2.94,0
+	  0.2,-2.99,0 -0.2,-2.99,0 -0.6,-2.94,0 1,-2.83,0 -1.4,-2.65,0
+	  -1.8,-2.4,0 -2.2,-2.04,0 -2.6,-1.5,0 -3,0,0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00);BRUSH(fc:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/cross.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/cross.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/cross.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="2">
+  <rings>
+  <ring>-3.0,0.0,0.0 3.0,0.0,0.0 0.0,0.0,0.0 0.0,3.0,0.0 0.0,-3.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/dash.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/dash.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/dash.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="2">
+  <rings>
+    <ring>0.0,-3.0,0.0 0.0,0.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/square.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/square.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/square.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="2">
+  <rings>
+    <ring>-3.0,4.0,0.0 4.0,4.0,0.0 4.0,-3.0,0.0 -3.0,-3.0,0.0 -3.0,4.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/square_filled.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/square_filled.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/square_filled.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="3">
+  <rings>
+    <ring>-3.0,4.0,0.0 4.0,4.0,0.0 4.0,-3.0,0.0 -3.0,-3.0,0.0 -3.0,4.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00);BRUSH(fc:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/triangle.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/triangle.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/triangle.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="2">
+  <rings>
+    <ring>-3.0,-3.0,0.0 4.0,-3.0,0.0 0.0,3.0,0.0 -3.0,-3.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/triangle_filled.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/triangle_filled.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/triangle_filled.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="3">
+  <rings>
+    <ring>-3.0,-3.0,0.0 4.0,-3.0,0.0 0.0,3.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00);BRUSH(fc:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/symbols/x.xml
===================================================================
--- packages/openev/branches/upstream/current/symbols/x.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/symbols/x.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,8 @@
+<GvShape type="2">
+  <rings>
+     <ring>-3.0,4.0,0.0 4.0,-3.0,0.0 0.0,0.0,0.0 4.0,4.0,0.0 -3.0,-3.0,0.0</ring>
+  </rings>
+  <Properties>
+    <Property name="_gv_ogrfs" value="PEN(c:#00FF00)"/>
+  </Properties>
+</GvShape>

Added: packages/openev/branches/upstream/current/testmain.c
===================================================================
--- packages/openev/branches/upstream/current/testmain.c	                        (rev 0)
+++ packages/openev/branches/upstream/current/testmain.c	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,460 @@
+/******************************************************************************
+ * $Id: testmain.c,v 1.33 2003/06/25 17:52:08 warmerda Exp $
+ *
+ * Project:  OpenEV
+ * Purpose:  Sample C mainline with no python dependencies.
+ * Author:   OpenEV Team
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc. (www.atlsci.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ ******************************************************************************
+ *
+ * $Log: testmain.c,v $
+ * Revision 1.33  2003/06/25 17:52:08  warmerda
+ * added rotate tool
+ *
+ * Revision 1.32  2002/11/04 21:42:07  sduclos
+ * change geometric data type name to gvgeocoord
+ *
+ * Revision 1.31  2002/10/10 15:05:27  sduclos
+ * add option [-ogr=<ogr_filename>]
+ *
+ * Revision 1.30  2002/09/30 20:05:18  warmerda
+ * added support for shapefile
+ *
+ * Revision 1.29  2002/03/07 02:44:13  warmerda
+ * updated add_height argument list
+ *
+ * Revision 1.28  2000/08/25 20:09:18  warmerda
+ * improve testing if dataset raster fetch fails
+ *
+ * Revision 1.27  2000/08/18 20:34:53  warmerda
+ * set initial window size
+ *
+ * Revision 1.26  2000/08/16 14:07:47  warmerda
+ * added prototype scrollbar support
+ *
+ * Revision 1.25  2000/07/17 20:20:50  warmerda
+ * stop using old style vector layers
+ *
+ * Revision 1.24  2000/07/12 16:21:53  warmerda
+ * bail on failure to create view
+ *
+ * Revision 1.23  2000/06/26 15:13:33  warmerda
+ * use manager for getting datasets
+ *
+ * Revision 1.22  2000/06/23 12:56:53  warmerda
+ * added multiple GvRasterSource support
+ *
+ * Revision 1.21  2000/06/20 13:26:55  warmerda
+ * added standard headers
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <gtk/gtkscrolledwindow.h>
+#include "gview.h"
+#include "gextra.h"
+#include "cpl_conv.h"
+
+GvToolbox *toolbox;
+GvViewLink *vlink;
+
+static void key_press_cb( GtkObject * object, GdkEventKey * event )
+
+{
+    GvViewArea     *view = GV_VIEW_AREA(object);
+
+    if( event->keyval == 't' )
+    {
+        double    start_time = g_get_current_time_as_double();
+        double    end_time, spf;
+        int       i, frame_count = 20;
+
+        for( i = 0; i < frame_count; i++ )
+            gv_view_area_expose(GTK_WIDGET(view), NULL);
+
+        end_time =  g_get_current_time_as_double();
+
+        spf = (end_time - start_time) / frame_count;
+        printf( "Speed is %.2ffps\n", 1.0 / spf );
+    }
+}
+
+GtkWidget *
+create_view(GvShapes *shapes, GvRaster *raster,  GvRaster *height)
+{
+    GtkWidget *win;
+    GtkWidget *view;
+    GtkWidget *swin;
+
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    view = gv_view_area_new();
+    if( view == NULL )
+        return NULL;
+
+    gtk_window_set_default_size( GTK_WINDOW(win), 512, 512 );
+
+    /* Set mode to 2D or 3D */
+    if ( height == NULL)
+    {
+        gv_view_area_set_mode(GV_VIEW_AREA(view), 0);
+    } else {
+        gv_view_area_set_mode(GV_VIEW_AREA(view), 1);
+    }
+
+    gtk_drawing_area_size(GTK_DRAWING_AREA(view), 500, 500);
+
+    swin = gtk_scrolled_window_new(NULL, NULL);
+
+    gtk_container_add(GTK_CONTAINER(win), swin);
+    gtk_container_add(GTK_CONTAINER(swin), view);
+
+    if( raster != NULL )
+    {
+        GtkObject *raster_layer;
+        raster_layer = gv_raster_layer_new(GV_RLM_AUTO, raster, NULL);
+
+        gv_view_area_add_layer(GV_VIEW_AREA(view), 
+                               raster_layer);
+
+        if( height != NULL)
+        {
+            gv_mesh_add_height(GV_RASTER_LAYER(raster_layer)->mesh, height,
+                               0.0);
+        }
+    }   
+
+    gv_view_area_add_layer(GV_VIEW_AREA(view), gv_shapes_layer_new(shapes));
+
+    gtk_signal_connect_object(GTK_OBJECT(view), "key-press-event", 
+                              GTK_SIGNAL_FUNC(key_press_cb), 
+                              GTK_OBJECT(view) );
+
+    gtk_widget_show(view);
+    gtk_widget_show(swin);
+    gtk_widget_show(win);
+    gtk_widget_grab_focus(view);
+
+    gtk_signal_connect(GTK_OBJECT(win), "delete-event",
+		       GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
+    
+    gtk_quit_add_destroy(1, GTK_OBJECT(win));
+
+    return view;
+}
+
+void
+toolbar_callback(GtkWidget *widget, gpointer data)
+{
+    gv_toolbox_activate_tool(toolbox, (gchar*)data);
+}
+
+void
+link_callback(GtkWidget *widget, gpointer data)
+{
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+    {
+	gv_view_link_enable(vlink);
+    }
+    else
+    {
+	gv_view_link_disable(vlink);
+    }
+}
+
+void
+create_toolbar()
+{
+    GtkWidget *win;
+    GtkWidget *toolbar;
+    GtkWidget *but;
+
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    toolbar = gtk_toolbar_new(GTK_ORIENTATION_VERTICAL, GTK_TOOLBAR_TEXT);
+    gtk_container_add(GTK_CONTAINER(win), toolbar);
+
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   NULL,
+				   "Zoom",
+				   "Zoom tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "zoompan");
+
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Select",
+				   "Selection tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "select");
+
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Draw Points",
+				   "Point drawing tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "point");
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Draw Line",
+				   "Line drawing tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "line");
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Rotate/Resize",
+				   "Rotate/resize tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "rotate");
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Draw Area",
+				   "Area drawing tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "area");
+
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Edit Node",
+				   "Node edit tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "node");
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_RADIOBUTTON,
+				   but,
+				   "Draw ROI",
+				   "ROI drawing tool",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(toolbar_callback),
+				   "roi");
+    but = 
+	gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+				   GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
+				   NULL,
+				   "Link",
+				   "Link views together",
+				   NULL,
+				   NULL,
+				   GTK_SIGNAL_FUNC(link_callback),
+				   NULL);
+
+    gtk_signal_connect(GTK_OBJECT(win), "delete-event",
+		       GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
+    
+    gtk_widget_show(toolbar);
+    gtk_widget_show(win);	
+}
+
+static 
+GvData *gv_raster_new_from_name( const char * filename )
+{
+    static int gdal_is_initialized = 0;
+    GDALDatasetH  dataset;
+
+    if( !gdal_is_initialized )
+    {
+        gdal_is_initialized = TRUE;
+        GDALAllRegister();
+    }
+
+    dataset = gv_manager_get_dataset( gv_get_manager(), filename );
+    if( dataset == NULL )
+        return NULL;
+    else
+    {
+        GvRaster *raster;
+
+        raster = gv_manager_get_dataset_raster( gv_get_manager(), 
+                                                dataset, 1);
+        if( raster == NULL )
+            return NULL;
+        else
+            return GV_DATA(raster);
+    }
+}
+
+static void _load_ogr(GtkWidget *view, char *filename)
+{
+    GvData     *raw_data;
+    GvShapes   *shape_data;
+    GtkObject  *layer;
+
+    int index = 0;
+
+    while (NULL != (raw_data = gv_shapes_from_ogr(filename, index++))){
+
+        if (NULL == (shape_data = GV_SHAPES(raw_data))){
+            printf( __FILE__ "_load_ogr(): error loading layer no: %i\n", index);
+            return;
+        }
+
+        gv_undo_register_data(GV_DATA(shape_data));
+
+        layer = gv_shapes_layer_new( shape_data );
+
+        gv_view_area_add_layer(GV_VIEW_AREA(view), layer);
+        gv_view_area_set_active_layer(GV_VIEW_AREA(view), layer);
+    }
+
+    return;
+}
+
+void 
+Usage()
+
+{
+    printf( "Usage: gvtest [-2] [-ogr=<ogr_filename>] [shapefile ...] [raster_filename [dem_filename]]\n" );
+    exit( 0 );
+}
+
+int
+main(int argc, char **argv)
+{
+    GtkWidget *view;
+    GvData *shapes;
+    GvRaster *raster = NULL;
+    GvRaster *dem = NULL;
+    int two_views = FALSE;
+    char *raster_filename = NULL;
+    char *dem_filename = NULL;
+    char *shapefile = NULL;
+    char *ogr_filename = NULL;
+    int   i;
+
+    gtk_init(&argc, &argv);
+    
+    for( i = 1; i < argc; i++ )
+    {
+        if( strcmp(argv[i],"-1") == 0 )
+            /* normal case */;
+        else if( strcmp(argv[i],"-2") == 0 )
+            two_views = TRUE;
+        else if( strncmp(argv[i], "-ogr=", 5) == 0)
+            ogr_filename = argv[i] + 5;
+        else if( argv[i][0] == '-' )
+            Usage();
+        else if( EQUAL(CPLGetExtension(argv[i]),"shp") )
+            shapefile = argv[i];
+        else if( raster_filename == NULL )
+            raster_filename = argv[i];
+        else if( dem_filename == NULL )
+            dem_filename = argv[i];
+        else
+            Usage();
+    }
+
+    toolbox = GV_TOOLBOX(gv_toolbox_new());
+    gv_toolbox_add_tool(toolbox, "select", gv_selection_tool_new());
+    gv_toolbox_add_tool(toolbox, "zoompan", gv_zoompan_tool_new());
+    gv_toolbox_add_tool(toolbox, "point", gv_point_tool_new());
+    gv_toolbox_add_tool(toolbox, "line", gv_line_tool_new());
+    gv_toolbox_add_tool(toolbox, "rotate", gv_rotate_tool_new());
+    gv_toolbox_add_tool(toolbox, "area", gv_area_tool_new());
+    gv_toolbox_add_tool(toolbox, "node", gv_node_tool_new());
+    gv_toolbox_add_tool(toolbox, "roi", gv_roi_tool_new());
+    
+    vlink = GV_VIEW_LINK(gv_view_link_new());
+
+    if( shapefile == NULL )
+    {
+        shapes = gv_shapes_new();
+    }
+    else
+    {
+        shapes = gv_shapes_from_shapefile(shapefile);
+    }
+
+    if( raster_filename != NULL )
+    {
+        GvData *data;
+        GvData *dem_data;
+
+        data = gv_raster_new_from_name( raster_filename );
+
+        if( data != NULL )
+            raster = GV_RASTER(data);
+
+        /* add height */
+        if (argc > 3)
+        {
+            dem_data = gv_raster_new_from_name( dem_filename );
+            
+            if( dem_data != NULL )
+                dem = GV_RASTER(dem_data);
+        }
+    }
+
+    gv_undo_register_data(shapes);
+
+    view = create_view(GV_SHAPES(shapes), raster, dem );
+    if( view == NULL )
+    {
+        fprintf( stderr, "Unable to create view.  Is OpenGL working?\n" );
+        exit( 100 );
+    }
+
+    if (ogr_filename != NULL)
+        _load_ogr(view, ogr_filename);
+
+    gv_tool_activate(GV_TOOL(toolbox), GV_VIEW_AREA(view));
+    gv_view_link_register_view(vlink, GV_VIEW_AREA(view));
+    gv_toolbox_activate_tool( toolbox, "zoompan" );
+
+    if (two_views)
+    {
+	view = create_view(GV_SHAPES(shapes), raster, dem );
+	gv_tool_activate(GV_TOOL(toolbox), GV_VIEW_AREA(view));
+	gv_view_link_register_view(vlink, GV_VIEW_AREA(view));
+    }
+
+    create_toolbar();
+    
+    gtk_main();
+
+    return 0;
+}

Added: packages/openev/branches/upstream/current/tools/.cvsignore
===================================================================
--- packages/openev/branches/upstream/current/tools/.cvsignore	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/.cvsignore	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+*.pyc

Added: packages/openev/branches/upstream/current/tools/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/tools/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,16 @@
+/.cvsignore/1.1/Mon Jun  9 15:58:40 2003//
+/Tool_DriverList.py/1.1/Thu May 13 00:50:37 2004//
+/Tool_Export.py/1.6/Tue May 24 18:42:36 2005//
+/Tool_ShapesGrid.py/1.6/Tue Mar  1 20:54:34 2005//
+/Tool_autopan.py/1.3/Mon Aug  1 09:23:11 2005//
+/calculator.py/1.4/Sat Oct 23 11:04:42 2004//
+/compose.py/1.4/Tue Nov 16 19:11:34 2004//
+/fft.py/1.1/Sat Jul 24 15:17:13 2004//
+/gvrastertools.py/1.5/Mon Jan 12 16:54:56 2004//
+/imgproctemplate.py/1.1/Mon Jun  9 16:00:11 2003//
+/isodata.py/1.1/Fri Aug 26 18:27:35 2005//
+/mil_symbols.py/1.5/Mon Feb 14 15:30:33 2005//
+/open_raw.py/1.6/Wed Sep  1 15:47:19 2004//
+/open_subarea.py/1.3/Sat Oct 23 11:03:40 2004//
+/rendertest.py/1.14/Tue Sep  2 18:35:54 2003//
+D

Added: packages/openev/branches/upstream/current/tools/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/tools/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/tools

Added: packages/openev/branches/upstream/current/tools/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/tools/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/tools/Tool_DriverList.py
===================================================================
--- packages/openev/branches/upstream/current/tools/Tool_DriverList.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/Tool_DriverList.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+##############################################################################
+# $Id: Tool_DriverList.py,v 1.1 2004/05/13 00:50:37 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Graphical tool to list drivers built into current version of GDAL.
+# Author:   Gillian Walter, gillian.walter at atlantis-scientific.com
+#
+# Notes: GDAL's html files must be copied into OpenEV's html directory for
+#        the help buttons to work properly.
+#
+###############################################################################
+# Copyright (c) 2003, Atlantis Scientific Inc. (www.atlantis-scientific.com)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: Tool_DriverList.py,v $
+#  Revision 1.1  2004/05/13 00:50:37  gmwalter
+#  Add format driver list tool.
+#
+
+import gviewapp
+import gdal
+import gvhtml
+import gtk
+
+class ToolDriverList(gviewapp.Tool_GViewApp):
+
+    def __init__(self, app=None,startpath=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+
+        self.init_menu()
+
+        self.supported_list = []
+        self.unsupported_list=[]
+        self._official_support=[]
+
+	for iDriver in gdal.GetDriverList():
+	    try:
+                next_list=[]
+                if iDriver.HelpTopic is not None:
+                    helpstr=iDriver.HelpTopic
+                else:
+                    helpstr=None
+                next_list.append(iDriver.LongName)
+                next_list.append(iDriver.ShortName)
+                mdata=iDriver.GetMetadata()
+                if mdata.has_key("DCAP_CREATECOPY"):
+                    next_list.append(mdata["DCAP_CREATECOPY"])
+                else:
+                    next_list.append("NO")
+                    
+                if mdata.has_key("DMD_CREATIONDATATYPES"):
+                    next_list.append(mdata["DMD_CREATIONDATATYPES"])
+                else:
+                    next_list.append('Unknown')
+
+                next_list.append(helpstr)
+                if iDriver.ShortName in self._official_support:
+                    self.supported_list.append(next_list)
+                else:
+                    self.unsupported_list.append(next_list)
+	    except KeyError:
+		pass        
+
+        self.init_dialog()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Help/Formats...",1,self.driver_tool_cb)
+
+
+    def init_dialog(self):
+        self.dialog = gtk.GtkWindow()
+        self.dialog.set_title('Available Image Formats')
+        self.dialog.set_usize(300,500)
+        self.dialog.set_border_width(10) 
+        self.dialog.set_policy(gtk.FALSE, gtk.TRUE, gtk.TRUE)
+        self.tooltips=gtk.GtkTooltips()
+        self.button_dict={}
+        # main shell 
+        mainshell = gtk.GtkVBox(spacing=1, homogeneous=gtk.FALSE)
+        self.dialog.add(mainshell)
+        self.show_list=[]
+        self.show_list.append(mainshell)
+
+        #frame1=gtk.GtkFrame('Supported')
+        #self.show_list.append(frame1)
+        #mainshell.pack_start(frame1,expand=gtk.FALSE)
+        #num_s=len(self.supported_list)
+        #if num_s > 0:
+        #    s_table = gtk.GtkTable(num_s,3)
+        #    row=0            
+        #    for fmt_list in self.supported_list:
+        #        clabel=gtk.GtkEntry()
+        #        clabel.set_editable(gtk.FALSE)
+        #        clabel.set_text(fmt_list[0])
+        #        self.show_list.append(clabel)
+        #        self._make_tooltip(clabel,fmt_list)
+        #        s_table.attach(clabel,0,1,row,row+1)
+        #        if fmt_list[4] is not None:
+        #            self.button_dict[fmt_list[1]]=gtk.GtkButton('Help')
+        #            self.button_dict[fmt_list[1]].connect('clicked',self.help_clicked_cb,fmt_list[4])
+        #            s_table.attach(self.button_dict[fmt_list[1]],1,2,row,row+1)
+        #        row=row+1
+        #    frame1.add(s_table)
+        #    self.show_list.append(s_table)
+        
+        num_us=len(self.unsupported_list)
+        frame2=gtk.GtkFrame()
+        pixel_scroll = gtk.GtkScrolledWindow()
+        self.show_list.append(pixel_scroll)
+        self.show_list.append(frame2)
+        mainshell.pack_start(frame2)
+        frame2.add(pixel_scroll)
+        num_us=len(self.unsupported_list)
+        if num_us > 0:
+            us_table = gtk.GtkTable(num_us,3)
+            row=0
+            for fmt_list in self.unsupported_list:
+                clabel=gtk.GtkEntry()
+                clabel.set_editable(gtk.FALSE)
+                clabel.set_text(fmt_list[0])
+                self.show_list.append(clabel)
+                self._make_tooltip(clabel,fmt_list)
+                us_table.attach(clabel,0,1,row,row+1)
+                if fmt_list[4] is not None:
+                    self.button_dict[fmt_list[1]]=gtk.GtkButton('Help')
+                    self.button_dict[fmt_list[1]].connect('clicked',self.help_clicked_cb,fmt_list[4])
+                    us_table.attach(self.button_dict[fmt_list[1]],1,2,row,row+1)
+                row=row+1
+            pixel_scroll.add_with_viewport(us_table)
+            self.show_list.append(us_table)
+        self.button_dict['close']=gtk.GtkButton('Close')
+        self.button_dict['close'].connect('clicked',self.close)
+        mainshell.pack_start(self.button_dict['close'],expand=gtk.FALSE)
+        self.show_list.append(self.button_dict['close'])
+        self.dialog.connect('delete-event',self.close)
+        for item in self.show_list:
+            item.show()
+
+    def help_clicked_cb(self,but,topic):
+        gvhtml.LaunchHTML(topic)
+
+    def driver_tool_cb(self,*args):
+        for item in self.show_list:
+            item.show()
+        self.dialog.show_all()
+        self.dialog.get_window()._raise()
+
+    def _make_tooltip(self,clabel,info_list):
+        txt='Long Name: '+info_list[0]+'\n'
+        txt=txt+'Short Name: '+info_list[1]+'\n'
+        txt=txt+'Creation support: '+info_list[2]+'\n'
+        txt=txt+'Data types: '+info_list[3]
+        self.tooltips.set_tip(clabel,txt)
+        
+    def close(self,*args):
+        self.dialog.hide()
+        return gtk.TRUE
+
+TOOL_LIST=['ToolDriverList']

Added: packages/openev/branches/upstream/current/tools/Tool_Export.py
===================================================================
--- packages/openev/branches/upstream/current/tools/Tool_Export.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/Tool_Export.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,803 @@
+#!/usr/bin/env python
+###############################################################################
+# $Id: Tool_Export.py,v 1.6 2005/05/24 18:42:36 andrey_kiselev Exp $
+#
+# Project:  OpenEV
+# Purpose:  Graphical tool for translating between formats.
+# Author:   Gillian Walter, gillian.walter at atlantis-scientific.com
+#
+###############################################################################
+# Copyright (c) 2003, Atlantis Scientific Inc. (www.atlantis-scientific.com)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: Tool_Export.py,v $
+#  Revision 1.6  2005/05/24 18:42:36  andrey_kiselev
+#  Fix in the format list parsing code.
+#
+#  Revision 1.5  2005/05/19 21:08:07  andrey_kiselev
+#  Improvements in export list: all writable file types should be now supported.
+#
+#  Revision 1.4  2004/10/26 14:58:33  gmwalter
+#  Set default start line/start pixel text to
+#  0 for window option to avoid user confusion
+#  (gdal indexes from 0).
+#
+#  Revision 1.3  2004/10/07 19:02:35  gmwalter
+#  Extract generic code into gvutils.py.
+#
+#  Revision 1.2  2004/08/20 15:57:17  gmwalter
+#  Various fixes to export tool, added
+#  raw band creation to vrt utilities.
+#
+#  Revision 1.1  2004/03/25 16:17:37  andrey_kiselev
+#  New (moved from pymod directory).
+#
+#
+        
+import gtk
+import gview, gdal, gdalconst, layerdlg, gvutils
+import GtkExtra
+import os, string
+import gviewapp
+import pgufilesel
+import gvhtml
+import vrtutils
+import pguprogress
+
+class GDALTool(gviewapp.Tool_GViewApp):
+
+    def __init__(self, app=None,startpath=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+
+        self.init_dialog()
+        self.init_menu()
+        
+        # store the id for the roi-changed signal
+        # connection so it can be disconnected later
+        self.roichanged_id=None
+
+        # Set help topic
+        gvhtml.set_help_topic(self.dialog,"Tool_Export.html")
+
+    def init_menu(self):
+        self.menu_entries.set_entry("File/Export",1,self.gdal_tool_cb)
+
+    def gdal_tool_cb(self,*args):
+        # Activate the view that the export tool was launched from
+        # so that it's active layer is displayed in the input field
+        for view in self.app.view_manager.view_list:
+            if view.title == args[1]:
+                self.app.view_manager.set_active_view(view)
+                
+        for item in self.show_list:
+            item.show()
+
+        if self.button_dict['Mode'].get_active() == gtk.TRUE:
+            for item in self.adv_show_list:
+                item.show()
+        else:
+            for item in self.adv_show_list:
+                item.hide()
+            
+        self.dialog.show()
+        
+        self.dialog.get_window()._raise()
+        self.reconnect()
+        self.refresh_cb()
+
+    def reconnect(self,*args):
+        self.roichanged_id=self.app.toolbar.roi_tool.connect('roi-changed',self.refresh_roiinfo)
+
+    def set_roitool(self, *args):
+        self.app.toolbar.roi_button.set_active(gtk.TRUE)
+
+    def refresh_cb(self,*args):
+        self.refresh_fileinfo()
+        if self.button_dict['IP_window'].get_active():
+            self.refresh_roiinfo()
+
+    def refresh_roiinfo(self,*args):
+        try:
+            roi_info = self.app.toolbar.get_roi()
+        except:
+            # if roi has been disabled (eg. if current selection
+            # mode is poi), leave it at the latest value
+            roi_info = None    
+
+
+        [cview, clayer] = self.app.layerdlg.get_selected_layer()
+        
+        if (cview is None) or (clayer is None):
+            # roi only makes sense in the context of a view and layer
+            return None
+        
+        if roi_info is None:
+            cds=clayer.get_parent().get_dataset()
+            npix=cds.RasterXSize
+            nline=cds.RasterYSize
+            roi_info = (0,0,npix,nline)
+        else:    
+            # Get a reference to the active view
+            cur_view = self.app.layerdlg.get_active_view()
+            if (cur_view.get_raw(clayer) == 0):
+                # View is not in row/col coords-- convert
+                # Note that region will not be exactly the one drawn,
+                # since a rectangle is extracted rather than a 
+                # general parallelogram.  Get biggest rectangle.
+        
+                roi_info_nogeo = ROI_view_to_pixel(clayer,roi_info)
+
+                max_pix = roi_info_nogeo[0] + roi_info_nogeo[2]
+                min_pix = roi_info_nogeo[0]
+                max_line = roi_info_nogeo[1] + roi_info_nogeo[3]
+                min_line = roi_info_nogeo[1]
+
+                # Make sure ROI is reasonable in terms of extraction
+                if min_pix < 0:
+                    print 'Warning- negative start pixel.  Resetting to 0.'
+                    min_pix = 0
+                    if max_pix < min_pix+1:
+                        max_pix = min_pix + 1
+
+                if min_line < 0:
+                    print 'Warning- negative start line.  Resetting to 0.'
+                    min_line = 0
+                    if max_line < min_line+1:
+                        max_line = min_line + 1
+
+                roi_info = (min_pix,min_line,max_pix-min_pix,max_line-min_line)
+
+        self.frame_dict['IP_window'].update(roi_info)
+
+
+    def refresh_fileinfo(self,*args):
+        clayer = self.app.sel_manager.get_active_layer()
+        try:
+            fname = clayer.get_parent().get_dataset().GetDescription()
+            self.frame_dict['Files'].set_dsfile(fname,'Input')
+        except:
+            pass
+
+    def init_dialog(self):
+        self.dialog = gtk.GtkWindow()
+        self.dialog.set_title('GDAL Export Tool')
+        self.dialog.set_border_width(10)
+        self.tips=gtk.GtkTooltips()
+        #self.dialog.set_default_size(500,400)
+        self.dialog.set_policy(gtk.FALSE, gtk.TRUE, gtk.TRUE)
+
+        # main shell 
+        mainshell = gtk.GtkHBox(spacing=1, homogeneous=gtk.FALSE)
+        self.dialog.add(mainshell)
+        self.show_list=[]
+        self.adv_show_list=[]            # advanced show list
+        self.show_list.append(mainshell)
+
+        #navigation shell
+        navshell = gtk.GtkVBox(spacing=1, homogeneous=gtk.FALSE)
+        mainshell.pack_start(navshell)
+        self.show_list.append(navshell)
+
+        self.frame_dict={}
+        self.button_dict={}
+        self.frame_dict['Files']=gvutils.GvDataFilesFrame('Data Files',
+                                            sel_list=('Input','Output'))
+        self.frame_dict['Files'].set_border_width(5)
+        self.frame_dict['Files'].set_spacings(5,5)
+        self.frame_dict['Files'].show_all()
+        navshell.pack_start(self.frame_dict['Files'])
+        self.show_list.append(self.frame_dict['Files'])
+
+        bopt_frame=gtk.GtkFrame('Basic Options')
+        self.frame_dict['Basic Options']=bopt_frame
+        self.show_list.append(bopt_frame)
+        navshell.pack_start(bopt_frame,gtk.FALSE,gtk.FALSE,0)
+        bopt_table=gtk.GtkTable(2,4,gtk.FALSE)
+        bopt_table.set_border_width(5)
+        bopt_table.set_row_spacings(5)
+        bopt_table.set_col_spacings(5)
+        bopt_frame.add(bopt_table)
+        self.show_list.append(bopt_table)
+       
+        # Might be nice to have more formats below, but
+        # this involves error checking to test for
+        # supported data types, etc.
+        fmtlabel=gtk.GtkLabel('Output Format: ')
+        fmtlabel.set_alignment(0,0.5)
+        self.show_list.append(fmtlabel)
+        bopt_table.attach(fmtlabel,0,1,0,1)
+
+        self.format_list = []
+        hist_idx=0
+	for iDriver in gdal.GetDriverList():
+	    create = None
+	    try:
+		create = iDriver.GetMetadata()["DCAP_CREATE"]
+	    except KeyError:
+		try:
+		    create = iDriver.GetMetadata()["DCAP_CREATECOPY"]
+		except KeyError:
+		    pass        
+	    if create == "YES":
+		if iDriver.ShortName == 'DTED':
+		    # DTED is a special case that needs certain
+		    # conditions to be valid.  Skip it.
+		    continue
+		self.format_list.append(iDriver.ShortName)
+        self.format_list.sort()
+        # Default to GTiff if possible
+	try:
+	    hist_idx=self.format_list.index('GTiff')
+	except ValueError:
+	    pass
+
+        self.format_menu = gvutils.GvOptionMenu(self.format_list)
+        self.format_menu.set_history(hist_idx)
+        self.show_list.append(self.format_menu)
+        bopt_table.attach(self.format_menu,1,2,0,1)
+        self.button_dict['Format_help']=gtk.GtkButton('Help')
+        self.show_list.append(self.button_dict['Format_help'])
+        bopt_table.attach(self.button_dict['Format_help'],2,3,0,1)
+        reslabel=gtk.GtkLabel('Output Resolution: ')
+        reslabel.set_alignment(0,0.5)
+        self.show_list.append(reslabel)
+        bopt_table.attach(reslabel,0,1,1,2)        
+        self.res_list = ['Full','1:2','1:4','1:8']        
+        self.res_menu = gvutils.GvOptionMenu(self.res_list)
+        bopt_table.attach(self.res_menu,1,2,1,2)
+        self.show_list.append(self.res_menu)
+
+
+        self.button_dict['Mode']=gtk.GtkCheckButton('Advanced Options')
+        navshell.pack_start(self.button_dict['Mode'])
+        self.show_list.append(self.button_dict['Mode'])
+        
+        self.frame_dict['IP_window']=DataWindowFrame(navshell)
+        self.adv_show_list.append(self.frame_dict['IP_window'])
+
+        iopt_frame=gtk.GtkFrame('Interactive Options')
+        self.frame_dict['Interactive Options']=iopt_frame
+        self.adv_show_list.append(iopt_frame)
+        navshell.pack_start(iopt_frame,gtk.FALSE,gtk.FALSE,0)
+        iopt_table=gtk.GtkTable(3,3,gtk.FALSE)
+        iopt_table.set_border_width(5)
+        iopt_table.set_row_spacings(5)
+        iopt_table.set_col_spacings(5)
+        iopt_frame.add(iopt_table)
+        self.adv_show_list.append(iopt_table)
+        self.button_dict['IP_window']=gtk.GtkCheckButton('Window Input File')
+        iopt_table.attach(self.button_dict['IP_window'],0,2,0,1)
+        self.adv_show_list.append(self.button_dict['IP_window'])                                     
+        self.button_dict['Scale']=gtk.GtkCheckButton('Scale to View Settings')
+        self.tips.set_tip(self.button_dict['Scale'],'Scale the output bands '+
+                          'according to the min/max settings of the '+
+                          'currently active raster layer.  This only '+
+                          'applies to real data.')
+        iopt_table.attach(self.button_dict['Scale'],0,2,1,2)
+        self.adv_show_list.append(self.button_dict['Scale'])
+
+        self.button_dict['Refresh']=gtk.GtkButton('Active Layer->Input Filename')
+        self.tips.set_tip(self.button_dict['Refresh'],'Set the input '+
+                          'filename to that of the currently active layer') 
+        iopt_table.attach(self.button_dict['Refresh'],0,1,2,3)
+        self.adv_show_list.append(self.button_dict['Refresh'])
+        self.button_dict['Enable_ROI']=gtk.GtkButton('Draw ROI mode')
+        self.tips.set_tip(self.button_dict['Enable_ROI'],'Re-activate the '+
+              'ROI mode used for interactive input file window definition')
+        iopt_table.attach(self.button_dict['Enable_ROI'],1,2,2,3)
+        self.adv_show_list.append(self.button_dict['Enable_ROI'])        
+
+        self.frame_dict['Other_Advanced']=gtk.GtkFrame('')
+        self.frame_dict['Other_Advanced'].set_shadow_type(gtk.SHADOW_NONE)
+        self.adv_show_list.append(self.frame_dict['Other_Advanced'])
+        oadvbox=gtk.GtkVBox(spacing=5,homogeneous=gtk.FALSE)
+        oadvbox.set_border_width(5)
+        self.adv_show_list.append(oadvbox)
+        
+        self.frame_dict['Other_Advanced'].add(oadvbox)
+
+        otable=gtk.GtkTable(2,3,gtk.FALSE)
+        otable.set_row_spacings(5)
+        otable.set_col_spacings(5)
+        self.adv_show_list.append(otable)
+        oadvbox.pack_start(otable)                
+        self._overview_list=['None','Nearest','Average']
+        self.overview_menu=gvutils.GvOptionMenu(self._overview_list)
+        ovrlabel=gtk.GtkLabel('Overviews:')
+        self.tips.set_tip(self.overview_menu,'Tiled overview creation options')
+        ovrlabel.set_alignment(0,0.5)
+        self.adv_show_list.append(ovrlabel)
+        otable.attach(ovrlabel,0,1,0,1)
+        otable.attach(self.overview_menu,1,2,0,1)
+        self.adv_show_list.append(self.overview_menu)
+
+        self._geocode_list=['Default','GCP','Geotransform']
+        self.geocoding_menu = gvutils.GvOptionMenu(self._geocode_list)
+        geolabel=gtk.GtkLabel('Geocoding:')
+        self.tips.set_tip(self.geocoding_menu,
+                          'Specify the type of georeferencing '+
+                          'information to output.  Default is to output '+
+                          'all available geocoding from the input file.  '+
+                          'If GCP or Geotransform is selected, geocoding '+
+                          'information will only be output if it is of the '+
+                          'selected type.  This may later be updated to '+
+                          'generate information of the specified form if '+
+                          'it is not present but can be accurately computed '+
+                          'from the existing information.')
+        geolabel.set_alignment(0,0.5)
+        self.adv_show_list.append(geolabel)
+        otable.attach(geolabel,0,1,1,2)
+        otable.attach(self.geocoding_menu,1,2,1,2)
+        self.adv_show_list.append(self.geocoding_menu)
+
+        opthbox=gtk.GtkHBox(spacing=5,homogeneous=gtk.FALSE)
+        self.adv_show_list.append(opthbox)
+        oadvbox.pack_start(opthbox)
+        optlabel=gtk.GtkLabel('Create Options:')
+        optlabel.set_alignment(0,0.5)
+        self.adv_show_list.append(optlabel)
+        self.optentry=gtk.GtkEntry()
+        self.optentry.set_editable(editable=gtk.TRUE)
+        self.optentry.set_usize(400,25)
+        self.optentry.set_text('')
+        self.adv_show_list.append(self.optentry)
+        opthbox.pack_start(optlabel)
+        opthbox.pack_start(self.optentry)
+        
+        navshell.pack_start(self.frame_dict['Other_Advanced'],
+                            gtk.FALSE,gtk.FALSE,0)
+        
+        echbox=gtk.GtkHBox(spacing=5,homogeneous=gtk.FALSE)
+        echbox.set_border_width(3)
+        navshell.pack_end(echbox,gtk.FALSE,gtk.FALSE,0)
+        self.show_list.append(echbox)
+        self.button_dict['Close']=gtk.GtkButton('Close')
+        echbox.pack_end(self.button_dict['Close'],expand=gtk.TRUE)
+        self.show_list.append(self.button_dict['Close'])
+        self.button_dict['Export']=gtk.GtkButton('Export')
+        echbox.pack_end(self.button_dict['Export'],expand=gtk.TRUE)
+        self.show_list.append(self.button_dict['Export'])
+
+        self.button_dict['Format_help'].connect('clicked',
+                                                self.format_help_cb)
+        
+        self.button_dict['Enable_ROI'].connect('clicked',self.set_roitool)
+        self.button_dict['Refresh'].connect('clicked',
+                                            self.refresh_fileinfo)        
+        self.button_dict['Export'].connect('clicked',self.export_cb)
+        self.button_dict['Close'].connect('clicked',self.close)
+        
+        self.button_dict['IP_window'].connect('toggled',
+                                              self.ip_window_toggled_cb)
+        self.button_dict['Mode'].connect('toggled',self.mode_toggled_cb)
+
+        self.button_dict['IP_window'].set_active(gtk.FALSE)
+        self.button_dict['Mode'].set_active(gtk.FALSE)
+        self.frame_dict['IP_window'].set_entry_sensitivities(gtk.FALSE)
+
+        
+        # Trap window close event
+        self.dialog.connect('delete-event', self.close)
+
+        for item in self.show_list:
+            item.show()
+
+        if self.button_dict['Mode'].get_active():
+            for item in self.adv_show_list:
+                item.show()
+        else:
+            for item in self.adv_show_list:
+                item.hide()
+            
+    def mode_toggled_cb(self,*args):
+        if self.button_dict['Mode'].get_active():
+            for item in self.adv_show_list:
+                item.show()
+        else:            
+            for item in self.adv_show_list:
+                item.hide()
+                
+
+    def ip_window_toggled_cb(self,*args):
+        if self.button_dict['IP_window'].get_active():
+            self.frame_dict['IP_window'].set_entry_sensitivities(gtk.TRUE)
+            self.set_roitool()
+        else:
+            self.frame_dict['IP_window'].set_entry_sensitivities(gtk.FALSE)
+            
+    def format_help_cb(self,*args):
+        opformat=self.format_list[self.format_menu.get_history()]
+        driver=gdal.GetDriverByName(opformat)
+        topic=driver.HelpTopic
+        if topic is not None:
+            # hash to indicate position in html isn't
+            # recognized by mozilla.  Make sure that
+            # frmt_various.html is brought up for
+            # formats that share this file.
+            ttopic=string.split(topic,"#")
+            gvhtml.LaunchHTML(ttopic[0])
+        else:
+            gvutils.warning('No html help available for '+opformat+' format')
+            
+    def export_cb(self,*args):
+        ipfile=self.frame_dict['Files'].get('Input')
+        opfile=self.frame_dict['Files'].get('Output')
+        if os.path.isfile(opfile):
+            resp=GtkExtra.message_box('Confirmation',opfile +
+                                      ' exists.  Overwrite?',('Yes','No'))
+            if resp == 'No':
+                return
+        elif len(opfile) == 0:
+            gvutils.error('No output filename entered!')
+            return
+            
+        use_viewscale=0
+        
+        rast = gdal.OpenShared(ipfile, gdalconst.GA_ReadOnly)
+        if rast is None:
+            if len(ipfile) == 0:
+                gvutils.error('Please specify an input file!')
+            else:
+                gvutils.error('Unable to open ' + ipfile + ' as a GDAL supported file!')
+            return
+
+        # Catch the case where the input file consists of in-memory VRT lines
+        # and the output format is also VRT.  In this case, the new VRT would
+        # no longer be valid once openev was exited because the input file
+        # is not on disk (filename looks something like '<VRTDataset....').
+        # If the user is just exporting the file as-is, simply copying the
+        # original lines to disk will suffice.  However, if they want to
+        # window or scale, we'd need more complicated manipulations.  For now,
+        # give an error message in that case.
+        opformat=self.format_list[self.format_menu.get_history()]
+        
+        if (ipfile[0] == '<') and (opformat == 'VRT'):
+            if self.res_list[self.res_menu.get_history()] != 'Full':
+                msg='Only full output resolution is currently\n'+\
+                    'supported for export of in-memory VRTs\n'+\
+                    'to on-disk VRTs.'
+                gvutils.error(msg)
+                return
+            
+            if ( (self.button_dict['Mode'].get_active()) and
+                ((self.button_dict['IP_window'].get_active()) or
+                (self.button_dict['Scale'].get_active()) or
+                (len(self.optentry.get_text()) > 0))):
+                msg='Scaling, windowing, and advanced creation\n'+\
+                    'options are not yet supported for export of \n'+\
+                    'in-memory VRTs to on-disk VRTs'
+                gvutils.error(msg)
+                return
+                
+            linelist=string.split(ipfile,'\n')
+            newlinelist=[]
+            for item in linelist:
+                newlinelist.append(item+'\n')
+            fh=open(opfile,'w')
+            fh.writelines(newlinelist)
+            fh.close()
+            
+            ovrs=self._overview_list[self.overview_menu.get_history()]
+            if ovrs != 'None':
+                outds=gdal.OpenShared(opfile)
+                if outds is None:
+                    gvutils.error('Error opening '+opfile+' for overview creation!')
+                    return
+                
+                progress = pguprogress.PGUProgressDialog( 'Building overviews...',
+                                                  cancel = gtk.TRUE )
+                if ovrs is 'Nearest':
+                    outds.BuildOverviews( "nearest",
+                                       callback = progress.ProgressCB )
+                else:
+                    outds.BuildOverviews( "average_magphase",
+                                       callback = progress.ProgressCB )
+                progress.destroy()
+            
+            return
+        
+            
+        
+        vrt_opts=vrtutils.VRTCreationOptions(rast.RasterCount)
+
+        if self._geocode_list[self.geocoding_menu.get_history()] == 'GCP':
+            vrt_opts.set_geopref('gcps')
+        elif self._geocode_list[self.geocoding_menu.get_history()] == 'Geotransform':
+            vrt_opts.set_geopref('geotransform')
+
+        band_list = None
+        
+        # Scale the output file according to the current view's
+        # min/max
+        if self.button_dict['Scale'].get_active():
+            try:
+                clayer = self.app.sel_manager.get_active_layer()
+                if clayer.get_parent().get_dataset().GetDescription() != ipfile:
+                    wtxt = 'Input file and active layer file names do not match- may '
+                    wtxt = wtxt + 'result in unexpected scaling!'
+                    gvutils.warning(wtxt)
+                if gvutils.is_of_class(clayer.__class__,'GvRasterLayer') == 0:
+                    gvutils.warning('Active layer is not a raster- view scaling ignored!')
+                else:
+                    src_count=clayer.sources
+                    band_list = []
+                    RGBAlist=['Red','Green','Blue','Alpha']
+                    for src in range(src_count):
+                        # layer sources are numbered 0...3; band sources are numbered 1,2,...
+                        src_bandnum=clayer.get_data(src).get_band_number()
+                        band_list.append(src_bandnum)
+                        vrt_opts.set_scaling((clayer.min_get(src),clayer.max_get(src),0,255),(src_bandnum,))
+                        vrt_opts.set_datatype(gdal.GDT_Byte,(src_bandnum,))
+                        if src_count == 3:
+                            vrt_opts.set_color_interp(RGBAlist[src],
+                                                      (src_bandnum,))
+
+                    # src_count is three even when there is an alpha channel
+                    # for rgb/rgba case
+                    if src_count == 3:
+                        try:
+                            src=3
+                            src_bandnum=clayer.get_data(src).get_band_number()
+                            band_list.append(src_bandnum)
+                            vrt_opts.set_scaling((clayer.min_get(src),
+                                                  clayer.max_get(src),0,255),
+                                                 (src_bandnum,))
+                            vrt_opts.set_datatype(gdal.GDT_Byte,(src_bandnum,))
+                            vrt_opts.set_color_interp(RGBAlist[src],
+                                                      (src_bandnum,))
+                        except:
+                            pass
+
+                    use_viewscale=1
+                    if clayer.get_mode()==gview.RLM_COMPLEX:
+                        # This doesn't deal with complex yet...                        
+                        gvutils.error('View scaling option is not yet supported for complex data!')
+                        return
+                    elif rast._band[0].DataType == gdal.GDT_CInt16:
+                        # This doesn't deal with complex yet...                        
+                        gvutils.error('View scaling option is not yet supported for complex data!')
+                        return
+                    elif rast._band[0].DataType == gdal.GDT_CInt32:
+                        # This doesn't deal with complex yet...                        
+                        gvutils.error('View scaling option is not yet supported for complex data!')
+                        return
+                    elif rast._band[0].DataType == gdal.GDT_CFloat32:
+                        # This doesn't deal with complex yet...                        
+                        gvutils.error('View scaling option is not yet supported for complex data!')
+                        return
+                    elif rast._band[0].DataType == gdal.GDT_CFloat64:
+                        # This doesn't deal with complex yet...                        
+                        gvutils.error('View scaling option is not yet supported for complex data!')
+                        return                    
+            except:
+                gvutils.error('Unable to find active raster layer for scaling!')
+                return
+
+        # Get windowing options
+        if self.button_dict['IP_window'].get_active():
+            try:
+                spix=int(self.frame_dict['IP_window'].entry_dict['start_pix'].get_text())
+                sline=int(self.frame_dict['IP_window'].entry_dict['start_line'].get_text())
+                npix=int(self.frame_dict['IP_window'].entry_dict['num_pix'].get_text())
+                nlines=int(self.frame_dict['IP_window'].entry_dict['num_lines'].get_text())
+                if (spix < 0) or (sline < 0):
+                    gvutils.error('Negative start pixel and/or line!  Aborting...')
+                    return
+                if (npix+spix > rast.RasterXSize):
+                    gvutils.error('Window is too large (last column in input: '+str(rast.RasterXSize)+')! Aborting...')
+                    return
+                if (nlines+sline > rast.RasterYSize):
+                    gvutils.error('Window is too large (last row in input: '+str(rast.RasterYSize)+')! Aborting...')
+                    return
+            except:
+                gvutils.error('Error retrieving window options!  Aborting...')
+                return
+        else:
+            spix=0
+            sline=0
+            npix=rast.RasterXSize
+            nlines=rast.RasterYSize
+
+        vrt_opts.set_src_window((spix,sline,npix,nlines))
+
+        if self.res_list[self.res_menu.get_history()] != 'Full':
+            ovrlevel=int(self.res_list[self.res_menu.get_history()][2])
+        else:
+            ovrlevel=1
+
+        vrt_opts.set_dst_window((0,0,npix/ovrlevel,nlines/ovrlevel))
+
+        vrt_tree=vrtutils.serializeDataset(rast,vrt_opts,band_list)
+        vrt_lines=gdal.SerializeXMLTree(vrt_tree)
+        vrtdataset=gdal.Open(vrt_lines)
+        
+        driver=gdal.GetDriverByName(opformat)
+
+        # Parse creation options:
+        optstr=string.strip(self.optentry.get_text())
+        if len(optstr) > 0:
+            # should be able to deal with several
+            # types of entries, eg.
+            # 'TILED=YES','TFW=YES'
+            # and
+            # TILED=YES,TFW=YES
+
+            if optstr[0] in ["'",'"']:
+                split1=string.split(optstr,",")
+                copts=[]
+                for item in split1:
+                    if len(item) > 2:
+                        copts.append(item[1:len(item)-1])
+            else:    
+                copts=string.split(optstr,',')
+        else:
+            copts=[]
+    
+        progress = pguprogress.PGUProgressDialog( 'Export to '+opfile,
+                                                  cancel = gtk.TRUE )
+        progress.SetDefaultMessage("translated")
+
+        outdataset=driver.CreateCopy(opfile,vrtdataset,
+                                     options=copts,
+                                     callback=progress.ProgressCB)
+        if outdataset is None:
+            progress.destroy()
+            gvutils.error('Unable to create output file '+opfile)
+            return
+        
+        ovrs=self._overview_list[self.overview_menu.get_history()]
+        if ovrs is 'Nearest':
+            progress.SetDefaultMessage("overviews built")
+            outdataset.BuildOverviews( "nearest",
+                                       callback = progress.ProgressCB )
+   
+        elif ovrs is 'Average':
+            progress.SetDefaultMessage("overviews built")
+            outdataset.BuildOverviews( "average_magphase",
+                                       callback = progress.ProgressCB )
+            
+        progress.destroy()
+
+
+        
+    
+    def close(self,*args):
+        if self.roichanged_id is not None:
+            self.app.toolbar.roi_tool.disconnect(self.roichanged_id)
+            self.roichanged_id=None
+            
+        self.dialog.hide()
+        return gtk.TRUE
+
+#-----------------------------------------------------------------
+# GUI STUFF
+#-----------------------------------------------------------------
+
+class DataWindowFrame:
+    def __init__(self,parent_box,title='Input Window'):
+        self.frame=gtk.GtkFrame(title)
+        self.show_list=[]
+        self.show_list.append(self.frame)
+        
+        patch_table = gtk.GtkTable(2,4,gtk.FALSE)
+        self.show_list.append(patch_table)
+        self.frame.add(patch_table)
+
+        patch_table.set_border_width(5)
+        patch_table.set_col_spacings(5)
+        patch_table.set_col_spacing(1, 20)
+
+        label1 = gtk.GtkLabel('Start Line: ')
+        label1.set_alignment(0, 0.5)
+        patch_table.attach(label1, 0,1, 0, 1)
+
+        self.entry_dict={}
+
+        self.entry_dict['start_line'] = gtk.GtkEntry()
+        self.entry_dict['start_line'].set_editable(gtk.TRUE)
+        self.entry_dict['start_line'].set_usize(90, 25)
+        self.entry_dict['start_line'].set_text('0')
+        patch_table.attach(self.entry_dict['start_line'], 1,2, 0,1)
+
+        label2 = gtk.GtkLabel('Start Pixel: ')
+        label2.set_alignment(0, 0.5)
+        patch_table.attach(label2, 2,3,0, 1)
+
+        self.entry_dict['start_pix'] = gtk.GtkEntry()
+        self.entry_dict['start_pix'].set_editable(gtk.TRUE)
+        self.entry_dict['start_pix'].set_usize(90, 25)
+        self.entry_dict['start_pix'].set_text('0')
+        patch_table.attach(self.entry_dict['start_pix'], 3,4, 0,1)
+
+        label3 = gtk.GtkLabel('Num. of Lines: ')
+        label3.set_alignment(0, 0.5)
+        patch_table.attach(label3, 0,1, 1, 2)
+
+        self.entry_dict['num_lines'] = gtk.GtkEntry()
+        self.entry_dict['num_lines'].set_editable(gtk.TRUE)
+        self.entry_dict['num_lines'].set_usize(90, 25)
+        self.entry_dict['num_lines'].set_text('1')
+        patch_table.attach(self.entry_dict['num_lines'], 1,2, 1,2)
+
+        label4 = gtk.GtkLabel('Num. of Pixels: ')
+        label4.set_alignment(0, 0.5)
+        patch_table.attach(label4, 2,3,1, 2)
+
+        self.entry_dict['num_pix'] = gtk.GtkEntry()
+        self.entry_dict['num_pix'].set_editable(gtk.TRUE)
+        self.entry_dict['num_pix'].set_usize(90, 25)
+        self.entry_dict['num_pix'].set_text('1')
+        patch_table.attach(self.entry_dict['num_pix'], 3,4, 1,2)
+
+        self.show_list.append(label1)
+        self.show_list.append(label2)
+        self.show_list.append(label3)
+        self.show_list.append(label4)
+        self.show_list.append(self.entry_dict['start_line'])
+        self.show_list.append(self.entry_dict['start_pix'])
+        self.show_list.append(self.entry_dict['num_lines'])
+        self.show_list.append(self.entry_dict['num_pix'])
+        
+        parent_box.pack_start(self.frame,gtk.FALSE,gtk.FALSE,0)
+
+
+    def update(self,roi_info):
+        self.entry_dict['start_line'].set_text(str(int(roi_info[1])))
+        self.entry_dict['start_pix'].set_text(str(int(roi_info[0])))
+        self.entry_dict['num_lines'].set_text(str(int(roi_info[3])))
+        self.entry_dict['num_pix'].set_text(str(int(roi_info[2])))
+
+    def set_entry_sensitivities(self,bool_val):
+        self.entry_dict['start_line'].set_sensitive(bool_val)
+        self.entry_dict['start_pix'].set_sensitive(bool_val)
+        self.entry_dict['num_lines'].set_sensitive(bool_val)
+        self.entry_dict['num_pix'].set_sensitive(bool_val)
+
+
+    def show(self,*args):
+        for item in self.show_list:
+            item.show()
+
+    def hide(self,*args):
+        for item in self.show_list:
+            item.hide()
+
+
+def ROI_view_to_pixel(clayer,roi_info):
+
+    [pixel,line] = clayer.view_to_pixel(roi_info[0],roi_info[1])
+    [pixel2,line2] = clayer.view_to_pixel(roi_info[0]+roi_info[2],
+                                          roi_info[1]+roi_info[3])
+    [pixel3,line3] = clayer.view_to_pixel(roi_info[0],
+                                          roi_info[1]+roi_info[3])
+    [pixel4,line4] = clayer.view_to_pixel(roi_info[0]+roi_info[2],
+                                          roi_info[1])
+
+    # Get pixel-space rectangle
+    max_pix = max(pixel,pixel2,pixel3,pixel4)
+    min_pix = min(pixel,pixel2,pixel3,pixel4)
+    max_line = max(line,line2,line3,line4)
+    min_line = min(line,line2,line3,line4)
+
+    roi_info_pixel = (min_pix,min_line,max_pix-min_pix,max_line-min_line)
+
+    return roi_info_pixel
+
+
+TOOL_LIST = ['GDALTool']
+ 

Added: packages/openev/branches/upstream/current/tools/Tool_ShapesGrid.py
===================================================================
--- packages/openev/branches/upstream/current/tools/Tool_ShapesGrid.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/Tool_ShapesGrid.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,633 @@
+###############################################################################
+# $Id: Tool_ShapesGrid.py,v 1.6 2005/03/01 20:54:34 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Tool using scrollable text area widget for displaying GvShapes data
+# Author:  
+#
+###############################################################################
+# Copyright (c) 2003, Atlantis Scientific Inc. (www.atlantis-scientific.com)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#  
+#  $Log: Tool_ShapesGrid.py,v $
+#  Revision 1.6  2005/03/01 20:54:34  gmwalter
+#  Fix configuration typo that was preventing
+#  cell editing from starting on single click.
+#
+#  Revision 1.5  2004/07/21 19:40:40  gmwalter
+#  Don't sort properties before displaying.
+#
+#  Revision 1.4  2004/03/23 18:08:25  gmwalter
+#  Add another variant to pgugrid and update
+#  comments; update shapesgrid tool to start
+#  editing on single left click.
+#
+#  Revision 1.3  2004/01/14 19:58:32  gmwalter
+#  Make sure grid is cleared properly when active layer changes.
+#
+#  Revision 1.2  2003/11/19 16:46:55  gmwalter
+#  Checked into main branch.
+#
+#
+
+
+
+import gviewapp
+import gtk
+import gview
+import pgugrid
+import gvutils
+import Numeric
+
+class ShapesGridTool(gviewapp.Tool_GViewApp):
+
+    def __init__(self, app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+     
+        self.layer = None
+        self.shapes = None
+        self.viewarea = None
+        self.layer_teardown_id=None
+        
+        self.init_dialog()
+        self.init_menu()
+
+    def init_dialog(self):
+        self.dialog = gtk.GtkWindow()
+        self.dialog.set_title('Tabular Shapes Attribute Grid Demo')
+        self.dialog.set_default_size(300,400)
+        self.dialog.set_policy(gtk.FALSE, gtk.TRUE, gtk.TRUE)
+
+        shell = gtk.GtkVBox(spacing=5)
+        shell.set_border_width(10)
+        self.dialog.add(shell)
+
+        self.pgugrid=pgugrid.pguGrid(config=(3,2,0,2,4,2,0,0,0))
+
+
+        shell.pack_start(self.pgugrid,expand=gtk.TRUE)
+
+        hbox=gtk.GtkHBox(spacing=5)
+        shell.pack_start(hbox)
+        self.column_button=gtk.GtkCheckButton("Schema properties only")
+        self.column_button.connect("toggled",self.refresh_columns)
+        self.column_button.set_active(gtk.TRUE)
+        hbox.pack_start(self.column_button)
+        
+        rbutton = gtk.GtkButton("Refresh columns")
+        rbutton.connect("clicked", self.refresh_columns)
+        hbox.pack_start(rbutton,expand=gtk.FALSE)
+        
+        
+        button = gtk.GtkButton("close")
+        button.connect("clicked", self.close)
+        shell.pack_start(button,expand=gtk.FALSE)
+        button.show()
+        
+        shell.show_all()
+
+        # Trap window close event
+        self.dialog.connect('delete-event', self.close)
+
+        self.app.sel_manager.subscribe( 'active-layer-changed',
+                                         self.layer_update )
+
+        self.pgugrid.subscribe("clicked",self.clicked_cb)
+        self.pgugrid.connect("button-release-event",self.clicked_nocolumns_cb)
+        #self.pgugrid.subscribe("cell-selection-changed",self.cell_cb)
+        #self.pgugrid.subscribe("cell-changed",self.cellch_cb)
+        #self.pgugrid.subscribe("row-selection-changed",self.row_cb)
+        #self.pgugrid.subscribe("column-selection-changed",self.column_cb)
+
+        # Popup menus:
+        itemlist=[#('Configuration',None,None),
+                  ('Set subset/Selected',self.set_subset_selected_cb,None),
+                  ('Set subset/Unseleted',self.set_subset_unselected_cb,None),
+                  ('Set subset/All',self.set_subset_all_cb,None),
+                  ('New layer/Selected',self.new_layer_selected_cb,None),
+                  ('New layer/Unselected',self.new_layer_unselected_cb,None),
+                  ('Edit Schema',self.edit_schema,None),
+                  #('Query',None,None),
+                  #('Save',None,None),
+                  #('Help',None,None)
+                  ]
+
+        self.cell_popup_menu=[]          
+        self.cell_popup_menu.append(gtk.GtkMenu())
+        for label, func, args in itemlist:
+            menu_item = gtk.GtkMenuItem(label)
+	    if func is not None:
+                menu_item.connect("activate", func, args)
+            self.cell_popup_menu[0].append(menu_item)
+            
+        self.cell_popup_menu[0].show_all()
+        
+        itemlist2=[#('Configuration',None,None),
+                  ('Set subset/Selected',self.set_subset_selected_cb,None),
+                  ('Set subset/Unseleted',self.set_subset_unselected_cb,None),
+                  ('Set subset/All',self.set_subset_all_cb,None),
+                  ('New layer/Selected',self.new_layer_selected_cb,None),
+                  ('New layer/Unselected',self.new_layer_unselected_cb,None),
+                  ('Edit Schema',self.edit_schema,None),
+                  ('Add property',self.add_property_column,None),
+                  #('Query',None,None),
+                  #('Save',None,None),
+                  #('Help',None,None)
+                  ]
+
+        self.cell_popup_menu.append(gtk.GtkMenu())
+        for label, func, args in itemlist2:
+            menu_item = gtk.GtkMenuItem(label)
+	    if func is not None:
+                menu_item.connect("activate", func, args)
+            self.cell_popup_menu[1].append(menu_item)
+            
+        self.cell_popup_menu[1].show_all()
+        # Only do actions when grid is visible
+        self.active=0
+
+                  
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Tabular Shapes Grid",1,self.show_cb)
+        
+    def clicked_cb(self,*args):
+        """ Show popup menus """
+        
+        if self.shapes is None:
+            return
+
+        row=args[1]
+        if row == -1:
+            return
+        
+        event=args[3]
+        if ((event.button == 3) and (not
+            (event.state & gtk.GDK.CONTROL_MASK)) and (not
+            (event.state & gtk.GDK.SHIFT_MASK))):
+
+            if self.column_button.get_active() == gtk.TRUE:
+                self.cell_popup_menu[0].popup(None,None,None,event.button,
+                                      event.time)
+            else:
+                self.cell_popup_menu[1].popup(None,None,None,event.button,
+                                      event.time)
+
+    def clicked_nocolumns_cb(self,*args):
+        """ In case where source has no columns defined yet,
+            pop up a menu (since clicked_cb won't be called
+            until grid has columns defined- grid only sends
+            out clicked notification if columns have been
+            defined) """
+
+        event=args[1]
+        if self.shapes is not None:
+            cc=self.pgugrid.get_current_columns()
+            if len(cc[0]) == 0:
+                if self.column_button.get_active() == gtk.TRUE:
+                    self.cell_popup_menu[0].popup(None,None,None,event.button,
+                                          event.time)
+                else:
+                    self.cell_popup_menu[1].popup(None,None,None,event.button,
+                                          event.time)
+                
+
+    def set_subset_selected_cb(self,*args):
+        """ Set the grid subset to selected rows only """
+
+        selected=self.pgugrid.get_selected_row_indices()
+        
+        if len(selected) == 0:
+            gvutils.warning('No rows selected- ignoring!')
+        else:
+            self.pgugrid.set_subset(selected)
+
+    def set_subset_unselected_cb(self,*args):
+        """ Set the grid subset to selected rows only """
+        
+        unselected=self.pgugrid.get_unselected_row_indices()
+
+        if len(unselected) == 0:
+            gvutils.warning('No rows unselected- ignoring!')
+        else:
+            self.pgugrid.set_subset(unselected)
+
+    def set_subset_all_cb(self,*args):
+        """ Set the grid to show all shapes """
+
+        self.pgugrid.set_subset(None)
+
+    def new_layer_selected_cb(self,*args):
+        """ Create new layer of only selected shapes """
+        
+        sel=self.pgugrid.get_selected_row_indices()
+        if len(sel) == 0:
+            gvutils.warning('No rows selected- ignoring!')
+            return
+
+        newshps=gview.GvShapes(name='Selected')
+        src=self.shapes
+        if src is None:
+            gvutils.warning('No source layer found- ignoring!')
+            return
+        
+        schema=src.get_schema()
+        for item in schema:
+            newshps.add_field(item[0],item[1],item[2],item[3])
+
+        for idx in sel:
+            if src[idx] is not None:
+                newshps.append(src[idx].copy())
+
+        gview.undo_register(newshps)
+        clayer=gview.GvShapesLayer(newshps)
+
+        cview=self.app.new_view()
+        cview.viewarea.add_layer(clayer)
+        cview.viewarea.set_active_layer(clayer)
+
+    def new_layer_unselected_cb(self,*args):
+        """ Create new layer of only selected shapes """
+        
+        unselected=self.pgugrid.get_unselected_row_indices()
+
+        if len(unselected) == 0:
+            gvutils.warning('No rows unselected- ignoring!')
+            return
+
+        newshps=gview.GvShapes(name='Unselected')
+        src=self.shapes
+        if src is None:
+            gvutils.warning('No source layer found- ignoring!')
+            return
+        
+        schema=src.get_schema()
+        for item in schema:
+            newshps.add_field(item[0],item[1],item[2],item[3])
+
+        for idx in unselected:
+            if src[idx] is not None:
+                newshps.append(src[idx].copy())
+
+        gview.undo_register(newshps)
+        clayer=gview.GvShapesLayer(newshps)
+
+        cview=self.app.new_view()
+        cview.viewarea.add_layer(clayer)
+        cview.viewarea.set_active_layer(clayer)
+
+    def get_columns(self,shapes):
+        """ Get the columns for a shapes object """
+
+        # should skip to except here if layer is a raster
+        shp_schema = self.shapes.get_schema()
+
+        klist=[]
+        fdict={}
+        for item in shp_schema:
+            klist.append(item[0])
+            if item[1] == 'integer':
+                fdict[item[0]]="%"+str(item[2])+"d"
+            elif item[1] == 'float':
+                fdict[item[0]]="%"+str(item[2])+"."+\
+                             str(item[3])+"f"
+            else:
+                fdict[item[0]]="%"+str(item[2])+"s"
+
+        if self.column_button.get_active() == gtk.FALSE:
+            for shp in self.shapes:
+                klist.extend(shp.get_properties().keys())
+
+            #klist.sort()
+            # Get unique keys
+            set={}
+            key_list=\
+               [set.setdefault(e,e) for e in klist if not set.has_key(e)]
+            
+        else:
+            #klist.sort()
+            key_list=klist
+
+        flist=[]
+        for item in key_list:
+            if fdict.has_key(item):
+                flist.append(fdict[item])
+            else:
+                flist.append("%s")
+            
+        return key_list,flist
+
+    def edit_schema(self,*args):
+        """ Edit the shapes schema. """
+        if self.shapes is None:
+            return
+
+        sch=SchemaDialog(self.shapes,self)
+        
+            
+    def add_property_column(self,*args):
+        """ Add a property column """
+
+        import GtkExtra
+        pname=GtkExtra.input_box(title="Property Name",
+              message="Please enter a name for the property")
+
+        if ((pname is None) or (len(pname) == 0)):
+            return
+        
+        current_columns=self.pgugrid.get_current_columns()
+        mems=current_columns[0]
+        titles=current_columns[1]
+        editables=current_columns[2]
+        formats=current_columns[3]
+        types=current_columns[4]
+        nodatas=current_columns[5]
+        justify=current_columns[6]
+        tjustify=current_columns[7]
+        
+        mems.append(pname)
+        titles.append(pname)
+        editables.append(1)
+        formats.append(None)
+        types.append('string')
+        nodatas.append('')
+        justify.append(0)
+        tjustify.append(2)
+
+        self.pgugrid.define_columns(members=mems,titles=titles,
+             editables=editables,formats=formats,types=types,
+             nodata=nodatas,justify=justify,title_justify=tjustify)
+        
+        
+    def refresh_columns(self,*args):
+        """ Update column headers """
+        if self.shapes is not None:
+            key_list,fmt_list=self.get_columns(self.shapes)
+            self.pgugrid.define_columns(members=key_list,formats=fmt_list)
+
+    def show_cb(self,*args):
+        # Activate the view that the grid was launched from
+        # (confusing for user if launching the grid from
+        # one view shows the vectors for another)
+        for view in self.app.view_manager.view_list:
+            if view.title == args[1]:
+                self.app.view_manager.set_active_view(view)
+                
+        self.active=1
+        self.dialog.show_all()
+        self.dialog.get_window()._raise()
+        self.layer_update()
+
+    def close(self,*args):
+        self.active=0
+        self.layer_teardown_cb()
+        self.dialog.hide()
+        return gtk.TRUE
+    
+
+    def layer_update(self,*args):
+        # Disconnect from the old layer
+        if self.active == 0:
+            return
+        
+        self.layer_teardown_cb()
+
+        try:
+            self.layer = self.app.sel_manager.get_active_layer()
+            self.viewarea = self.app.sel_manager.get_active_view()
+            self.shapes = self.layer.get_parent()
+
+            # should skip to except here if layer is a raster
+            shp_schema = self.shapes.get_schema()
+
+        except:
+            # Layer is None or a raster
+            self.layer = None
+            self.shapes = None
+            self.viewarea = None
+            self.pgugrid.set_source(None,expose=1)
+            self.layer_teardown_id=None
+            return
+
+        try:
+            # get display columns
+            key_list,fmt_list=self.get_columns(self.shapes)
+
+            self.pgugrid.set_source(self.layer,self.viewarea)
+            self.pgugrid.define_columns(members=key_list,formats=fmt_list)
+            
+            self.layer_teardown_id = \
+                self.layer.connect('teardown',self.layer_teardown_cb)
+        except:
+            # Layer is None or a raster, or couldn't be loaded
+            self.layer = None
+            self.shapes = None
+            self.viewarea = None
+            self.layer_teardown_id=None
+            self.pgugrid.set_source(None,expose=1)
+            gvutils.warning('Unable to load shapes into grid!')
+            return
+
+    def layer_teardown_cb(self,*args):
+        self.pgugrid.clear()
+        if self.layer_teardown_id is not None:
+            self.layer.disconnect(self.layer_teardown_id)
+            self.layer_teardown_id = None
+        self.viewarea = None
+
+
+    def clicked_test_cb(self,*args):
+        """ Test the grid selection mechanism- debug code """
+        print "clicked..."
+        print "grid row: ",args[1]
+        src=self.pgugrid.grid2src(args[1])
+        print "source row: ",src
+        print "grid row 2: ",self.pgugrid.src2grid(src)
+        print "grid column: ",args[2]
+        print "dir(event): ",dir(args[3])
+
+    def cell_cb(self,*args):
+        """ Test the grid selection mechanism- debug code """
+        print "cell_cb..."
+        print "cell selection: ",args[1]
+            
+    def cellch_cb(self,*args):
+        """ Test the grid selection mechanism- debug code """
+        print "cellch_cb..."
+        print "altered cell: ",args[1]
+            
+    def row_cb(self,*args):
+        """ Test the grid selection mechanism- debug code """
+        print "row_cb..."
+        print "row selection: ",args[1]
+        src=self.pgugrid.src2grid(args[1])
+        print "grid row selection: ",src
+        print "row selection 2: ",self.pgugrid.grid2src(src)
+        
+    def column_cb(self,*args):
+        """ Test the grid selection mechanism- debug code """
+        print "column_cb..."
+        print "column selection: ",args[1]
+            
+
+class SchemaDialog(gtk.GtkWindow):
+    def __init__(self,shapes,shapesgridtool=None):
+        gtk.GtkWindow.__init__(self)
+        self.set_title('Schema')
+        shell=gtk.GtkVBox(spacing=5)
+        shell.set_border_width(10)
+        self.add(shell)
+        self.grid=pgugrid.pguGrid(config=(2,0,0,1,4,0,0,0))
+        self.grid.subscribe("cell-changed",self.changed_field)
+        self.shapes=shapes
+        self.shapesgridtool=shapesgridtool
+        shell.pack_start(self.grid)
+
+        # New field
+        box3 = gtk.GtkTable(rows=5,cols=3)
+        box3.set_row_spacings(5)
+        box3.set_col_spacings(5)
+        box3.set_border_width(10)
+        nf_frame =  gtk.GtkFrame('Add Field')
+        nf_frame.add(box3)
+        self.new_field_name_entry =  gtk.GtkEntry(10)
+        self.new_field_name_entry.set_text('')
+        self.new_field_name_entry.set_editable(gtk.TRUE) 
+        self.new_field_width_entry =  gtk.GtkEntry(2)
+        self.new_field_width_entry.set_text('20')
+        self.new_field_width_entry.set_editable(gtk.TRUE)        
+        self.new_field_precision_entry =  gtk.GtkEntry(2)
+        self.new_field_precision_entry.set_text('0')
+        self.new_field_precision_entry.set_editable(gtk.FALSE)
+        self.new_field_precision_entry.set_sensitive(gtk.FALSE)
+        
+        self.new_field_types = ('string','integer','float')
+        self.new_field_type_menu = gvutils.GvOptionMenu(self.new_field_types, self.new_field_precision_cb)
+        self.new_field_type_menu.set_history(0)
+        box3.attach(gtk.GtkLabel('Name'),0,1,0,1)
+        box3.attach(self.new_field_name_entry,1,2,0,1)
+        box3.attach(gtk.GtkLabel('Type'),0,1,1,2)
+        box3.attach(self.new_field_type_menu,1,2,1,2)
+        box3.attach(gtk.GtkLabel('Width'),0,1,2,3)
+        box3.attach(self.new_field_width_entry,1,2,2,3)
+        box3.attach(gtk.GtkLabel('Precision'),0,1,3,4)
+        box3.attach(self.new_field_precision_entry,1,2,3,4)
+        button = gtk.GtkButton("Add")
+        box3.attach(button,0,2,4,5)
+        button.connect("clicked", self.add_field)
+        
+        shell.pack_start(nf_frame)
+        nf_frame.show_all()
+        
+        # Ability to delete fields?
+        self.fill_grid()
+        self.grid.resize_to_default()
+        self.show_all()
+
+    def fill_grid(self):
+        """ Get the schema and fill the grid """
+        sch=self.shapes.get_schema()
+        schl=[]
+        for item in sch:
+            schl.append(list(item))
+        if len(schl) > 0:    
+            self.grid.set_source(schl,expose=0)
+        self.grid.define_columns(titles=['Name','Type','Width','Precision'],
+                            editables=[0,0,1,1])
+
+    def add_field(self,*args):
+        """ Add field """
+        import string
+        
+        sch=self.shapes.get_schema()
+        name=self.new_field_name_entry.get_text()
+
+        for item in sch:
+            if string.lower(item[0]) == string.lower(name):
+                gvutils.error('Field '+name+' already present!')
+                return
+
+        
+        ftype = self.new_field_types[self.new_field_type_menu.get_history()]
+
+
+        try:
+            fwidth = int(self.new_field_width_entry.get_text())
+        except:
+            gvutils.error('Field width must be an integer!')
+            return
+
+        if ftype == 'float':
+            try:
+                fprec = int(self.new_field_precision_entry.get_text())
+            except:
+                gvutils.error('Precision width must be an integer!')
+                return
+        else:
+            fprec = 0
+
+        self.shapes.add_field(name,ftype,fwidth,fprec)
+        self.fill_grid()
+        if self.shapesgridtool is not None:
+            self.shapesgridtool.refresh_columns()
+            
+    def changed_field(self,*args):
+        """ User changed a field """
+        cell=args[1]
+        if cell[1] == 2:
+            # width updated
+            idx=str(cell[0]+1)
+            self.shapes.set_property('_field_width_'+str(idx),
+                      self.grid.get_cell_data_string(cell[0],cell[1]))
+            if self.shapesgridtool is not None:
+                self.shapesgridtool.refresh_columns()
+          
+        elif cell[1] == 3:
+            # precision updated
+            # width updated
+            idx=str(cell[0]+1)
+            if self.grid.get_cell_data_string(cell[0],1) == 'float':
+                self.shapes.set_property('_field_precision_'+str(idx),
+                     self.grid.get_cell_data_string(cell[0],cell[1]))
+                if self.shapesgridtool is not None:
+                    self.shapesgridtool.refresh_columns()
+                
+            else:
+                if self.grid.get_cell_data_string(cell[0],cell[1]) != '0':
+                    self.grid.set_cell_data_string(cell[0],cell[1],0)
+                    gvutils.error('Precision can only be reset for float.')
+                   
+        else:
+            gvutils.error('Name and type of a field cannot be changed.')
+            
+
+    def new_field_precision_cb(self,*args):
+        if self.new_field_types[self.new_field_type_menu.get_history()] ==\
+               'float':
+            # precision is only relevant for float
+            self.new_field_precision_entry.set_editable(gtk.TRUE)
+            self.new_field_precision_entry.set_sensitive(gtk.TRUE)
+        else:
+            self.new_field_precision_entry.set_text('0')
+            self.new_field_precision_entry.set_editable(gtk.FALSE)
+            self.new_field_precision_entry.set_sensitive(gtk.FALSE)
+            
+        
+
+TOOL_LIST = ["ShapesGridTool"]
+                   
+        

Added: packages/openev/branches/upstream/current/tools/Tool_autopan.py
===================================================================
--- packages/openev/branches/upstream/current/tools/Tool_autopan.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/Tool_autopan.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,586 @@
+#!/usr/bin/env python 
+###############################################################################
+# $Id: Tool_autopan.py,v 1.3 2005/08/01 09:23:11 andrey_kiselev Exp $
+#
+# Project:  OpenEV
+# Purpose:  Test for panning capabilities (an OpenEV tool).
+# Author:   Gillian Walter <gillian.walter at atlantis-scientific.com>
+#
+# Developed by Atlantis Scientific Inc. (www.atlantis-scientific.com) for
+# DRDC Ottawa
+#
+###############################################################################
+# Copyright (c) Her majesty the Queen in right of Canada as represented
+# by the Minister of National Defence, 2003.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+#
+#  $Log: Tool_autopan.py,v $
+#  Revision 1.3  2005/08/01 09:23:11  andrey_kiselev
+#  Return empty string if LD_LIBRARY_PATH/PATH environment variables are not set.
+#
+#  Revision 1.2  2005/02/22 13:22:39  gmwalter
+#  Add autopan tool.
+#
+#  Revision 1.1.2.1  2005/02/18 01:27:23  gmwalter
+#  Added autopan test tool.
+#
+#  Revision 1.9  2005/01/31 21:17:55  gwalter
+#  Update autopan, fix a few gui issues.
+#
+#  Revision 1.8  2005/01/29 04:14:44  gwalter
+#  Add trail test.
+#
+#  Revision 1.7  2005/01/20 20:11:42  gwalter
+#  Update autopan to add path type option,
+#  add secondary view to test tool.
+#
+#  Revision 1.6  2005/01/17 21:24:13  gwalter
+#  Avoid problems when window is shut and
+#  then opened again.
+#
+#  Revision 1.5  2005/01/17 16:36:01  gwalter
+#  Updated pan test tool.
+#
+#  Revision 1.4  2005/01/14 15:51:18  gwalter
+#  Help and autopan updates.
+#
+#  Revision 1.3  2004/11/25 21:17:17  gwalter
+#  Updated.
+#
+
+import gviewapp
+import Numeric
+import gtk
+import time
+import gview
+import gvutils
+import os
+import string
+import gdal
+import vrtutils
+import pgucolorsel
+
+class PyAutopanTool(gviewapp.Tool_GViewApp):
+    def __init__(self, app=None, startpath=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+
+        self.init_menu()
+        self.view=None
+        self.playing=0
+        self.id=None
+        self.speed = 0.01
+
+        gtk.quit_add(0,self.quit_cb)
+        
+    def quit_cb(self,*args):
+        self.playing = 0
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/PyAutopan",1,
+                                    self.startpan_cb)
+
+    def startpan_cb(self, *args):
+        if self.playing == 1:
+            print 'already panning- left click to stop'
+            return
+
+        for view in self.app.view_manager.view_list:
+            if view.title == args[1]:
+                self.app.view_manager.set_active_view(view)
+                self.view = view.viewarea
+                self.viewwin = view
+
+        xmin,ymin,xmax,ymax = self.view.get_extents()
+        xwidth = xmax-xmin
+        ywidth = ymax-ymin
+        self.id=self.viewwin.connect('button-release-event',self.stoppan_cb)
+        self.playing = 1
+
+        self.xmin,self.ymin,self.xwidth,self.ywidth=xmin,ymin,xwidth,ywidth
+
+        self.set_speed(0.01)       
+        self.view.fit_extents(self.centers[0][0],self.centers[0][1],
+                              xwidth/8,ywidth/8)
+
+        while self.playing == 1:
+            self.view.set_translation(self.centers[self.idx][0],
+                                      self.centers[self.idx][1])
+            self.idx = self.idx+1
+            if self.idx > len(self.centers)-1:
+                self.idx = 0
+
+            while gtk.events_pending():
+                gtk.mainiteration()
+
+    def set_speed(self,speed):
+        self.speed = speed
+        if self.speed > 0:
+            self.speed = min(1.0,max(self.speed,0.000001))
+        else:
+            self.speed = max(-1.0,min(self.speed,-0.000001))
+
+        self.centers=[]
+        xmin,ymin,xwidth,ywidth=self.xmin,self.ymin,self.xwidth,self.ywidth
+        
+        for dy in Numeric.arange(ymin,ymin+ywidth,ywidth/10):
+            for dx in Numeric.arange(xmin,xmin+xwidth,xwidth*speed/8):
+                self.centers.append((-dx,-dy))
+        if self.speed < 0:
+            self.centers.reverse()
+        self.idx = 0
+        
+    def stoppan_cb(self, *args):
+        if self.playing == 0:
+            return
+
+        event=args[1]
+        if event.button == 1:
+        
+            if self.id is not None:
+                self.viewwin.disconnect(self.id)
+                self.id = None
+
+            self.playing = 0
+            self.view = None
+            self.viewwin = None
+            self.id = None
+
+        elif event.button == 2:
+            self.speed = self.speed*1.25
+            self.set_speed(self.speed)
+        elif event.button == 3:
+            self.speed = self.speed/1.25
+            self.set_speed(self.speed)
+
+
+class AutopanTool(gviewapp.Tool_GViewApp):
+    def __init__(self, app=None, startpath=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+
+        self.init_menu()
+        self.view=None
+        self.second_view = None
+        
+        self.playing=0
+        self.id=None
+        self.tool = gview.GvAutopanTool()
+        if os.name != 'nt':
+            lp = os.environ.get('LD_LIBRARY_PATH', '')
+        else:
+            lp = os.environ.get('PATH', '')
+
+        if string.find(lp,'Mesa') == -1:
+            self.speed = 0.001
+            self.default_speed = 0.001
+        else:
+            self.speed = 0.01
+            self.default_speed = 0.01
+            
+        self.active = 0
+        self.dialog = None
+        self.ext = None
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Autopan",1,
+                                    self.showgui_cb)
+
+    def showgui_cb(self, *args):
+        if self.dialog is None:
+            self.dialog = gtk.GtkWindow()
+            self.dialog.connect('delete-event',self.close)
+            table = gtk.GtkTable(9,4)
+            table.set_border_width(10)
+            table.set_row_spacings(10)
+            table.set_col_spacings(10)
+            vbox = gtk.GtkVBox()
+            self.dialog.add(vbox)
+            vbox.pack_start(table)
+
+            iconbar = gtk.GtkToolbar(gtk.ORIENTATION_HORIZONTAL,
+                                      gtk.TOOLBAR_ICONS)
+            
+            arrows = []
+            atypes = [gtk.ARROW_LEFT,gtk.ARROW_UP,
+                      gtk.ARROW_DOWN,gtk.ARROW_RIGHT]
+
+            cbs = [self.rewind_cb, self.speed_up_cb, self.speed_down_cb,
+                   self.play_cb]
+            for atype,cb in map(None,atypes,cbs):
+                arr = gtk.GtkButton()
+                arr.set_border_width(0)
+                arr.set_relief(gtk.RELIEF_NONE)
+                arr.add(gtk.GtkArrow(atype))
+                arr.connect('clicked',cb)
+                arrows.append(arr)
+        
+            vbox = gtk.GtkVBox(spacing=0, homogeneous=gtk.FALSE)
+            vbox.pack_start(arrows[1], expand=gtk.FALSE)
+            vbox.pack_start(arrows[2], expand=gtk.FALSE)
+        
+            # Now put them in the toolbar
+            iconbar.append_widget(arrows[0], 'Rewind', 'Rewind')
+            iconbar.append_widget(vbox, 'Adjust speed', 'Adjust speed')
+            iconbar.append_widget(arrows[3], 'Play', 'Play')
+
+            table.attach(iconbar,0,1,0,1)
+            but = gtk.GtkButton('Stop')
+            table.attach(but,1,2,0,1)
+            but.connect('clicked',self.stop_pan_cb)
+            
+            but = gtk.GtkButton('Pause')
+            table.attach(but,2,3,0,1)
+            but.connect('clicked',self.pause_pan_cb)
+
+            label = gtk.GtkLabel('Block Size Mode:')
+            label.set_alignment(0,0.5)
+            table.attach(label,0,1,1,2)
+            self.block_size_menu = gvutils.GvOptionMenu(
+         ('Relative to Pan Extents','View Coordinates','Constant Resolution'),
+                self.block_mode_changed)
+            table.attach(self.block_size_menu,1,3,1,2)
+            
+            self.block_label_list=['Block x size (0-1):',
+                                  'Block x size:',
+                                  'View units/Pixel:']
+            self.block_label = gtk.GtkLabel(self.block_label_list[0])
+            self.block_label.set_alignment(0,0.5)
+            table.attach(self.block_label,0,1,2,3)
+            self.block_entry = gtk.GtkEntry()
+            self.block_entry.set_editable(gtk.TRUE)
+            self.block_entry.set_text('0.125')
+            self.block_entry.connect('leave-notify-event',
+                                     self.block_size_changed)
+            table.attach(self.block_entry,1,3,2,3)
+
+            label = gtk.GtkLabel('Overlap (0-1):')
+            label.set_alignment(0,0.5)
+            table.attach(label,0,1,3,4)
+            self.overlap_entry = gtk.GtkEntry()
+            self.overlap_entry.set_editable(gtk.TRUE)
+            self.overlap_entry.set_text('0.1')
+            self.overlap_entry.connect('leave-notify-event',
+                                     self.overlap_changed)
+            table.attach(self.overlap_entry,1,3,3,4)
+
+            label = gtk.GtkLabel('Path type:')
+            label.set_alignment(0,0.5)
+            table.attach(label,0,1,4,5)
+            self.path_menu = gvutils.GvOptionMenu(
+                           ('0','1','2','3','4'),
+                self.path_changed)
+            table.attach(self.path_menu,1,3,4,5)
+
+            label = gtk.GtkLabel('Show trail:')
+            label.set_alignment(0,0.5)
+            table.attach(label,0,1,5,6)
+            self.trail_menu = gvutils.GvOptionMenu(
+                           ('No','Yes'),
+                self.trail_changed)
+            table.attach(self.trail_menu,1,2,5,6)
+            button = gtk.GtkButton('Clear Trail')
+            table.attach(button,2,3,5,6)
+            button.connect('clicked', self.clear_trail)
+
+            label = gtk.GtkLabel('Trail Color:')
+            label.set_alignment(0,0.5)
+            table.attach(label,0,1,6,7)
+
+            self.trail_color = pgucolorsel.ColorControl('Trail Color',
+                                                  self.trail_color_cb)
+            table.attach(self.trail_color,1,3,6,7)
+            self.trail_color.set_color((1.0,0.75,0.0,0.5))
+            self.second_view = gview.GvViewArea()
+            self.second_view.set_usize(300,300)
+            
+            table.attach(self.second_view,0,3,7,8)
+            
+            self.dialog.show_all()
+            self.dialog.connect('delete-event',self.close)
+
+        self.viewtitle = args[1]
+        self.dialog.show()
+        self.dialog.get_window()._raise()
+
+    def close(self,*args):
+        if self.view is not None:
+            self.tool.deactivate(self.view)
+        self.playing = 0
+        self.active = 0
+        self.view = None
+        self.dialog.hide()
+        return gtk.TRUE
+
+    def path_changed(self, *args):
+        path_type = int(self.path_menu.get_history())
+        self.tool.set_standard_path(path_type)
+
+    def block_mode_changed(self,*args):
+        mode = self.block_size_menu.get_history()
+        self.block_label.set_text(self.block_label_list[mode])
+        self.block_size_changed()
+
+    def trail_changed(self, *args):
+        if self.view is None:
+            return
+
+        self.tool.set_trail_mode(self.second_view,
+                                 self.trail_menu.get_history())
+
+    def trail_color_cb(self,*args):
+        if self.view is not None:
+            self.tool.set_trail_color(self.second_view,
+                                  self.trail_color.current_color[0],
+                                  self.trail_color.current_color[1],
+                                  self.trail_color.current_color[2],
+                                  self.trail_color.current_color[3])
+
+
+    def clear_trail(self, *args):
+        self.tool.clear_trail()
+
+    def block_size_changed(self, *args):
+        if self.active == 0 or self.playing == 0:
+            return
+        mode = self.block_size_menu.get_history()
+        defaults = [0.1, 0.1*self.ext[2], 0.1*self.ext[2]]
+        try:
+            xorres = abs(float(self.block_entry.get_text()))
+        except:
+            if mode == 0:
+                msg = 'Relative block size must be a number, 0-1!'
+            elif mode == 1:
+                msg = 'Block size must be a number!'
+            elif mode == 2:
+                msg = 'Resolution must be a number!'
+            gvutils.error(msg)
+            xorres = defaults[mode]
+            self.block_size_entry.set_text(str(xorres))
+
+        if (mode == 0) and ((xorres < 0) or (xorres > 1)):
+            msg = 'Relative block size must be a number, 0-1!'
+            gvutils.error(msg)
+            xorres = defaults[mode]
+            self.block_size_entry.set_text(str(xorres))
+
+        if mode < 2:
+            self.tool.set_block_x_size(xorres, mode)
+        else:
+            self.tool.set_x_resolution(xorres)
+
+    def overlap_changed(self,*args):
+        if self.active == 0:
+            return
+        txt = self.overlap_entry.get_text()
+        try:
+            val = float(txt)
+        except:
+            gvutils.error('Overlap must be a number, 0 <= overlap < 1!')
+            val = 0.1
+            self.overlap_entry.set_text('0.1')
+
+        if (val < 0) or (val >= 1):
+            gvutils.error('Overlap must be a number, 0 <= overlap < 1!')
+            val = 0.1
+            self.overlap_entry.set_text('0.1')
+
+        self.tool.set_overlap(val)
+        
+
+    def pause_pan_cb(self, *args):
+        self.playing = 2
+        self.tool.pause()
+        
+    def stop_pan_cb(self, *args):
+        self.tool.pause()
+        self.playing = 0
+        if self.ext is not None:
+            if self.view is not None:    
+                self.view.fit_extents(self.ext[0],self.ext[1],
+                                  self.ext[2],self.ext[3])
+                self.tool.deactivate(self.view) 
+            self.ext = None   
+        self.view = None
+
+    def speed_up_cb(self, *args):
+        if self.active == 0:
+            return
+        self.speed *= 1.25
+        self.tool.set_speed(self.speed)
+
+    def speed_down_cb(self, *args):
+        if self.active == 0:
+            return
+        self.speed /= 1.25
+        self.tool.set_speed(self.speed)
+    
+    def rewind_cb(self, *args):
+        self.speed = -1*self.default_speed
+        if self.playing == 1:
+            self.tool.set_speed(self.speed)
+            return
+
+        if self.playing == 2:
+            self.tool.set_speed(self.speed)
+            self.tool.play()
+            return
+        
+        self.setup_playing()
+        
+        
+    def play_cb(self, *args):
+        self.speed = self.default_speed
+        if self.playing == 1:
+            self.tool.set_speed(self.speed)
+            return
+
+        if self.playing == 2:
+            self.tool.set_speed(self.speed)
+            self.tool.play()
+            return
+        
+        self.setup_playing()
+
+    def setup_playing(self, *args):
+        if self.view is not None:
+            self.tool.deactivate(self.view)
+            
+        for view in self.app.view_manager.view_list:
+            if view.title == self.viewtitle:
+                self.app.view_manager.set_active_view(view)
+                self.view = view.viewarea
+                self.tool.activate(self.view)
+                self.tool.register_view(self.second_view,0,1,
+                                        self.trail_menu.get_history())
+                self.tool.set_trail_color(self.second_view,
+                                  self.trail_color.current_color[0],
+                                  self.trail_color.current_color[1],
+                                  self.trail_color.current_color[2],
+                                  self.trail_color.current_color[3])
+
+
+        # update the secondary view with a background raster
+        plyr = self.view.active_layer()
+        rst = None
+        ds = None
+        try:
+            rst = plyr.get_parent()
+            ds = rst.get_dataset()
+        except:
+            llist = self.view.list_layers()
+            llist.reverse()
+            for plyr in llist:
+                try:
+                    rst = plyr.get_parent()
+                    ds = rst.get_dataset()
+                    break
+                except:
+                    pass
+
+        if ds is None:
+            gvutils.error('Error- no raster to pan over in active view!')
+            return
+
+        dtype = gdal.GetDataTypeName(ds.GetRasterBand(1).DataType)
+        
+        xsize = ds.RasterXSize
+        ysize = ds.RasterYSize
+        while (xsize > 512) and (ysize > 512):
+            xsize = xsize/2
+            ysize = ysize/2
+            
+        srcrect = [0,0,ds.RasterXSize, ds.RasterYSize]
+        dstrect = [0,0,xsize,ysize]
+
+        vrt = vrtutils.VRTDatasetConstructor(xsize,ysize)
+        
+        fname = ds.GetDescription()
+        
+        lyrs = self.second_view.list_layers()
+        for lyr in lyrs:
+            self.second_view.remove_layer(lyr)
+
+        gcps = ds.GetGCPs()
+        if len(gcps) > 0:
+            vrt.SetGCPs(gcps,ds.GetGCPProjection(),None,
+                        srcrect,dstrect)
+        else:
+            gt = ds.GetGeoTransform()
+            if gt != (0.0,1.0,0.0,0.0,0.0,1.0):
+                vrt.SetSRS(ds.GetProjection())
+            vrt.SetGeoTransform(gt,srcrect,dstrect,1)
+                
+        for idx in range(0,plyr.sources):
+            rst = plyr.get_data(idx)
+            if rst is not None:
+                cmin = plyr.min_get(idx)
+                cmax = plyr.max_get(idx)
+                srcdiff=cmax-cmin
+                if abs(srcdiff) > 0.0:
+                    ratio = 255/srcdiff
+                else:
+                    ratio = 1.0
+                offset = -cmin*ratio
+                
+                vrt.AddSimpleBand(fname,rst.get_band_number(),dtype,
+                                  srcrect,dstrect,ScaleOffset=offset,
+                                  ScaleRatio = ratio)
+                        
+        ds2 = gview.manager.get_dataset(vrt.GetVRTString())
+        raster = gview.manager.get_dataset_raster( ds2, 1 )
+        options = []
+        if ( ds2.RasterCount == 1 ):
+            raster_layer = gview.GvRasterLayer( raster, options,
+                                       rl_mode = gview.RLM_AUTO ) 
+        else:
+            raster_layer = gview.GvRasterLayer( raster, options,
+                                       rl_mode = gview.RLM_RGBA )
+
+            if ( raster_layer.get_mode() == gview.RLM_RGBA ):
+
+                green_raster = gview.manager.get_dataset_raster( ds2, 2)
+                raster_layer.set_source( 1, green_raster ) 
+                if ( ds2.RasterCount > 2 ):
+                    blue_raster = \
+                              gview.manager.get_dataset_raster( ds2, 3 )
+                    raster_layer.set_source( 2, blue_raster )
+
+                if ( ds2.RasterCount > 3 ):
+                    band = ds2.GetRasterBand(4)
+                    if ( band.GetRasterColorInterpretation() ==
+                         gdal.GCI_AlphaBand ):
+                        raster_layer.blend_mode_set(
+                                                gview.RL_BLEND_FILTER )
+                        alpha_raster = \
+                              gview.manager.get_dataset_raster( ds2, 4 )
+                        raster_layer.set_source( 3, alpha_raster )
+                        
+        self.second_view.add_layer(raster_layer)
+
+        self.active = 1
+        xmin,ymin,xmax,ymax = self.view.get_extents()
+        xwidth = xmax-xmin
+        ywidth = ymax-ymin
+        self.ext = (xmin,ymin,xwidth,ywidth)
+        self.tool.set_extents((xmin,ymin,xwidth,ywidth))
+        self.playing = 1
+        self.tool.set_speed(self.speed)
+        self.block_size_changed()
+        self.overlap_changed()
+        self.tool.play()        
+
+TOOL_LIST = ['AutopanTool']

Added: packages/openev/branches/upstream/current/tools/calculator.py
===================================================================
--- packages/openev/branches/upstream/current/tools/calculator.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/calculator.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1061 @@
+##############################################################################
+# $Id: calculator.py,v 1.4 2004/10/23 11:04:42 andrey_kiselev Exp $
+#
+# Project:  OpenEV
+# Purpose:  Interactive tool to perform calculations on pair of images.
+# Authors:  Iscander Latypov
+#	    Andrey Kiselev, dron at remotesensing.org
+#
+###############################################################################
+# Copyright (c) 2004, American Museum of Natural History. All rights reserved.
+# This software is based upon work supported by NASA under award
+# number NAG5-12333
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: calculator.py,v $
+#  Revision 1.4  2004/10/23 11:04:42  andrey_kiselev
+#  Can work with the layers, opened via 'Open Subarea' dialog.
+#
+#  Revision 1.3  2004/07/04 09:50:41  andrey_kiselev
+#  More checks for valid raster layer.
+#
+#  Revision 1.2  2004/04/21 13:25:38  andrey_kiselev
+#  Added differently styled RCalculatorDialog.
+#
+#  Revision 1.1  2004/02/25 21:08:56  andrey_kiselev
+#  New.
+#
+#
+
+from gtk import *
+
+import Numeric
+from Numeric import cos,log,exp,sin,sqrt,maximum,minimum,tan,arcsin,arccos
+from Numeric import arctan,bitwise_and,bitwise_or,bitwise_xor,invert,hypot
+from Numeric import left_shift,right_shift
+
+import gview
+import gvutils
+import gviewapp
+import gdal
+from gdalconst import *
+import gdalnumeric
+
+
+"""Image Calculator is the tool, that operate with the pair of images.
+   The source images are the bands of opened layers, the result
+   is new layer with only band, that contains image computed from
+   sources by given formula. The coefficients for calculation
+   defined by user in dialog box."""
+   
+########################################################################
+def get_raster_size(_layer):
+    w =  _layer.get_parent().get_dataset().GetRasterBand(1).XSize
+    h =  _layer.get_parent().get_dataset().GetRasterBand(1).YSize
+    return (w,h)
+
+########################################################################
+def get_list_of_layers_as_dict():
+    """Returns dictionary of opened layers. The key of dictionary is
+    the name of layer, members of dictionary are view object (index = 0)
+    and layer object (index = 1)"""
+
+    dict = {}
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    curname = curlayer.get_name()
+	    dict[curname] = (curview,curlayer) 
+    if dict is None:
+	return None
+    return dict
+
+########################################################################
+def get_list_of_layers_as_menu():
+    """Returns GtkMenu object for selecting of opened layers. 
+    If number of opened layers is zero, the result is None"""
+    menu = GtkMenu()
+    group = None
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    curname = curlayer.get_name()
+	    menuitem = GtkRadioMenuItem(group,curname)
+	    group = menuitem
+	    menu.append(menuitem)
+	    menuitem.show()
+    if group is None:
+	return None
+    return menu
+	    
+#########################################################################
+def layer_is_raster(layer):
+    """returns TRUE if layer is raster and FALSE if else"""
+    try:
+        layer.get_nodata(0)
+        return TRUE
+    except:
+        return FALSE
+
+#########################################################################
+def get_filename_from_vrtdataset(vrt_dataset_name):
+    if vrt_dataset_name[:11] != "<VRTDataset": return vrt_dataset_name
+    ss = vrt_dataset_name.find("<SourceFilename relativeToVRT")
+    ss = vrt_dataset_name.find(">",ss)
+    se = vrt_dataset_name.find("</SourceFilename",ss)
+    return "SubArea_"+vrt_dataset_name[ss+1:se]
+		    
+
+########################################################################
+def get_list_of_bands_as_dict():
+    """Returns dictionary of the bands of the opened layers. The key 
+    of dictionary is the name of layer+band number, members of dictionary 
+    are view-object (index = 0), layer-object (index = 1) and band-number"""
+
+    active_layer = gview.app.sel_manager.get_active_layer()
+    if not layer_is_raster(active_layer):
+    	return None
+    _size = get_raster_size(active_layer)
+    dict = {}
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    if not layer_is_raster(curlayer):
+    	        continue   
+    	    curname = curlayer.get_name()
+	    curname = get_filename_from_vrtdataset(curname)
+	    cursize = get_raster_size(curlayer)
+	    if cursize == _size:
+		num_bands = curlayer.get_parent().get_dataset().RasterCount
+		for i in range(num_bands):
+	    	    curband = curname + '.band['+ str(i) + ']'
+		    dict[curband] = (curview,curlayer,i) 
+    return dict
+
+#########################################################################
+def clip_result(numtype, array):
+	if numtype is Numeric.UnsignedInt8:
+	    return Numeric.clip(array, 0.0, 255.0)
+	elif numtype is Numeric.Int16:
+	    return Numeric.clip(array, -32768.0, 32767.0)
+	elif numtype is Numeric.Int32:
+	    return Numeric.clip(array, -2147483648.0, 2147483647.0)
+	else:
+	    return array
+
+############################################################################
+#
+#
+############################################################################
+class CalculatorTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+	try:
+	    self.win = CalculatorDialog()
+	except:
+	    gvutils.error("Please select a raster layer.");
+	    return
+
+	self.win.update_gui()
+	self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Image/Calculator...",2,
+                                    self.launch_dialog)
+
+############################################################################
+class CalculatorDialog(GtkWindow):
+
+    def __init__(self,app=None):
+	rlayer = gview.app.sel_manager.get_active_layer()
+	if rlayer is None:
+            raise TypeError
+	try:
+	    gdal_dataset = rlayer.get_data().get_dataset()
+	except:
+	    raise TypeError
+	    
+	GtkWindow.__init__(self)
+	self.set_title('Image Calculator')
+	self.set_policy(FALSE, TRUE, TRUE)
+	
+	try:
+	    self.create_gui()
+	except:
+	    raise TypeError
+	
+	GtkWindow.show_all(self)
+	
+    def close(self,*args):
+	self.destroy()
+	return TRUE
+
+    def create_gui(self):
+	self.dict_of_bands = get_list_of_bands_as_dict()
+	if self.dict_of_bands is None:
+	    raise
+	self.list_of_bands = self.dict_of_bands.keys()
+	
+	title_width = 120
+
+	box1 = GtkVBox(spacing=5)     	
+        self.add(box1)
+        box1.show()
+
+#### OPERATIONS ####################################################
+	box_op = GtkHBox(spacing=10)
+	box1.set_border_width(10)
+	box1.pack_start(box_op, expand=FALSE)
+	box_op.show()
+	
+	op_label  = GtkLabel('Operation:')
+	op_label.set_alignment(0, 0.5)
+	box_op.pack_start(op_label, expand=FALSE)
+
+#	The item <operations_dict> is the dictionary, that contains 
+#	the list of calculator operation names as keys and the set 
+#	of corresponding functions.
+	self.operations_dict = {}
+	self.operations_dict['Add  [Res = A * X + B * Y + C]'] = \
+	    (3,self.add_bands)
+	self.operations_dict['Multiply  [Res = X * Y]'] = \
+	    (0,self.multiply_bands)
+	self.operations_dict['Divide  [Res = A * X / (Y + B) + C]'] = \
+	    (3,self.divide_bands)
+	self.operations_dict['Vegetation Index  [Res = A * (X - Y) / ( X + Y) + B]'] = (2,self.veg_index)
+	
+	self.operations_list = self.operations_dict.keys()
+	
+	self.operation = \
+	    gvutils.GvOptionMenu(self.operations_list,self.update_gui)
+	box_op.pack_start(self.operation)
+	self.operation.show()
+
+### COEFFICIENTS ######################################################
+	self.box_coeffs = GtkHBox(spacing = 10)
+        box1.pack_start(self.box_coeffs, expand=FALSE)
+        self.box_coeffs.show()
+
+	self.box_coeff_a = GtkHBox(spacing = 5)
+        self.box_coeffs.pack_start(self.box_coeff_a, expand=FALSE)
+        self.box_coeff_a.show()
+
+	a_label = GtkLabel('A =')
+	a_label.set_alignment(0, 0.5)
+        self.box_coeff_a.pack_start(a_label)
+	
+	self.a_const = GtkEntry()
+	self.a_const.set_usize(80,30)
+	self.a_const.set_text('1')
+        self.box_coeff_a.pack_start(self.a_const, expand=FALSE)
+        self.a_const.show()
+	
+	self.box_coeff_b = GtkHBox(spacing = 5)
+        self.box_coeffs.pack_start(self.box_coeff_b, expand=FALSE)
+        self.box_coeff_b.show()
+
+	b_label = GtkLabel('B =')
+	b_label.set_alignment(0, 0.5)
+        self.box_coeff_b.pack_start(b_label)
+	
+	self.b_const = GtkEntry()
+	self.b_const.set_usize(80,30)
+	self.b_const.set_text('1')
+        self.box_coeff_b.pack_start(self.b_const, expand=FALSE)
+        self.b_const.show()
+	
+	self.box_coeff_c = GtkHBox(spacing = 5)
+        self.box_coeffs.pack_start(self.box_coeff_c, expand=FALSE)
+        self.box_coeff_c.show()
+
+	c_label = GtkLabel('C =')
+	c_label.set_alignment(0, 0.5)
+        self.box_coeff_c.pack_start(c_label)
+	
+	self.c_const = GtkEntry()
+	self.c_const.set_usize(80,30)
+	self.c_const.set_text('1')
+        self.box_coeff_c.pack_start(self.c_const, expand=FALSE)
+        self.c_const.show()
+
+	self.coeffs_vis = [self.box_coeff_a,self.box_coeff_b,self.box_coeff_c]
+
+### source1 #############################################################	
+	frame1 = GtkFrame("Select Image Bands To Compute")
+	frame1.show()
+        box1.pack_start(frame1, expand=FALSE)
+	
+	box2 = GtkVBox(spacing=10)
+        box2.set_border_width(10)
+	frame1.add(box2)
+	box2.show()
+	
+	
+	box_s1 = GtkHBox(spacing=10)
+        box2.pack_start(box_s1, expand=FALSE)
+	box_s1.show()
+
+	source1_label = GtkLabel('Source 1  < X >:')
+	source1_label.set_alignment(0, 0.5)
+	box_s1.pack_start(source1_label,expand=FALSE)
+	
+	self.s1_list = gvutils.GvOptionMenu(self.list_of_bands)
+	box_s1.pack_start(self.s1_list)
+	self.s1_list.show()
+	
+##  source2 ##############################################################
+        box_s2 = GtkHBox(spacing=10)
+        box2.pack_start(box_s2, expand=FALSE)
+        box_s2.show()
+
+	source2_label = GtkLabel('Source 2  < Y >:')
+	source2_label.set_alignment(0, 0.5)
+	box_s2.pack_start(source2_label,expand=FALSE)
+	
+	self.s2_list = gvutils.GvOptionMenu(self.list_of_bands)
+	box_s2.pack_start(self.s2_list)
+	self.s2_list.show()
+	
+#####OUT TYPES#########################################################
+	box_types = GtkHBox(spacing=10)
+	box2.pack_start(box_types, expand=FALSE)
+	box_types.show()
+
+	types_label  = GtkLabel('Image data type:')
+	types_label.set_alignment(0, 0.5)
+	box_types.pack_start(types_label, expand=FALSE)
+	
+	self.types_list = []
+	i = GDT_Byte
+	while i < GDT_TypeCount:
+	    self.types_list.append(gdal.GetDataTypeName(i))
+	    i += 1
+
+	self.types = gvutils.GvOptionMenu(self.types_list)
+	box_types.pack_start(self.types)
+	self.types.show()
+	
+	
+#### NEW VIEW ##########################################################
+	self.switch_new_view = GtkCheckButton("Create New View")
+	box1.pack_start(self.switch_new_view)
+	self.switch_new_view.show()
+	
+#### BUTTONS ###########################################################
+        box_buttons = GtkHBox(spacing=15)
+        box1.pack_start(box_buttons, expand=FALSE)
+        box_buttons.show()
+
+        self.ok_btn = GtkButton("Ok")
+        self.ok_btn.connect("clicked", self.compute)
+        box_buttons.pack_start(self.ok_btn)
+	
+        self.cancel_btn = GtkButton("Cancel")
+        self.cancel_btn.connect("clicked", self.close)
+        box_buttons.pack_start(self.cancel_btn)
+	
+        return TRUE
+
+#########################################################################
+#########################################################################	
+    def update_gui(self,*args):
+	i = self.operation.get_history()
+	self.op = self.operations_list[i]
+	k = self.operations_dict[self.op][0]
+	if k == 0:
+	    self.box_coeffs.hide()
+	else:
+	    self.box_coeffs.show()
+	    for j in range(3):
+		if j < k:
+		    self.coeffs_vis[j].show() 
+		else:
+		    self.coeffs_vis[j].hide()
+#########################################################################	
+    def compute(self,*args):
+        def get_band(name):
+	    layer = self.dict_of_bands[name][1]
+	    b_num = self.dict_of_bands[name][2]
+	    band = layer.get_parent().get_dataset().GetRasterBand(b_num+1)
+	    return band	    
+
+	def create_new_layer(pview,player,w,h):
+	    """Creates new raster layer like <player> with width = w
+	    and height = h"""
+        
+	    gview.app.view_manager.set_active_view(pview)
+	    pview.viewarea.set_active_layer(player)	
+    	    target_ds = player.get_parent().get_dataset()
+	    rl_mode_value = player.get_mode()
+    	    new_layer = gview.GvRasterLayer( \
+		gview.GvRaster(dataset = target_ds,real=1), \
+		rl_mode = rl_mode_value)
+	    pview.viewarea.list_layers().append(new_layer)
+	    return new_layer
+	
+	
+	#  extract computing parameters
+	b1_name = self.list_of_bands[self.s1_list.get_history()]
+	b2_name = self.list_of_bands[self.s2_list.get_history()]
+	if b1_name <> None:
+	    if self.switch_new_view.get_active():
+		gview.app.new_view()
+	    op_index = self.operation.get_history()
+	    type_index = self.types.get_history()
+	
+	    a = float(self.a_const.get_text())
+	    b = float(self.b_const.get_text())
+	    c = float(self.c_const.get_text())
+	    b1 = get_band(b1_name)
+	    b2 = get_band(b2_name)
+
+	    type = \
+		gdal.GetDataTypeByName(self.types_list[self.types.get_history()])
+	    self.numtype = gdalnumeric.GDALTypeCodeToNumericTypeCode(type)
+	    if self.numtype == None:
+		gvutils.error("Error! Type " + self.numtype + " is not supported!")
+		return FALSE 
+	    
+	    proto_ds = self.dict_of_bands[b1_name][1].get_parent().get_dataset()
+	    op_func = self.operations_dict[self.op][1]
+	    self.out_buf = Numeric.zeros((b1.YSize, b1.XSize), self.numtype)
+	    try:
+		op_func(s1=b1,s2=b2,a=a,b=b,c=c)
+	    except:
+		gvutils.error("Try to change coefficients.")
+		return FALSE
+	    res_ds = gdalnumeric.OpenArray(self.out_buf,proto_ds)
+	    gview.app.open_gdal_dataset(res_ds)
+	    self.close()
+	else:
+	    gvutils.error("Source1 and Source2 have to differ!")
+	    return FALSE
+
+#########################################################################
+    def add_bands(self,s1,s2,a,b,c):
+	"""<add_bands> method computes the linear combination of the bands
+	   Arguments:
+		s1 - first source band;
+		s2 - second source band;
+		a,b,c - linear combination coefficients;
+	   The output raster pixel density is calculated by formula
+	        res = a * X + b * Y + c
+	   where  X and Y are densities of first and second source bands
+	"""
+	for i in range(s1.YSize):
+	    s1_buf = gdalnumeric.BandReadAsArray(s1,0,i,s1.XSize,1)[0]
+	    s2_buf = gdalnumeric.BandReadAsArray(s2,0,i,s1.XSize,1)[0]
+	    temp_buf = a * s1_buf + b * s2_buf + c
+	    self.out_buf[i, 0:] = \
+		clip_result(self.numtype, temp_buf).astype(self.numtype)
+	    
+#########################################################################
+    def multiply_bands(self,s1,s2,a,b,c):
+	"""Method <multiply_bands> computes the production of the bands
+	   Arguments:
+		s1 - first source band;
+		s2 - second source band;
+		a,b,c - unusable;
+	   The output raster pixel density is calculated by formula
+	        res =  X * Y
+	   where  X and Y are densities of first and second source bands.
+	"""
+	for i in range(s1.YSize):
+	    s1_buf = gdalnumeric.BandReadAsArray(s1,0,i,s1.XSize,1)[0]
+	    s2_buf = gdalnumeric.BandReadAsArray(s2,0,i,s1.XSize,1)[0]
+	    temp_buf = 1. * s1_buf * s2_buf
+	    self.out_buf[i, 0:] = \
+		clip_result(self.numtype, temp_buf).astype(self.numtype)
+
+#########################################################################
+    def divide_bands(self,s1,s2,a,b,c):
+	"""<divide_bands> method executes the division of the bands
+	   Arguments:
+		s1 - first source band;
+		s2 - second source band;
+		a,b,c - coefficients;
+	   The output raster pixel density is calculated by formula
+	        res = a * X / (b + Y) + c
+	   where  X and Y are densities of first and second source bands
+	"""
+	for i in range(s1.YSize):
+	    s1_buf = gdalnumeric.BandReadAsArray(s1,0,i,s1.XSize,1)[0]
+	    s2_buf = gdalnumeric.BandReadAsArray(s2,0,i,s1.XSize,1)[0]
+	    try:
+		temp_buf = a * s1_buf / ( b + s2_buf) + c
+	    except:
+		raise
+	    self.out_buf[i, 0:] = \
+		clip_result(self.numtype, temp_buf).astype(self.numtype)
+	    
+#########################################################################
+    def veg_index(self,s1,s2,a,b,c):
+	"""Method <veg_index> computes the vegetation index for pair of the bands
+	   Arguments:
+		s1 - first source band;
+		s2 - second source band;
+		a,b - coefficients;
+		c - unusable;
+	   The output raster pixel density is calculated by formula
+	        res = a * (X - Y) / (X + Y) + b
+	   where  X and Y are densities of first and second source bands.
+	"""
+	for i in range(s1.YSize):
+	    s1_buf = gdalnumeric.BandReadAsArray(s1,0,i,s1.XSize,1)[0]
+	    s2_buf = gdalnumeric.BandReadAsArray(s2,0,i,s1.XSize,1)[0]
+	    temp_buf1 = s1_buf + s2_buf + 1
+	    temp_buf = a * (s1_buf-s2_buf) / temp_buf1 + b
+	    self.out_buf[i, 0:] = \
+		clip_result(self.numtype, temp_buf).astype(self.numtype)
+	
+############################################################################
+#
+#
+############################################################################
+class RCalculatorTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+	try:
+	    self.win = RCalculatorDialog()
+	except:
+	    gvutils.error("Please select a raster layer.")
+	    return
+    
+	self.win.update_gui()
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Image/Raster Calculator...", 2,
+                                    self.launch_dialog)
+
+############################################################################
+class RCalculatorDialog(GtkWindow):
+
+    def __init__(self,app=None):
+	rlayer = gview.app.sel_manager.get_active_layer()
+	if rlayer is None:
+            raise TypeError
+        try:
+    	    gdal_dataset = rlayer.get_data().get_dataset()
+        except:
+    	    raise TypeError
+	    		
+	GtkWindow.__init__(self)
+	self.set_title('Image Calculator')
+	self.set_policy(FALSE, TRUE, TRUE)
+	self.text_pos = 0
+	self.tooltips = GtkTooltips()
+    	self.expression = ""
+		   	
+	try:
+	    self.create_gui()
+	except:
+            raise TypeError
+	    
+	GtkWindow.show_all(self)
+	
+    def close(self,*args):
+	self.destroy()
+	return TRUE
+
+######################################################################
+    def bit_operations_group(self,funcbox):
+
+	self.bfunc_table = GtkTable(4,2)
+	self.bfunc_table.set_border_width(5)
+	self.bfunc_table.set_row_spacings(5)
+	self.bfunc_table.set_col_spacings(5)
+	funcbox.pack_start(self.bfunc_table, expand=FALSE)
+	
+	btn = GtkButton("AND")
+	btn.connect("clicked",self.fbutton_pressed,"AND()")
+        self.bfunc_table.attach(btn,0,1,0,1)
+
+	btn = GtkButton("OR")
+	btn.connect("clicked",self.fbutton_pressed,"OR()")
+        self.bfunc_table.attach(btn,1,2,0,1)
+
+	btn = GtkButton("XOR")
+	btn.connect("clicked",self.fbutton_pressed,"XOR()")
+        self.bfunc_table.attach(btn,0,1,1,2)
+
+	btn = GtkButton("invert")
+	btn.connect("clicked",self.fbutton_pressed,"invert()")
+        self.bfunc_table.attach(btn,1,2,1,2)
+	
+	btn = GtkButton("LShift")
+	btn.connect("clicked",self.fbutton_pressed,"LShift()")
+        self.bfunc_table.attach(btn,0,1,2,3)
+
+	btn = GtkButton("RShift")
+	btn.connect("clicked",self.fbutton_pressed,"RShift()")
+        self.bfunc_table.attach(btn,1,2,2,3)
+	
+	self.bfunc_table.hide()
+
+############################################################################
+    def mathematics_group(self,funcbox):
+	self.mfunc_table = GtkTable(4,2)
+	self.mfunc_table.set_border_width(5)
+	self.mfunc_table.set_row_spacings(5)
+	self.mfunc_table.set_col_spacings(5)
+	funcbox.pack_start(self.mfunc_table, expand=FALSE)
+
+	btn = GtkButton("log")
+	btn.connect("clicked",self.fbutton_pressed,"log()")
+        self.mfunc_table.attach(btn,0,1,0,1)
+
+	btn = GtkButton("exp")
+	btn.connect("clicked",self.fbutton_pressed,"exp()")
+        self.mfunc_table.attach(btn,1,2,0,1)
+
+	btn = GtkButton("sin")
+	btn.connect("clicked",self.fbutton_pressed,"sin()")
+        self.mfunc_table.attach(btn,0,1,1,2)
+
+	btn = GtkButton("cos")
+	btn.connect("clicked",self.fbutton_pressed,"cos()")
+        self.mfunc_table.attach(btn,1,2,1,2)
+	
+	btn = GtkButton("sqrt")
+	btn.connect("clicked",self.fbutton_pressed,"sqrt()")
+        self.mfunc_table.attach(btn,0,1,2,3)
+
+	btn = GtkButton("tan")
+	btn.connect("clicked",self.fbutton_pressed,"tan()")
+        self.mfunc_table.attach(btn,1,2,2,3)
+	
+	btn = GtkButton("max")
+	btn.connect("clicked",self.fbutton_pressed,"max()")
+        self.mfunc_table.attach(btn,0,1,3,4)
+
+	btn = GtkButton("min")
+	btn.connect("clicked",self.fbutton_pressed,"min()")
+        self.mfunc_table.attach(btn,1,2,3,4)
+	
+	self.mfunc_table.hide()
+
+######################################################################
+    def trigonometry_group(self,funcbox):
+	self.tfunc_table = GtkTable(4,2)
+	self.tfunc_table.set_border_width(5)
+	self.tfunc_table.set_row_spacings(5)
+	self.tfunc_table.set_col_spacings(5)
+	funcbox.pack_start(self.tfunc_table, expand=FALSE)
+
+	btn = GtkButton("sin")
+	btn.connect("clicked",self.fbutton_pressed,"sin()")
+        self.tfunc_table.attach(btn,0,1,0,1)
+
+	btn = GtkButton("cos")
+	btn.connect("clicked",self.fbutton_pressed,"cos()")
+        self.tfunc_table.attach(btn,1,2,0,1)
+
+	btn = GtkButton("tan")
+	btn.connect("clicked",self.fbutton_pressed,"tan()")
+        self.tfunc_table.attach(btn,0,1,1,2)
+
+	btn = GtkButton("asin")
+	btn.connect("clicked",self.fbutton_pressed,"asin()")
+        self.tfunc_table.attach(btn,1,2,1,2)
+	
+	btn = GtkButton("acos")
+	btn.connect("clicked",self.fbutton_pressed,"acos()")
+        self.tfunc_table.attach(btn,0,1,2,3)
+
+	btn = GtkButton("atan")
+	btn.connect("clicked",self.fbutton_pressed,"atan()")
+        self.tfunc_table.attach(btn,1,2,2,3)
+	
+	btn = GtkButton("hypot")
+	btn.connect("clicked",self.fbutton_pressed,"hypot()")
+        self.tfunc_table.attach(btn,0,1,3,4)
+
+	btn = GtkButton("atan2")
+	btn.connect("clicked",self.fbutton_pressed,"atan2()")
+        self.tfunc_table.attach(btn,1,2,3,4)
+	
+	self.tfunc_table.hide()
+
+######################################################################
+    def special_funcs_group(self,funcbox):
+
+	self.sfunc_table = GtkTable(4,2)
+	self.sfunc_table.set_border_width(5)
+	self.sfunc_table.set_row_spacings(5)
+	self.sfunc_table.set_col_spacings(5)
+	funcbox.pack_start(self.sfunc_table, expand=FALSE)
+
+	btn = GtkButton("NDVI")
+	btn.connect("clicked",self.fbutton_pressed,"NDVI()")
+        self.sfunc_table.attach(btn,0,1,0,1)
+	self.tooltips.set_tip(btn, 'NDVI(X,Y) = (Y - X)/(X + Y) * 128 + 128')
+
+	self.sfunc_table.hide()
+
+##############################################################################
+    
+    def create_gui(self):
+	self.dict_of_bands = get_list_of_bands_as_dict()
+	if self.dict_of_bands is None:
+	    raise TypeError
+	self.list_of_bands = self.dict_of_bands.keys()
+	self.list_of_bands.sort()
+	
+	title_width = 120
+
+	box1 = GtkVBox(spacing=10)
+	box1.set_border_width(10)
+        self.add(box1)
+        box1.show()
+
+	box2 = GtkVBox(spacing=5)
+	box2.set_border_width(5)
+	box1.pack_start(box2)
+	box2.show()
+
+	self.expression_unit = GtkEntry()
+	self.expression_unit.set_text(self.expression)
+	self.expression_unit.connect("changed",self.expression_edit_cb)
+        box2.pack_start(self.expression_unit)
+        self.expression_unit.show()
+	
+	box3 = GtkHBox(spacing=5)
+	box1.pack_start(box3)
+	box3.show()
+
+#####	
+	funcbox = GtkVBox(spacing=5)
+	funcbox.set_border_width(10)
+	box3.pack_start(funcbox)
+	funcbox.show()
+
+	fg_list = ["Mathematics","Bit Operations","Trigionometry","Special"]
+	self.fun_group_list = gvutils.GvOptionMenu(fg_list,self.group_changed)
+	funcbox.pack_start(self.fun_group_list, expand=FALSE)
+	
+
+	self.mathematics_group(funcbox)
+	self.bit_operations_group(funcbox)
+	self.trigonometry_group(funcbox)
+	self.special_funcs_group(funcbox)
+	
+#####
+	digitbox = GtkVBox(spacing=10)
+	digitbox.set_border_width(10)
+	box3.pack_start(digitbox)
+	digitbox.show()
+	
+	digit_table = GtkTable(5,5)
+	digit_table.set_border_width(5)
+	digit_table.set_row_spacings(5)
+	digit_table.set_col_spacings(5)
+	digitbox.pack_start(digit_table)
+	digit_table.show()
+	
+	btn = GtkButton("Back")
+        btn.connect("clicked", self.back_button_pressed)
+        digit_table.attach(btn,1,3,0,1)
+
+	btn = GtkButton("C")
+        btn.connect("clicked", self.clear_button_pressed)
+        digit_table.attach(btn,3,5,0,1)
+
+        btn = GtkButton("7")
+        btn.connect("clicked", self.button_pressed,"7")
+        digit_table.attach(btn,0,1,1,2)
+
+        btn = GtkButton("8")
+        btn.connect("clicked", self.button_pressed,"8")
+        digit_table.attach(btn,1,2,1,2)
+ 
+        btn = GtkButton("9")
+        btn.connect("clicked", self.button_pressed,"9")
+        digit_table.attach(btn,2,3,1,2)
+  
+        btn = GtkButton(" / ")
+        btn.connect("clicked", self.button_pressed,"/")
+        digit_table.attach(btn,3,4,1,2)
+ 
+        btn = GtkButton("(")
+        btn.connect("clicked", self.button_pressed,"(")
+        digit_table.attach(btn,4,5,1,2)
+
+        btn = GtkButton("4")
+        btn.connect("clicked", self.button_pressed,"4")
+        digit_table.attach(btn,0,1,2,3)
+
+        btn = GtkButton("5")
+        btn.connect("clicked", self.button_pressed,"5")
+        digit_table.attach(btn,1,2,2,3)
+
+        btn = GtkButton("6")
+        btn.connect("clicked", self.button_pressed,"6")
+        digit_table.attach(btn,2,3,2,3)
+ 
+        btn = GtkButton("*")
+        btn.connect("clicked", self.button_pressed,"*")
+        digit_table.attach(btn,3,4,2,3)
+
+        btn = GtkButton(")")
+        btn.connect("clicked", self.button_pressed,")")
+        digit_table.attach(btn,4,5,2,3)
+ 
+        btn = GtkButton("1")
+        btn.connect("clicked", self.button_pressed,"1")
+        digit_table.attach(btn,0,1,3,4)
+ 
+        btn = GtkButton("2")
+        btn.connect("clicked", self.button_pressed,"2")
+        digit_table.attach(btn,1,2,3,4)
+
+        btn = GtkButton("3")
+        btn.connect("clicked", self.button_pressed,"3")
+        digit_table.attach(btn,2,3,3,4)
+
+        btn = GtkButton("-")
+        btn.connect("clicked", self.button_pressed,"-")
+        digit_table.attach(btn,3,4,3,4)
+
+        btn = GtkButton("End")
+        btn.connect("clicked", self.end_button_pressed)
+        digit_table.attach(btn,4,5,3,4)
+
+        btn = GtkButton("0")
+        btn.connect("clicked", self.button_pressed,"0")
+        digit_table.attach(btn,0,1,4,5)
+
+        btn = GtkButton(",")
+        btn.connect("clicked", self.button_pressed,",")
+        digit_table.attach(btn,1,2,4,5)
+ 
+        btn = GtkButton(".")
+        btn.connect("clicked", self.button_pressed,".")
+        digit_table.attach(btn,2,3,4,5)
+
+        btn = GtkButton("+")
+        btn.connect("clicked", self.button_pressed,"+")
+        digit_table.attach(btn,3,4,4,5)
+
+        btn = GtkButton("=")
+        btn.connect("clicked", self.compute)
+        digit_table.attach(btn,4,5,4,5)
+
+#####
+	rastersbox = GtkVBox(spacing=5)
+	box1.pack_start(rastersbox)
+	rastersbox.show()
+	
+### source list #############################################################	
+	frame1 = GtkFrame("Select Image Bands To Compute")
+	frame1.show()
+        box1.pack_start(frame1, expand=FALSE)
+	
+	box2r = GtkVBox(spacing=10)
+        box2r.set_border_width(10)
+	frame1.add(box2r)
+	box2r.show()
+	
+	self.s1_list = \
+	    gvutils.GvOptionMenu(self.list_of_bands, self.raster_selected_cb)
+	box2r.pack_start(self.s1_list)
+	self.s1_list.set_history(-1)
+	self.s1_list.show()
+		
+##### OUT TYPES #########################################################
+	box_types = GtkHBox(spacing=10)
+	box2r.pack_start(box_types)
+	box_types.show()
+
+	types_label = GtkLabel('Image Data Type:')
+	types_label.set_alignment(0, 0.5)
+	box_types.pack_start(types_label, expand=FALSE)
+	
+	self.types_list = []
+	i = GDT_Byte
+	while i < GDT_TypeCount:
+	    self.types_list.append(gdal.GetDataTypeName(i))
+	    i += 1
+
+	self.types = gvutils.GvOptionMenu(self.types_list)
+	box_types.pack_start(self.types)
+	self.types.show()
+	
+#### NEW VIEW ##########################################################
+	self.switch_new_view = GtkCheckButton("Create New View")
+	box1.pack_start(self.switch_new_view)
+	self.switch_new_view.show()
+	
+        return TRUE
+
+#########################################################################	
+    def expression_edit_cb(self,*args):
+	self.expression = self.expression_unit.get_text()
+	
+#########################################################################
+    def button_pressed(self,widget,txt):
+	s = self.expression
+	self.expression = s[:self.text_pos] + txt + s[self.text_pos:]
+	self.expression_unit.set_text(self.expression)
+	self.text_pos += len(txt)
+
+#########################################################################
+    def fbutton_pressed(self,widget,txt):
+	s = self.expression
+	self.expression = s[:self.text_pos] + txt + s[self.text_pos:]
+	self.expression_unit.set_text(self.expression)
+	self.text_pos += len(txt) - 1
+
+#########################################################################
+    def clear_button_pressed(self,*args):
+	self.expression = ""
+	self.text_pos = 0
+	self.expression_unit.set_text(self.expression)
+	self.expression_unit.set_position(self.text_pos)
+
+#########################################################################
+    def back_button_pressed(self,*args):
+	self.expression = \
+	    self.expression[:self.text_pos-1]+self.expression[self.text_pos:]
+	self.text_pos -= 1
+	self.expression_unit.set_text(self.expression)
+    
+#########################################################################
+    def raster_selected_cb(self,*args):
+	i_band = self.s1_list.get_history() + 1
+	if i_band > 0:
+	    s_band = "%" + str(i_band) 
+	    s = self.expression
+	    self.expression = s[:self.text_pos]+s_band+s[self.text_pos:]
+	    self.text_pos += len(s_band)
+	    self.expression_unit.set_text(self.expression)
+	self.s1_list.set_history(-1)
+
+#########################################################################
+    def end_button_pressed(self,*args):
+	self.text_pos = len(self.expression)
+	self.expression_unit.set_position(self.text_pos)
+
+#########################################################################
+    def update_gui(self,*args):
+	self.group_changed(self.fun_group_list)
+
+#########################################################################
+    def group_changed(self,widget):
+	i = self.fun_group_list.get_history()
+	self.mfunc_table.hide()
+	self.tfunc_table.hide()
+	self.bfunc_table.hide()
+	self.sfunc_table.hide()
+	if i == 0:
+	    self.mfunc_table.show()
+	elif i == 1:
+	    self.bfunc_table.show()
+	elif i == 2:
+	    self.tfunc_table.show()
+	else:
+	    self.sfunc_table.show()
+#########################################################################	
+    def get_band(self,name):
+	layer = self.dict_of_bands[name][1]
+	b_num = self.dict_of_bands[name][2]
+	band = layer.get_parent().get_dataset().GetRasterBand(b_num+1)
+	return band	    
+########################################################################
+    def NDVI(self,s1,s2):
+	rb = 128.+ 128. *(s2-s1)/(s1+s2)
+	return rb
+#########################################################################	
+    def compute(self,*args):
+	import re
+	ex_exp = self.expression
+
+	type = gdal.GetDataTypeByName(self.types_list[self.types.get_history()])
+	numtype = gdalnumeric.GDALTypeCodeToNumericTypeCode(type)
+	if numtype == None:
+	    gvutils.error("Error! Type " + numtype + " is not supported!")
+	    return FALSE 
+
+	fun_names_dict = {}
+	fun_names_dict["max"] = ("maximum",2)
+	fun_names_dict["min"] = ("minimum",2)
+	fun_names_dict["asin"] = ("arcsin",1)
+	fun_names_dict["acos"] = ("arccos",1)
+	fun_names_dict["atan"] = ("arctan",1)
+	fun_names_dict["AND"] = ("bitwise_and",2)
+	fun_names_dict["OR"] = ("bitwise_or",2)
+	fun_names_dict["XOR"] = ("bitwise_xor",2)
+	fun_names_dict["inv"] = ("invert",1)
+	fun_names_dict["LShift"] = ("left_shift",2)
+	fun_names_dict["RShift"] = ("right_shift",1)
+	fun_names_dict['NDVI'] = ("self.NDVI",2)
+	
+	sh_names_list = fun_names_dict.keys()
+	for item in sh_names_list:
+	    ex_exp = re.sub(item,fun_names_dict[item][0],ex_exp)
+	     
+	test_exp = ex_exp
+	test_array = Numeric.zeros((1, 5), numtype)
+	for i in range(len(self.list_of_bands)):
+	    patt_i = "%"+str(i+1)
+	    repl_i = "sb["+str(i)+"]"
+	    ex_exp = re.sub(patt_i,repl_i,ex_exp)
+	    test_exp = re.sub(patt_i,"test_array",test_exp)
+	ex_exp = "rb="+ex_exp
+	test_exp = "test_res="+test_exp
+	
+	try:
+	    exec test_exp
+	except:
+	    gvutils.error("Illegal expression!")
+	    return FALSE
+
+	if self.switch_new_view.get_active():
+	    gview.app.new_view()
+	    
+	b1_name = self.list_of_bands[self.s1_list.get_history()]
+	band_list = []
+	for i in range(len(self.list_of_bands)):
+	    band_list.append(self.get_band(self.list_of_bands[i]))
+	b1 = self.get_band(b1_name)
+	proto_ds = self.dict_of_bands[b1_name][1].get_parent().get_dataset()
+	self.out_buf = Numeric.zeros((b1.YSize, b1.XSize), numtype)
+
+	sb = range(len(self.list_of_bands))
+	for y in range(b1.YSize):
+	    for i in range(len(self.list_of_bands)):
+		sb[i] = \
+		    gdalnumeric.BandReadAsArray(band_list[i],0,y,b1.XSize,1)[0]
+	    try:
+		exec ex_exp
+	    except:    
+		gvutils.error("ZeroDivide?")
+		return FALSE
+	    self.out_buf[y, 0:] = clip_result(numtype, rb).astype(numtype)
+	
+	res_ds = gdalnumeric.OpenArray(self.out_buf,proto_ds)
+	gview.app.open_gdal_dataset(res_ds)
+
+
+TOOL_LIST = ['CalculatorTool', 'RCalculatorTool']
+

Added: packages/openev/branches/upstream/current/tools/compose.py
===================================================================
--- packages/openev/branches/upstream/current/tools/compose.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/compose.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,1218 @@
+##############################################################################
+# $Id: compose.py,v 1.4 2004/11/16 19:11:34 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Tool for combining/merging datasets without loading
+#           them directly into memory.
+#
+# Author:   Andrey Kiselev, dron at remotesensing.org
+#	    Iscander Latypov
+#           Gillian Walter (modifications)
+#
+###############################################################################
+# Copyright (c) 2004, American Museum of Natural History. All rights reserved.
+# This software is based upon work supported by NASA under award
+# number NAG5-12333
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: compose.py,v $
+#  Revision 1.4  2004/11/16 19:11:34  gmwalter
+#  Fixed typo.
+#
+#  Revision 1.3  2004/09/01 15:52:55  gmwalter
+#  Updated to use VRT instead of numpy, and
+#  also to add the ability to specify
+#  geocoding information.
+#
+#  Revision 1.2  2004/04/21 13:24:04  andrey_kiselev
+#  Check whether we have a raster layer on input.
+#
+#  Revision 1.1  2004/03/09 11:01:08  andrey_kiselev
+#  New.
+#
+#
+
+#
+# To do: 1) Nicer editing windows for projection info?  WKT
+#           strings can be a bit hard to follow.
+#        2) Add Metadata frame and colour interpretation frame
+#
+
+
+import gtk
+import gview
+import string
+import gvutils
+import gviewapp
+import Numeric
+import os
+import GtkExtra
+import gdal
+import osr
+import gdalnumeric
+import pgugrid
+import vrtutils
+import gvsignaler
+
+spc=5
+
+class ComposeTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        self.win = DatasetComposeDialog(self.app)
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Image/Compose...",1,
+                                    self.launch_dialog)
+
+
+class DatasetComposeDialog(gtk.GtkWindow):
+    def __init__(self,app=None):
+        gtk.GtkWindow.__init__(self)
+        self.set_title('Compose Dataset')
+        self.set_policy(gtk.FALSE, gtk.TRUE, gtk.TRUE)
+        self.set_border_width(10)
+        self.shell=gtk.GtkVBox(spacing=spc)
+        self.add(self.shell)
+        self.tips=gtk.GtkTooltips()
+        self.input_frame=InputFrame(self, self.tips)
+        self.show_list=[]
+        self.adv_show_list=[]
+        self.show_list.append(self.input_frame)
+        self.app=app
+
+        self.button_dict={}
+        self.button_dict['Mode']=gtk.GtkCheckButton('Advanced Options')
+        self.shell.pack_start(self.button_dict['Mode'])
+        self.show_list.append(self.button_dict['Mode'])
+
+ 
+        self.adv_notebook = gtk.GtkNotebook()
+        self.shell.pack_start(self.adv_notebook)
+        self.adv_show_list.append(self.adv_notebook)
+        self.geo_frame=GeocodingFrame(self.tips)
+        self.adv_notebook.append_page(self.geo_frame,
+                                      gtk.GtkLabel('Geocoding')) 
+
+        echbox=gtk.GtkHBox(spacing=5,homogeneous=gtk.FALSE)
+        echbox.set_border_width(3)
+        self.shell.pack_end(echbox,gtk.FALSE,gtk.FALSE,0)
+        self.show_list.append(echbox)
+                              
+        self.button_dict['Close']=gtk.GtkButton('Close')
+        echbox.pack_end(self.button_dict['Close'],expand=gtk.TRUE)
+        self.button_dict['Save']=gtk.GtkButton('Save VRT')
+        echbox.pack_end(self.button_dict['Save'],expand=gtk.TRUE)
+        self.button_dict['New']=gtk.GtkButton('New View')
+        echbox.pack_end(self.button_dict['New'],expand=gtk.TRUE)
+        self.button_dict['Current']=gtk.GtkButton('Current View')
+        echbox.pack_end(self.button_dict['Current'],expand=gtk.TRUE)
+
+        self.tips.set_tip(self.button_dict['Close'],
+                          'Exit the Compose Dataset tool')
+        self.tips.set_tip(self.button_dict['Save'],
+                          'Create dataset and save to a VRT format file')
+        self.tips.set_tip(self.button_dict['New'],
+                          'Create dataset and display in a new view')
+        self.tips.set_tip(self.button_dict['Current'],
+                          'Create dataset and display in current view')
+
+        for item in self.show_list:
+            item.show_all()
+            item.show()
+
+        # geocode frame hides some of its contents
+        self.geo_frame.show()
+        
+        self.button_dict['Save'].connect('clicked',self.create_cb,'Save')
+        self.button_dict['New'].connect('clicked',self.create_cb,'New')
+        self.button_dict['Current'].connect('clicked',self.create_cb,'Current')
+        self.button_dict['Close'].connect('clicked',self.close)        
+        self.button_dict['Mode'].connect('toggled',self.mode_toggled_cb)
+
+        self.input_frame.subscribe('output-bands-empty', self.clear_defaults)
+        self.input_frame.subscribe('output-bands-notempty',
+                                   self.update_defaults)
+                       
+        self.button_dict['Mode'].set_active(0)
+        self.mode_toggled_cb()
+        self.shell.show()
+
+    def mode_toggled_cb(self,*args):
+        if self.button_dict['Mode'].get_active():
+            for item in self.adv_show_list:
+                item.show()
+        else:
+            for item in self.adv_show_list:
+                item.hide()
+                
+    def close(self,*args):
+        self.destroy()
+
+    def create_cb(self,*args):
+        bands = self.input_frame.get_output_bands()
+        if len(bands) == 0:
+            gvutils.error('No output bands specified!')
+            return
+
+        vrtbase = [gdal.CXT_Element,'VRTDataset']
+        vrtbase.append([gdal.CXT_Attribute,'rasterXSize',
+                     [gdal.CXT_Text,str(bands[0][0].RasterXSize)]])
+        vrtbase.append([gdal.CXT_Attribute,'rasterYSize',
+                     [gdal.CXT_Text,str(bands[0][0].RasterYSize)]])
+
+        # Metadata is currently taken from first output band.
+        # This may be updatable later.
+        mbase = vrtutils.serializeMetadata(bands[0][0])
+        if mbase is not None:
+            vrtbase.append(mbase)
+            
+        gbase = self.geo_frame.get_geocoding()
+        for item in gbase:
+            vrtbase.append(item)
+
+        outband = 1
+        for item in bands:
+            dict={}
+            dict['band']=outband
+            dict['SourceBand'] = item[1]
+            dict['ColorInterp'] = 'Undefined'
+            bbase = vrtutils.serializeBand(item[0],opt_dict=dict)
+            vrtbase.append(bbase)
+            outband=outband+1
+
+        vrtlines = gdal.SerializeXMLTree(vrtbase)
+    
+        vrtds = gdal.OpenShared(vrtlines)
+        
+        if args[1] == 'Save':
+            fname = GtkExtra.file_sel_box(title="Save File")
+            if fname is None:
+                return
+            driver = gdal.GetDriverByName('VRT')
+            driver.CreateCopy(fname,vrtds)
+        elif args[1] == 'New':
+            self.app.new_view()
+            self.app.open_gdal_dataset(vrtds) 
+        else:
+            self.app.open_gdal_dataset(vrtds)
+
+    def update_defaults(self, *args):
+        bands = self.input_frame.get_output_bands()
+        self.geo_frame.update_default_frame(bands[0][0].GetDescription())
+        self.geo_frame.update_gcp_frame_to_defaults()
+        self.geo_frame.update_geotransform_frame_to_defaults()
+
+    def clear_defaults(self, *args):
+        self.geo_frame.update_default_frame(None)
+        self.geo_frame.clear_gcp_frame()
+        self.geo_frame.clear_geotransform_frame()
+    
+class GeocodingFrame(gtk.GtkVBox):
+    def __init__(self,tips):
+        gtk.GtkVBox.__init__(self)
+
+        self.frames = {}
+        self.tips=tips
+        
+        hbox=gtk.GtkHBox()
+        hbox.set_border_width(spc)
+        self.pack_start(hbox)
+        label=gtk.GtkLabel("Input Geocoding: ")
+        label.set_alignment(0,0.5)
+        hbox.pack_start(label)
+        self.geocode_menu_list=['Default','GCPs','Geotransform']
+        self.geocode_menu = gvutils.GvOptionMenu(self.geocode_menu_list,
+                                                 self.geotype_toggled_cb)
+        self.tips.set_tip(self.geocode_menu,
+                          'Default: Derive geocoding from input bands\n'+
+                          'GCPs: Define new ground control points\n'+
+                          'Geotransform: Define a new affine transformation')
+        hbox.pack_start(self.geocode_menu)
+        hbox.show_all()
+        self.create_default_frame()
+        self.create_gcp_frame()
+        self.create_geotransform_frame()
+        self.geocode_menu.set_history(0)
+        self.geotype_toggled_cb()
+        
+        self.default_fname=None # dataset to use for default info
+        self.default_geotransform=None
+        self.default_gcps=None
+        self.default_prj=''
+
+    def geotype_toggled_cb(self,*args):
+        hist=self.geocode_menu.get_history()
+        for idx in range(len(self.geocode_menu_list)):
+            if idx == hist:
+                self.frames[self.geocode_menu_list[idx]].show()
+            else:
+                self.frames[self.geocode_menu_list[idx]].hide()
+                
+
+    def create_default_frame(self):
+        self.frames['Default']=gtk.GtkFrame('')
+        self.frames['Default'].set_shadow_type(gtk.SHADOW_NONE)
+        vbox=gtk.GtkVBox()
+        vbox.set_spacing(spc)
+        self.frames['Default'].add(vbox)
+        self.default_scrolled_text = gtk.GtkText()
+        self.default_scrolled_text.set_line_wrap(gtk.FALSE)
+        self.default_scrolled_text.set_word_wrap(gtk.FALSE)
+        self.default_scrolled_text.set_editable(gtk.FALSE)
+        self.default_scrolled_win = gtk.GtkScrolledWindow()
+        self.default_scrolled_win.set_usize(200,200)
+        self.default_scrolled_win.add( self.default_scrolled_text)
+        vbox.pack_start(self.default_scrolled_win,expand=gtk.TRUE)
+        self.frames['Default'].show_all()
+        
+        self.pack_start(self.frames['Default'])
+
+        
+    def create_gcp_frame(self):
+        self.frames['GCPs']=gtk.GtkFrame('')
+        self.frames['GCPs'].set_shadow_type(gtk.SHADOW_NONE)
+        vbox=gtk.GtkVBox()
+        vbox.set_spacing(spc)
+        self.frames['GCPs'].add(vbox)
+        self.gcpgrid = pgugrid.pguGrid((3,1,0,1,7,0,0,0,3))
+        self.gcpgrid.set_usize(200,200)
+        self.gcplist=[]
+        self.gcpgrid.set_source(self.gcplist,
+           members=['Id','GCPPixel','GCPLine','GCPX','GCPY','GCPZ'],
+           titles = ['ID','Pixel','Line','X','Y','Z'],
+           types = ['string','float','float','float','float','float'])
+        vbox.pack_start(self.gcpgrid)
+        hbox=gtk.GtkHBox()
+        hbox.set_spacing(spc)
+        vbox.pack_start(hbox)
+        self.add_gcp_button=gtk.GtkButton('  Add GCP  ')
+        self.add_gcp_button.connect('clicked',self.add_gcp_cb)
+        hbox.pack_start(self.add_gcp_button, expand=gtk.FALSE)
+        self.tips.set_tip(self.add_gcp_button,'Add a new GCP')
+        self.load_gcp_button=gtk.GtkButton('Load GCPs')
+        self.tips.set_tip(self.load_gcp_button,
+           'Clear existing GCPs and load '+
+           'new ones from a text file')
+        self.load_gcp_button.connect('clicked',self.load_gcps_cb)
+        hbox.pack_start(self.load_gcp_button, expand=gtk.FALSE)
+        self.copy_gcp_button=gtk.GtkButton('Copy GCPs')
+        self.copy_gcp_button.connect('clicked',self.copy_gcps_cb)
+        self.tips.set_tip(self.copy_gcp_button,
+            'Clear existing GCPs and copy new '+
+            'ones from another GDAL dataset')
+        hbox.pack_start(self.copy_gcp_button, expand=gtk.FALSE)
+        self.clear_gcp_button=gtk.GtkButton('Clear GCPs')
+        self.clear_gcp_button.connect('clicked',self.clear_gcps)
+        self.tips.set_tip(self.clear_gcp_button,
+            'Clear all GCPs')
+        hbox.pack_start(self.clear_gcp_button, expand=gtk.FALSE)
+        self.gcpprjbox=ProjectionBox(self.tips)
+        vbox.pack_start(self.gcpprjbox)
+        self.frames['GCPs'].show_all()
+        self.pack_start(self.frames['GCPs'])
+
+    def load_gcps_cb(self,*args):
+        """ Load gcps from a text file """
+        self.clear_gcps()
+        info=getgcpfile()
+        if info is None:
+            # user pressed cancel
+            return
+        if ((info[2] is None) or (info[3] is None) or
+            (info[4] is None) or (info[5] is None)):
+            gvutils.error('Invalid column info for GCP text file!')
+            return
+
+        try:
+            fh=open(info[0],'r')
+            flines=fh.readlines()
+        except:
+            gvutils.error('Unable to read GCP text file!')
+            return
+
+        idx=0
+        for cline in flines:
+            if string.strip(info[1]) == '':
+                # whitespace delimited
+                sline=string.split(cline)
+            else:
+                sline=string.split(cline,info[1])
+            try:
+                gcp=gdal.GCP()
+                gcp.GCPPixel=float(string.strip(sline[info[2]-1]))
+                gcp.GCPLine=float(string.strip(sline[info[3]-1]))
+                gcp.GCPX=float(string.strip(sline[info[4]-1]))
+                gcp.GCPY=float(string.strip(sline[info[5]-1]))
+                if info[6] is not None:
+                    gcp.GCPZ=float(string.strip(sline[info[6]-1]))
+                if info[7] is not None:
+                    gcp.Id=string.strip(sline[info[7]-1])
+                if info[8] is not None:
+                    gcp.Info=string.strip(sline[info[8]-1])
+                self.gcplist.append(gcp)
+            except:
+                # first line might have column names, so
+                # ignore errors.  otherwise, report invalid
+                # lines
+                if idx != 0:
+                    print 'Warning: invalid line '+str(idx)+' in GCP file!'
+                    
+            idx=idx+1
+
+        self.gcpgrid.refresh()
+
+    def clear_gcp_frame(self):
+        self.clear_gcps()
+        self.gcpprjbox.set_input_projection('')
+        self.gcpprjbox.set_output_projection('')
+
+    def update_gcp_frame_to_defaults(self):
+        self.clear_gcp_frame()
+        if self.default_gcps is not None:
+            for item in self.default_gcps:
+                gcp=CopyGDALGCP(item) # copy so original doesn't get changed
+                self.gcplist.append(gcp)
+                
+            self.gcpgrid.refresh()
+                
+            self.gcpprjbox.set_input_projection(self.default_prj)
+            self.gcpprjbox.set_output_projection(self.default_prj)
+               
+    def clear_gcps(self,*args):
+        while len(self.gcplist) > 0:
+            self.gcplist.pop()
+        self.gcpgrid.refresh()
+
+    def copy_gcps_cb(self,*args):
+        """ Copy gcps from an existing gdal dataset. """
+        fname=GtkExtra.file_sel_box(title="Select GDAL Dataset")
+        if fname is None:
+            return
+
+        try:
+            fh=gdal.OpenShared(fname)
+        except:
+            gvutils.error('Unable to open '+fname+' as a GDAL dataset!')
+            return
+        
+        gcps=fh.GetGCPs()
+        prj=fh.GetGCPProjection()
+        self.clear_gcps()
+        for gcp in gcps:
+            ngcp=CopyGDALGCP(gcp)
+            self.gcplist.append(ngcp)
+        self.gcpgrid.refresh()
+            
+        self.gcpprjbox.set_input_projection(prj)
+
+    def add_gcp_cb(self,*args):
+        self.gcplist.append(gdal.GCP())
+        self.gcpgrid.refresh()
+
+    def create_geotransform_frame(self):
+        self.frames['Geotransform']=gtk.GtkFrame('')
+        self.frames['Geotransform'].set_shadow_type(gtk.SHADOW_NONE)
+        vbox=gtk.GtkVBox()
+        vbox.set_spacing(spc)
+        self.frames['Geotransform'].add(vbox)
+        label=gtk.GtkLabel('Xgeo = GT(0) + XPixel*GT(1) + YLine*GT(2)')
+        label.set_alignment(0,0.5)
+        vbox.pack_start(label)
+        label=gtk.GtkLabel('Ygeo = GT(3) + XPixel*GT(4) + YLine*GT(5)')
+        label.set_alignment(0,0.5)
+        vbox.pack_start(label)
+        table=gtk.GtkTable(rows=6,cols=3)
+        self.geotransform_entries=[]
+        for idx in range(6):
+            label=gtk.GtkLabel('GT('+str(idx)+'):')
+            label.set_alignment(0,0.5)
+            table.attach(label,0,1,idx,idx+1)
+            newentry=gtk.GtkEntry()
+            self.geotransform_entries.append(newentry)
+            table.attach(newentry,1,2,idx,idx+1)
+        vbox.pack_start(table)
+        self.geotransformprjbox=ProjectionBox(self.tips)
+        vbox.pack_start(self.geotransformprjbox)
+        self.frames['Geotransform'].show_all()
+        self.pack_start(self.frames['Geotransform'])
+
+    def clear_geotransform_frame(self):
+        for item in self.geotransform_entries:
+            item.set_text('')
+            
+        self.geotransformprjbox.set_input_projection('')
+        self.geotransformprjbox.set_output_projection('')
+
+    def update_geotransform_frame_to_defaults(self):
+        self.clear_geotransform_frame()
+        if self.default_geotransform is not None:
+            for idx in range(6):
+                self.geotransform_entries[idx].set_text(
+                    "%f" % self.default_geotransform[idx])
+            self.geotransformprjbox.set_input_projection(self.default_prj)
+            self.geotransformprjbox.set_output_projection(self.default_prj)
+        
+    def update_default_frame(self,fname):
+        nchars=self.default_scrolled_text.get_length()
+        self.default_scrolled_text.backward_delete(nchars)
+        if fname is None:
+            self.default_fname=None
+            self.default_geotransform=None
+            self.default_gcps=None
+            self.default_prj=''
+            return
+            
+        sr=osr.SpatialReference()
+        
+        fh=gdal.OpenShared(fname)
+        prj=''
+        geot=fh.GetGeoTransform()
+        if ((tuple(geot) == (0,1,0,0,0,1)) or
+            (tuple(geot) == (0.0,1.0,0.0,0.0,0.0,1.0))):
+            self.default_geotransform=None
+            gcps = fh.GetGCPs()
+            if len(gcps) > 0:
+                self.default_gcps=gcps
+                txt='Type of Geocoding: GCPs ('+str(len(gcps))+')\n\n'
+                prj=fh.GetGCPProjection()
+                if sr.ImportFromWkt(prj) == 0:
+                    prjtxt=sr.ExportToPrettyWkt(simplify=1)
+                else:
+                    prjtxt=''
+                txt=txt+'Projection: '+prjtxt
+            else:
+                self.default_gcps=None
+                txt='Type of Geocoding: None\n\nProjection: None'
+        else:
+            self.default_geotransform=geot
+            self.default_gcps=None
+            txt='Type of Geocoding: Geotransform\n\n'
+            prj=fh.GetProjection()
+            if sr.ImportFromWkt(prj) == 0:
+                prjtxt=sr.ExportToPrettyWkt(simplify=1)
+            else:
+                prjtxt=''
+            txt=txt+'Projection: '+prjtxt
+                    
+        self.default_scrolled_text.insert_defaults(txt)
+        self.default_prj=prj
+        self.default_fname=fname
+            
+    def clear_defaults(self,*args):
+        self.update_default_frame(None)
+
+    def get_geocoding(self):
+        # returns serialized geocoding information as a list
+        gt=self.geocode_menu_list[self.geocode_menu.get_history()]
+        serialtxt=[]
+        if gt == 'Default':
+            if self.default_geotransform is not None:
+                if self.default_prj != '':
+                    serialtxt.append([gdal.CXT_Element,'SRS',[gdal.CXT_Text,
+                                                       self.default_prj]])
+
+                gtxt = vrtutils.serializeGeoTransform(
+                               geotransform=self.default_geotransform)
+                if gtxt is not None:
+                    serialtxt.append(gtxt)
+                    
+            elif self.default_gcps is not None:
+                serialtxt.append(vrtutils.serializeGCPs(
+                                 gcplist=self.default_gcps,
+                              with_Z=1,projection_attr_txt=self.default_prj))
+            else:
+                return []
+                    
+        elif gt == 'GCPs':
+            if len(self.gcplist) == 0:
+                return []
+            
+            inprj,outprj=self.gcpprjbox.get_projections()
+            if (outprj == '') or (outprj == inprj) or (inprj == ''):
+                reproj=None
+                if (outprj != '') and (inprj == ''):
+                    gvutils.warning('Warning: output projection specified,\n'+
+                                    'but no input projection.  Cannot\n'+
+                                    'reproject!')
+            else:
+                reproj=outprj
+            gcplistcopy=CopyGDALGCPs(self.gcplist)    
+            serialtxt.append(vrtutils.serializeGCPs(
+                                             gcplist=gcplistcopy,with_Z=1,
+                                             projection_attr_txt=inprj,
+                                             reproj=reproj))
+        else:
+            inprj,outprj=self.geotransformprjbox.get_projections()
+            gt=[]
+            try:
+                for idx in range(6):
+                    gt.append(float(
+                        self.geotransform_entries[idx].get_text()))
+            except:
+                gvutils.warning('Invalid Geotransform information- '+
+                                'ignoring!')
+                return []    
+
+            if (outprj == '') or (outprj == inprj) or (inprj == ''):
+                reproj=None
+                if (outprj != '') and (inprj == ''):
+                    gvutils.warning('Warning: output projection specified,\n'+
+                                    'but no input projection.  Cannot\n'+
+                                    'reproject!')
+
+                if inprj != '':
+                    serialtxt.append([gdal.CXT_Element,'SRS',
+                                      [gdal.CXT_Text,inprj]])
+
+                gbase=vrtutils.serializeGeoTransform(geotransform=gt)
+                if gbase is not None:
+                    serialtxt.append(gbase)
+                
+            else:
+                ds=gdal.OpenShared(self.default_fname)
+                gcps=vrtutils.GeoTransformToGCPs(gt,ds.RasterXSize,
+                                                 ds.RasterYSize,grid=2)
+                serialtxt.append(vrtutils.serializeGCPs(gcplist=gcps,with_Z=1,
+                                                 projection_attr_txt=inprj,
+                                                 reproj=outprj))
+                
+        return serialtxt
+            
+
+def getprjinfo():
+    fname=GtkExtra.file_sel_box(title="Select GDAL Dataset or WKT text file")
+    if fname is None:
+        return None
+    try:
+        fh=gdal.OpenShared(fname)
+        prj=fh.GetProjection()
+        if prj == '':
+            prj=fh.GetGCPProjection()
+    except:
+        fh=open(fname,'r')
+        prj=string.strip(fh.readline())
+        # prj files from shapes seem to have
+        # an extra character at the end
+        if prj[-1:] == '\x00':
+            prj=prj[:-1]
+
+    sr=osr.SpatialReference()
+    val=sr.ImportFromWkt(prj)
+    if val != 0:
+        gvutils.error('Invalid projection information in '+fname+'!')
+        return None
+
+    return prj
+
+def editprjinfo(wktinit=''):
+    win=editprjwin("Projection (Well Known Text string)",wktinit)
+    win.show()
+    gtk.mainloop()
+    prj=win.ret
+    if prj is None:
+        return
+
+    if len(prj) == 0:
+        return ''
+    
+    sr=osr.SpatialReference()
+    val=sr.ImportFromWkt(prj)
+    if val != 0:
+        gvutils.error('Invalid projection information entered!')
+        return ''
+    
+    return prj
+
+class editprjwin(gtk.GtkWindow):
+    def __init__(self,title,wkt):
+        gtk.GtkWindow.__init__(self)
+        self.set_title(title)
+        self.set_usize(350,300)
+        vbox=gtk.GtkVBox()
+        self.add(vbox)
+        self.text=gtk.GtkText()
+        self.text.set_editable(gtk.TRUE)
+        self.text.insert_defaults(wkt)
+        vbox.pack_start(self.text)
+        hbox=gtk.GtkHBox()
+        vbox.pack_start(hbox,expand=gtk.FALSE)
+        self.cancel_button=gtk.GtkButton('  Cancel  ')
+        self.ok_button=gtk.GtkButton('     OK     ')
+        hbox.pack_end(self.cancel_button,expand=gtk.FALSE)
+        hbox.pack_end(self.ok_button,expand=gtk.FALSE)
+        
+        self.cancel_button.connect('clicked', self.quit)
+        self.ok_button.connect('clicked', self.ok_cb)
+        self.ret=None
+        self.show_all()
+        
+    def quit(self, *args):
+        self.hide()
+        self.destroy()
+        gtk.mainquit()
+        
+    def ok_cb(self, b):
+        self.ret = self.text.get_chars(0,self.text.get_length())
+        self.quit()
+        
+
+def getgcpfile():
+    win=GCPFileDialog()
+    win.show()
+    gtk.mainloop()
+    return win.ret
+
+class GCPFileDialog(gtk.GtkFileSelection):
+    def __init__(self):
+        gtk.GtkFileSelection.__init__(self)
+        self.set_title('Select GCP Text File')
+        self.connect("destroy", self.quit)
+        self.connect("delete_event", self.quit)
+        gtk.grab_add(self) # is modal
+        table=gtk.GtkTable(rows=8,cols=3)
+        self.entries=[]
+        labels=['Delimiter: ',
+                'Pixel Column # (1-N: required): ',
+                'Line Column # (required): ',
+                'X (East/West) Column # (required): ',
+                'Y (North/South) Column # (required): ',
+                'Z (Height) Column # (optional): ',
+                'ID Column # (optional): ',
+                'Info Column # (optional): ']
+        for idx in range(len(labels)):
+            nl=gtk.GtkLabel(labels[idx])
+            nl.set_alignment(0,0.5)
+            self.entries.append(gtk.GtkEntry(maxlen=3))
+            table.attach(nl,0,1,idx,idx+1)
+            table.attach(self.entries[idx],1,2,idx,idx+1)
+        self.main_vbox.pack_end(table)
+        table.show_all()           
+      	self.cancel_button.connect('clicked', self.quit)
+        self.ok_button.connect('clicked', self.ok_cb)
+        self.ret = None
+        
+    def quit(self, *args):
+        self.hide()
+      	self.destroy()
+       	gtk.mainquit()
+
+    def ok_cb(self, b):
+        ret=[self.get_filename(),self.entries[0].get_text()]
+        for idx in range(1,len(self.entries)):
+            try:
+                val=int(self.entries[idx].get_text())
+            except:
+                val=None
+            ret.append(val)
+        self.ret = tuple(ret)
+        self.quit()
+
+class ProjectionBox(gtk.GtkTable):
+    def __init__(self,tips):
+        gtk.GtkTable.__init__(self,rows=2,cols=7,homogeneous=gtk.FALSE)
+        self.set_row_spacings(spc)
+        self.set_col_spacings(spc)
+        self.set_col_spacing(2,3*spc)
+        label=gtk.GtkLabel('Input Projection: ')
+        label.set_alignment(0,0.5)
+        self.attach(label,0,1,0,1)
+        self.buttons={}
+        self.buttons['input-edit']=gtk.GtkButton('      Edit      ')
+        self.buttons['input-load']=gtk.GtkButton('Load From File')
+        self.buttons['input-view']=gtk.GtkButton('Load From View')
+        tips.set_tip(self.buttons['input-edit'],
+                     'Edit projection information (should '+
+                     'be a Well Known Text string)')
+        tips.set_tip(self.buttons['input-load'],
+                     'Load projection information from a GDAL '+
+                     'dataset, or a file with a single line '+
+                     'of Well Known Text specifying a projection.')
+        tips.set_tip(self.buttons['input-view'],
+                     'Load projection information from the '+
+                     'currently active view.')
+        self.attach(self.buttons['input-edit'],0,1,1,2)
+        self.attach(self.buttons['input-view'],1,2,1,2)
+        self.attach(self.buttons['input-load'],2,3,1,2)
+        self.inprj=''
+
+        label=gtk.GtkLabel('Output Projection: ')
+        self.attach(label,3,4,0,1)
+        self.out_toggle_useinput=gtk.GtkRadioButton(label='Use Input')
+        tips.set_tip(self.out_toggle_useinput,
+                     'Geocoding information entered above '+
+                     'will not be reprojected.')
+        self.out_toggle_reproj=gtk.GtkRadioButton(label='Reproject',
+                          group=self.out_toggle_useinput)
+        tips.set_tip(self.out_toggle_reproj,
+                     'Attempt to reproject geocoding information '+
+                     'entered above to the Output projection.  No '+
+                     'resampling of the binary data will take place.  '+
+                     'If the input '+
+                     'geocoding is a Geotransform, it will be '+
+                     'converted to 16 GCPs prior to reprojection. '+
+                     'Note that applications that do not use GDAL '+
+                     'may not recognize this information, as it '+
+                     'is less common for applications to recognize '+
+                     'GCPs than Geotransforms.')
+        self.out_toggle_useinput.connect('toggled',self.toggle_output_cb)
+        self.attach(self.out_toggle_useinput,4,5,0,1)
+        self.attach(self.out_toggle_reproj,5,6,0,1)
+        self.buttons['output-edit']=gtk.GtkButton('Edit')
+        self.buttons['output-view']=gtk.GtkButton('Load From View')
+        self.buttons['output-load']=gtk.GtkButton('Load From File')
+        tips.set_tip(self.buttons['output-edit'],
+                     'Edit projection information (should '+
+                     'be a Well Known Text string)')
+        tips.set_tip(self.buttons['output-load'],
+                     'Load projection information from a GDAL '+
+                     'dataset, or a file with a single line '+
+                     'of Well Known Text specifying a projection.')
+        tips.set_tip(self.buttons['output-view'],
+                     'Load projection information from the '+
+                     'currently active view.')
+        self.attach(self.buttons['output-edit'],3,4,1,2)
+        self.attach(self.buttons['output-view'],4,5,1,2)
+        self.attach(self.buttons['output-load'],5,6,1,2)        
+        self.outprj=''
+        
+        self.buttons['input-load'].connect('clicked',self.load_input_cb)
+        self.buttons['input-view'].connect('clicked',self.view_input_cb)
+        self.buttons['output-load'].connect('clicked',self.load_output_cb)
+        self.buttons['output-view'].connect('clicked',self.view_output_cb)
+        self.buttons['input-edit'].connect('clicked',self.edit_input_cb)
+        self.buttons['output-edit'].connect('clicked',self.edit_output_cb)
+
+        self.toggle_output_cb()
+        
+    def load_input_cb(self,*args):
+        prj=getprjinfo()
+        if prj is not None:
+            self.inprj=prj
+
+    def load_output_cb(self,*args):
+        prj=getprjinfo()
+        if prj is not None:
+            self.outprj=prj
+
+    def view_input_cb(self,*args):
+        prj=gview.app.sel_manager.get_active_view().get_projection()
+        if prj is None:
+            gvutils.error('Current view does not contain projection info!')
+            self.inprj=''
+            return
+
+        if len(prj) == 0:
+            gvutils.error('Current view does not contain projection info!')
+            self.inprj=''
+            return
+        
+        sr=osr.SpatialReference()
+        val=sr.ImportFromWkt(prj)
+        if val == 0:
+            self.inprj=prj
+        else:
+            gvutils.error('Current view contains invalid projection info!')
+            self.inprj=''
+            
+    def view_output_cb(self,*args):
+        prj=gview.app.sel_manager.get_active_view().get_projection()
+        if prj is None:
+            gvutils.error('Current view does not contain projection info!')
+            self.outprj=''
+            return
+
+        if len(prj) == 0:
+            gvutils.error('Current view does not contain projection info!')
+            self.outprj=''
+            return
+        
+        sr=osr.SpatialReference()
+        val=sr.ImportFromWkt(prj)
+        if val == 0:
+            self.outprj=prj
+        else:
+            gvutils.error('Current view contains invalid projection info!')
+            self.outprj=''
+            
+    def edit_input_cb(self,*args):
+        prj=editprjinfo(self.inprj)
+        if prj is not None:
+            self.inprj=prj
+
+    def edit_output_cb(self,*args):
+        prj=editprjinfo(self.outprj)
+        if prj is not None:
+            self.outprj=prj
+
+    def set_input_projection(self,prj):
+        self.inprj=prj
+
+    def set_output_projection(self,prj):
+        self.outprj=prj
+        
+    def toggle_output_cb(self,*args):
+        if self.out_toggle_useinput.get_active() == 1:
+            self.buttons['output-load'].set_sensitive(0)
+            self.buttons['output-view'].set_sensitive(0)
+            self.buttons['output-edit'].set_sensitive(0)
+        else:
+            self.buttons['output-load'].set_sensitive(1)
+            self.buttons['output-view'].set_sensitive(1)
+            self.buttons['output-edit'].set_sensitive(1)
+
+    def get_projections(self,*args):
+        if self.out_toggle_useinput.get_active() == 1:
+            return (self.inprj, self.inprj)
+        else:
+            return (self.inprj, self.outprj)
+        
+class InputFrame(gvsignaler.Signaler):
+    def __init__(self,parent,tips):
+        self.frame=gtk.GtkFrame('Raster Bands')
+        self.tips=tips
+        self.input_bands=get_list_of_bands_as_dict()
+        self.output_bands={}
+        
+        hbox1=gtk.GtkHBox(spacing=spc)
+        hbox1.set_border_width(spc)
+        self.frame.add(hbox1)
+
+        # source (input)
+        srcvbox=gtk.GtkVBox(spacing=spc)
+        label=gtk.GtkLabel('Input:')
+        label.set_alignment(0,0.5)
+        srcvbox.pack_start(label,expand=gtk.FALSE)
+        hbox1.pack_start(srcvbox)
+	source_win = gtk.GtkScrolledWindow()
+	source_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+	source_win.set_usize(300,200)
+	srcvbox.pack_start(source_win)
+
+	source_list = gtk.GtkList()
+	source_list.set_selection_mode(gtk.SELECTION_MULTIPLE)
+	source_win.add_with_viewport(source_list)
+	source_list.append_items(self.input_bands.keys())
+
+        # signals sent up to top level so that defaults can
+        # be updated.  Defaults are updated whenever the output
+        # bands are cleared, or the first one is specified.
+        self.publish('output-bands-empty')
+        self.publish('output-bands-notempty')
+
+        def src_load(_button,*args):
+            fname=GtkExtra.file_sel_box(title="Select GDAL Dataset")
+            if fname is None:
+                return
+            ds=gdal.OpenShared(fname)
+            if ds is None:
+                gvutils.error('Not a valid gdal dataset!')
+                return
+
+            dict={}
+            for i in range(1,ds.RasterCount+1):
+                curband=fname + '.band[' + str(i) + ']'
+                dict[gtk.GtkListItem(curband)] = (ds,i,curband)
+                
+            if srctoggle.get_active() == gtk.TRUE:
+                slist=vrtutils.GetSimilarFiles(fname)
+                for nname in slist:
+                    ds = gdal.OpenShared(nname)
+                    if ds is None:
+                        continue
+                    for i in range(1,ds.RasterCount+1):
+                        curband=nname + '.band[' + str(i) + ']'
+                        dict[gtk.GtkListItem(curband)] = (ds,i,curband)
+
+            self.add_input_bands(dict)    
+
+        def src_get_active_layers(_button,*args):
+            size=None
+            if len(self.input_bands) > 0:
+                ckey=self.input_bands.keys()[0]
+                size=(self.input_bands[ckey][0].RasterXSize,
+                      self.input_bands[ckey][0].RasterYSize)
+            new_dict=get_list_of_bands_as_dict(size)
+            self.add_input_bands(new_dict)
+
+        def src_clear(_button,*args):
+            self.clear_input_bands()
+
+        self.source_list=source_list
+
+        # source control buttons
+        srcbbox=gtk.GtkHBox(spacing=spc)
+        srcvbox.pack_start(srcbbox,expand=gtk.FALSE)
+        load_btn=gtk.GtkButton("Load File")
+        self.tips.set_tip(load_btn,'Add bands from a file to the input list')
+        srcbbox.pack_start(load_btn)
+        load_btn.connect("clicked",src_load)
+        act_btn=gtk.GtkButton("Views->List")
+        self.tips.set_tip(act_btn,'Add bands from views to the input list')
+        srcbbox.pack_start(act_btn)
+        act_btn.connect("clicked",src_get_active_layers)
+        clear_btn=gtk.GtkButton("Clear")
+        srcbbox.pack_start(clear_btn)
+        clear_btn.connect("clicked",src_clear)
+
+        srctoggle=gtk.GtkCheckButton("Include Similar")
+        self.tips.set_tip(srctoggle,'Include bands from same-size files '+
+                          'in the same directory when using Load File.')
+        srcbbox.pack_start(srctoggle,expand=gtk.FALSE)
+        srctoggle.set_active(gtk.TRUE)
+        
+        # destination
+	btn_box = gtk.GtkVBox(spacing=10)
+	btn_box.set_border_width(10)
+	hbox1.pack_start(btn_box,expand=gtk.FALSE)
+	btn_box.show()
+
+	def dest_add(_button,*args):
+            sel = source_list.get_selection()
+            sel.reverse()  # add in order of selection
+            if len(self.output_bands.keys()) == 0:
+                refreshflag = 1
+            else:
+                refreshflag = 0
+                
+	    for i in sel:
+		list_item = gtk.GtkListItem(self.input_bands[i][2])
+		self.dest_list.append_items([list_item])
+		list_item.show()
+		self.dest_list.select_child(self.dest_list.children()[-1])
+		self.output_bands[list_item] = self.input_bands[i]
+
+            if (refreshflag == 1) and (len(sel) > 0):
+                self.notify('output-bands-notempty')
+                
+	def dest_del(_button,*args):
+	    selection = self.dest_list.get_selection()
+	    self.dest_list.remove_items(selection)
+	    for i in selection:
+		del self.output_bands[i]
+		i.destroy()
+	    rest = self.dest_list.children()
+	    if len(rest) > 0:
+		self.dest_list.select_child(self.dest_list.children()[-1])
+            else:
+                self.notify('output-bands-empty')
+                
+        def dest_raise(_button, *args):
+            selection = self.dest_list.get_selection()
+            if len(selection) != 1:
+                return
+            pos=self.dest_list.child_position(selection[0])
+            if pos < 1:
+                return
+            self.dest_list.remove_items(selection)
+            self.dest_list.insert_items(selection,pos-1)
+            self.dest_list.select_item(pos-1)
+
+        def dest_lower(_button, *args):
+            selection = self.dest_list.get_selection()
+            if len(selection) != 1:
+                return
+            pos=self.dest_list.child_position(selection[0])
+            if pos > len(self.output_bands)-2:
+                return
+            self.dest_list.remove_items(selection)
+            self.dest_list.insert_items(selection,pos+1)
+            self.dest_list.select_item(pos+1)
+            
+            
+	add_btn = gtk.GtkButton("Add->")
+        add_btn.connect("clicked", dest_add)
+        # The label below just makes things align more nicely (adds space)
+        btn_box.pack_start(gtk.GtkLabel(''),expand=gtk.FALSE)
+	btn_box.pack_start(add_btn,expand=gtk.FALSE)
+
+
+        destvbox=gtk.GtkVBox(spacing=spc)
+        label=gtk.GtkLabel('Output:')
+        label.set_alignment(0,0.5)
+        destvbox.pack_start(label,expand=gtk.FALSE)
+	dest_win = gtk.GtkScrolledWindow()
+	dest_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+	dest_win.set_usize(300,200)
+	destvbox.pack_start(dest_win)
+
+        hbox1.pack_start(destvbox)
+        destbbox=gtk.GtkHBox(spacing=spc)
+        destvbox.pack_start(destbbox,expand=gtk.FALSE)
+	del_btn = gtk.GtkButton()
+        del_btn.add(gtk.GtkPixmap(parent,
+                     os.path.join(gview.home_dir,'pics','delete.xpm')))
+        del_btn.connect("clicked", dest_del)
+	destbbox.pack_start(del_btn,expand=gtk.FALSE)
+	r_btn = gtk.GtkButton()
+        r_btn.add(gtk.GtkPixmap(parent,
+                     os.path.join(gview.home_dir,'pics','raise.xpm')))
+        r_btn.connect("clicked", dest_raise)
+	destbbox.pack_start(r_btn,expand=gtk.FALSE)
+	l_btn = gtk.GtkButton()
+        l_btn.add(gtk.GtkPixmap(parent,
+                     os.path.join(gview.home_dir,'pics','lower.xpm')))
+        l_btn.connect("clicked", dest_lower)
+	destbbox.pack_start(l_btn,expand=gtk.FALSE)
+        
+	self.dest_list = gtk.GtkList()
+	self.dest_list.set_selection_mode(gtk.SELECTION_BROWSE)
+	dest_win.add_with_viewport(self.dest_list)
+
+        parent.shell.pack_start(self.frame)
+
+    def clear_input_bands(self):
+        self.input_bands={}
+        self.source_list.select_all()
+        sel=self.source_list.get_selection()
+        self.source_list.remove_items(sel)
+
+    def set_input_bands(self,bands):
+        self.input_bands=bands
+        self.clear_input_bands()
+        self.source_list.append_items(self.input_bands.keys())
+        for item in self.input_bands.keys():
+            item.show()
+
+    def add_input_bands(self,bands):
+        if len(self.output_bands) > 0:
+            keys=self.output_bands.keys()
+            xsize,ysize=(self.output_bands[keys[0]][0].RasterXSize,
+                         self.output_bands[keys[0]][0].RasterYSize)
+        elif len(self.input_bands) > 0:
+            keys=self.input_bands.keys()
+            xsize,ysize=(self.input_bands[keys[0]][0].RasterXSize,
+                         self.input_bands[keys[0]][0].RasterYSize)
+        else:
+            keys=bands.keys()
+            xsize,ysize=(bands[keys[0]][0].RasterXSize,
+                         bands[keys[0]][0].RasterYSize)
+            
+        # Only add bands that are of the same size
+        invalid=0
+        oldbands=[]
+        for ckey in self.input_bands.keys():
+            oldbands.append(self.input_bands[ckey][2])
+
+        newbands=[]    
+        for ckey in bands.keys():
+            cxsize,cysize=(bands[ckey][0].RasterXSize,
+                           bands[ckey][0].RasterYSize)
+            if (cxsize == xsize) and (cysize == ysize):
+                if ((bands[ckey][2] not in oldbands) and
+                    (bands[ckey][2] not in newbands)):
+                    self.input_bands[ckey]=bands[ckey]
+                    newbands.append(self.input_bands[ckey][2])
+            else:
+                invalid=1
+                
+        if invalid != 0:
+            txt='Bands for composed dataset must all\n'+\
+                'be the same size.  Bands that are not\n'+\
+                str(xsize)+' pixels x '+str(ysize)+' lines\n'+\
+                'will be excluded from the input list.'
+            gvutils.warning(txt)
+        print 'Xsize: ',xsize,' Ysize: ',ysize        
+        # clear old list in case some of the new keys replace
+        # older ones
+        self.source_list.select_all()
+        sel=self.source_list.get_selection()
+        self.source_list.remove_items(sel)
+        self.source_list.append_items(self.input_bands.keys())
+        for item in self.input_bands.keys():
+            item.show()
+
+    def get_output_bands(self):
+        """ Return a list of bands.  Each list is
+            a tuple- (gdal dataset, band number)
+        """
+        dlist=self.dest_list.children()
+        out_list=[]
+        for item in dlist:
+            out_list.append((self.output_bands[item][0],
+                             self.output_bands[item][1]))
+        return out_list
+    
+    def show_all(self,*args):
+        self.frame.show_all()
+ 
+    def show(self,*args):
+        self.frame.show()
+
+
+def CopyGDALGCP(gcp):
+    ngcp=gdal.GCP()
+    ngcp.GCPPixel=gcp.GCPPixel
+    ngcp.GCPLine=gcp.GCPLine
+    ngcp.GCPX=gcp.GCPX
+    ngcp.GCPY=gcp.GCPY
+    ngcp.GCPZ=gcp.GCPZ
+    ngcp.Id=gcp.Id
+    ngcp.Info=gcp.Info
+
+    return ngcp
+
+def CopyGDALGCPs(gcplist):
+    ngcplist=[]
+    for gcp in gcplist:
+        ngcplist.append(CopyGDALGCP(gcp))
+    return ngcplist
+
+               
+########################################################################
+def get_raster_size(_layer):
+    w =  _layer.get_parent().get_dataset().GetRasterBand(1).XSize
+    h =  _layer.get_parent().get_dataset().GetRasterBand(1).YSize
+    return (w,h)
+ 
+
+#########################################################################
+def get_list_of_bands_as_dict(size=None):
+    """Returns dictionary of the bands of the opened layers. The key 
+    of dictionary is the GtkListItem object produced from the name of
+    layer+band number, members of dictionary are view-object (index = 0),
+    layer-object (index = 1), band-number and name"""
+
+    layer = gview.app.sel_manager.get_active_layer()
+    if layer is None:
+	return {}
+    if size is None:
+        size = get_raster_size(layer)
+    dict = {}
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    curname = curlayer.get_name()
+            cursize = get_raster_size(curlayer)
+	    if cursize == size:
+		num_bands = curlayer.get_parent().get_dataset().RasterCount
+                ds=curlayer.get_parent().get_dataset()
+		for i in range(1,num_bands+1):
+	    	    curband = curname + '.band['+ str(i) + ']'
+		    dict[gtk.GtkListItem(curband)] = (ds,i,curband)
+    if dict is None:
+	return None
+    return dict
+
+
+TOOL_LIST = ['ComposeTool']
+

Added: packages/openev/branches/upstream/current/tools/fft.py
===================================================================
--- packages/openev/branches/upstream/current/tools/fft.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/fft.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,135 @@
+###############################################################################
+# $Id: fft.py,v 1.1 2004/07/24 15:17:13 andrey_kiselev Exp $
+#
+# Project:  OpenEV Python tools
+# Purpose:  Tool to calculate forward or inverse Fast Fourier Transform
+# Author:   Andrey Kiselev, dron at remotesensing.org
+#
+###############################################################################
+# Copyright (c) 2004, Andrey Kiselev <dron at remotesensing.org>
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+###############################################################################
+#
+#  $Log: fft.py,v $
+#  Revision 1.1  2004/07/24 15:17:13  andrey_kiselev
+#  New.
+#
+#
+
+from gtk import *
+
+import gview
+import gviewapp
+import gdalnumeric
+import gvutils
+import FFT
+
+def layer_is_raster(layer):
+    """Return TRUE if layer is raster and FALSE otherwise"""
+    try:
+        layer.get_nodata(0)
+        return TRUE
+    except:
+        return FALSE
+
+class FFTTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+	self.win = FFTDialog()
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Filter/FFT",2,self.launch_dialog)
+
+class FFTDialog(GtkWindow):
+    def __init__(self,app=None):
+        
+        GtkWindow.__init__(self)
+        self.set_title('Fast Fourier Transform')
+        self.create_gui()
+        self.show()
+
+    def show(self):
+        GtkWindow.show_all(self)
+
+    def close(self, *args):
+        self.hide()
+        self.visibility_flag = 0
+        return TRUE
+
+    def create_gui(self):
+        box1 = GtkVBox(spacing = 10)
+	box1.set_border_width(10)
+        self.add(box1)
+        box1.show()
+
+	self.switch_forward = GtkRadioButton(None, "Forward")
+	box1.pack_start(self.switch_forward)
+	self.switch_forward.show()
+	self.switch_inverse = GtkRadioButton(self.switch_forward, "Inverse")
+	box1.pack_start(self.switch_inverse)
+	self.switch_inverse.show()
+
+	separator = GtkHSeparator()
+	box1.pack_start(separator, expand=FALSE)
+
+	self.switch_new_view = GtkCheckButton("Create new view")
+	box1.pack_start(self.switch_new_view)
+	self.switch_new_view.show()
+
+	separator = GtkHSeparator()
+	box1.pack_start(separator, expand=FALSE)
+
+	box2 = GtkHBox(spacing=10)
+        box1.pack_start(box2, expand=FALSE)
+        box2.show()
+
+        execute_btn = GtkButton("Ok")
+        execute_btn.connect("clicked", self.execute_cb)
+	box2.pack_start(execute_btn)
+        
+        close_btn = GtkButton("Cancel")
+        close_btn.connect("clicked", self.close)
+        box2.pack_start(close_btn)
+
+    def execute_cb( self, *args ):
+	layer = gview.app.sel_manager.get_active_layer()
+	if not layer_is_raster(layer):
+	    gvutils.error("Please select a raster layer.");
+	    return
+	ds = layer.get_parent().get_dataset()
+	data = gdalnumeric.DatasetReadAsArray(ds)
+
+	if self.switch_forward.get_active():
+	    data_tr = FFT.fft2d(data)
+	else:
+	    data_tr = FFT.inverse_fft2d(data)
+
+	array_name = gdalnumeric.GetArrayFilename(data_tr)
+	if self.switch_new_view.get_active():
+	    gview.app.new_view()
+	gview.app.file_open_by_name(array_name)
+
+TOOL_LIST = ['FFTTool']
+

Added: packages/openev/branches/upstream/current/tools/gvrastertools.py
===================================================================
--- packages/openev/branches/upstream/current/tools/gvrastertools.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/gvrastertools.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,232 @@
+###############################################################################
+# $Id: gvrastertools.py,v 1.5 2004/01/12 16:54:56 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Raster tools for OpenEV built using Gillian's Tool Architecture.
+#           Currently just Histogram.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2002, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: gvrastertools.py,v $
+#  Revision 1.5  2004/01/12 16:54:56  warmerda
+#  Properly apply scaling for histogram data.
+#  http://bugzilla.remotesensing.org/show_bug.cgi?id=457
+#
+#  Revision 1.4  2002/07/07 21:08:18  warmerda
+#  avoid depending on Numeric at the top level
+#
+#  Revision 1.3  2002/04/25 15:19:58  gmwalter
+#  Updated the menu extension mechanism to get rid of the extend_menu function and to allow the help menu to remain on the right side of the menubar.  Got rid of some extra print statements and the gnuplot 0 y-range warning in the histogram tool initialization.
+#
+#  Revision 1.2  2002/04/18 20:36:09  warmerda
+#  ensure the histogram dialog gets initialized
+#
+#  Revision 1.1  2002/04/18 20:23:24  warmerda
+#  New
+#
+
+import gviewapp
+import gtk
+import os
+import gdal
+import string
+import gvsignaler
+import toolexample
+
+FALSE = gtk.FALSE
+TRUE = gtk.TRUE
+import gview,gvutils
+
+
+class HistogramROITool(toolexample.GeneralROITool):
+
+    def __init__(self,app=None):
+        toolexample.GeneralROITool.__init__(self,app)
+
+    def init_dialog(self):
+        self.RP_ToolDlg = Histogram_ToolDlg()
+
+    def init_menu(self):
+        # print 'in Histogram init_menu()'
+        self.menu_entries.set_entry("Tools/Histogram Tool",2,
+                                    self.roipoitool_cb)
+
+    def update_dlgroi_frame(self,*args):
+        # Update frame as in general tool
+        if self.RP_ToolDlg.is_active():       
+            roi_info = self.RP_Stored.roi
+            self.RP_ToolDlg.update_roiframe(roi_info)
+
+        else:
+            return
+
+        self.update_roi_view()
+
+        if self.RP_ToolDlg.is_auto_updating():
+            # Automatic analysis is on
+            self.RP_ToolDlg.analyze_cb(self)
+
+    def update_roi_view(self,*args):
+        pass
+    
+    def analyze_cb(self,*args):
+        # Find the view and layer
+        cview = self.app.view_manager.get_active_view_window().title
+        clayer = self.app.sel_manager.get_active_layer()
+        if (cview is None) or (clayer is None):
+            # analysis only makes sense in the context of a view and layer
+            gvutils.error('No View/Layer selected!')
+            return
+
+        text = self.basic_region_analysis(cview,clayer) 
+
+    def basic_region_analysis(self,cview,clayer):
+	import Numeric, gdalnumeric
+
+        line = int(self.RP_ToolDlg.entry_dict['start_line'].get_text())
+        pix = int(self.RP_ToolDlg.entry_dict['start_pix'].get_text())
+        sl =  int(self.RP_ToolDlg.entry_dict['num_lines'].get_text())
+        sp =  int(self.RP_ToolDlg.entry_dict['num_pix'].get_text())
+
+        clayer = self.app.sel_manager.get_active_layer()
+        if clayer is None:
+            # Target can only be extracted if a layer is selected
+            return 'No selected raster layer'
+
+        dsb = clayer.get_parent().get_band()
+
+        # Modify default to be whole image.
+        if sl is 1 and sp is 1 and line is 1 and pix is 1:
+            sl = dsb.YSize - line + 1
+            sp = dsb.XSize - pix + 1
+
+        # Create a temporary band for sampling if we have a subrect.
+        temp_copy = 0
+        if line != 1 or pix != 1 or sp != dsb.XSize or sl != dsb.YSize:
+            # print line, pix, sp, sl
+            temp_copy = 1
+            filename = clayer.get_parent().get_dataset().GetDescription()
+            target_data = gdalnumeric.LoadFile(filename,pix-1,line-1,sp,sl)
+            target_ds = gdalnumeric.OpenArray(target_data)
+            dsb = target_ds.GetRasterBand(1)
+
+        # Compute the histogram.
+        
+        min = clayer.min_get(0)
+        max = clayer.max_get(0)
+        histogram = dsb.GetHistogram( min, max, 256, 1, 0 )
+
+        (pm, mask) = self.RP_ToolDlg.get_histview( histogram, min, max )
+        
+        self.RP_ToolDlg.viewarea.set(pm,mask)
+        
+class Histogram_ToolDlg(toolexample.General_ROIToolDlg):
+    def __init__(self):
+        toolexample.General_ROIToolDlg.__init__(self)
+
+    def init_setup_window(self):
+        self.set_title('Raster Histogram Tool')
+        self.set_border_width(10)
+
+    def init_customize_gui_panel(self):
+        # Inherit all the usual stuff...
+        toolexample.General_ROIToolDlg.init_customize_gui_panel(self)
+
+        # Add new frame with pixel info, keeping track of
+        # the frame and text object...
+        self.frame_dict['histogram_frame'] = gtk.GtkFrame()
+        self.show_list.append(self.frame_dict['histogram_frame'])
+        
+        # Initialize with dummy histogram.
+        
+        histogram = []
+        for i in range(256):
+            histogram.append( 0 )
+
+        (pm, mask) = self.get_histview(histogram,0,256,-1,1)
+        self.viewarea = gtk.GtkPixmap(pm,mask)
+        self.entry_dict['histogram_view'] = self.viewarea
+        self.show_list.append(self.viewarea)
+        self.frame_dict['histogram_frame'].add(self.viewarea)
+
+        self.main_panel.pack_start(self.frame_dict['histogram_frame'],
+                                   gtk.TRUE,gtk.TRUE,0)   
+
+    def set_entry_sensitivities(self,bool_val):
+        self.entry_dict['start_line'].set_sensitive(bool_val)
+        self.entry_dict['start_pix'].set_sensitive(bool_val)
+        self.entry_dict['num_lines'].set_sensitive(bool_val)
+        self.entry_dict['num_pix'].set_sensitive(bool_val)
+
+    def activate_toggled(self,*args):
+        if self.button_dict['Activate'].get_active():
+            self.set_entry_sensitivities(gtk.TRUE)
+            self.button_dict['Auto Update'].set_sensitive(gtk.TRUE)
+            self.button_dict['Auto Update'].set_active(gtk.FALSE)
+            self.button_dict['Analyze'].set_sensitive(gtk.TRUE)
+            self.button_dict['Set Tool'].set_sensitive(gtk.TRUE)
+            self.notify('re-activated')
+        else:
+            self.set_entry_sensitivities(gtk.FALSE)
+            self.button_dict['Auto Update'].set_active(gtk.FALSE)
+            self.button_dict['Auto Update'].set_sensitive(gtk.FALSE)
+            self.button_dict['Analyze'].set_sensitive(gtk.FALSE)
+            self.button_dict['Set Tool'].set_sensitive(gtk.FALSE)
+       
+    def get_histview( self, histogram, min, max,set_ymin=None,set_ymax=None ):
+
+        # Prepare histogram plot
+
+        data = []
+        bucket_width = (max - min) / 256.0
+        for bucket_index in range(256):
+            data.append( (min + bucket_width * bucket_index,
+                          histogram[bucket_index]) )
+
+        # Generate histogram graph
+        
+        import gvplot
+
+        xpm_file = gvutils.tempnam(extension='xpm')
+        if ((set_ymin is not None) and (set_ymax is not None)):
+            # Allows you to explicitly set ymin/ymax at startup
+            # to avoid the gnuplot 0 y-range warning.
+            gvplot.plot( data, xaxis = 'Pixel Value', yaxis = 'Count',
+                         xmin = min, xmax = max, ymin = set_ymin,
+                         ymax = set_ymax, output = xpm_file,
+                         title = 'Histogram', terminal = 'xpm' )
+        else:
+            gvplot.plot( data, xaxis = 'Pixel Value', yaxis = 'Count',
+                         xmin = min, xmax = max, output = xpm_file,
+                         title = 'Histogram', terminal = 'xpm' )
+
+        # apply it to display.
+
+        gdk_pixmap, gdk_mask = \
+              gtk.create_pixmap_from_xpm(self,None,xpm_file)
+
+        os.unlink( xpm_file )
+
+        return (gdk_pixmap, gdk_mask)
+
+
+TOOL_LIST = ['HistogramROITool']
+

Added: packages/openev/branches/upstream/current/tools/imgproctemplate.py
===================================================================
--- packages/openev/branches/upstream/current/tools/imgproctemplate.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/imgproctemplate.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,193 @@
+###############################################################################
+# $Id: imgproctemplate.py,v 1.1 2003/06/09 16:00:11 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Template Image Processing "tool".  Seach for CHANGEME.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2002, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: imgproctemplate.py,v $
+#  Revision 1.1  2003/06/09 16:00:11  warmerda
+#  New
+#
+#
+
+from gtk import *
+
+import gview
+import string           
+import gvutils
+import GtkExtra
+import os
+import sys
+import gviewapp		
+import gdalnumeric
+
+class ImageProcTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        self.win = ImageProcDialog()
+        self.win.update_gui()
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Image Processing",2,
+                                    self.launch_dialog)
+
+class ImageProcDialog(GtkWindow):
+
+    def __init__(self,app=None):
+        GtkWindow.__init__(self)
+
+        self.set_title('Image Processing')
+
+        self.create_gui()
+        self.show()
+
+    def show(self):
+        GtkWindow.show_all(self)
+
+    def close(self, *args):
+        self.hide()
+        self.visibility_flag = 0
+        return TRUE
+
+    def create_gui(self):
+        box1 = GtkVBox()
+        self.add(box1)
+        box1.show()
+
+        text = GtkText()
+        text.set_usize(400,150)
+        text.set_line_wrap(FALSE)
+        text.set_word_wrap(FALSE)
+        text.set_editable(FALSE)
+        text.show()
+        self.text = text
+        box1.pack_start(text, expand=TRUE)
+
+        box2 = GtkHBox()
+        box1.pack_start(box2, expand=FALSE)
+        box2.show()
+
+        self.execute_btn = GtkButton("Execute")
+        self.execute_btn.connect("clicked", self.execute_cb)
+        box2.pack_start(self.execute_btn)
+        
+        self.close_btn = GtkButton("Close")
+        self.close_btn.connect("clicked", self.close)
+        box2.pack_start(self.close_btn)
+
+    def update_gui(self,*args):
+        pass
+
+    ##########################################################################
+    # Return the currently selected raster layer.
+    #
+    # Reports an error and returns None if there isn't a suitable raster
+    # layer selected
+    #
+    def get_cur_rlayer(self):
+
+        rlayer = gview.app.sel_manager.get_active_layer()
+
+        if rlayer is None:
+            gvutils.error( 'Please select a raster layer.' )
+            return None
+
+        # This will only work for a real GvRasterLayer
+        try:
+            nd = rlayer.get_nodata(0)
+            return rlayer
+        
+        except:
+            gvutils.error( 'Please select a raster layer.' )
+            return None
+
+    ##########################################################################
+    # Load indicated raster layer into a memory array.
+    #
+    # Note that all bands are loaded, not just the ones being displayed in
+    # in the view. 
+
+    def load_layer(self, rl, xoff=0, yoff=0, xsize=None, ysize=None ):
+        filename = rl.get_parent().get_dataset().GetDescription()
+        target_data = gdalnumeric.LoadFile(filename,xoff,yoff,xsize,ysize)
+        return target_data;
+
+    ##########################################################################
+    # Save image array into existing raster file.
+    #
+
+    def save_layer(self, rl, rd, xoff=0, yoff=0 ):
+        ds = rl.get_parent().get_dataset()
+
+        if len(rd.shape) == 3:
+            for iBand in range(len(rd.shape)):
+                gdal_band = ds.GetRasterBand(iBand+1)
+                rd_band = rd[iBand]
+
+                gdal_band.WriteArray( rd_band, xoff, yoff )
+        else:
+                gdal_band = ds.GetRasterBand(1)
+                gdal_band.WriteArray( rd, xoff, yoff )
+
+    ##########################################################################
+    # Main algorithm execution callback.
+    
+    def execute_cb( self, *args ):
+
+        print 'Entering execute_cb()'
+
+        try:
+            # Determine the currently selected raster layer.
+            rl = self.get_cur_rlayer()
+            if rl is None:
+                print 'didnt get rlayer'
+                return
+
+            # Load it into memory.
+            rd = self.load_layer( rl )
+            if rd is None:
+                print 'didnt load layer'
+                return 
+
+            # Apply algorithm to raster data. *** CHANGEME ***
+            rd = rd * 0.5
+
+            # Save results back into source raster layer.
+            self.save_layer( rl, rd )
+            rl.refresh()
+            
+        except:
+            print 'Trapped Error'
+            sys.excepthook(sys.exc_info()[0],
+                           sys.exc_info()[1],
+                           sys.exc_info()[2])
+
+        print 'Exiting execute_cb()'
+
+TOOL_LIST = ['ImageProcTool']
+

Added: packages/openev/branches/upstream/current/tools/isodata.py
===================================================================
--- packages/openev/branches/upstream/current/tools/isodata.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/isodata.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,561 @@
+##############################################################################
+# $Id: isodata.py,v 1.1 2005/08/26 18:27:35 andrey_kiselev Exp $
+#
+# Project:  OpenEV
+# Purpose:  ISODATA clustering
+# Author:   Iscander Latypov
+#	    Andrey Kiselev, dron at remotesensing.org
+#
+###############################################################################
+# Copyright (c) 2004, American Museum of Natural History. All rights reserved.
+# This software is based upon work supported by NASA under award
+# number NAG5-12333
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: isodata.py,v $
+#  Revision 1.1  2005/08/26 18:27:35  andrey_kiselev
+#  New.
+#
+#
+
+from gtk import *
+
+from Numeric import *
+import gview
+import gvutils
+import gviewapp
+import gdal
+from gdalconst import *
+import gdalnumeric
+
+
+   
+########################################################################
+def get_raster_size(_layer):
+    w =  _layer.get_parent().get_dataset().GetRasterBand(1).XSize
+    h =  _layer.get_parent().get_dataset().GetRasterBand(1).YSize
+    return (w,h)
+
+########################################################################
+def get_list_of_layers_as_dict():
+    """Returns dictionary of opened layers. The key of dictionary is
+    the name of layer, members of dictionary are view object (index = 0)
+    and layer object (index = 1)"""
+
+    dict = {}
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    curname = curlayer.get_name()
+	    dict[curname] = (curview,curlayer) 
+    if dict is None:
+	return None
+    return dict
+
+########################################################################
+def get_list_of_layers_as_menu():
+    """Returns GtkMenu object for selecting of opened layers. 
+    If number of opened layers is zero, the result is None"""
+    menu = GtkMenu()
+    group = None
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    curname = curlayer.get_name()
+	    menuitem = GtkRadioMenuItem(group,curname)
+	    group = menuitem
+	    menu.append(menuitem)
+	    menuitem.show()
+    if group is None:
+	return None
+    return menu
+	    
+#########################################################################
+def get_list_of_bands_as_dict():
+    """Returns dictionary of the bands of the opened layers. The key 
+    of dictionary is the name of layer+band number, members of dictionary 
+    are view-object (index = 0), layer-object (index = 1) and band-number"""
+
+    _size = get_raster_size(gview.app.sel_manager.get_active_layer())
+    dict = {}
+    for curview in gview.app.view_manager.view_list:
+        for curlayer in curview.viewarea.list_layers():
+    	    curname = curlayer.get_name()
+	    cursize = get_raster_size(curlayer)
+	    if cursize == _size:
+		num_bands = curlayer.get_parent().get_dataset().RasterCount
+		for i in range(num_bands):
+	    	    curband = curname + '['+ str(i) + ']'
+		    dict[curband] = (curview,curlayer,i) 
+    if dict is None:
+	return None
+    return dict
+
+############################################################################
+#
+#
+############################################################################
+def L1(v1,v2):
+    return sum(abs(v1-v2))
+###########################################################################    
+def L2(v1,v2):
+    delta = (v1-v2)
+    return dot(delta,delta)
+###########################################################################
+def L3(v1,v2):
+    delta = abs(v1-v2)
+    return max(delta)
+###########################################################################
+class IsodataTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        self.win = ISODATADialog()
+	if self.win is None:
+	    return
+	else:
+	    self.win.update_gui()
+    	    self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Image/Classification/ISOData",2,
+                                    self.launch_dialog)
+
+############################################################################
+class ClassesTable(GtkWindow):
+    def __init__(self,nBands,app=None):
+	GtkWindow.__init__(self)
+	self.set_title('Classification Info')
+	self.nBands = nBands
+	self.create_gui()
+	self.stop_flag = FALSE
+	GtkWindow.show_all(self)
+
+    def close(self,*args):
+	self.destroy()
+	return TRUE
+	
+    def create_gui(self):
+	box1 = GtkVBox(spacing=5)     	
+        self.add(box1)
+        box1.show()
+
+	self.line_label = GtkLabel()
+	box1.pack_start(self.line_label)
+
+	self.it_label = GtkLabel()
+	box1.pack_start(self.it_label)
+	
+	self.data_list = GtkCList(cols=self.nBands+1)
+	self.data_list.set_selection_mode(SELECTION_SINGLE)
+	self.data_list.set_column_width(0,20)
+	for i in range(self.nBands):
+	    self.data_list.set_column_width(i+1,60)
+	box1.pack_start(self.data_list)
+	self.data_list.show()
+
+	self.progress_bar = GtkProgressBar()
+	box1.pack_start(self.progress_bar)
+	
+	self.stop_button = GtkButton("Stop process")
+	self.stop_button.connect("clicked",self.stop_cb)
+	box1.pack_start(self.stop_button)
+
+    def set_data(self,centres,n_iter):
+	self.it_label.set_text("Classes after "+str(n_iter+1)+" iterations") 
+	self.data_list.freeze()
+	self.data_list.clear()
+	for i in range(len(centres)):
+	    cur_list = [str(i)]
+	    for j in range(self.nBands):
+	        #c = str(centres[i][j])
+		c = "%7.2f"%centres[i][j]
+		cur_list.append(c)
+	    self.data_list.append(cur_list)
+	
+	self.data_list.thaw()
+
+    def stop_cb(self,*args):
+	self.stop_flag = TRUE
+	self.progress_bar.hide()
+	self.stop_button.hide()
+	    
+    def stop_pressed(self,*args):
+	return self.stop_flag
+	
+    def show_progress(self,percents,line):
+	self.line_label.set_text("Current Line Number "+str(line)) 
+	self.progress_bar.set_percentage(percents/100.)
+	while events_pending():
+	    mainiteration(FALSE)
+
+    def finish(self):
+	self.line_label.hide()
+	self.it_label.set_text("Classification Complete") 
+############################################################################
+class ISODATADialog(GtkWindow):
+
+    def __init__(self,app=None):
+	GtkWindow.__init__(self)
+	self.set_title('ISODATA Classification')
+	self.set_policy(FALSE, TRUE, TRUE)
+	self.text_pos = 0
+	self.tooltips = GtkTooltips()
+	
+        try:
+    	    gdal_dataset = gview.app.sel_manager.get_active_layer().get_data().get_dataset()
+        except:
+	    gvutils.error("Active Layer is not a raster layer")
+    	    return None 
+	    		
+	self.band_list_to_classify = []    		
+		   	
+	gui_OK = self.create_gui()
+	if gui_OK is FALSE:
+	    return None
+	else:
+	    GtkWindow.show_all(self)
+	
+    def close(self,*args):
+	self.destroy()
+	return TRUE
+
+######################################################################
+    def create_gui(self):
+
+	self.dict_of_bands = get_list_of_bands_as_dict()
+	self.list_of_bands = self.dict_of_bands.keys()
+	self.list_of_bands.sort()
+	
+	title_width = 120
+	self.metric_num = 0
+
+	box1 = GtkVBox(spacing=5)     	
+        self.add(box1)
+        box1.show()
+
+	rastersbox = GtkVBox(spacing=5)
+	box1.pack_start(rastersbox)
+	rastersbox.show()
+	
+### source list #############################################################	
+	frame1 = GtkFrame("Raster Specifications")
+	frame1.show()
+        rastersbox.pack_start(frame1, expand=FALSE)
+	
+	box2r = GtkHBox(spacing=10)
+        box2r.set_border_width(10)
+	frame1.add(box2r)
+	box2r.show()
+			
+	box2r1 = GtkVBox(spacing=5)
+	box2r.add(box2r1)
+	box2r1.show()
+	
+	self.src_list = GtkCList(cols=1)
+	self.src_list.set_selection_mode(SELECTION_SINGLE)
+	self.src_list.set_column_width(0,120)
+	self.src_list.connect('button-press-event',self.src_band_selected_cb)
+	self.src_list.freeze()
+	self.src_list.clear()
+	
+	for name in self.list_of_bands:
+	    self.src_list.append([name])
+	self.src_list.thaw()
+	box2r1.pack_start(self.src_list)
+	self.src_list.show()
+
+	box2r2 = GtkVBox(spacing=5)
+	box2r.add(box2r2)
+	box2r2.show()
+
+	button_copy = GtkButton(">>")
+	box2r2.pack_start(button_copy)
+	button_copy.connect("clicked",self.copy_band_cb)
+	button_remove = GtkButton("<<")
+	box2r2.pack_start(button_remove)
+	button_remove.connect("clicked",self.remove_band_cb)
+
+	box2r3 = GtkVBox(spacing=5)
+	box2r.add(box2r3)
+	box2r3.show()
+	
+	self.sel_list = GtkCList(cols=1)
+	self.sel_list.set_selection_mode(SELECTION_SINGLE)
+	self.sel_list.connect('button-press-event',self.sel_band_selected_cb)
+	self.sel_list.set_column_width(0,120)
+	box2r3.pack_start(self.sel_list)
+	self.sel_list.show()
+
+	self.src_row_to_copy   = -1
+	self.sel_row_to_delete = -1
+	
+#### Entries ###########################################################
+	entries_table = GtkTable(3,4)
+	entries_table.set_border_width(5) 
+	box1.pack_start(entries_table)
+        
+	label = GtkLabel("Desirable Number of Clusters")
+	entries_table.attach(label,0,2,0,1)
+	self.clusters = GtkEntry()
+	self.clusters.set_text("8")
+	entries_table.attach(self.clusters,2,3,0,1)
+	
+	
+	label = GtkLabel("Maximal Number of Iterations")
+	entries_table.attach(label,0,2,1,2)
+	self.maxiter = GtkEntry()
+	self.maxiter.set_text("20")
+	entries_table.attach(self.maxiter,2,3,1,2)
+	
+	label = GtkLabel("Minimal Cluster Volume")
+	entries_table.attach(label,0,2,2,3)
+	self.min_volume = GtkEntry()
+	self.min_volume.set_text("1000")
+	entries_table.attach(self.min_volume,2,3,2,3)
+
+	label = GtkLabel("Movement Classes Treshold")
+	entries_table.attach(label,0,2,3,4)
+	self.mov_treshold = GtkEntry()
+	self.mov_treshold.set_text("0.1")
+	entries_table.attach(self.mov_treshold,2,3,3,4)
+#######################################################################
+	metric_box = GtkHBox(spacing=10)
+	box1.pack_start(metric_box)
+	
+	metric_list = ["Manhattan Metric","Euclidian Metric","Chebyshev Metric"]
+	self.metric_menu = gvutils.GvOptionMenu(metric_list,self.metrics_selected_cb)
+	metric_box.pack_start(self.metric_menu)
+#### Buttons ###########################################################
+	button_box = GtkHBox(spacing = 10)
+        button_box.set_border_width(10)
+	box1.pack_start(button_box)
+
+	button_ok = GtkButton("Classify")
+	button_ok.connect("clicked",self.classify_cb)
+	button_box.pack_start(button_ok)
+	
+	button_cancel = GtkButton("Cancel")
+	button_cancel.connect("clicked",self.close)
+	button_box.pack_start(button_cancel)
+	
+        return TRUE
+########################################################################
+    def metrics_selected_cb(self,*args):
+	self.metric_num = self.metric_menu.get_history()
+########################################################################
+    def src_band_selected_cb(self,widget,event,*args):
+	try:
+	    row,col = widget.get_selection_info(int(event.x),int(event.y))
+	except:
+	    return
+	self.src_row_to_copy = row
+########################################################################
+    def sel_band_selected_cb(self,widget,event,*args):
+	try:
+	    row,col = widget.get_selection_info(int(event.x),int(event.y))
+	except:
+	    return
+	self.sel_row_to_delete = row 
+########################################################################	    
+    def copy_band_cb(self,*args):
+	if self.src_row_to_copy > -1:
+	    text = self.src_list.get_text(self.src_row_to_copy,0)	   
+    	    self.sel_list.freeze()
+	    self.sel_list.append([text])
+	    self.sel_list.thaw()
+	    self.src_list.remove(self.src_row_to_copy)
+	    self.src_row_to_copy = -1
+	    self.band_list_to_classify.append(self.get_band(text)) 
+########################################################################	
+    def remove_band_cb(self,*args):
+	if self.sel_row_to_delete > -1:
+    	    self.sel_list.freeze()
+	    text = self.sel_list.get_text(self.sel_row_to_delete,0)	   
+	    self.sel_list.remove(self.sel_row_to_delete)
+	    self.sel_list.thaw()	
+	    self.src_list.append([text])	   
+	    self.sel_row_to_delete = -1
+	    self.band_list_to_classify.delete(self.get_band(text))
+#########################################################################
+    def update_gui(self,*args):
+	pass
+#########################################################################	
+    def get_band(self,name):
+	layer = self.dict_of_bands[name][1]
+	b_num = self.dict_of_bands[name][2]
+	band = layer.get_parent().get_dataset().GetRasterBand(b_num+1)
+	return band	    
+#########################################################################	
+    def classify_cb(self,*args):
+	if len(self.band_list_to_classify) < 2:
+	    return
+	if self.metric_num == 0:
+	    distance = L1
+	elif self.metric_num == 2:
+	    distance = L3
+	else: 
+	    distance = L2
+	nClusters = int(self.clusters.get_text())
+	minvol = int(self.min_volume.get_text())
+	maxit = int(self.maxiter.get_text())
+	movement_treshold = float(self.mov_treshold.get_text())
+	nBands = len(self.band_list_to_classify)
+	cl_table = ClassesTable(nBands)
+	self.hide()
+
+	gview.app.new_view()
+
+	b1_name = self.sel_list.get_text(0,0)
+	band_list = self.band_list_to_classify
+	b1 = self.get_band(b1_name)
+	proto_ds = self.dict_of_bands[b1_name][1].get_parent().get_dataset()
+	self.out_buf = zeros((b1.YSize, b1.XSize),UnsignedInt8)
+
+
+	sb = range(len(band_list))
+	cp = zeros(nBands)
+	d_means = zeros(nClusters,Float)
+	volumes = range(nClusters)
+	
+	centres = []
+	disp = []
+	maxVal = 256.
+	ymax = b1.YSize / 20
+	ys = 20
+	max_percent_val = 11 * maxit * b1.YSize / 40
+	cur_percent_val = 0
+	for i in range(nClusters):
+	    centres.append(((ones(nBands,Float)*i)*maxVal)/nClusters)
+	    disp.append(zeros(nBands,Float))
+	for kit in range(maxit):
+	    centres_s = []
+	    for i in range(len(centres)):
+		volumes[i] = 0
+		centres_s.append(zeros(nBands,Float))
+	    flag = 0
+	    nc = len(centres)
+	    if kit >= maxit/2:
+	        ymax = b1.YSize
+		ys = 1
+	    for yc in range(ymax):
+		y = yc * ys
+		for i in range(nBands):
+		    sb[i] = gdalnumeric.BandReadAsArray(band_list[i],0,y,b1.XSize,1)[0]
+
+		rb = self.out_buf[y,0:]
+		for x in range(b1.XSize):
+		    for i in range(nBands):
+			cp[i] = sb[i][x]
+		    mind = 1e29
+		    for i in range(nc):
+			d = distance(centres[i],cp)
+			if d < mind:
+			    mind = d
+			    ic = i
+		    if rb[x] != ic:
+			rb[x] = ic
+			flag = 1
+		    centres_s[ic] += cp
+		    disp[ic] += cp * cp 
+		    volumes[ic] += 1
+		    if (distance == L2):
+			d_means[ic] += sqrt(mind)
+		    else:
+			d_means[ic] += mind
+		self.out_buf[y, 0:] = rb
+		if y%2 == 0:
+		    cur_percent_val += 1
+		    cl_table.show_progress(100.*cur_percent_val/max_percent_val,y)
+	    ##########################################################
+	    new_centres = []
+	    d_tresh = sum(d_means)/sum(volumes)
+	    for i in range(len(centres)):
+	        cur_vol = volumes[i]
+		if cur_vol > minvol/ys:
+		    cur_centre = centres_s[i]/cur_vol
+		    disp[i]  = disp[i] / cur_vol - cur_centre * cur_centre
+		    disp[i] = sqrt(disp[i])
+		    d_means[i] /= cur_vol
+		    if len(centres) < nClusters and cur_vol > 2 * minvol/ys and d_means[i] > d_tresh:
+			new_centres.append(cur_centre-disp[i])
+			new_centres.append(cur_centre+disp[i])
+		    else:			    
+		    	new_centres.append(cur_centre)
+		    
+	    ncl = len(new_centres)
+	    if ncl > nClusters:
+		mind = 1e29
+		for i in range(ncl):
+		    for j in range(i):
+		        d = distance(new_centres[i],new_centres[j])
+			if d < mind:
+			    mind = d
+			    i1 = i
+			    i2 = j
+		new_centres[i1] += new_centres[i2]
+		new_centres[i1] /= 2
+		new_centres.remove(new_centres[i2])
+				
+	    cl_table.set_data(new_centres,kit)
+	    
+	    if cl_table.stop_pressed():
+		if ys > 1:
+	    	    ymax = b1.YSize
+		    ys = 1
+		else:
+		    break       #stop_pressed
+		
+	    if len(centres) == len(new_centres):
+		movement_flag = 0
+		for i in range(len(centres)):
+		    d = distance(centres[i],new_centres[i])
+		    d /= d_tresh
+		    if d > movement_treshold:
+			movement_flag = 1	
+
+		if movement_flag == 0:
+		    if ys > 1:
+	    		ymax = b1.YSize
+			ys = 1
+		    else:
+			break		# centres movement less then treshold
+		
+	    centres = new_centres
+	    disp = []
+	    for i in range(len(centres)):
+		disp.append(zeros(nBands,Float))
+	    volumes = zeros(len(centres))	
+	    d_means = zeros(len(centres))
+	    
+	    
+	    if flag == 0:
+		if ys > 1:
+	    	    ymax = b1.YSize
+		    ys = 1
+		else:
+		    break       #no changed points
+
+	cl_table.finish()
+	res_ds = gdalnumeric.OpenArray(self.out_buf,proto_ds)
+	gview.app.open_gdal_dataset(res_ds)
+	self.close()
+	
+TOOL_LIST = ['IsodataTool']
+

Added: packages/openev/branches/upstream/current/tools/mil_symbols.py
===================================================================
--- packages/openev/branches/upstream/current/tools/mil_symbols.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/mil_symbols.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,566 @@
+###############################################################################
+# $Id: mil_symbols.py,v 1.5 2005/02/14 15:30:33 zjamesatdm Exp $
+#
+# Purpose:  test page for military symbols
+# Author:   Paul Spencer (pgs at magma.ca)
+#
+###############################################################################
+# Copyright (c) 2003, Paul Spencer (pgs at magma.ca)
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: mil_symbols.py,v $
+#  Revision 1.5  2005/02/14 15:30:33  zjamesatdm
+#  removed project comment
+#
+#  Revision 1.4  2003/05/16 17:44:07  warmerda
+#  symbol_offset now the same as tf_offset.
+#
+#  Revision 1.3  2003/05/16 11:42:33  pgs
+#  cleaned up code
+#
+#  Revision 1.2  2003/05/15 21:08:13  pgs
+#  added some task force indicators
+#
+#  Revision 1.1  2003/05/15 18:50:58  pgs
+#  new file
+#
+#
+
+from gtk import *
+
+import gview
+import string           
+import gvutils
+import GtkExtra
+import os
+import gviewapp
+
+class MilitarySymbolTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        self.win = RenderTest()
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Military Symbols",2,
+                                    self.launch_dialog)
+
+class RenderTest(GtkWindow):
+
+    def __init__(self,app=None):
+        GtkWindow.__init__(self)
+
+        self.set_title('Military Symbols')
+
+        self.view = gview.app.sel_manager.get_active_view()
+        
+        self.text_contents = ''
+        self.selected_shape = None
+        self.layer = None
+        self.create_gui()
+
+        self.step_list = [ self.startup,
+                           self.size_symbols ]
+        self.step = 0
+        self.cleanup_func = None
+
+    def show(self):
+        GtkWindow.show_all(self)
+        self.show_step()
+
+    def close(self, *args):
+        self.hide()
+        self.visibility_flag = 0
+        return TRUE
+
+    def create_gui(self):
+        box1 = GtkVBox()
+        self.add(box1)
+        box1.show()
+
+        text = GtkText()
+        text.set_usize(400,150)
+        text.set_line_wrap(FALSE)
+        text.set_word_wrap(FALSE)
+        text.set_editable(FALSE)
+        text.show()
+        self.text = text
+        box1.pack_start(text, expand=TRUE)
+
+        box2 = GtkHBox()
+        box1.pack_start(box2, expand=FALSE)
+        box2.show()
+
+        self.prev_btn = GtkButton("<--- Previous")
+        self.prev_btn.connect("clicked", self.prev_cb)
+        box2.pack_start(self.prev_btn)
+        
+        self.next_btn = GtkButton("Next --->")
+        self.next_btn.connect("clicked", self.next_cb)
+        box2.pack_start(self.next_btn)
+
+    def next_cb( self, *args ):
+        if self.step < len(self.step_list)-1:
+            self.step = self.step + 1
+            self.show_step()
+
+    def prev_cb( self, *args ):
+        if self.step > 0:
+            self.step = self.step - 1
+            self.show_step()
+
+    def show_step( self ):
+        self.cleanup()
+        
+        func = self.step_list[self.step]
+        func()
+
+    def cleanup( self ):
+        if self.cleanup_func is not None:
+            self.cleanup_func()
+
+        layer_list = self.view.list_layers()
+        for layer in layer_list:
+            self.view.remove_layer( layer )
+
+    def set_step_name( self, text ):
+        self.set_title( '%d: %s' % (self.step, text) )
+    
+    def set_text( self, text ):
+
+        self.text.freeze()
+        self.text.delete_text(0,-1)
+        self.text.insert_defaults( text )
+        self.text.thaw()
+
+###############################################################################
+#   Initial test screen.
+
+    def startup( self ):
+        self.set_step_name( 'Military Symbols Introduction' )
+        self.set_text( 'Welcome to the military symbols rendering tool.' )
+
+
+###############################################################################
+#   Vector symbols (GvShapes via symbolmanager requested via ogrfs).
+        
+    def size_symbols( self ):
+        self.set_step_name( 'Military Size Symbols' )
+        self.set_text( \
+            'Here you will see each of the size designators for various sizes\n'
+            + 'of military symbols\n' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        sm = gview.GvSymbolManager()
+        
+        #symbol_size * base_size * 2 define the size of the symbol on screen.
+        base_size = 1.0
+        symbol_size = 3.0 
+        
+        #the number of pixels wide to make the task force indicator
+        tf_offset = ( symbol_size * base_size * 2 ) + 2 
+        symbol_offset = tf_offset
+                           
+        #create the circle symbol as a multiline with n points
+        import math
+        circle = gview.GvShape( type=gview.GVSHAPE_LINE )
+        filled_circle = gview.GvShape( type=gview.GVSHAPE_AREA )
+        n = 8
+        radius = base_size
+        for i in range( n + 1 ):
+            angle = (float(i)*(360.0/float(n))) / (180.0/math.pi)
+            x = math.sin( angle ) * radius
+            y = math.cos( angle ) * radius
+            circle.add_node( x, y, 0.0 )
+            filled_circle.add_node( x, y, 0.0 )
+        circle.set_property( "_gv_ogrfs", "PEN(w:1)" )
+        filled_circle.set_property( "_gv_ogrfs", "PEN(w:1);BRUSH()" )
+        sm.inject_vector_symbol( "%scircle" % os.sep, circle )
+        sm.inject_vector_symbol( "%sfilled_circle" % os.sep, filled_circle)
+        
+        #create a vertical bar symbol
+        shape = gview.GvShape(type=gview.GVSHAPE_LINE )
+        shape.add_node( 0.0, -base_size, 0.0 )
+        shape.add_node( 0.0, base_size, 0.0 )
+        shape.set_property( "_gv_ogrfs", "PEN(w:1)" )
+        sm.inject_vector_symbol( "%svertical_bar" % os.sep, shape )
+
+        #now create a cross from rotated vertical bars
+        shape = gview.GvShape(type=gview.GVSHAPE_LINE )
+        shape.add_node( -base_size, -base_size, 0.0 )
+        shape.add_node( base_size, base_size, 0.0 )
+        shape.set_property( "_gv_ogrfs", "PEN(w:1)" )
+        sm.inject_vector_symbol( "%sline_45" % os.sep, shape )
+
+        shape = gview.GvShape(type=gview.GVSHAPE_LINE )
+        shape.add_node( -base_size, base_size, 0.0 )
+        shape.add_node( base_size, -base_size, 0.0 )
+        shape.set_property( "_gv_ogrfs", "PEN(w:1)" )
+        sm.inject_vector_symbol( "%sline_315" % os.sep, shape )
+        
+        shape = gview.GvShape(type=gview.GVSHAPE_POINT )
+        shape.add_node( 0.0, 0.0, 0.0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(id:%sline_45);SYMBOL(id:%sline_315)" % ( os.sep, os.sep ) )
+        sm.inject_vector_symbol( "%scross" % os.sep, shape )
+
+        #taskforce 1
+        shape = self.create_taskforce_indicator( 1, symbol_size * base_size, tf_offset )
+        sm.inject_vector_symbol( "%stf_1" % os.sep, shape )
+        
+        #taskforce 2
+        shape = self.create_taskforce_indicator( 2, symbol_size * base_size, tf_offset )
+        sm.inject_vector_symbol( "%stf_2" % os.sep, shape )
+
+        #taskforce 3
+        shape = self.create_taskforce_indicator( 3, symbol_size * base_size, tf_offset )
+        sm.inject_vector_symbol( "%stf_3" % os.sep, shape )
+
+        #taskforce 4
+        shape = self.create_taskforce_indicator( 4, symbol_size * base_size, tf_offset )
+        sm.inject_vector_symbol( "%stf_4" % os.sep, shape )
+
+        #taskforce 5
+        shape = self.create_taskforce_indicator( 5, symbol_size * base_size, tf_offset )
+        sm.inject_vector_symbol( "%stf_5" % os.sep, shape )
+        
+        #taskforce 6
+        shape = self.create_taskforce_indicator( 6, symbol_size * base_size, tf_offset )
+        sm.inject_vector_symbol( "%stf_6" % os.sep, shape )
+        
+
+        #team symbol
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 0.0, 0.0, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(id:%scircle,s:%s);SYMBOL(id:%sline_45,s:%s)" % (os.sep, symbol_size, os.sep, symbol_size) )
+        sm.inject_vector_symbol( "%steam" % os.sep, shape )
+        
+        #squad symbol
+        shape = self.create_size_indicator( "filled_circle", 1, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%ssquad" % os.sep, shape )
+
+        #section symbol
+        shape = self.create_size_indicator( "filled_circle", 2, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%ssection" % os.sep, shape )
+
+        #platoon symbol
+        shape = self.create_size_indicator( "filled_circle", 3, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%splatoon" % os.sep, shape )
+
+        #company symbol
+        shape = self.create_size_indicator( "vertical_bar", 1, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%scompany" % os.sep, shape )
+        
+        #battalion symbol
+        shape = self.create_size_indicator( "vertical_bar", 2, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sbattalion" % os.sep, shape )
+        
+        #regiment symbol
+        shape = self.create_size_indicator( "vertical_bar", 3, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sregiment" % os.sep, shape )
+        
+        #brigade symbol
+        shape = self.create_size_indicator( "cross", 1, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sbrigade" % os.sep, shape )
+
+        #division symbol
+        shape = self.create_size_indicator( "cross", 2, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sdivision" % os.sep, shape )
+        
+        print 'Division:' + shape.get_property('_gv_ogrfs')
+        
+        #corps symbol
+        shape = self.create_size_indicator( "cross", 3, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%scorps" % os.sep, shape )
+        
+        #army symbol
+        shape = self.create_size_indicator( "cross", 4, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sarmy" % os.sep, shape )
+        
+        #armygroup symbol
+        shape = self.create_size_indicator( "cross", 5, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sarmygroup" % os.sep, shape )
+        
+        #region symbol
+        shape = self.create_size_indicator( "cross", 6, symbol_size, symbol_offset )
+        sm.inject_vector_symbol( "%sregion" % os.sep, shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 10, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"team\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 10, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%steam)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 10, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%steam);SYMBOL(c:#00FFFF,id:%stf_1" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 15, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"squad\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 15, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%ssquad)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 15, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%ssquad);SYMBOL(c:#00FFFF,id:%stf_1" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 20, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"section\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 20, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%ssection)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 20, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%ssection);SYMBOL(c:#00FFFF,id:%stf_2" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 25, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"platoon\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 25, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%splatoon)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 25, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%splatoon);SYMBOL(c:#00FFFF,id:%stf_3" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 30, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"company\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 30, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%scompany)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 30, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%scompany);SYMBOL(c:#00FFFF,id:%stf_1" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 35, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"battalion\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 35, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sbattalion)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 35, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sbattalion);SYMBOL(c:#00FFFF,id:%stf_2" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 40, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"regiment\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 40, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sregiment)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 40, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sregiment);SYMBOL(c:#00FFFF,id:%stf_3" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 45, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"brigade\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 45, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sbrigade)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 45, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sbrigade);SYMBOL(c:#00FFFF,id:%stf_1" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 50, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"division\")") 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 50, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sdivision)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sdivision);SYMBOL(c:#00FFFF,id:%stf_2" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 55, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"corps\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 55, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%scorps)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 55, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%scorps);SYMBOL(c:#00FFFF,id:%stf_3" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 60, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"army\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 60, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sarmy)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 60, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sarmy);SYMBOL(c:#00FFFF,id:%stf_4" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 65, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"armygroup\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 65, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sarmygroup)" % os.sep ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 65, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sarmygroup);SYMBOL(c:#00FFFF,id:%stf_5" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 10, 70, node=0 )
+        shape.set_property( "_gv_ogrfs", "LABEL(c:#00FFFF,t:\"region\")" ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 70, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sregion)" % os.sep ) 
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 70, node=0 )
+        shape.set_property( "_gv_ogrfs", "SYMBOL(c:#00FFFF,id:%sregion);SYMBOL(c:#00FFFF,id:%stf_6" % (os.sep,os.sep) ) 
+        shapes.append( shape )
+
+        # Create the layer and display
+
+        #turn off display lists for now.
+        #gview.set_preference('display_lists', "OFF" )
+        
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        layer.set_property( "_gl_antialias", "1" )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+        
+    def create_size_indicator( self, sym_name, repeat, size, offset ):
+        """
+        compose a size indicator based on some number (repeat) of symbols
+        (sym_name) drawn at scale (size). The symbols are drawn horizontally
+        spaced at (offset) pixels apart, centered at 0.
+        """
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 0.0, 0.0, node=0 )
+        ogrfs = ""
+        dx_val = -1 * (offset / 2) * (repeat - 1)
+        for i in range( repeat ):
+            dx = ""
+            if abs(dx_val) > 0.001:
+                dx = ",dx:%spx" % dx_val
+            sym = "SYMBOL(id:%s%s%s,s:%s)" % (os.sep, sym_name, dx, size)
+            if ogrfs != "":
+                ogrfs = ogrfs + ";"
+            ogrfs = ogrfs + sym
+            dx_val = dx_val + offset
+        shape.set_property( "_gv_ogrfs", ogrfs )
+        
+        return shape
+        
+    def create_taskforce_indicator( self, repeat, size, offset ):
+        """
+        create a taskforce indicator which is a rectangle with
+        no bottom that fits over a size indicator.  Repeat is the
+        number of size indicator symbols to cover.  Offset is
+        the number of pixels apart that they are.  Size is the
+        vertical size of the symbol.
+        """
+        
+        dx = ((offset/2.0) * (repeat)) + 2
+        dy = size + 2
+
+        shape = gview.GvShape( type = gview.GVSHAPE_LINE )
+        
+        shape.add_node( -dx, -dy )
+        shape.add_node( -dx, dy )
+        shape.add_node( dx, dy )
+        shape.add_node( dx, -dy )
+        
+        return shape
+        
+                
+
+
+
+
+TOOL_LIST = ['MilitarySymbolTool']
+

Added: packages/openev/branches/upstream/current/tools/open_raw.py
===================================================================
--- packages/openev/branches/upstream/current/tools/open_raw.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/open_raw.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,512 @@
+###############################################################################
+# $Id: open_raw.py,v 1.6 2004/09/01 15:47:19 gmwalter Exp $
+#
+# Project:  OpenEV
+# Purpose:  Interactive tool to open raw image files.
+# Author:   Andrey Kiselev, dron at remotesensing.org
+#
+###############################################################################
+# Copyright (c) 2004, American Museum of Natural History. All rights reserved.
+# This software is based upon work supported by NASA under award
+# number NAG5-12333
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: open_raw.py,v $
+#  Revision 1.6  2004/09/01 15:47:19  gmwalter
+#  Fix default path settings.
+#
+#  Revision 1.5  2004/08/31 20:52:10  gmwalter
+#  Updated to use VRT.
+#
+#  Revision 1.4  2004/03/25 16:16:04  andrey_kiselev
+#  Fix swapped/unswapped misinterpreting.
+#
+#  Revision 1.3  2004/03/21 16:20:59  andrey_kiselev
+#  Added geometry guessing feature.
+#
+#  Revision 1.2  2004/03/09 10:05:09  andrey_kiselev
+#  Few fixes.
+#
+#  Revision 1.1  2004/01/18 16:39:56  andrey_kiselev
+#  New.
+#
+#
+
+
+# TO DO:
+#
+# 1) Allow output of more header types by adding a header-only
+#    creation option for flat binary raster + header type images?
+#
+# 2) Add advanced option to window the input file:  The current
+#   implementation doesn't make full use of vrt flexibility- the
+#   user is forced to load the whole image.  The
+#   LineOffset, PixelOffset, and ImageOffset parameters could
+#   also be used to load a sub image rather than the whole thing.
+#   The user would have to specify the existing parameters
+#   (which describe the whole file) plus line/pixel offsets and
+#   sizes for the subwindow.
+
+from gtk import *
+import GtkExtra
+
+from stat import *
+import os
+import os.path
+import sys
+import math
+import Numeric
+
+import gdal
+import gdalnumeric
+import gview
+import gviewapp
+import gvutils
+import string
+import vrtutils
+import pgufilesel
+
+class OpenRaw(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+	self.wins = {}
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        win=OpenRawDialog(self.app)
+        win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("File/Open Raw",2,self.launch_dialog)
+
+class OpenRawDialog(GtkWindow):
+
+    def __init__(self,app=None):
+	self.wins = {}
+        GtkWindow.__init__(self)
+        self.set_title('Open Raw Image File')
+        self.create_gui()
+        self.show()
+        self.app=app
+
+    def show(self):
+        GtkWindow.show_all(self)
+
+    def close(self, *args):
+        self.destroy()
+
+    def create_gui(self):
+        box1 = GtkVBox(spacing = 10)
+	box1.set_border_width(10)
+        self.add(box1)
+        self.tips=GtkTooltips()
+        box1.show()
+
+	# File open controls
+	frame1 = GtkFrame('Select raw image file')
+	frame1.show()
+        box1.pack_start(frame1, expand=FALSE)
+	box2 = GtkHBox(spacing = 5)
+	box2.set_border_width(5)
+        box2.show()
+
+	open_btn = GtkButton('Open...')
+	open_btn.connect("clicked", self.open_cb)
+	box2.pack_start(open_btn)
+	self.open_entry = GtkEntry()
+	self.open_entry.set_editable(TRUE)
+	self.open_entry.set_text('')
+	box2.pack_start(self.open_entry)
+	frame1.add(box2)
+
+	# Image geometry controls
+	frame2 = GtkFrame('Set image geometry')
+	frame2.show()
+        box1.pack_start(frame2, expand=FALSE)
+	tbl = GtkTable(4, 5)
+	tbl.set_border_width(5)
+	tbl.set_row_spacings(5)
+	tbl.set_col_spacings(5)
+        tbl.show()
+
+	width_label = GtkLabel('Image width:')
+	width_label.set_alignment(0, 0.5)
+	tbl.attach(width_label, 0, 1, 0, 1)
+	self.width_entry = GtkEntry()
+	self.width_entry.set_text('0')
+	self.width_entry.set_editable(TRUE)
+	tbl.attach(self.width_entry, 1, 2, 0, 1)
+
+	height_label = GtkLabel('Image height:')
+	height_label.set_alignment(0, 0.5)
+	tbl.attach(height_label, 0, 1, 1, 2)
+	self.height_entry = GtkEntry()
+	self.height_entry.set_text('0')
+	self.height_entry.set_editable(TRUE)
+	tbl.attach(self.height_entry, 1, 2, 1, 2)
+
+	bands_label = GtkLabel('Number of bands:')
+	bands_label.set_alignment(0, 0.5)
+	tbl.attach(bands_label, 0, 1, 2, 3)
+	self.bands_entry = GtkEntry()
+	self.bands_entry.set_text('1')
+	self.bands_entry.set_editable(TRUE)
+	tbl.attach(self.bands_entry, 1, 2, 2, 3)
+
+	header_label = GtkLabel('Image header size:')
+	header_label.set_alignment(0, 0.5)
+	tbl.attach(header_label, 0, 1, 3, 4)
+	self.header_entry = GtkEntry()
+	self.header_entry.set_text('0')
+	self.header_entry.set_editable(TRUE)
+	tbl.attach(self.header_entry, 1, 2, 3, 4)
+
+	guess_btn = GtkButton("Guess image geometry")
+        guess_btn.connect("clicked", self.guess_cb)
+	tbl.attach(guess_btn, 0, 2, 4, 5)
+
+	size_label = GtkLabel('File size in bytes:')
+	size_label.set_alignment(0, 0.5)
+	tbl.attach(size_label, 2, 3, 0, 1)
+	self.bytes_label = GtkLabel('')
+	self.bytes_label.set_alignment(0, 0.5)
+	tbl.attach(self.bytes_label, 3, 4, 0, 1)
+
+	type_label = GtkLabel('Image data type:')
+	type_label.set_alignment(0, 0.5)
+	tbl.attach(type_label, 2, 3, 1, 2)
+	self.type_list = ['Byte', 'UInt16', 'Int16', 'UInt32','Int32',
+                          'Float32','Float64','CInt16','CInt32',
+                          'CFloat32','CFloat64']
+	self.type_menu = gvutils.GvOptionMenu(self.type_list)
+	tbl.attach(self.type_menu, 3, 4, 1, 2)
+
+	swap_label = GtkLabel('Byte Order:')
+	swap_label.set_alignment(0, 0.5)
+	tbl.attach(swap_label, 2, 3, 2, 3)
+	swap_list = ['LSB (Swapped)', 'MSB (Unswapped)']
+	self.swap_menu = gvutils.GvOptionMenu(swap_list)
+	tbl.attach(self.swap_menu, 3, 4, 2, 3)
+
+	interleave_label = GtkLabel('Type of interleaving:')
+	interleave_label.set_alignment(0, 0.5)
+	tbl.attach(interleave_label, 2, 3, 3, 4)
+	self.interleave_list = ['Pixel', 'Band', 'Line']
+	self.interleave_menu = gvutils.GvOptionMenu(self.interleave_list)
+	tbl.attach(self.interleave_menu, 3, 4, 3, 4)
+
+	frame2.add(tbl)
+
+	# Output header format.  A possible route for future
+        # improvement would be adding a creation option to CreateCopy for the
+        # simple-flat-binary-raster-plus-header formats that specifies
+        # header-only output and avoids copying all the associated binary data.
+        # We might also want to add a GDAL_DMD-type metadata item to the driver
+        # so that the tool can pick up which formats support header-only output
+        # (sort of like the datatypes and creation options are currently set as
+        # metadata items in the drivers).
+        
+        sbox = GtkHBox(spacing=10)
+        label=GtkLabel('Output Header Format:')
+        label.set_alignment(0,0.5)
+        label.show()
+        sbox.pack_start(label)
+
+        self.format_list = ['VRT','PAux']
+	self.format_menu = gvutils.GvOptionMenu(self.format_list)
+	self.format_menu.show()
+	sbox.pack_start(self.format_menu)
+        box1.pack_start(sbox)
+        sbox.show()
+
+	# Ok/Cancel buttons
+	separator = GtkHSeparator()
+	box1.pack_start(separator, expand=FALSE)
+
+	box3 = GtkHBox(spacing=10)
+        box1.pack_start(box3, expand=FALSE)
+
+        current_btn = GtkButton("Current View")
+        current_btn.connect("clicked", self.import_cb,'Current')
+	box3.pack_start(current_btn)
+
+        new_btn = GtkButton("New View")
+        new_btn.connect("clicked", self.import_cb,'New')
+	box3.pack_start(new_btn)
+
+        save_btn = GtkButton("Save")
+        save_btn.connect("clicked", self.import_cb,'Save')
+	box3.pack_start(save_btn)
+                
+        close_btn = GtkButton("Close")
+        close_btn.connect("clicked", self.close)
+        box3.pack_start(close_btn)
+
+        self.tips.set_tip(close_btn,
+                          'Exit the Open Raw tool')
+        self.tips.set_tip(save_btn,
+                          'Create dataset and save header in selected format')
+        self.tips.set_tip(new_btn,
+                          'Create dataset and display in a new view')
+        self.tips.set_tip(current_btn,
+                          'Create dataset and display in current view')
+        box3.show()
+
+    def open_cb(self, *args):
+        if gview.get_preference('save_recent_directory') == 'on':
+	    recent_dir = gview.get_preference('recent_directory')
+	else:
+	    recent_dir = None
+            
+        filename=pgufilesel.GetFileName(title="Open raw image file",
+                                        default_filename=recent_dir)
+        if filename is None:
+            return
+        self.open_entry.set_text(filename)
+        self.bytes_label.set_text(str(os.stat(filename)[ST_SIZE]))
+
+    def import_cb(self, *args):
+	# Check if the parameters valid
+	filename = self.open_entry.get_text()
+	if filename is '':
+	    gvutils.error('You should select a raw file to load!')
+	    return
+        
+	if not os.path.isfile(filename):
+	    gvutils.error('Unable to load '+ filename)
+	    return
+
+        if args[1] == 'Save':
+            self.create_header(filename)
+        else:
+            lines=self.create_vrt_lines(filename)
+            vrtds=gdal.OpenShared(lines)
+            if args[1] == 'New':
+                self.app.new_view()
+            self.app.open_gdal_dataset(vrtds)
+
+    def create_vrt_lines(self,filename):
+	image_offset = long(self.header_entry.get_text())
+	width = long(self.width_entry.get_text())
+	height = long(self.height_entry.get_text())
+	bands = long(self.bands_entry.get_text())
+	interleaving = self.interleave_list[self.interleave_menu.get_history()]
+        dtype = self.type_list[self.type_menu.get_history()]
+	gdaltype = gdal.GetDataTypeByName(dtype)
+	datasize = gdal.GetDataTypeSize(gdaltype) / 8
+        byteorder = ['LSB','MSB'][self.swap_menu.get_history()]
+
+        vrtdsc = vrtutils.VRTDatasetConstructor(width,height)
+        
+        if interleaving == 'Pixel':
+            pixoff = datasize*bands
+            lineoff = pixoff*width
+            for idx in range(bands):
+                imoff = image_offset + idx*datasize
+                vrtdsc.AddRawBand(filename, dtype, byteorder,
+                                 imoff, pixoff, lineoff)
+                
+        elif interleaving == 'Line':
+            pixoff=datasize
+            lineoff=datasize*width*bands
+            for idx in range(bands):
+                imoff = image_offset + idx*lineoff
+                vrtdsc.AddRawBand(filename, dtype, byteorder,
+                                 imoff, pixoff, lineoff)
+            
+        else:
+            pixoff=datasize
+            lineoff=datasize*width
+            for idx in range(bands):
+                imoff = image_offset + datasize*width*height*idx
+                vrtdsc.AddRawBand(filename, dtype, byteorder,
+                                 imoff, pixoff, lineoff)
+
+        return vrtdsc.GetVRTLines()
+        
+        
+    def create_header(self,filename):
+        fmt=self.format_list[self.format_menu.get_history()]
+        dtype=self.type_list[self.type_menu.get_history()]
+        dr=gdal.GetDriverByName(fmt)
+        tlist=string.split(dr.GetMetadata()["DMD_CREATIONDATATYPES"])
+        if dtype not in tlist:
+            gvutils.error(fmt+' format does not support '+dtype+' data type!')
+            return
+        
+        if fmt == 'PAux':
+            self.create_paux_header(filename,dtype)
+        else:
+            fname,ext = os.path.splitext(filename)
+            vrtname = fname+'.vrt'
+                
+            fname=pgufilesel.GetFileName(title="Select VRT Save Name",
+                                         default_filename=vrtname)
+            if fname is None:
+                return
+            if os.path.exists(fname):
+	        resp = GtkExtra.message_box('Confirmation', \
+		        fname + ' exists. Overwrite?', ('Yes','No'))
+                if resp == 'No':
+                    return
+            lines=self.create_vrt_lines(filename)
+            fh=open(fname,'w')
+            fh.writelines(lines)
+            fh.close()
+
+    def create_paux_header(self,filename,datatype):    
+	(path, ext) = os.path.splitext(filename)
+	auxname = path + ".aux"
+	if os.path.isfile(auxname):
+	    resp = GtkExtra.message_box('Confirmation', \
+		    auxname + ' exists. Overwrite?', ('Yes','No'))
+            if resp == 'No':
+                return
+
+	# Take the image parameters
+	header = long(self.header_entry.get_text())
+	width = long(self.width_entry.get_text())
+	height = long(self.height_entry.get_text())
+	bands = long(self.bands_entry.get_text())
+        aux_type_dict={'Byte':'8U','Int16':'16S','UInt16':'16U',
+                       'Float32':'32R'}
+	aux_type_list = ['8U', '16S', '16U', '32R']
+	type = aux_type_dict[datatype]
+	gdaltype = gdal.GetDataTypeByName(datatype)
+	interleaving = self.interleave_list[self.interleave_menu.get_history()]
+	datasize = gdal.GetDataTypeSize(gdaltype) / 8
+
+	# Calculate offsets
+	pixel_offset = []
+	line_offset = []
+	image_offset = []
+	if interleaving is 'Pixel':
+	    for i in range(bands):
+		pixel_offset.append(datasize * bands)
+		line_offset.append(datasize * width * bands)
+		image_offset.append(header + datasize * i)
+	elif interleaving is 'Band':
+	    for i in range(bands):
+		pixel_offset.append(datasize)
+		line_offset.append(datasize * width)
+		image_offset.append(header + datasize * width * height * i)
+	elif interleaving is 'Line':
+	    for i in range(bands):
+		pixel_offset.append(datasize)
+		line_offset.append(datasize * width * bands)
+		image_offset.append(header + datasize * width * i)
+	else:
+	    raise 'Unsupported interleaving type!'
+	
+	aux_swap_list = ['Swapped', 'Unswapped']
+	swap = aux_swap_list[self.swap_menu.get_history()]
+
+	# Write out the auxilary file
+	aux = open(auxname, "wt")
+	aux.write("AuxilaryTarget: " +  os.path.basename(filename) + '\n')
+	aux.write("RawDefinition: " + str(width) + ' ' \
+		+ str(height) + ' ' + str(bands) + '\n')
+	for i in range(bands):
+	    aux.write("ChanDefinition-" + str(i + 1) + ': ' + type + ' ' \
+		    + str(image_offset[i]) + ' ' + str(pixel_offset[i]) \
+		    + ' ' + str(line_offset[i]) + ' ' + swap + '\n')
+	aux.close()
+	aux = None
+
+    def guess_cb(self, *args):
+	"""Guess image geometry parameters."""
+
+	def correlation(array1, array2):
+	    """Calculate correlation coefficient of two arrays."""
+	    n_elems = float(array1.shape[0])
+	    M1 = Numeric.add.reduce(array1)
+	    M2 = Numeric.add.reduce(array2)
+	    D1 = Numeric.add.reduce(array1 * array1) - M1 * M1 / n_elems
+	    D2 = Numeric.add.reduce(array2 * array2) - M2 * M2 / n_elems
+            K = (Numeric.add.reduce(array1 * array2) - M1 * M2 / n_elems) / math.sqrt(D1 * D2)
+
+	    return K
+
+	header = long(self.header_entry.get_text())
+	width = long(self.width_entry.get_text())
+	height = long(self.height_entry.get_text())
+	bands = long(self.bands_entry.get_text())
+	gdaltype = \
+	    gdal.GetDataTypeByName(self.type_list[self.type_menu.get_history()])
+	numtype = gdalnumeric.GDALTypeCodeToNumericTypeCode(gdaltype)
+	depth = gdal.GetDataTypeSize(gdaltype) / 8
+	
+	filename = self.open_entry.get_text()
+        if os.path.isfile(filename) == 0:
+            gvutils.error('Input file '+filename+' does not exist!')
+            return
+        
+	filesize = os.stat(filename)[ST_SIZE]
+	if filesize < header:
+	    gvutils.error('Specified header size larger then file size!')
+            return
+	imagesize = (filesize - header) / bands / depth
+
+	if width != 0 and height == 0:
+	    height = imagesize / width
+	elif width == 0 and height != 0:
+	    width = imagesize / height
+	else:
+	    rawfile = open(filename, 'rb')
+	    longt = 40.0	# maximum possible height/width ratio
+	    cor_coef = 0.0
+	    w = long(math.sqrt(imagesize / longt))
+	    w_max = long(math.sqrt(imagesize * longt))
+	    if (self.swap_menu.get_history() == 0 \
+		and sys.byteorder == 'little') or \
+	       (self.swap_menu.get_history() == 1 and sys.byteorder == 'big'):
+		swap = 0
+	    else:
+		swap = 1
+	    while w < w_max:
+		if imagesize % w == 0:
+		    scanlinesize = w * depth
+		    h = imagesize / w
+		    rawfile.seek(header + h / 2 * scanlinesize)
+		    buf1 = rawfile.read(scanlinesize)
+		    buf2 = rawfile.read(scanlinesize)
+		    a1 = Numeric.fromstring(buf1, numtype)
+		    a2 = Numeric.fromstring(buf2, numtype)
+		    if swap:
+			a1.byteswapped()
+			a2.byteswapped()
+
+		    try:
+  		        tmp = correlation(a1.astype(Numeric.Float32), a2.astype(Numeric.Float32))
+                    except:
+                        # catch 0 division errors
+                        gvutils.error('Unable to guess image geometry!')
+                        return
+                    
+		    if tmp > cor_coef:
+			cor_coef = tmp
+			width = w
+			height = h
+		w += 1
+
+	self.width_entry.set_text(str(width))
+	self.height_entry.set_text(str(height))
+	
+TOOL_LIST = ['OpenRaw']
+

Added: packages/openev/branches/upstream/current/tools/open_subarea.py
===================================================================
--- packages/openev/branches/upstream/current/tools/open_subarea.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/open_subarea.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,339 @@
+##############################################################################
+# $Id: open_subarea.py,v 1.3 2004/10/23 11:03:40 andrey_kiselev Exp $
+#
+# Project:  OpenEV
+# Purpose:  Interactive tool to perform calculations on pair of images.
+# Authors:  Iscander Latypov
+#	    Andrey Kiselev, dron at remotesensing.org
+#
+###############################################################################
+# Copyright (c) 2004, American Museum of Natural History. All rights reserved.
+# This software is based upon work supported by NASA under award
+# number NAG5-12333
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: open_subarea.py,v $
+#  Revision 1.3  2004/10/23 11:03:40  andrey_kiselev
+#  Updated to reflect changes in pgugrid.
+#
+#  Revision 1.2  2004/07/04 09:21:30  andrey_kiselev
+#  Added ability to select individual bands.
+#
+#  Revision 1.1  2004/05/18 19:17:54  andrey_kiselev
+#  New.
+#
+ 
+from gtk import *
+import gview, gdal, gdalconst, gvutils
+import gviewapp
+import vrtutils
+import pgugrid
+import osr
+
+def CoordFrame(name,f_names,visible):
+    frame = GtkFrame(name)
+    table = GtkTable(2,4,FALSE)
+    table.set_border_width(5)
+    table.set_row_spacings(5)
+    table.set_col_spacings(5)
+    frame.add(table)
+    table.show()
+    list_entries = []
+    k = 0
+    for i in range(2):
+        for j in range(2):
+            label = GtkLabel(f_names[k])
+	    label.set_alignment(0, 0.5)
+            table.attach(label,2*j,2*j+1,i,i+1)
+            label.show()
+            entry = GtkEntry()
+            list_entries.append(entry)
+            entry.show()
+            table.attach(entry,2*j+1,2*j+2,i,i+1)
+	    k = k + 1
+	    
+    return [frame, list_entries, visible]
+
+def SetInitialCoord(dict_item, coords_list):
+    """This routine sets initial values of out image 
+    to coordinates of input image corners"""
+    for i in range(4):
+	dict_item[1][i].set_text(str(coords_list[i]))    
+
+def PixelCoordToGeocoord(x,y,geotransform):
+    px = geotransform[0]
+    py = geotransform[3]
+    px += geotransform[1] * x + geotransform[2] * y
+    py += geotransform[4] * x + geotransform[5] * y
+    return (px, py)
+    
+def GeocoordToPixelCoord(px,py,geotransform):
+    s = px - geotransform[0]
+    t = py - geotransform[3]
+    det = geotransform[1] * geotransform[5] - geotransform[2] * geotransform[4]
+    x = (s * geotransform[5] - geotransform[2] * t) / det
+    y = (t * geotransform[1] - geotransform[4] * s) / det
+    return (x, y)
+    
+def GetProjRect(pixcoords,geotransform):    
+    (ulx,uly) = PixelCoordToGeocoord(pixcoords[1],pixcoords[0],geotransform)
+    (lrx,lry) = PixelCoordToGeocoord(pixcoords[3],pixcoords[2],geotransform)
+    return (uly, ulx, lry, lrx)
+
+def GeocoordToLatLong(gx,gy,raster):
+    """Build Spatial Reference object based on coordinate system,
+    fecthed from the opened dataset"""
+    srs = osr.SpatialReference()
+    srs.ImportFromWkt(raster.GetProjection())
+    srsLatLong = srs.CloneGeogCS()
+    ct = osr.CoordinateTransformation(srs, srsLatLong)
+    (lat, long, height) = ct.TransformPoint(gx, gy)
+    return (lat, long)		
+
+def LatLongToGeocoord(lat,long,raster):
+    """Build Spatial Reference object based on coordinate system,
+    fecthed from the opened dataset"""
+    srs = osr.SpatialReference()
+    srs.ImportFromWkt(raster.GetProjection())
+    srsLatLong = srs.CloneGeogCS()
+    ct = osr.CoordinateTransformation(srsLatLong,srs)
+    (gx, gy, height) = ct.TransformPoint(lat, long)
+    return (gx, gy)		
+
+def GetGeogrRect(raster,geocoord):
+    (ul_lat, ul_long) = GeocoordToLatLong(geocoord[1],geocoord[0],raster)
+    (br_lat, br_long) = GeocoordToLatLong(geocoord[3],geocoord[2],raster)
+    return (ul_lat,br_lat,ul_long,br_long)		
+
+def ProjRectToPixelRect(proj_rect,geotransform):
+    (lx,ty) = GeocoordToPixelCoord(proj_rect[0],proj_rect[1],geotransform)
+    (rx,by) = GeocoordToPixelCoord(proj_rect[2],proj_rect[3],geotransform)
+    return (ty, lx, by-ty, rx-lx)
+    
+def EmptyGeotransform(gt):
+    if gt[0] != 0 or gt[1] != 1 or gt[2] != 0:
+        return FALSE
+    if gt[3] != 0 or gt[4] != 0 or gt[5] != 1:
+        return FALSE;
+    return TRUE
+    
+class OpenSubArea(gviewapp.Tool_GViewApp):
+    def __init__(self, app = None):
+	gviewapp.Tool_GViewApp.__init__(self,app)
+	self.init_menu()
+   
+    def init_menu(self):
+        self.menu_entries.set_entry("File/Open Subarea...",4,self.launch_dialog)
+    
+    def file_selection_ok(self,*args):
+	self.source_name = self.o_s_d.get_filename()
+	self.o_s_d.hide()
+	
+	rast = gdal.OpenShared(self.source_name, gdalconst.GA_ReadOnly)
+	self.input_rast = rast
+	
+	self.pixsubarea = [0,0,rast.RasterYSize,rast.RasterXSize]
+	SetInitialCoord(self.frame_dict['pixcoord'],self.pixsubarea)
+	
+	self.geotransform = rast.GetGeoTransform()
+	
+	if EmptyGeotransform(self.geotransform):
+	    self.coord_system.set_history(0)
+	    self.update_gui()
+	    self.coord_system.hide()
+	else:
+	    self.projsubarea = GetProjRect(self.pixsubarea,self.geotransform)
+	    SetInitialCoord(self.frame_dict['geocoord'],self.projsubarea) 
+	
+	    self.geogsubarea = GetGeogrRect(rast,self.projsubarea)
+	    SetInitialCoord(self.frame_dict['geodetic'],self.geogsubarea) 
+	
+	self.band_list = []
+	for i in range(rast.RasterCount):
+	    item = ["Band " + str(i), "Yes"]
+	    self.band_list.append(item)
+	if len(self.band_list) > 1:    
+            self.band_grid.set_source(self.band_list,expose=0)
+	    grid_titles = ['Band number', 'Load']
+	    self.band_grid.define_columns(titles=grid_titles,editables=[0,1])
+            self.band_grid.resize_to_default()
+	    for i in range(len(self.band_list)):
+		self.band_num_list.append(i+1)
+            self.band_grid.show_all()
+        else:
+            self.band_grid.hide()
+ 	self.dialog.show()
+	    
+    def launch_dialog(self,*args):
+	self.band_num_list = []
+	self.init_dialog()
+	self.o_s_d = GtkFileSelection("Source File Open")
+        self.o_s_d.ok_button.connect("clicked",self.file_selection_ok) 
+	self.o_s_d.cancel_button.connect("clicked",self.o_s_d.hide)
+   	self.o_s_d.show()
+    	
+    def open_subarea_cb(self,*args):
+ 	
+ 	self.vrt_options = vrtutils.VRTCreationOptions(len(self.band_list))
+ 
+	if self.geocoding == 1:
+	    # get data from pixel-frame and transform lat/long
+	    # to proj and to pixels	 	
+ 	    g_east = float(self.frame_dict['geodetic'][1][0].get_text())
+ 	    g_west = float(self.frame_dict['geodetic'][1][1].get_text())
+ 	    g_north = float(self.frame_dict['geodetic'][1][2].get_text())
+ 	    g_south = float(self.frame_dict['geodetic'][1][3].get_text())
+ 	    (east,north) = LatLongToGeocoord(g_east,g_north,self.input_rast)
+ 	    (west,south) = LatLongToGeocoord(g_west,g_south,self.input_rast)
+ 	    proj_rect = (east,north,west,south)
+ 	    (sline,spix,nlines,npix) = \
+		ProjRectToPixelRect(proj_rect, self.geotransform)
+ 	elif self.geocoding == 2:
+ 	    # get data from pixel-frame and translate proj to pixels
+ 	    east = float(self.frame_dict['geocoord'][1][1].get_text())
+ 	    west = float(self.frame_dict['geocoord'][1][3].get_text())
+ 	    north = float(self.frame_dict['geocoord'][1][0].get_text())
+ 	    south = float(self.frame_dict['geocoord'][1][2].get_text())
+ 	    proj_rect = (east,north,west,south)
+ 	    (sline,spix,nlines,npix) = \
+		ProjRectToPixelRect(proj_rect,self.geotransform)
+ 	else:
+ 	    # get data from pixel-frame
+ 	    spix = int(self.frame_dict['pixcoord'][1][1].get_text())
+ 	    sline = int(self.frame_dict['pixcoord'][1][0].get_text())
+ 	    npix = int(self.frame_dict['pixcoord'][1][3].get_text())
+ 	    nlines = int(self.frame_dict['pixcoord'][1][2].get_text())
+ 	
+	if spix < 0:
+	    spix = 0
+	elif spix >  self.pixsubarea[3]:
+	    gvutils.error("Source Area does not cover required Rectangle")
+	    return
+	if sline < 0:
+	    sline = 0
+	elif sline >  self.pixsubarea[2]:
+	    gvutils.error("Source Area does not cover required Rectangle")
+	    return
+	if spix + npix > self.pixsubarea[3]:
+	    npix = self.pixsubarea[3] - spix
+	if sline + nlines > self.pixsubarea[2]:
+	    nlines = self.pixsubarea[2] - sline
+	
+ 	self.vrt_options.set_src_window((spix,sline,npix,nlines),self.band_num_list)
+ 	self.vrt_options.set_dst_window((0,0,npix,nlines))
+ 	
+        vrt_tree=vrtutils.serializeDataset(self.input_rast,self.vrt_options,self.band_num_list)
+        vrt_lines=gdal.SerializeXMLTree(vrt_tree)
+        vrtdataset=gdal.Open(vrt_lines)
+        gview.app.open_gdal_dataset(vrtdataset)
+        self.close()
+    	    	
+    def update_gui(self,*args):
+	for item in self.frame_dict.keys():
+	    self.frame_dict[item][2] = FALSE
+	    self.frame_dict[item][0].hide()
+ 	self.geocoding = self.coord_system.get_history()
+	if self.geocoding == 0:
+	    self.frame_dict['pixcoord'][2] = TRUE
+	elif self.geocoding == 1:
+	    self.frame_dict['geodetic'][2] = TRUE
+	else:
+	    self.frame_dict['geocoord'][2] = TRUE
+
+        for item in self.frame_dict.keys():
+            if self.frame_dict[item][2]: 
+              self.frame_dict[item][0].show()
+      
+    def init_dialog(self):
+	self.dialog = GtkWindow()
+        self.dialog.set_title('Open Subarea')
+        self.dialog.set_border_width(10)   
+        
+        mainshell = GtkVBox(spacing=5)
+        self.dialog.add(mainshell)
+	mainshell.show()
+	
+	self.geocoding = 0
+	coord_system_list = ["Pixels", "Geodetic (Lat/Long)", "Georeferenced"]
+	self.coord_system = \
+	       gvutils.GvOptionMenu(coord_system_list,self.update_gui)
+	mainshell.pack_start(self.coord_system)
+	self.coord_system.show()
+	
+	self.frame_dict = {}
+	pix_fields_names = \
+	    ('Start Line','Start Pixel','Num of Lines','Num of Pixels')
+	self.frame_dict['pixcoord'] = \
+	    CoordFrame('Pixel Coordinates', pix_fields_names, TRUE)
+	mainshell.pack_start(self.frame_dict['pixcoord'][0], expand=FALSE)
+
+	geo_fields_names = ('Westmost Longitude', 'Eastmost Longitude', \
+	    'Northmost Latitude', 'Southmost Latitude')
+	self.frame_dict['geodetic'] = \
+	    CoordFrame('Geodetic (Lat/Long) Coordinates', \
+	    geo_fields_names, FALSE)
+	mainshell.pack_start(self.frame_dict['geodetic'][0], expand=FALSE)
+
+	proj_fields_names = ('Northing', 'Westing', 'Southing', 'Easting')
+	self.frame_dict['geocoord'] = \
+	    CoordFrame('Georeferenced Coordinates', proj_fields_names, FALSE)
+	mainshell.pack_start(self.frame_dict['geocoord'][0], expand=FALSE)
+	
+	self.band_grid = pgugrid.pguGrid(config=(2,0,1,1,4,0,0,0))
+	self.band_grid.subscribe("cell-selection-changed",self.band_selected_cb)
+	mainshell.pack_start(self.band_grid,expand=TRUE)
+	
+	button_box = GtkHBox(spacing = 10)
+	mainshell.pack_start(button_box, expand=FALSE)
+	button_box.show()
+	btOK = GtkButton('OK')
+ 	button_box.pack_start(btOK)
+ 	btOK.connect("clicked",self.open_subarea_cb)
+ 	btOK.show()
+ 	
+	btCancel = GtkButton('Cancel')
+ 	button_box.pack_start(btCancel)
+	btCancel.connect("clicked", self.close)
+	btCancel.show()
+
+        # Trap window close event
+        self.dialog.connect('delete-event', self.close)
+
+        for item in self.frame_dict.keys():
+            if self.frame_dict[item][2]: 
+              self.frame_dict[item][0].show()
+    
+        for item in self.frame_dict.keys():
+            if self.frame_dict[item][2]: 
+              self.frame_dict[item][0].show()
+ 
+    def band_selected_cb(self,widget,cell):
+        row = cell[0][0]
+        if self.band_list[row][1] == "NO":
+            self.band_list[row][1] = "YES"
+	    self.band_num_list.append(row+1)	
+	else:
+	    self.band_list[row][1] = "NO"
+	    self.band_num_list.remove(row+1)	
+	self.band_grid.refresh()
+
+    def close(self,*args):
+        self.dialog.destroy()
+    
+
+TOOL_LIST = ['OpenSubArea']

Added: packages/openev/branches/upstream/current/tools/rendertest.py
===================================================================
--- packages/openev/branches/upstream/current/tools/rendertest.py	                        (rev 0)
+++ packages/openev/branches/upstream/current/tools/rendertest.py	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,920 @@
+###############################################################################
+# $Id: rendertest.py,v 1.14 2003/09/02 18:35:54 warmerda Exp $
+#
+# Project:  OpenEV
+# Purpose:  Interactive Test Suite for Various Rendering Modes.
+# Author:   Frank Warmerdam, warmerdam at pobox.com
+#
+###############################################################################
+# Copyright (c) 2003, Frank Warmerdam <warmerdam at pobox.com>
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+# 
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+###############################################################################
+# 
+#  $Log: rendertest.py,v $
+#  Revision 1.14  2003/09/02 18:35:54  warmerda
+#  test layer symbol manager
+#
+#  Revision 1.13  2003/05/17 02:12:01  warmerda
+#  updated to test color inheritance
+#
+#  Revision 1.12  2003/05/16 20:30:21  warmerda
+#  improve label placement test
+#
+#  Revision 1.11  2003/05/16 18:48:23  pgs
+#  added text positioning tests
+#
+#  Revision 1.10  2003/05/16 17:44:32  warmerda
+#  added some extra tests of multi-level symbols (symbols containing symbols).
+#
+#  Revision 1.9  2003/04/09 17:00:00  pgs
+#  added tests for halo and dropshadow effects
+#
+#  Revision 1.8  2003/04/08 11:57:31  andrey_kiselev
+#  More symbol tests in vector_symbol_from_file().
+#
+#  Revision 1.7  2003/04/07 16:57:42  andrey_kiselev
+#  Typo fixed.
+#
+#  Revision 1.6  2003/04/07 16:55:32  andrey_kiselev
+#  Correct filename handling when loading symbol from file.
+#
+#  Revision 1.5  2003/04/07 16:36:41  pgs
+#  fixed path problems with symbols on nt platforms
+#
+#  Revision 1.4  2003/04/07 15:58:27  andrey_kiselev
+#  New test vector_symbol_from_file().
+#
+#  Revision 1.3  2003/04/07 15:50:42  pgs
+#  added PEN pattern test
+#
+#  Revision 1.2  2003/04/03 14:14:39  warmerda
+#  *** empty log message ***
+#
+#  Revision 1.1  2003/03/07 22:52:16  warmerda
+#  New
+#
+
+from gtk import *
+
+import gview
+import string           
+import gvutils
+import GtkExtra
+import os
+import gviewapp
+
+class RenderTestTool(gviewapp.Tool_GViewApp):
+    
+    def __init__(self,app=None):
+        gviewapp.Tool_GViewApp.__init__(self,app)
+        self.init_menu()
+
+    def launch_dialog(self,*args):
+        self.win = RenderTest()
+        self.win.show()
+
+    def init_menu(self):
+        self.menu_entries.set_entry("Tools/Render Test",2,
+                                    self.launch_dialog)
+
+class RenderTest(GtkWindow):
+
+    def __init__(self,app=None):
+        GtkWindow.__init__(self)
+
+        self.set_title('GvShapesLayer Render Test')
+
+        self.view = gview.app.sel_manager.get_active_view()
+        
+        self.text_contents = ''
+        self.selected_shape = None
+        self.layer = None
+        self.create_gui()
+
+        self.step_list = [ self.startup,
+                           self.vector_symbol,
+                           self.vector_symbol_from_file,
+                           self.ogrfs_points,
+                           self.ogrfs_points2,
+                           self.ogrfs_lines,
+                           self.raster_symbol,
+                           self.simple_poly,
+                           self.transparent_poly,
+                           self.simple_points_and_lines,
+                           self.ogrfs_labels ]
+        self.step = 0
+        self.cleanup_func = None
+
+    def show(self):
+        GtkWindow.show_all(self)
+        self.show_step()
+
+    def close(self, *args):
+        self.hide()
+        self.visibility_flag = 0
+        return TRUE
+
+    def create_gui(self):
+        box1 = GtkVBox()
+        self.add(box1)
+        box1.show()
+
+        text = GtkText()
+        text.set_usize(400,150)
+        text.set_line_wrap(FALSE)
+        text.set_word_wrap(FALSE)
+        text.set_editable(FALSE)
+        text.show()
+        self.text = text
+        box1.pack_start(text, expand=TRUE)
+
+        box2 = GtkHBox()
+        box1.pack_start(box2, expand=FALSE)
+        box2.show()
+
+        self.prev_btn = GtkButton("<--- Previous")
+        self.prev_btn.connect("clicked", self.prev_cb)
+        box2.pack_start(self.prev_btn)
+        
+        self.next_btn = GtkButton("Next --->")
+        self.next_btn.connect("clicked", self.next_cb)
+        box2.pack_start(self.next_btn)
+
+    def next_cb( self, *args ):
+        if self.step < len(self.step_list)-1:
+            self.step = self.step + 1
+            self.show_step()
+
+    def prev_cb( self, *args ):
+        if self.step > 0:
+            self.step = self.step - 1
+            self.show_step()
+
+    def show_step( self ):
+        self.cleanup()
+        
+        func = self.step_list[self.step]
+        func()
+
+    def cleanup( self ):
+        if self.cleanup_func is not None:
+            self.cleanup_func()
+
+        layer_list = self.view.list_layers()
+        for layer in layer_list:
+            self.view.remove_layer( layer )
+
+    def set_step_name( self, text ):
+        self.set_title( '%d: %s' % (self.step, text) )
+    
+    def set_text( self, text ):
+
+        self.text.freeze()
+        self.text.delete_text(0,-1)
+        self.text.insert_defaults( text )
+        self.text.thaw()
+
+###############################################################################
+#   Initial test screen.
+
+    def startup( self ):
+        self.set_step_name( 'Render Test Introduction' )
+        self.set_text( 'Welcome to the render test.' )
+
+###############################################################################
+#   Display a simple polygon with fill using the layer defaults
+#       mechanism. 
+        
+    def simple_poly( self ):
+        self.set_step_name( 'Simple Polygon Display' )
+        self.set_text( 'You should see a five sided polygon with a red edge\n'
+                       +'and blue fill.  The edge should be 3 pixels wide.\n'
+                       +'The polygon should have a triangular hole.')
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        poly = gview.GvShape( type = gview.GVSHAPE_AREA )
+        poly.set_node( 10, 10, node=0 )
+        poly.set_node( 80, 10, node=1 )
+        poly.set_node( 80, 90, node=2 )
+        poly.set_node( 50, 80, node=3 )
+        poly.set_node( 10, 90, node=4 )
+        poly.set_node( 10, 10, node=5 )
+        poly.set_node( 20, 20, node=0, ring=1 )
+        poly.set_node( 40, 20, node=1, ring=1 )
+        poly.set_node( 30, 50, node=2, ring=1 )
+        shapes.append( poly )
+
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test_poly' )
+        layer.set_property( '_area_edge_color', '255 0 0 255' )
+        layer.set_property( '_area_fill_color', '0 0 255 255' )
+        layer.set_property( '_area_edge_width', '3' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+
+###############################################################################
+#   Transparent poly.
+        
+    def transparent_poly( self ):
+        self.set_step_name( 'Polygon Transparency' )
+        self.set_text( 'You should see two overlaping polygons, with the\n'
+                       +'rear one showing through the top one a little.' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        poly = gview.GvShape( type = gview.GVSHAPE_AREA )
+        poly.set_node( 20, 5, node=0 )
+        poly.set_node( 40, 5, node=1 )
+        poly.set_node( 30, 50, node=2 )
+        shapes.append( poly )
+
+        poly = gview.GvShape( type = gview.GVSHAPE_AREA )
+        poly.set_node( 10, 10, node=0 )
+        poly.set_node( 80, 10, node=1 )
+        poly.set_node( 80, 90, node=2 )
+        poly.set_node( 50, 80, node=3 )
+        poly.set_node( 10, 90, node=4 )
+        poly.set_node( 10, 10, node=5 )
+        shapes.append( poly )
+
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test_poly' )
+        layer.set_property( '_area_edge_color', '255 0 0 255' )
+        layer.set_property( '_area_fill_color', '0 0 255 150' )
+        layer.set_property( '_area_edge_width', '3' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   Simple points and lines.
+        
+    def simple_points_and_lines( self ):
+        self.set_step_name( 'Simple Points and Lines' )
+        self.set_text( 'You should see a four segment red line, and\n'
+                       +'a blue cross hair 12 pixels tall.\n\n'
+                       +'Try zooming in, the crosshair should *not* get bigger.' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 20, 50, node=0 )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_LINE )
+        shape.set_node( 10, 10, node=0 )
+        shape.set_node( 80, 10, node=1 )
+        shape.set_node( 80, 90, node=2 )
+        shape.set_node( 50, 80, node=3 )
+        shape.set_node( 10, 90, node=4 )
+        shapes.append( shape )
+
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test_poly' )
+        layer.set_property( '_line_color', '255 0 0 255' )
+        layer.set_property( '_line_width', '3' )
+        layer.set_property( '_point_color', '0 0 255 255' )
+        layer.set_property( '_point_size', '12' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   Test of raster symbol.
+        
+    def raster_symbol( self ):
+        self.set_step_name( 'Raster Symbol Test' )
+        self.set_text( 'A red symbol (the busy indicator from thet toolbar)\n'
+                       +'should be shown.  When zooming in or out it should\n'
+                       +'remain the same size on screen.\n'
+                       +''
+                       +'NOTE: This symbols should really be green but it\n'
+                       +'seems that currently the color attribute of\n'
+                       +'SYMBOL directives for raster symbols is ignored\n'
+                       +'\n'
+                       +'\n'
+                       +'' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        
+        if os.name == "nt":
+            sym_file = gview.home_dir + '\\pics\\busy.xpm'
+            sym_file2 = gview.home_dir + '\\pics\\idle.xpm'
+            sym_name = '\\three_idle'
+        else:
+            sym_file = gview.home_dir + '/pics/busy.xpm'
+            sym_file2 = gview.home_dir + '/pics/idle.xpm'
+            sym_name = '/three_idle'
+
+
+        # Unoffset icon
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 20, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#00FF00,id:"'+sym_file+'")' )
+        shapes.append( shape )
+
+        # Pixel Offset
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 20, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#00FF00,dx:30px,dy:-10px,id:"'+sym_file+'")' )
+        shapes.append( shape )
+
+        # Geo Offset
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 20, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#00FF00,dx:30g,dy:-30g,id:"'+sym_file+'")' )
+        shapes.append( shape )
+
+        # Inject a vector icon symbol that consists of three raster symbols,
+        # but do it on the vector layer.
+
+        sm = layer.get_symbol_manager( 1 )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 0.0, 0, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(dy:-10px,dx:-40px,id:"' + sym_file2 + '");' + \
+                           'SYMBOL(id:"' + sym_file2 + '");' + \
+                           'SYMBOL(dy:10px,dx:40px,id:"' + sym_file2 + '")' )
+
+        sm.inject_vector_symbol( sym_name, shape )
+
+        # Place the "3" idle symbol.
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#00FF00,id:"'+sym_name+'")' )
+        shapes.append( shape )
+
+        # Add line for context.
+        shape = gview.GvShape( type = gview.GVSHAPE_LINE )
+        shape.set_node( 10, 10, node=0 )
+        shape.set_node( 80, 10, node=1 )
+        shape.set_node( 80, 90, node=2 )
+        shape.set_node( 50, 80, node=3 )
+        shape.set_node( 10, 90, node=4 )
+        shapes.append( shape )
+
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   _gv_ogrfs controlled lines. 
+        
+    def ogrfs_lines( self ):
+        self.set_step_name( 'OGRFS Lines' )
+        self.set_text('You should see a thick (8 pixels wide) green line\n'
+                      +'with a thin (2 pixel wide) red line down the center.\n'
+                      +'\n'
+                      +'You should also see a second line in blue rendered \n'
+                      +'in a dash-dot pattern\n'
+                      +'')
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        shape = gview.GvShape( type = gview.GVSHAPE_LINE )
+        shape.set_node( 10, 10, node=0 )
+        shape.set_node( 80, 10, node=1 )
+        shape.set_node( 80, 90, node=2 )
+        shape.set_node( 50, 80, node=3 )
+        shape.set_node( 10, 90, node=4 )
+        shape.set_property( '_gv_ogrfs',
+                            'PEN(c:#00FF00,w:8);PEN(c:#FF0000,w:2)' )
+        shapes.append( shape )
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_LINE )
+        shape.set_node( 20, 20, node=0 )
+        shape.set_node( 40, 20, node=1 )
+        shape.set_node( 40, 40, node=2 )
+        shape.set_node( 60, 60, node=3 )
+        shape.set_node( 40, 80, node=4 )
+        shape.set_property( '_gv_ogrfs', 'PEN(c:#0000FF,w:2,p:ogr-pen-6)' )
+        shapes.append( shape )
+
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+        
+        
+
+###############################################################################
+#   _gv_ogrfs controlled points. 
+        
+    def ogrfs_points( self ):
+        self.set_step_name( 'OGRFS Point Symbols' )
+        self.set_text('You should see two rows of yellow symbols.\n'
+                      +'First Row: plus, circle, box, triangle, start\n'
+                      +'Second Row: X, filled circle, box, triangle and star\n'
+                      +'\n'
+                      +'Symbols should stay the same size when you zoom.\n'
+                      +'\n'
+                      +'\n'
+                      +'\n'
+                      +'' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 15, 30, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-1)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 15, 15, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 15, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-2)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 30, 30, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-3)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 45, 15, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-4)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 45, 30, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-5)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 15, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-6)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 30, 35, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-7)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 75, 15, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-8)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 75, 30, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,s:3,id:ogr-sym-9)' )
+        shapes.append( shape )
+
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   _gv_ogrfs controlled points. 
+        
+    def ogrfs_points2( self ):
+        self.set_step_name( 'OGRFS Point Symbols' )
+        self.set_text( \
+            'In the bottom right corner should be a yellow triangle\n'
+            + 'pointing "north west".\n'
+            + '\n'
+            + 'In the middle should be a white cross hair.\n'
+            + 'To the top/right is a red X.\n'
+            + 'To the lower/left is a green X.\n'
+            + '\n'
+            + 'As you zoom in the red X should remain a constant distance\n'
+            + 'in screen pixels from the white cross.  It is "offset" from\n'
+            + 'the white cross a distance in screen pixels.\n'
+            + '\n'
+            + 'The green X is offset by a "georeferenced" distance, and\n'
+            + 'should move closer as you zoom out, and further as you zoom\n'
+            + 'in.\n'
+            + '\n'
+            + '\n'
+            + '\n' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 15, 15, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFF00,a:45,s:3,id:ogr-sym-7)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFFFF,s:2,id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FF0000,dx:20px,dy:10px,s:1,id:ogr-sym-1)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#00FF00,dx:-10g,dy:-5g,s:1,id:ogr-sym-1)' )
+        shapes.append( shape )
+
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   _gv_labels
+        
+    def ogrfs_labels( self ):
+        self.set_step_name( 'OGRFS Labels' )
+        self.set_text( \
+            'At the top of the screen should be two labels (red and blue)\n'
+            + 'The red label has a halo effect.  The blue label has a drop\n'
+            + 'shadow effect\n'
+            + '\n'
+            + 'In the middle of the screen should be four labels (red and\n'
+            + 'yellow).  They have different anchor positions, and should\n'
+            + 'be arrayed around a center reference point (not shown).\n'
+            + 'The labels indicate where they should be (ie. TOP_LEFT should\n'
+            + 'be to the top, and right of the center point).\n'
+            + '\n'
+            + 'In the lower left quadrant is a yellow "PIXEL_OFFSET" label\n'
+            + 'that should be right and down of the white reference point.\n'
+            + 'The offset is in screen pixels, so as you zoom it should\n'
+            + 'remain a constant distance away from the reference point.\n'
+            + '\n'
+            + 'In the bottom right quadrant is a similar situation but\n'
+            + 'the GEO_OFFSET label is offset in georeference coordinates\n'
+            + 'so it will move closer to the ref point as you zoom out, and\n'
+            + 'further from it as you zoom in.\n'
+            + '\n'
+            + 'All labels should be selectable and manipulatable.  When\n'
+            + 'selected they should have a selection box around them.\n'
+            + '\n' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        # Test anchors.
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FFFF00,t:"TOP_RIGHT")' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FF0000,p:7,t:"BOTTOM_RIGHT")' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FFFF00,p:9,t:"BOTTOM_LEFT")' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FF0000,p:3,t:"TOP_LEFT")' )
+        shapes.append( shape )
+        
+        # more anchor tests
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 35, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#00FF00,p:2,t:"TOP_CENTER");' + \
+	                   'SYMBOL(id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 43, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#00FF00,p:5,t:"CENTER_CENTER");' + \
+	                   'SYMBOL(id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 35, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#00FF00,p:8,t:"BOTTOM_CENTER");' + \
+	                   'SYMBOL(id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        # Test geographic and pixel offsets.
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 25, 25, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFFFF,s:2,id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 25, 25, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FFFF00,dx:20px,dy:10px,t:"PIXEL_OFFSET")')
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 25, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FFFFFF,s:2,id:ogr-sym-0)' )
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 25, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FFFF00,dx:6g,dy:3g,t:"GEO_OFFSET")')
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 75, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#00FF00,b:#339933,t:"Text with shadow",sh:)')
+        shapes.append( shape )
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 80, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'LABEL(c:#FF0000,b:#993333,t:"Text with halo",h:)')
+        shapes.append( shape )
+
+        
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   Vector symbols (GvShapes via symbolmanager requested via ogrfs).
+        
+    def vector_symbol( self ):
+        self.set_step_name( 'OGRFS Vector Symbol' )
+        self.set_text( \
+            'You should see two three symbols.  In the middle of the screen\n'
+            + 'an arrow pointing straight up.  Up and right of that should\n'
+            + 'be a crossed arrow (slightly larger) pointing northeast and\n'
+            + 'north west.  The north-west arrow should be green, and the\n'
+            + 'north-east one red.\n'
+            + '\n'
+            + 'To the right of that should be a pair of blue arrows meeting\n'
+            + 'at their bases.\n'
+            + '\n'
+            + '\n' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        # Insert a vector symbol that looks like an arrow into the symbol
+        # manager.
+
+        sm = gview.GvSymbolManager()
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_LINE )
+        shape.set_node( 0.0, -6, node=0 )
+        shape.set_node( 0.0, 6,  node=1 )
+        shape.set_node( -2, 4, node=2 )
+        shape.set_node( 0.0, 6,  node=3 )
+        shape.set_node( 2, 4,  node=4 )
+        shape.set_property('_gv_ogrfs','PEN(w:2)' )
+
+        if os.name == "nt":
+            sym_name = "\\rendertest_arrow"
+        else:
+            sym_name = "/rendertest_arrow"
+
+        sm.inject_vector_symbol( sym_name, shape )
+                                 
+        # Place the symbol.
+    
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FF0000,s:2,id:"'+sym_name+'")' )
+        shapes.append( shape )
+
+        # Insert a vector symbol consisting of crossed arrows.
+
+        sm = gview.GvSymbolManager()
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 0.0, 0, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(a:45,c:#00FF00,id:"' + sym_name + '");' + \
+                           'SYMBOL(a:-45,id:"' + sym_name + '")' )
+
+        if os.name == "nt":
+            sym_name2 = "\\rendertest_carrow"
+        else:
+            sym_name2 = "/rendertest_carrow"
+        sm.inject_vector_symbol( sym_name2, shape )
+                                 
+        # Place the symbol. 
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 60, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#FF0000,s:4,id:"'+sym_name2+'")' )
+        shapes.append( shape )
+
+        # Insert a vector symbol consisting scaled arrows with offsets.
+
+        sm = gview.GvSymbolManager()
+        
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 0.0, 0, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(s:0.3333,a:45,dx:-6px,dy:-6px,id:"' + sym_name + '");' + \
+                           'SYMBOL(s:0.666,a:-45,dx:12px,dy:-12px,id:"' + sym_name + '")')
+
+        if os.name == "nt":
+            sym_name2 = "\\rendertest_carrow2"
+        else:
+            sym_name2 = "/rendertest_carrow2"
+        sm.inject_vector_symbol( sym_name2, shape )
+                                 
+        # Place the symbol. 
+
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 85, 60, node=0 )
+        shape.set_property('_gv_ogrfs',
+                           'SYMBOL(c:#0000FF,s:4,id:"'+sym_name2+'")' )
+        shapes.append( shape )
+
+        # Create the layer and display
+        
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+###############################################################################
+#   Loading vector symbols from XML file.
+        
+    def vector_symbol_from_file( self ):
+        symbols_dir = os.path.join(gview.home_dir, 'symbols' )
+        symbol1 = os.path.join( symbols_dir, 'square.xml' )
+        symbol2 = os.path.join( symbols_dir, 'square_filled.xml' )
+        symbol3 = os.path.join( symbols_dir, 'cross.xml' )
+        symbol4 = os.path.join( symbols_dir, 'x.xml' )
+        symbol5 = os.path.join( symbols_dir, 'triangle.xml' )
+        symbol6 = os.path.join( symbols_dir, 'triangle_filled.xml' )
+        symbol7 = os.path.join( symbols_dir, 'circle.xml' )
+        symbol8 = os.path.join( symbols_dir, 'circle_filled.xml' )
+        symbol9 = os.path.join( symbols_dir, 'dash.xml' )
+        self.set_step_name( 'OGRFS Vector Symbol from File' )
+        self.set_text( \
+            'In the middle of the screen should be 9 green symbols.\n'
+        'Symbols loaded from the following files:\n'
+        + symbol1 + '\n'
+        + symbol2 + '\n'
+        + symbol3 + '\n'
+        + symbol4 + '\n'
+        + symbol5 + '\n'
+        + symbol6 + '\n'
+        + symbol7 + '\n'
+        + symbol8 + '\n'
+        + symbol9 + '\n'
+            + '\n'
+            + '\n' )
+
+        shapes = gview.GvShapes()
+        gview.undo_register( shapes)
+
+        sm = gview.GvSymbolManager()
+
+        sm.get_symbol(symbol1)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 40, 40, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol1 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol2)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 40, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol2 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol3)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 40, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol3 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol4)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 40, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol4 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol5)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol5 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol6)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 50, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol6 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol7)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 40, 60, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol7 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol8)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 50, 60, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol8 +'")' )
+        shapes.append( shape )
+
+        sm.get_symbol(symbol9)
+        shape = gview.GvShape( type = gview.GVSHAPE_POINT )
+        shape.set_node( 60, 60, node=0 )
+        shape.set_property('_gv_ogrfs',
+               'SYMBOL(c:#FF0000,s:2,id:"' + symbol9 +'")' )
+        shapes.append( shape )
+
+        # Create the layer and display
+        
+        layer = gview.GvShapesLayer( shapes )
+        layer.set_name( 'test' )
+        self.view.add_layer( layer )
+        self.view.set_active_layer( layer )
+
+        self.view.fit_extents( 0, 0, 100, 100 )
+
+
+TOOL_LIST = ['RenderTestTool']
+

Added: packages/openev/branches/upstream/current/xmlconfig/CVS/Entries
===================================================================
--- packages/openev/branches/upstream/current/xmlconfig/CVS/Entries	                        (rev 0)
+++ packages/openev/branches/upstream/current/xmlconfig/CVS/Entries	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,4 @@
+/DefaultIconFile.xml/1.2/Mon Aug  9 13:39:28 2004//
+/DefaultMenuFile.xml/1.2/Fri Jul  2 16:40:51 2004//
+/DefaultPyshellFile.xml/1.1/Mon Jul 28 19:45:07 2003//
+D

Added: packages/openev/branches/upstream/current/xmlconfig/CVS/Repository
===================================================================
--- packages/openev/branches/upstream/current/xmlconfig/CVS/Repository	                        (rev 0)
+++ packages/openev/branches/upstream/current/xmlconfig/CVS/Repository	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+openev/xmlconfig

Added: packages/openev/branches/upstream/current/xmlconfig/CVS/Root
===================================================================
--- packages/openev/branches/upstream/current/xmlconfig/CVS/Root	                        (rev 0)
+++ packages/openev/branches/upstream/current/xmlconfig/CVS/Root	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1 @@
+:pserver:anonymous at cvs.sourceforge.net:/cvsroot/openev

Added: packages/openev/branches/upstream/current/xmlconfig/DefaultIconFile.xml
===================================================================
--- packages/openev/branches/upstream/current/xmlconfig/DefaultIconFile.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/xmlconfig/DefaultIconFile.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,89 @@
+<GViewAppIconBar>
+ <Iconbar>
+   <icon>
+     <xpm>'openfile.xpm'</xpm>
+     <hint>'Open and Display Raster/Vector File'</hint>
+     <callback>self.file_open_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'print.xpm'</xpm>
+     <hint>'Print Current View'</hint>
+     <callback>self.print_cb</callback>
+     <help>'gvprint.html'</help>
+   </icon>
+   <icon>
+     <xpm>'nonelut.xpm'</xpm>
+     <hint>'Revert to no Enhancement'</hint>
+     <callback>self.nonelut_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'linear.xpm'</xpm>
+     <hint>'Linear Stretch/Enhancement'</hint>
+     <callback>self.linear_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'equalize.xpm'</xpm>
+     <hint>'Apply Equalization Enhancement to Raster'</hint>
+     <callback>self.equalize_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'log.xpm'</xpm>
+     <hint>'Logarithmic Enhancement to Raster'</hint>
+     <callback>self.log_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'windowed.xpm'</xpm>
+     <hint>'Windowed Raster Re-enhancement'</hint>
+     <callback>self.restretch_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'classify.xpm'</xpm>
+     <hint>'Classify Layer'</hint>
+     <callback>self.classify_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'legend.xpm'</xpm>
+     <hint>'Show Legend'</hint>
+     <callback>self.show_legend_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'seeall.xpm'</xpm>
+     <hint>'Fit All Layers'</hint>
+     <callback>self.seeall_cb</callback>
+   </icon>
+   <icon>
+     <widget>self.zoom_factor</widget>
+     <hint>'Zoom Ratio'</hint>
+   </icon>
+   <icon>
+     <xpm>'zoomin.xpm'</xpm>
+     <hint>'Zoom in x2'</hint>
+     <callback>self.zoomin_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'zoomout.xpm'</xpm>
+     <hint>'Zoom out x2'</hint>
+     <callback>self.zoomout_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'refresh.xpm'</xpm>
+     <hint>'Refresh Rasters From Disk'</hint>
+     <callback>self.refresh_cb</callback>
+   </icon>
+   <icon>
+     <pixmap>self.rawgeo_pixmap</pixmap>
+     <hint>'Georeferenced'</hint>
+     <callback>self.rawgeo_cb</callback>
+   </icon>
+   <icon>
+     <xpm>'help.xpm'</xpm>
+     <hint>'Launch Online Help'</hint>
+     <callback>self.helpcb</callback>
+   </icon>
+   <icon>
+     <pixmap>self.idlebusy_pixmap</pixmap>
+     <hint>'Busy Indicator'</hint>
+     <callback>self.do_nothing</callback>
+   </icon>
+ </Iconbar>
+</GViewAppIconBar>

Added: packages/openev/branches/upstream/current/xmlconfig/DefaultMenuFile.xml
===================================================================
--- packages/openev/branches/upstream/current/xmlconfig/DefaultMenuFile.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/xmlconfig/DefaultMenuFile.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,140 @@
+<GViewAppMenu>
+ <Menu>
+  <entry>
+    <path>'File/Import'</path>
+    <callback>self.file_import_cb</callback>
+  </entry>
+  <entry>
+    <path>'File/Open'</path>
+    <accelerator>control+O</accelerator>
+    <callback>self.file_open_cb</callback>
+  </entry>
+  <entry>
+    <path>'File/Open 3D'</path>
+    <callback>self.open_3D_request</callback>
+  </entry>
+  <entry>
+    <path>'File/Save Vector Layer'</path>
+    <callback>self.save_vector_layer_request</callback>
+  </entry>
+  <entry>
+    <path>'File/Save Project'</path>
+    <callback>self.menu_save_project</callback>
+  </entry>
+  <entry>
+    <path>'File/Save Project as...'</path>
+    <callback>self.menu_save_project_as</callback>
+  </entry>
+  <entry>
+    <path>'File/New View'</path>
+    <callback>self.menu_new_view</callback>
+  </entry>
+  <entry>
+    <path>'File/Print'</path>
+    <callback>self.print_cb</callback>
+  </entry>
+  <entry>
+    <path type="separator">'File/'</path>
+  </entry>
+  <entry>
+    <path>'File/rfl1'</path>
+    <callback>self.rfl_cb</callback>
+    <arguments>
+      <arg>1</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path>'File/rfl2'</path>
+    <callback>self.rfl_cb</callback>
+    <arguments>
+      <arg>2</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path>'File/rfl3'</path>
+    <callback>self.rfl_cb</callback>
+    <arguments>
+      <arg>3</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path>'File/rfl4'</path>
+    <callback>self.rfl_cb</callback>
+    <arguments>
+      <arg>4</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path>'File/rfl5'</path>
+    <callback>self.rfl_cb</callback>
+    <arguments>
+      <arg>5</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path type="separator">'File/'</path>
+  </entry>
+  <entry>
+    <path>'File/Close'</path>
+    <callback>self.close</callback>
+  </entry>
+  <entry>
+    <path>'File/Exit'</path>
+    <accelerator>control+Q</accelerator>
+    <callback>self.exit</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Undo'</path>
+    <callback>self.undo</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Layers...'</path>
+    <callback>self.app.show_layerdlg</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Vector Layer Attributes...'</path>
+    <callback>self.show_oeattedit</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Edit Toolbar...'</path>
+    <callback>self.app.show_toolbardlg</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Go To...'</path>
+    <callback>self.goto_dlg</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Python Shell...'</path>
+    <callback>self.pyshell</callback>
+  </entry>
+  <entry>
+    <path>'Edit/3D Position...'</path>
+    <callback>self.position_3d</callback>
+  </entry>
+  <entry>
+    <path>'Edit/Preferences...'</path>
+    <callback>self.app.launch_preferences</callback>
+  </entry>
+  <entry>
+    <path>'Help/Help...'</path>
+    <callback>self.helpcb</callback>
+    <arguments>
+      <arg>'openevmain.html'</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path type="separator">'Help/'</path>
+  </entry>
+  <entry>
+    <path>'Help/Web Page...'</path>
+    <callback>self.helpcb</callback>
+    <arguments>
+      <arg>'http://OpenEV.Sourceforge.net/'</arg>
+    </arguments>
+  </entry>
+  <entry>
+    <path>'Help/About...'</path>
+    <callback>self.aboutcb</callback>
+  </entry>
+ </Menu>
+</GViewAppMenu>
\ No newline at end of file

Added: packages/openev/branches/upstream/current/xmlconfig/DefaultPyshellFile.xml
===================================================================
--- packages/openev/branches/upstream/current/xmlconfig/DefaultPyshellFile.xml	                        (rev 0)
+++ packages/openev/branches/upstream/current/xmlconfig/DefaultPyshellFile.xml	2007-06-23 22:26:29 UTC (rev 916)
@@ -0,0 +1,2 @@
+<GViewAppPyshell>
+</GViewAppPyshell>
\ No newline at end of file




More information about the Pkg-grass-devel mailing list