
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                    Module Library:                    //
//                        Grid_IO                        //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//                     ESRI_E00.cpp                      //
//                                                       //
//                 Copyright (C) 2003 by                 //
//                      Olaf Conrad                      //
//                                                       //
//-------------------------------------------------------//
//                                                       //
// This file is part of 'SAGA - System for Automated     //
// Geoscientific Analyses'. SAGA 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; version 2 of the License.   //
//                                                       //
// SAGA 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.                                                  //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//    e-mail:     oconrad@saga-gis.org                   //
//                                                       //
//    contact:    Olaf Conrad                            //
//                Institute of Geography                 //
//                University of Goettingen               //
//                Goldschmidtstr. 5                      //
//                37077 Goettingen                       //
//                Germany                                //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#include "ESRI_E00_Import.h"


///////////////////////////////////////////////////////////
//														 //
//						Import							 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
CESRI_E00_Import::CESRI_E00_Import(void)
{
	//-----------------------------------------------------
	// 1. info_Table...

	Set_Name	(_TL("Import ESRI E00 File"));

	Set_Author	(_TL("Copyrights (c) 2004 by Olaf Conrad"));

	Set_Description	(_TW(
		"Import data sets from ESRI's E00 interchange format.\n\n"

		"This import filter is based on the E00 format analysis of the GRASS GIS module "
		"\'m.in.e00\' written by Michel J. Wurtz. Go to the "
		"<a target=\"_blank\" href=\"http://grass.itc.it/\">GRASS GIS Hompage</a> "
		"for more information.\n"

		"The <a target=\"_blank\" href=\"http://avce00.maptools.org/e00compr/index.html\">\'E00Compr\' library</a> "
		"written by Daniel Morissette has been used for e00 file access, so that "
		"compressed e00 files also can be read.\n")
	);


	//-----------------------------------------------------
	// 2. Parameters...

	Parameters.Add_Grid_Output(
		NULL	, "GRID"	, _TL("Grid"),
		_TL("")
	);

	Parameters.Add_Shapes_Output(
		NULL	, "ARCS"	, _TL("Arcs"),
		_TL("")
	);

	Parameters.Add_Shapes_Output(
		NULL	, "SITES"	, _TL("Sites"),
		_TL("")
	);

	Parameters.Add_Shapes_Output(
		NULL	, "LABELS"	, _TL("Labels"),
		_TL("")
	);

	Parameters.Add_Shapes_Output(
		NULL	, "BND"		, _TL("Boundary"),
		_TL("")
	);

	Parameters.Add_Shapes_Output(
		NULL	, "TIC"		, _TL("Tick Points"),
		_TL("")
	);

	Parameters.Add_Table_Output(
		NULL	, "TABLE"	, _TL("Table"),
		_TL("")
	);

	Parameters.Add_FilePath(
		NULL	, "FILE"	, _TL("File"),
		_TL(""),
		_TL("ESRI E00 Files|*.e00;*.e0*|All Files|*.*")
	);
}

//---------------------------------------------------------
CESRI_E00_Import::~CESRI_E00_Import(void)
{}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CESRI_E00_Import::On_Execute(void)
{
	bool	bResult;

	bResult		= false;
	hReadPtr	= NULL;

	if( Open(Parameters("FILE")->asString()) )
	{
		bResult	= Load();
	}

	if( hReadPtr )
	{
		E00ReadClose(hReadPtr);
	}

	return( bResult );
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CESRI_E00_Import::E00GotoLine(int iLine)
{
	if( hReadPtr )
	{
		E00ReadRewind(hReadPtr);

		while( E00ReadNextLine(hReadPtr) && hReadPtr->nInputLineNo != iLine );

		return( hReadPtr->nInputLineNo == iLine );
	}

	return( false );
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CESRI_E00_Import::Open(const SG_Char *FileName)
{
	const char	*Line;

	//-----------------------------------------------------
	if( FileName == NULL || (hReadPtr = E00ReadOpen(CSG_String(FileName).b_str())) == NULL )
	{
		Error_Set(CSG_String::Format(_TL("%s - not found\n"), FileName));

		return( false );
	}

	//-----------------------------------------------------
	if( (Line = E00ReadNextLine(hReadPtr)) == NULL )
	{
		Error_Set(CSG_String::Format(_TL("\"%s\" is not an Arc-info_Table Export file !\n"), FileName));

		return( false );
	}

	//-----------------------------------------------------
	if( strncmp(Line, "EXP", 3) )
	{
		Error_Set(CSG_String::Format(_TL("\"%s\" is not an Arc-info_Table Export file !\n"), FileName));

		return( false );
	}

	//-----------------------------------------------------
	e00_Name	= FileName;

	return( true );
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CESRI_E00_Import::Load(void)
{
	const char	*line;

	int			prec_grd, prec_arc, prec_lab, prec_pal;

	long		current_line,
				offset_grd	= 0,
				offset_arc	= 0,
				offset_lab	= 0,
				offset_pal	= 0;

	double		scale		= 1.0;

	TSG_Shape_Type	shape_type;

	CSG_Grid		*pGrid;

	CSG_Shapes		*pShapes;

	//-----------------------------------------------------
	pPAT	= NULL;
	pAAT	= NULL;

	//-----------------------------------------------------
	while( (line = E00ReadNextLine(hReadPtr)) != NULL && strncmp(line, "EOS", 3) )
	{
		current_line = hReadPtr->nInputLineNo;

		// GRID SECTION
		if( !strncmp(line, "GRD  ", 5) )
		{
			offset_grd	= current_line;
			prec_grd	= line[5] - '2';
			skip("EOG");
			continue;
		}

		// ARC SECTION
		if( !strncmp(line, "ARC  ", 5) )
		{
			offset_arc	= current_line;
			prec_arc	= line[5] - '2';
			skip_arc(prec_arc);
			continue;
		}

		// POLYGON TOPOLOGY
		if(	!strncmp(line, "PAL  ", 5)
		||	!strncmp(line, "PFF  ", 5)	)
		{
			offset_pal	= current_line;
			prec_pal	= line[5] - '2';
			skip_pal(prec_pal);
			continue;
		}

		// CENTROID SECTION
		if( !strncmp(line, "CNT  ", 5) )
		{
			skip_dat();
			continue;
		}

		// LABEL SECTION
		if( !strncmp(line, "LAB  ", 5))
		{
			offset_lab	= current_line;
			prec_lab	= line[5] - '2';
			skip_lab(prec_lab);
			continue;
		}

		// INFO SECTION
		if( !strncmp(line, "IFO  ", 5) )
		{
			info_Get_Tables();
			continue;
		}

		// PROJECTION INFOS
		if( !strncmp(line, "PRJ  ", 5) )
		{
			scale		= getproj();
			continue;
		}

		// Annotations (text). To be imported ? Does anybody have an idea ?
		if( !strncmp(line, "TXT  ", 5) )
		{
			skip_txt(line[5] - '2');
			continue;
		}

		// Mask description ? Noting to do with it
		if( !strncmp(line, "MSK  ", 5) )
		{
			skip_msk();
			continue;
		}

		// TOLERANCE SECTION. Should we really use it ?
		if( !strncmp(line, "TOL  ", 5) )
		{
			skip_dat();
			continue;
		}

		// UNKNOW KEYWORD SECTION. Don't know what to do with. Does anybody have an idea?
		if( !strncmp(line, "LNK  ", 5) )
		{
			skip("END OF LINK DATA");
			continue;
		}

		// SPATIAL INDEX SECTION. Noting to do with it
		if( !strncmp(line, "SIN  ", 5) )
		{
			skip("EOX");
			continue;
		}

		// Line pattern and palette. Shade pattern and palette end same as e00 archive !
		if(	!strncmp(line, "CLN  ", 5)
		||	!strncmp(line, "CSH  ", 5)	)
		{
			skip("EOS");
			continue;
		}

		// Font description ? Noting to do with it
		if( !strncmp(line, "FNT  ", 5) )
		{
			skip("EOF");
			continue;
		}

		// PLOT SECTION. Why should we import it ?
		if( !strncmp(line, "PLT  ", 5) )
		{
			skip("EOP");
			continue;
		}

		// LOG SECTION. Nothing to do with it
		if( !strncmp(line, "LOG  ", 5) )
		{
			skip("EOL");
			continue;
		}

		if(	!strncmp(line, "RPL  ", 5)		// Specific to regions. Contains PAL formated data for each subclass
		||	!strncmp(line, "RXP  ", 5)		// Specific to regions. Seems to link regions IDs to PAL polygons IDs
		||	!strncmp(line, "TX6  ", 5)		// Other kind of annotations not same termination. Other differences ?
		||	!strncmp(line, "TX7  ", 5)	)	// Very close from TX6. So same questions and same rules...
		{
			skip("JABBERWOCKY");
			continue;
		}
	}

	//-----------------------------------------------------
	switch( pPAT ? (pAAT ? 3 : 2) : (pAAT ? 1 : 0) )
	{
	case 0: default:
		shape_type	= offset_arc != 0 ? SHAPE_TYPE_Line : SHAPE_TYPE_Point;
		break;

	case 1:	// pAAT
		shape_type	= SHAPE_TYPE_Line;
		break;

	case 2:	// pPAT
		shape_type	= offset_arc != 0 ? SHAPE_TYPE_Polygon : SHAPE_TYPE_Point;
		break;

	case 3:	// pAAT && pPAT
		shape_type	= offset_pal != 0 || offset_lab != 0 ? SHAPE_TYPE_Polygon : SHAPE_TYPE_Line;
		break;
	}

	//-----------------------------------------------------
	// Extracting useful information as noted before...

	//-----------------------------------------------------
	if( offset_grd > 0 )
	{
		E00GotoLine(offset_grd);

		if( (pGrid = getraster	(prec_grd, scale)) != NULL )
		{
			pGrid->Set_Name(e00_Name);

			Parameters("GRID")->Set_Value(pGrid);
		}
	}

	//-----------------------------------------------------
	if( offset_arc != 0 )
	{
		E00GotoLine(offset_arc);

		if( (pShapes = getarcs	(prec_arc, scale, shape_type)) != NULL )
		{
			pShapes->Set_Name(e00_Name);

			Parameters("ARCS")->Set_Value(pShapes);
		}
	}

	//-----------------------------------------------------
	if( offset_lab != 0 && shape_type == SHAPE_TYPE_Point )
	{
		E00GotoLine(offset_lab);

		if( (pShapes = getsites	(prec_lab, scale)) != NULL )
		{
			pShapes->Set_Name(e00_Name);

			Parameters("SITES")->Set_Value(pShapes);
		}
	}

	//-----------------------------------------------------
	if( offset_lab != 0 && shape_type != SHAPE_TYPE_Point )
	{
		E00GotoLine(offset_lab);

		if( (pShapes = getlabels(prec_lab, scale)) != NULL )
		{
			pShapes->Set_Name(e00_Name);

			Parameters("LABELS")->Set_Value(pShapes);
		}
	}

	//-----------------------------------------------------
	return( true );
}


///////////////////////////////////////////////////////////
//														 //
//						Grid							 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
CSG_Grid * CESRI_E00_Import::getraster(int prec, double scale)
{
	const char	*line;

	int		x, y, ix;
	long	rows, cols, depth, p[5];
	float	f[5];
	double	xres, yres, xmin, ymin, xmax, ymax, nul_val, d[3];
	CSG_Grid	*pGrid;

	//-----------------------------------------------------
	if( (line = E00ReadNextLine(hReadPtr)) == NULL )
		return( NULL );
//	sscanf(line, "%ld%ld%ld", &cols, &rows, &depth, &nul_val);
	sscanf(line, "%ld%ld%ld%lf", &cols, &rows, &depth, &nul_val);

	if( (line = E00ReadNextLine(hReadPtr)) == NULL )
		return( NULL );
	sscanf(line, "%lf%lf", &xres, &yres);

	if( (line = E00ReadNextLine(hReadPtr)) == NULL )
		return( NULL );
	sscanf(line, "%lf%lf", &xmin, &ymin);

	if( (line = E00ReadNextLine(hReadPtr)) == NULL )
		return( NULL );
	sscanf(line, "%lf%lf", &xmax, &ymax);

	xmax	= xmax * scale;
	xmin	= xmin * scale;
	ymax	= ymax * scale;
	ymin	= ymin * scale;
	xres	= xres * scale;
	yres	= yres * scale;

	if( depth == 2 && prec )
	{
		depth	= 3;
	}

	//-----------------------------------------------------
	switch( depth )
	{
	default:
		pGrid	= NULL;
		break;

	//-----------------------------------------------------
	case 1:
		pGrid	= SG_Create_Grid(GRID_TYPE_Int, cols, rows, xres, xmin, ymin);
		pGrid->Set_NoData_Value(nul_val);

		for(y=0; y<rows && line && Set_Progress(y, rows); y++)
		{
			for(x=0; x<cols; x+= 5)
			{
				if( (line = E00ReadNextLine(hReadPtr)) != NULL )
				{
					sscanf(line, "%ld%ld%ld%ld%ld", p, p+1, p+2, p+3, p+4);

					for(ix=0; ix<5 && x+ix<cols; ix++)
					{
						pGrid->Set_Value(x + ix, y, p[ix]);
					}
				}
			}
		}
		break;

	//-----------------------------------------------------
	case 2:
		pGrid	= SG_Create_Grid(GRID_TYPE_Float, cols, rows, xres, xmin, ymin);
		pGrid->Set_NoData_Value(nul_val);

		for(y=0; y<rows && line && Set_Progress(y, rows); y++)
		{
			for(x=0; x<cols; x+= 5)
			{
				if( (line = E00ReadNextLine(hReadPtr)) != NULL )
				{
					sscanf(line, "%f%f%f%f%f", f, f+1, f+2, f+3, f+4);

					for(ix=0; ix<5 && x+ix<cols; ix++)
					{
						pGrid->Set_Value(x + ix, y, f[ix]);
					}
				}
			}
		}
		break;

	//-----------------------------------------------------
	case 3:
		pGrid	= SG_Create_Grid(GRID_TYPE_Double, cols, rows, xres, xmin, ymin);
		pGrid->Set_NoData_Value(nul_val);

		for(y=0; y<rows && line && Set_Progress(y, rows); y++)
		{
			for(x=0; x<cols; x+= 3)
			{
				if( (line = E00ReadNextLine(hReadPtr)) != NULL )
				{
					sscanf(line, "%lf%lf%lf", d, d+1, d+2);

					for(ix=0; ix<3 && x+ix<cols; ix++)
					{
						pGrid->Set_Value(x + ix, y, d[ix]);
					}
				}
			}
		}
		break;
	}

	//-----------------------------------------------------
	skip("EOG");

	return( pGrid );
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#define ARC_FNODE	2
#define ARC_TNODE	3
#define ARC_LPOL	4
#define ARC_RPOL	5

//---------------------------------------------------------
CSG_Shapes * CESRI_E00_Import::getarcs(int prec, double scale, TSG_Shape_Type &shape_type)
{
	const char	*line;

	int		covnum, cov_id, fnode, tnode, lpol, rpol, nPoints, iPoint;
	double	x_buf[2], y_buf[2];
	CSG_Shape	*pShape;
	CSG_Shapes	*pShapes;

	//-----------------------------------------------------
	pShapes	= SG_Create_Shapes(shape_type);
	pShapes->Get_Table().Add_Field("ID"	, TABLE_FIELDTYPE_Int);
	pShapes->Get_Table().Add_Field("ID#"	, TABLE_FIELDTYPE_Int);
	pShapes->Get_Table().Add_Field("FNODE"	, TABLE_FIELDTYPE_Int);
	pShapes->Get_Table().Add_Field("TNODE"	, TABLE_FIELDTYPE_Int);
	pShapes->Get_Table().Add_Field("LPOL"	, TABLE_FIELDTYPE_Int);
	pShapes->Get_Table().Add_Field("RPOL"	, TABLE_FIELDTYPE_Int);

	Set_Progress(0, 100);

	//-----------------------------------------------------
	do
	{
		Process_Set_Text(CSG_String::Format(SG_T("Loaded arcs: %d"), pShapes->Get_Count()));

		if( (line = E00ReadNextLine(hReadPtr)) == NULL )
		{
			covnum	= -1;
		}
		else
		{
			sscanf(line, "%d %d %d %d %d %d %d", &covnum, &cov_id, &fnode, &tnode, &lpol, &rpol, &nPoints);
		}

		if( covnum != -1 )
		{
			pShape	= pShapes->Add_Shape();

			pShape->Get_Record()->Set_Value(0			, covnum);
			pShape->Get_Record()->Set_Value(1			, cov_id);
			pShape->Get_Record()->Set_Value(ARC_FNODE	, fnode);
			pShape->Get_Record()->Set_Value(ARC_TNODE	, tnode);
			pShape->Get_Record()->Set_Value(ARC_LPOL	, lpol);
			pShape->Get_Record()->Set_Value(ARC_RPOL	, rpol);

			//---------------------------------------------
			if( prec )	// double precision : 1 coord pair / line
			{
				for(iPoint=0; iPoint<nPoints && line; iPoint++)
				{
					if( (line = E00ReadNextLine(hReadPtr)) != NULL )
					{
						sscanf(line, "%lf %lf", x_buf, y_buf);

						pShape->Add_Point(x_buf[0] * scale, y_buf[0] * scale);
					}
				}
			}

			//---------------------------------------------
			else		// single precision : 2 x,y pairs / line
			{
				for(iPoint=0; iPoint<nPoints && line; iPoint+=2)
				{
					if( (line = E00ReadNextLine(hReadPtr)) != NULL )
					{
						sscanf(line, "%lf %lf %lf %lf", x_buf, y_buf, x_buf + 1, y_buf + 1);

						pShape->Add_Point(x_buf[0] * scale, y_buf[0] * scale);

						if( iPoint + 1 < nPoints )
						{
							pShape->Add_Point(x_buf[1] * scale, y_buf[1] * scale);
						}
					}
				}
			}
		}
	}
	while( covnum != -1 && line && Process_Get_Okay(false) );

	//-----------------------------------------------------
	if( pShapes->Get_Count() == 0 )
	{
		delete(pShapes);

		shape_type	= SHAPE_TYPE_Point;

		return( NULL );
	}

	if( shape_type == SHAPE_TYPE_Polygon )
	{
		pShapes	= Arcs2Polygons(pShapes);

		Assign_Attributes(pShapes);
	}

	return( pShapes );
}

//---------------------------------------------------------
CSG_Shapes * CESRI_E00_Import::Arcs2Polygons(CSG_Shapes *pArcs)
{
	int			iArc, nArcs, id;
	CSG_Shapes	*pPolygons;

	//-----------------------------------------------------
	Process_Set_Text(_TL("Arcs to polygons"));

	pPolygons	= SG_Create_Shapes(SHAPE_TYPE_Polygon);
	pPolygons->Get_Table().Add_Field("ID", TABLE_FIELDTYPE_Int);

	nArcs		= pArcs->Get_Count();

	//-----------------------------------------------------
	while( (iArc = pArcs->Get_Count() - 1) >= 0 && Set_Progress(nArcs - iArc - 1, nArcs) )
	{
		id	= pArcs->Get_Shape(iArc)->Get_Record()->asInt(ARC_LPOL);

		if( id == pArcs->Get_Shape(iArc)->Get_Record()->asInt(ARC_RPOL) )
		{
			pArcs->Del_Shape(iArc);
		}
		else if( id > 1 )
		{
			Arcs2Polygon(pArcs, pPolygons, id);
		}

		if( (iArc = pArcs->Get_Count() - 1) >= 0 )
		{
			id	= pArcs->Get_Shape(iArc)->Get_Record()->asInt(ARC_RPOL);

			if( id > 1 )
			{
				Arcs2Polygon(pArcs, pPolygons, id);
			}
		}
	}

	//-----------------------------------------------------
	delete( pArcs );

	return( pPolygons );
}

//---------------------------------------------------------
void CESRI_E00_Import::Arcs2Polygon(CSG_Shapes *pArcs, CSG_Shapes *pPolygons, int id)
{
	int		iShape, iPart, iPoint, iNode;
	CSG_Shape	*pArc, *pShape;
	CSG_Shapes	Arcs;

	//-----------------------------------------------------
	Arcs.Create(SHAPE_TYPE_Line);
	Arcs.Get_Table().Add_Field("FROM_NODE", TABLE_FIELDTYPE_Int);
	Arcs.Get_Table().Add_Field("TO___NODE", TABLE_FIELDTYPE_Int);

	//-----------------------------------------------------
	for(iShape=pArcs->Get_Count()-1; iShape>=0; iShape--)
	{
		pShape	= pArcs->Get_Shape(iShape);

		if( id == pShape->Get_Record()->asInt(ARC_LPOL) )
		{
			pArc	= Arcs.Add_Shape();
			pArc->Get_Record()->Set_Value(0, pShape->Get_Record()->asInt(ARC_FNODE));
			pArc->Get_Record()->Set_Value(1, pShape->Get_Record()->asInt(ARC_TNODE));

			for(iPoint=0; iPoint<pShape->Get_Point_Count(0); iPoint++)
			{
				pArc->Add_Point(pShape->Get_Point(iPoint, 0), 0);
			}

			if( pShape->Get_Record()->asInt(ARC_RPOL) <= 1 )
			{
				pArcs->Del_Shape(iShape);
			}
			else
			{
				pShape->Get_Record()->Set_Value(ARC_LPOL, 1);
			}
		}
		else if( id == pShape->Get_Record()->asInt(ARC_RPOL) )
		{
			pArc	= Arcs.Add_Shape();
			pArc->Get_Record()->Set_Value(1, pShape->Get_Record()->asInt(ARC_FNODE));
			pArc->Get_Record()->Set_Value(0, pShape->Get_Record()->asInt(ARC_TNODE));

			for(iPoint=pShape->Get_Point_Count(0)-1; iPoint>=0; iPoint--)
			{
				pArc->Add_Point(pShape->Get_Point(iPoint, 0), 0);
			}

			if( pShape->Get_Record()->asInt(ARC_LPOL) <= 1 )
			{
				pArcs->Del_Shape(iShape);
			}
			else
			{
				pShape->Get_Record()->Set_Value(ARC_RPOL, 1);
			}
		}
	}

	//-----------------------------------------------------
	if( Arcs.Get_Count() > 0 )
	{
		iPart	= 0;
		pShape	= pPolygons->Add_Shape();
		pShape->Get_Record()->Set_Value(0, id);

		do
		{
			pArc	= Arcs.Get_Shape(0);

			while( pArc )
			{
				for(iPoint=0; iPoint<pArc->Get_Point_Count(0); iPoint++)
				{
					pShape->Add_Point(pArc->Get_Point(iPoint, 0), iPart);
				}

				iNode	= pArc->Get_Record()->asInt(1);
				Arcs.Del_Shape(pArc);

				for(iShape=0, pArc=NULL; iShape<Arcs.Get_Count() && !pArc; iShape++)
				{
					if( iNode == Arcs.Get_Shape(iShape)->Get_Record()->asInt(0) )
					{
						pArc	= Arcs.Get_Shape(iShape);
					}
				}
			}

			iPart++;
		}
		while( Arcs.Get_Count() > 0 );
	}
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
CSG_Shapes * CESRI_E00_Import::getlabels(int prec, double scale)	// shape_type: LINE or AREA
{
	const char	*line;

	int		num, id;	// coverage-# and coverage-ID
	double	x, y;
	CSG_Shapes	*pShapes;
	CSG_Shape	*pShape;

	pShapes	= SG_Create_Shapes(SHAPE_TYPE_Point);

	pShapes->Get_Table().Add_Field("ID#"	, TABLE_FIELDTYPE_Int);
	pShapes->Get_Table().Add_Field("ID"	, TABLE_FIELDTYPE_Int);

	while( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf(line, "%d %d %lf %lf", &id, &num, &x, &y);

		if( id == -1 )
		{
			break;
		}
		else
		{
			pShape	= pShapes->Add_Shape();

			pShape->Add_Point(x * scale, y * scale);

			pShape->Get_Record()->Set_Value(0, num);
			pShape->Get_Record()->Set_Value(1, id);

			//---------------------------------------------
			E00ReadNextLine(hReadPtr);		// 4 values to skip

			if( prec )
			{
				E00ReadNextLine(hReadPtr);	// on 2nd line when double precision
			}
		}
	}

	if( pShapes->Get_Count() <= 0 )
	{
		delete( pShapes );
		pShapes	= NULL;
	}

	return( pShapes );
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
CSG_Shapes * CESRI_E00_Import::getsites(int prec, double scale)
{
	const char	*line;

	int		id;
	double	x, y;
	CSG_Shape	*pShape;
	CSG_Shapes	*pShapes;

	pShapes	= SG_Create_Shapes(SHAPE_TYPE_Point);
	pShapes->Get_Table().Add_Field("ID", TABLE_FIELDTYPE_Int);

	while( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf(line, "%d %*d %lf %lf", &id, &x, &y);

		if( id == -1 )
		{
			break;
		}

		pShape	= pShapes->Add_Shape();

		pShape->Add_Point(x * scale, y * scale);
		pShape->Get_Record()->Set_Value(0, id);

		//-------------------------------------------------
		E00ReadNextLine(hReadPtr);		// 4 values to skip

		if( prec )
		{
			E00ReadNextLine(hReadPtr);	// on 2nd line when double precision
		}
	}

	if( pShapes->Get_Count() <= 0 )
	{
		delete( pShapes );
		pShapes	= NULL;
	}
	else
	{
		Assign_Attributes(pShapes);
	}

	return( pShapes );
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
double CESRI_E00_Import::getproj(void)
{
	const char	*line;

	double	scale	= 1.0;

	while( (line = E00ReadNextLine(hReadPtr)) != NULL && strncmp(line, "EOP", 3) )
	{
		if( !strncmp(line, "Units", 5) )
		{
			sscanf(line + 6, "%lf", &scale);
		}
	}

	scale	= 1.0 / scale;

	return( scale );
}


///////////////////////////////////////////////////////////
//														 //
//						info section					 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
struct info_Field
{
	char				Name[18];	// name of item

	int					Position,	// position in data line
						Size,		// size for reading
						Type;		// type of data
};

//---------------------------------------------------------
struct info_Table
{
	char				Name[34],
						AI[4];		// XX if Arc/info file, spaces otherwise

	int					uFields,	// number of usable items in this table
						nFields,	// number of items in this table
						ldr;		// length of data record

	long				nRecords,	// number of data records
						length;		// total length for one data line

	struct info_Field	*Field;		// One per field...
};

//---------------------------------------------------------
// [06.06.2006] ESRI E00 Import crash fix, James Flemer
int CESRI_E00_Import::info_Get_Tables(void)
{
	const char *line;

	char				tmp[12], *p;
	int					i;
	CSG_String			s;
	CSG_Table				*pTable;
	struct info_Table	info;

	//-----------------------------------------------------
	while( (line = E00ReadNextLine(hReadPtr)) != NULL && strncmp(line, "EOI", 3) )
	{
		strncpy(info.Name, line, 32);
		info.Name[32]	= 0;
		p	= strchr(info.Name, ' ');
		if( p != NULL )
			*p	= 0;
		p	= strchr(info.Name, '.');
		if( p == 0 )
			p	= info.Name;
		else
			p++;
		s	= p;

		strncpy(info.AI	, line + 32,  2);	info.AI[2]	= 0;
		strncpy(tmp		, line + 34,  4);	tmp[ 4]		= 0;	info.uFields	= atoi(tmp);
		strncpy(tmp		, line + 38,  4);	tmp[ 4]		= 0;	info.nFields	= atoi(tmp);
		strncpy(tmp		, line + 42,  4);	tmp[ 4]		= 0;	info.ldr		= atoi(tmp);
		strncpy(tmp		, line + 46, 11);	tmp[11]		= 0;	info.nRecords	= atol(tmp);

		info.length	= 0;
		info.Field	= (struct info_Field *)malloc(info.nFields * sizeof(struct info_Field));

		//---------------------------------------------
		for(i=0; i<info.nFields; i++)
		{
			if( (line = E00ReadNextLine(hReadPtr)) != NULL )
			{
				sscanf(line, "%16s", info.Field[i].Name);
				info.Field[i].Size	= atoi(&line[16]);
				info.Field[i].Type	= atoi(&line[34]);
			}

			//---------------------------------------------
			switch( info.Field[i].Type )
			{
			case 60:	// float / double
				info.Field[i].Size = info.Field[i].Size == 4 ? 14 : 24;
				break;

			case 50:	// short / long
				info.Field[i].Size = info.Field[i].Size == 2 ?  6 : 11;
				break;

			case 40:	// float
				info.Field[i].Size = 14;
				break;

			case 10:
				info.Field[i].Size = 8;
				break;

			default:	// string
				break;
			}

			if( i < info.uFields )
			{
				info.length			+= info.Field[i].Size;
			}

			if( i == 0 )
			{
				info.Field[i].Position	= 0;
			}
			else
			{
				info.Field[i].Position	= info.Field[i-1].Position + info.Field[i-1].Size;
			}
		}

		//---------------------------------------------
		pTable	= NULL;

		if     ( !s.CmpNoCase(SG_T("aat")) && pAAT == NULL )
		{
			pTable	= pAAT	= info_Get_Table(info);
		}
		else if( !s.CmpNoCase(SG_T("pat")) && pPAT == NULL )
		{
			pTable	= pPAT	= info_Get_Table(info);
		}
	//	else if( !s.CmpNoCase("vat") )		// value table (grid)
	//	else if( !s.CmpNoCase("bnd") )		// coverage boundaries
	//	else if( !s.CmpNoCase("tic") )		// tick marks
	//	else if( !s.CmpNoCase("sta") )		// stats on grid
	//	else if( !s.CmpNoCase("lut") )		// look-up table
	//	else if( !s.CmpNoCase("acode") )	// arc attributes
	//	else if( !s.CmpNoCase("pcode") )	// polygon attributes
	//	else								// non graphic tables
		else	// come on, let's get every table we can get...
		{
			pTable	= info_Get_Table(info);
		}

		//-------------------------------------------------
		free(info.Field);

		if( pTable )
		{
			CSG_Table_Record	*pRecord;
			CSG_Shape			*pShape;
			CSG_Shapes			*pBND, *pTIC;

			if     ( !s.CmpNoCase(SG_T("bnd")) )	// coverage boundaries
			{
				pBND	= SG_Create_Shapes(SHAPE_TYPE_Polygon, SG_T("Boundary"));
				pBND->Get_Table().Add_Field("XMIN", TABLE_FIELDTYPE_Double);
				pBND->Get_Table().Add_Field("YMIN", TABLE_FIELDTYPE_Double);
				pBND->Get_Table().Add_Field("XMAX", TABLE_FIELDTYPE_Double);
				pBND->Get_Table().Add_Field("YMAX", TABLE_FIELDTYPE_Double);
				pRecord	= pTable->Get_Record(0);
				pShape	= pBND->Add_Shape();
				pShape->Get_Record()->Set_Value(0, pRecord->asDouble(0));
				pShape->Get_Record()->Set_Value(1, pRecord->asDouble(1));
				pShape->Get_Record()->Set_Value(2, pRecord->asDouble(2));
				pShape->Get_Record()->Set_Value(3, pRecord->asDouble(3));
				pShape->Add_Point(pRecord->asDouble(0), pRecord->asDouble(1));
				pShape->Add_Point(pRecord->asDouble(0), pRecord->asDouble(3));
				pShape->Add_Point(pRecord->asDouble(2), pRecord->asDouble(3));
				pShape->Add_Point(pRecord->asDouble(2), pRecord->asDouble(1));
				Parameters("BND")->Set_Value(pBND);
				delete(pTable);
			}
			else if( !s.CmpNoCase(SG_T("tic")) )	// tick marks
			{
				pTIC	= SG_Create_Shapes(SHAPE_TYPE_Point, SG_T("Tick Points"));
				pTIC->Get_Table().Add_Field("ID", TABLE_FIELDTYPE_Int);
				pTIC->Get_Table().Add_Field("X" , TABLE_FIELDTYPE_Double);
				pTIC->Get_Table().Add_Field("Y" , TABLE_FIELDTYPE_Double);
				for(i=0; i<pTable->Get_Record_Count(); i++)
				{
					pRecord	= pTable->Get_Record(i);
					pShape	= pTIC->Add_Shape();
					pShape->Get_Record()->Set_Value(0, pRecord->asInt   (0));
					pShape->Get_Record()->Set_Value(1, pRecord->asDouble(1));
					pShape->Get_Record()->Set_Value(2, pRecord->asDouble(2));
					pShape->Add_Point(pRecord->asDouble(1), pRecord->asDouble(2));
				}
				Parameters("TIC")->Set_Value(pTIC);
				delete(pTable);
			}
			else
			{
				Parameters("TABLE")->Set_Value(pTable);
			}
		}
	}

	//-----------------------------------------------------
	// 0 if none, 1 if AAT, 2 if PAT, 3 if both
	return( pPAT ? (pAAT ? 3 : 2) : (pAAT ? 1 : 0) );
}

//---------------------------------------------------------
CSG_Table * CESRI_E00_Import::info_Get_Table(struct info_Table info)
{
	char			*buffer_record, *buffer_item;
	int				iRecord, iField;
	CSG_Table			*pTable;
	CSG_Table_Record	*pRecord;

	//-----------------------------------------------------
	Process_Set_Text(CSG_String(info.Name));

	buffer_record	= (char *)malloc(info.length + 3);
	buffer_item		= (char *)malloc(info.length + 3);

	pTable			= SG_Create_Table();
	pTable->Set_Name(CSG_String(info.Name));

	//-----------------------------------------------------
	for(iField=0; iField<info.uFields; iField++)
	{
		switch( info.Field[iField].Type )
		{
		case 60:	// float / double
			pTable->Add_Field(info.Field[iField].Name, TABLE_FIELDTYPE_Double);
			break;

		case 50:	// short / long
			pTable->Add_Field(info.Field[iField].Name, TABLE_FIELDTYPE_Int);
			break;

		case 40:	// float
			pTable->Add_Field(info.Field[iField].Name, TABLE_FIELDTYPE_Double);
			break;

		case 10:	// short
			pTable->Add_Field(info.Field[iField].Name, TABLE_FIELDTYPE_Int);
			break;

		default:	// string
			pTable->Add_Field(info.Field[iField].Name, TABLE_FIELDTYPE_String);
			break;
		}
	}

	//-----------------------------------------------------
	for(iRecord=0; iRecord<info.nRecords && Set_Progress(iRecord, info.nRecords); iRecord++)
	{
		info_Get_Record(buffer_record, info.length);

		pRecord	= pTable->Add_Record();

		for(iField=0; iField<info.uFields; iField++)
		{
			strncpy(buffer_item, &buffer_record[info.Field[iField].Position], info.Field[iField].Size);
			buffer_item[info.Field[iField].Size] = 0;

			switch( pTable->Get_Field_Type(iField) )
			{
			default:
				pRecord->Set_Value(iField, atof(buffer_item));
				break;

			case TABLE_FIELDTYPE_Int:
				pRecord->Set_Value(iField, atoi(buffer_item));
				break;

			case TABLE_FIELDTYPE_String:
				pRecord->Set_Value(iField, CSG_String(buffer_item));
				break;
			}
		}
	}

	//-----------------------------------------------------
	free(buffer_record);
	free(buffer_item);

	return( pTable );
}

//---------------------------------------------------------
void CESRI_E00_Import::info_Skip_Table(struct info_Table info)
{
	char	*buffer_record;
	int		iRecord;

	buffer_record	= (char *)malloc(info.length + 3);

	for(iRecord=0; iRecord<info.nRecords; iRecord++)
	{
		info_Get_Record(buffer_record, info.length);
	}

	free(buffer_record);
}

//---------------------------------------------------------
void CESRI_E00_Import::info_Get_Record(char *buffer, int buffer_length)
{
	const char *line;

	char	*p;
	int		l;

	//-----------------------------------------------------
	p	= buffer;
	l	= 0;

	//-----------------------------------------------------
	if( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		strncpy(buffer, line, buffer_length < 84 ? buffer_length : 84);

		while( l < buffer_length )
		{
			if( *p =='\r' || *p == '\n' || *p == 0 )
			{
				while( (l % 80 || p == buffer) && l < buffer_length )
				{
					l++;
					*p++	= ' ';
				}

				if( l == buffer_length )
				{
					break;
				}
				else if( (line = E00ReadNextLine(hReadPtr)) != NULL )
				{
					strncpy(p, line, buffer_length - l < 84 ? buffer_length - l : 84);

					if( *p =='\r' || *p == '\n' || *p == 0 )	// if empty line
					{
						l++;
						*p++	= ' ';
						*p		= 0;
					}
				}
			}
			else
			{
				l++;
				p++;
			}
		}

		*p	= 0;
	}
}


///////////////////////////////////////////////////////////
//														 //
//						Skips							 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CESRI_E00_Import::Assign_Attributes(CSG_Shapes *pShapes)
{
	int				iShape, iRecord, iField, oField, id;
	CSG_Table_Record	*pRec;
	CSG_Shape			*pShape;

	if( pShapes && pShapes->Get_Table().Get_Field_Count() > 0 && pPAT && pPAT->Get_Field_Count() > 2 )
	{
		Process_Set_Text(_TL("Assign attributes to shapes..."));

		oField	= pShapes->Get_Table().Get_Field_Count();

		for(iField=0; iField<pPAT->Get_Field_Count(); iField++)
		{
			pShapes->Get_Table().Add_Field(pPAT->Get_Field_Name(iField), pPAT->Get_Field_Type(iField));
		}

		for(iShape=0; iShape<pShapes->Get_Count() && Set_Progress(iShape, pShapes->Get_Count()); iShape++)
		{
			pShape	= pShapes->Get_Shape(iShape);
			id		= pShape->Get_Record()->asInt(0);

			for(iRecord=0; iRecord<pPAT->Get_Record_Count(); iRecord++)
			{
				pRec	= pPAT->Get_Record(iRecord);

				if( id == pRec->asInt(2) )
				{
					for(iField=0; iField<pPAT->Get_Field_Count(); iField++)
					{
						switch( pPAT->Get_Field_Type(iField) )
						{
						case TABLE_FIELDTYPE_String:
							pShape->Get_Record()->Set_Value(oField + iField, pRec->asString(iField));
							break;

						default:
							pShape->Get_Record()->Set_Value(oField + iField, pRec->asDouble(iField));
							break;
						}
					}

					break;
				}
			}

		}

		return( true );
	}

	return( false );
}


///////////////////////////////////////////////////////////
//														 //
//						Skips							 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
void CESRI_E00_Import::skip(char *end)
{
	const char	*line;

	int		l	= strlen(end);

	while( (line = E00ReadNextLine(hReadPtr)) != NULL && strncmp(line, end, l) );
}

//---------------------------------------------------------
void CESRI_E00_Import::skip_dat(void)
{
	const char	*line;

	int		i	= 0;
	
	while( (line = E00ReadNextLine(hReadPtr)) != NULL && i != -1 )
	{
		sscanf(line, "%d", &i);
	}
}

//---------------------------------------------------------
void CESRI_E00_Import::skip_msk(void)
{
	const char	*line;

	double	xmin, ymin, xmax, ymax, res, sk;
	long	xsize, ysize, nskip;

	if( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf(line, "%lf %lf %lf", &xmin, &ymin, &xmax);

		if( (line = E00ReadNextLine(hReadPtr)) != NULL )
		{
			sscanf(line, "%lf %lf %ld %ld", &ymax, &res, &xsize, &ysize);

			sk		= ((ymax - ymin) / res) * ((xmax - xmin) / res) / 32.0;
			nskip	= (long)ceil(sk / 7.0);

			while( nskip-- )
			{
				E00ReadNextLine(hReadPtr);
			}
		}
	}
}

//---------------------------------------------------------
void CESRI_E00_Import::skip_arc(int prec)
{
	const char	*line;

	int		i, covnum, nPoints;

	while( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf(line, "%d %*d %*d %*d %*d %*d %d", &covnum, &nPoints);

		if( covnum == -1 )
			break;

		if( prec == 0 )
			nPoints	= (nPoints + 1) / 2;	// number of coordinate lines

		for(i=0; i<nPoints; i++)
		{
			E00ReadNextLine(hReadPtr);
		}
	}
}

//---------------------------------------------------------
void CESRI_E00_Import::skip_lab(int prec)
{
	const char	*line;

	long	covid;

	while( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf(line, "%ld", &covid);

		if( covid == -1 )
			break;

		E00ReadNextLine(hReadPtr);

		if( prec )	// two lines of coordinates in double precision
			E00ReadNextLine(hReadPtr);
	}
}

//---------------------------------------------------------
void CESRI_E00_Import::skip_pal(int prec)
{
	const char	*line;

	int		i, narcs;

	while( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf(line, "%d", &narcs);

		if( prec )	// two lines of coordinates in double precision
			E00ReadNextLine(hReadPtr);

		if( narcs == -1 )
			break;

		for(i=(narcs+1)/2; i; i--)
			E00ReadNextLine(hReadPtr);
	}
}

//---------------------------------------------------------
void CESRI_E00_Import::skip_txt(int prec)
{
	const char	*line;

	int		i, n, nskip;

	nskip	= prec ? 7 : 5;

	while( (line = E00ReadNextLine(hReadPtr)) != NULL )
	{
		sscanf( line, "%d", &n);

		if( n == -1 )
			break;

		for(i=0; i<nskip; i++)
			E00ReadNextLine(hReadPtr);
	}
}


///////////////////////////////////////////////////////////
//														 //
//														 //
//														 //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
