
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                     Tool Library                      //
//                      Grid_Shapes                      //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//                  Grid_To_Contour.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, either version 2 of the     //
// License, or (at your option) any later version.       //
//                                                       //
// 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, see   //
// <http://www.gnu.org/licenses/>.                       //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//    e-mail:     oconrad@saga-gis.org                   //
//                                                       //
//    contact:    Olaf Conrad                            //
//                Institute of Geography                 //
//                University of Goettingen               //
//                Goldschmidtstr. 5                      //
//                37077 Goettingen                       //
//                Germany                                //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#include "Grid_To_Contour.h"


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

//---------------------------------------------------------
CGrid_To_Contour::CGrid_To_Contour(void)
{
	Set_Name		(_TL("Contour Lines from Grid"));

	Set_Author		("O.Conrad (c) 2001");

	Set_Description	(_TW(
		"Derive contour lines (isolines) from a grid. "
	));

	//-----------------------------------------------------
	Parameters.Add_Grid("", "GRID", _TL("Grid"), _TL(""), PARAMETER_INPUT);

	Parameters.Add_Shapes(""        , "CONTOUR"   , _TL("Contour"    ), _TL(""), PARAMETER_OUTPUT         , SHAPE_TYPE_Line   );
	Parameters.Add_Choice("CONTOUR" , "VERTEX"    , _TL("Vertex Type"), _TL(""), "x, y|x, y, z", 0);
	Parameters.Add_Bool  ("CONTOUR" , "LINE_PARTS", _TL("Split Parts"), _TL(""),  true);

	Parameters.Add_Shapes(""        , "POLYGONS"  , _TL("Polygons"   ), _TL(""), PARAMETER_OUTPUT_OPTIONAL, SHAPE_TYPE_Polygon);
	Parameters.Add_Bool  ("POLYGONS", "POLY_PARTS", _TL("Split Parts"), _TL(""), false);

	Parameters.Add_Double("",
		"SCALE"		, _TL("Interpolation Scale"),
		_TL("set greater one for line smoothing"),
		1., 0., true
	);

	Parameters.Add_Double("", "ZSTEP", _TL("Contour Interval"     ), _TL(""),  100., 0., true);
	Parameters.Add_Double("", "ZMIN" , _TL("Base Contour Value"   ), _TL(""),    0.);
	Parameters.Add_Double("", "ZMAX" , _TL("Maximum Contour Value"), _TL(""), 1000.);
}


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

//---------------------------------------------------------
int CGrid_To_Contour::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if( pParameter->Cmp_Identifier("GRID") && pParameter->asGrid() != NULL )
	{
		double zStep = SG_Get_Rounded_To_SignificantFigures(pParameter->asGrid()->Get_Range() / 10., 1);

		pParameters->Get_Parameter("ZSTEP")->Set_Value(zStep);

		pParameters->Set_Enabled("ZMAX", zStep > 0.);

		if( zStep > 0. )
		{
			pParameters->Get_Parameter("ZMIN")->Set_Value(zStep * floor(pParameter->asGrid()->Get_Min() / zStep));
			pParameters->Get_Parameter("ZMAX")->Set_Value(zStep * ceil (pParameter->asGrid()->Get_Max() / zStep));
		}
		else
		{
			pParameters->Get_Parameter("ZMIN")->Set_Value(pParameter->asGrid()->Get_Min());
			pParameters->Get_Parameter("ZMAX")->Set_Value(pParameter->asGrid()->Get_Max());
		}
	}

	return( CSG_Tool::On_Parameter_Changed(pParameters, pParameter) );
}

//---------------------------------------------------------
int CGrid_To_Contour::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if( pParameter->Cmp_Identifier("ZSTEP") )
	{
		pParameters->Set_Enabled("ZMAX", pParameter->asDouble() > 0.);
	}

	if( pParameter->Cmp_Identifier("POLYGONS") )
	{
		pParameters->Set_Enabled("LINE_PARTS", pParameter->asPointer() == NULL);
		pParameters->Set_Enabled("POLY_PARTS", pParameter->asPointer() != NULL);
	}

	return( CSG_Tool::On_Parameters_Enable(pParameters, pParameter) );
}


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

//---------------------------------------------------------
bool CGrid_To_Contour::On_Execute(void)
{
	m_pGrid      = Parameters("GRID"      )->asGrid  ();
	m_pContours  = Parameters("CONTOUR"   )->asShapes();
	m_pPolygons  = Parameters("POLYGONS"  )->asShapes();

	m_bParts     = Parameters("LINE_PARTS")->asBool  () && !m_pPolygons;	// only split parts if polygons are not requested

	double zMin	 = Parameters("ZMIN"      )->asDouble();
	double zMax	 = Parameters("ZMAX"      )->asDouble();
	double zStep = Parameters("ZSTEP"     )->asDouble();

	//-----------------------------------------------------
	m_pContours->Create(SHAPE_TYPE_Line, NULL, NULL, Parameters("VERTEX")->asInt() == 0 ? SG_VERTEX_TYPE_XY : SG_VERTEX_TYPE_XYZ);

	m_pContours->Add_Field("ID"   , SG_DATATYPE_Int   );
	m_pContours->Add_Field("VALUE", SG_DATATYPE_Double);

	if( zStep > 0. )
	{
		if( zMin < m_pGrid->Get_Min() )
		{
			zMin += zStep * (int)((m_pGrid->Get_Min() - zMin) / zStep);
		}

		if( zMax > m_pGrid->Get_Max() )
		{
			zMax  = m_pGrid->Get_Max();
		}

		m_pContours->Fmt_Name("%s [%s %s]", m_pGrid->Get_Name(), _TL("Interval"), SG_Get_String(zStep).c_str());
	}
	else // just one contour value (zMin)
	{
		zStep = 1.; zMax = zMin;

		m_pContours->Fmt_Name("%s [%s %s]", m_pGrid->Get_Name(), _TL("Contour" ), SG_Get_String(zMin ).c_str());
	}

	//-----------------------------------------------------
	if( m_pPolygons )
	{
		m_pPolygons->Create(SHAPE_TYPE_Polygon, m_pContours->Get_Name(), NULL,
			Parameters("VERTEX")->asInt() == 0 ? SG_VERTEX_TYPE_XY : SG_VERTEX_TYPE_XYZ
		);

		m_pPolygons->Add_Field("ID"   , SG_DATATYPE_Int   );
		m_pPolygons->Add_Field("MIN"  , SG_DATATYPE_Double);
		m_pPolygons->Add_Field("MAX"  , SG_DATATYPE_Double);
		m_pPolygons->Add_Field("RANGE", SG_DATATYPE_String);
	}

	//-----------------------------------------------------
	double Scale = Parameters("SCALE")->asDouble(); CSG_Grid Scaled;

	if( Scale > 0. && Scale != 1. )
	{
		if( !Scaled.Create(CSG_Grid_System(m_pGrid->Get_Cellsize() / Scale, m_pGrid->Get_Extent())) )
		{
			Error_Set(_TL("could allocate memory for scaled grid"));

			return( false );
		}

		Scaled.Assign(m_pGrid, GRID_RESAMPLING_BSpline);

		m_pGrid	= &Scaled;
	}

	//-----------------------------------------------------
	m_Edge.Create(SG_DATATYPE_Char, m_pGrid->Get_NX() + 1, m_pGrid->Get_NY() + 1, m_pGrid->Get_Cellsize(), m_pGrid->Get_XMin(), m_pGrid->Get_YMin());

	for(double z=zMin; z<=zMax && Set_Progress(z - zMin, zMax - zMin); z+=zStep)
	{
		if( z >= m_pGrid->Get_Min() && z <= m_pGrid->Get_Max() )
		{
			Process_Set_Text("%s: %s", _TL("Contour"), SG_Get_String(z, -2).c_str());

			Get_Contour(z);
		}
	}

	//-----------------------------------------------------
	if( m_pPolygons && m_pContours->Get_Count() > 0 && Set_Progress(0))
	{
		Get_Polygons(NULL, (CSG_Shape_Line *)m_pContours->Get_Shape(0));

		for(int i=1; i<m_pContours->Get_Count() && Set_Progress(i, m_pContours->Get_Count()); i++)
		{
			Get_Polygons((CSG_Shape_Line *)m_pContours->Get_Shape(i - 1), (CSG_Shape_Line *)m_pContours->Get_Shape(i));
		}

		if( Set_Progress(100) )
		{
			Get_Polygons((CSG_Shape_Line *)m_pContours->Get_Shape(m_pContours->Get_Count() - 1), NULL);
		}

		if( Parameters("POLY_PARTS")->asBool() )
		{
			Split_Polygon_Parts(m_pPolygons);
		}
	}

	m_Edge.Destroy();

	//-----------------------------------------------------
	return( m_pContours->Get_Count() > 0 );
}


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

//---------------------------------------------------------
#define EDGE_ROW	0x01
#define EDGE_COL	0x02

//---------------------------------------------------------
inline bool CGrid_To_Contour::is_Edge(int x, int y)
{
	return(  m_pGrid->is_InGrid(x    , y    )
		&& (!m_pGrid->is_InGrid(x - 1, y - 1)
		||  !m_pGrid->is_InGrid(x    , y - 1)
		||  !m_pGrid->is_InGrid(x + 1, y - 1)
		||  !m_pGrid->is_InGrid(x + 1, y    )
		||  !m_pGrid->is_InGrid(x + 1, y + 1)
		||  !m_pGrid->is_InGrid(x    , y + 1)
		||  !m_pGrid->is_InGrid(x - 1, y + 1)
		||  !m_pGrid->is_InGrid(x - 1, y    ) )
	);
}

//---------------------------------------------------------
inline void CGrid_To_Contour::Set_Row(int x, int y, bool bOn)
{
	int	Edge	= m_Edge.asInt(x, y);

	m_Edge.Set_Value(x, y, bOn ? (Edge | EDGE_ROW) : (Edge & EDGE_COL));
}

inline bool CGrid_To_Contour::Get_Row(int x, int y)
{
	return( (m_Edge.asInt(x, y) & EDGE_ROW) != 0 );
}

//---------------------------------------------------------
inline void CGrid_To_Contour::Set_Col(int x, int y, bool bOn)
{
	int	Edge	= m_Edge.asInt(x, y);

	m_Edge.Set_Value(x, y, bOn ? (Edge | EDGE_COL) : (Edge & EDGE_ROW));
}

inline bool CGrid_To_Contour::Get_Col(int x, int y)
{
	return( (m_Edge.asInt(x, y) & EDGE_COL) != 0 );
}


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

//---------------------------------------------------------
bool CGrid_To_Contour::Get_Contour(double z)
{
	#pragma omp parallel for
	for(int y=0; y<m_pGrid->Get_NY(); y++)	// Find Border Cells
	{
		for(int x=0; x<m_pGrid->Get_NX(); x++)
		{
			if( !m_pGrid->is_NoData(x, y) )
			{
				if( m_pGrid->asDouble(x, y) >= z )
				{
					if( m_pGrid->is_InGrid(x + 1, y    ) && m_pGrid->asDouble(x + 1, y    ) <  z ) Set_Row(x, y, true);
					if( m_pGrid->is_InGrid(x    , y + 1) && m_pGrid->asDouble(x    , y + 1) <  z ) Set_Col(x, y, true);
				}
				else // if( m_pGrid->asDouble(x, y) < z )
				{
					if( m_pGrid->is_InGrid(x + 1, y    ) && m_pGrid->asDouble(x + 1, y    ) >= z ) Set_Row(x, y, true);
					if( m_pGrid->is_InGrid(x    , y + 1) && m_pGrid->asDouble(x    , y + 1) >= z ) Set_Col(x, y, true);
				}
			}
		}
 	}

	//-----------------------------------------------------
	if( m_bParts == false )
	{
		CSG_Shape *pContour = m_pContours->Add_Shape();

		pContour->Set_Value(0, m_pContours->Get_Count());
		pContour->Set_Value(1, z);
	}

	for(int y=0; y<m_pGrid->Get_NY(); y++)	// find unclosed contours first so that these start/end at edges and not somewhere else
	{
		for(int x=0; x<m_pGrid->Get_NX(); x++)
		{
			if( m_Edge.asInt(x, y) && is_Edge(x, y) )
			{
				Get_Contour(z, x, y);
			}
		}
	}

	for(int y=0; y<m_pGrid->Get_NY(); y++)
	{
		for(int x=0; x<m_pGrid->Get_NX(); x++)
		{
			while( Get_Contour(z, x, y) );
		}
	}

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


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

//---------------------------------------------------------
bool CGrid_To_Contour::Get_Contour(double z, int x, int y)
{
	bool	bRow;

	if( Get_Row(x, y) )
	{
		bRow	= true;
	}
	else if( Get_Col(x, y) )
	{
		bRow	= false;
	}
	else
	{
		return( false );
	}

	//-----------------------------------------------------
	CSG_Shape *pContour;

	if( m_bParts )
	{
		pContour = m_pContours->Add_Shape();

		pContour->Set_Value(0, m_pContours->Get_Count());
		pContour->Set_Value(1, z);
	}
	else
	{
		pContour = m_pContours->Get_Shape(m_pContours->Get_Count() - 1);
	}

	int iPart = m_bParts ? 0 : pContour->Get_Part_Count();

	//-----------------------------------------------------
	for(int Dir=0, x0=x, y0=y, bRow0=bRow?1:0; Dir>=0; )
	{
		int zx = bRow ? x + 1 : x;
		int zy = bRow ? y : y + 1;

		double d = m_pGrid->asDouble(x, y); d = (d - z) / (d - m_pGrid->asDouble(zx, zy));

		pContour->Add_Point(
			m_pGrid->Get_XMin() + m_pGrid->Get_Cellsize() * (x + d * (zx - x)),
			m_pGrid->Get_YMin() + m_pGrid->Get_Cellsize() * (y + d * (zy - y)), iPart
		);

		if( pContour->Get_Vertex_Type() != SG_VERTEX_TYPE_XY )
		{
			pContour->Set_Z(z, pContour->Get_Point_Count(iPart) - 1, iPart);
		}

		if( Get_Contour_Cell(Dir, x, y, bRow) || Get_Contour_Cell(Dir, x, y, bRow) )
		{
			if( bRow  )	{ Set_Row(x , y , false); } else { Set_Col(x , y , false); }
		}
		else
		{
			if( bRow0 )	{ Set_Row(x0, y0, false); } else { Set_Col(x0, y0, false); }

			Dir	= -1;
		}
	}

	//-----------------------------------------------------
	return( pContour->Get_Point_Count(iPart) > 1 );
}

//---------------------------------------------------------
inline bool CGrid_To_Contour::Get_Contour_Cell(int &Dir, int &x, int &y, bool &bRow)
{
	if( bRow )
	{
		switch( Dir )
		{
		case  0: if( Get_Row(x    , y + 1) ) { Dir = 5;                    y++; return( true ); }	// Norden
		case  1: if( Get_Col(x + 1, y    ) ) { Dir = 6; bRow = false; x++;      return( true ); }	// Nord-Ost
		case  2:	// Osten ist nicht...
		case  3: if( y - 1 >= 0
				 &&  Get_Col(x + 1, y - 1) ) { Dir = 0; bRow = false; x++; y--; return( true ); }	// Sued-Ost
		case  4: if( y - 1 >= 0
				 &&  Get_Row(x    , y - 1) ) { Dir = 1;                    y--; return( true ); }	// Sueden
		case  5: if( y - 1 >= 0
				 &&  Get_Col(x    , y - 1) ) { Dir = 2; bRow = false;      y--; return( true ); }	// Sued-West
		case  6:	// Westen ist nicht...
		case  7: if( Get_Col(x    , y    ) ) { Dir = 4; bRow = false;           return( true ); }	// Nord-West
		default:
			Dir = 0;
		}
	}
	else
	{
		switch( Dir )
		{
		case  0:	// Norden ist nicht...
		case  1: if( Get_Row(x    , y + 1) ) { Dir = 6; bRow =  true;      y++; return( true ); }	// Nord-Ost
		case  2: if( Get_Col(x + 1, y    ) ) { Dir = 7;               x++;      return( true ); }	// Osten
		case  3: if( Get_Row(x    , y    ) ) { Dir = 0; bRow =  true;           return( true ); }	// Sued-Ost
		case  4:	// Sueden ist nicht...
		case  5: if( x - 1 >= 0
				 &&  Get_Row(x - 1, y    ) ) { Dir = 2; bRow =  true; x--;      return( true ); }	// Sued-West
		case  6: if( x - 1 >= 0
				 &&  Get_Col(x - 1, y    ) ) { Dir = 3;               x--;      return( true ); }	// Westen
		case  7: if( x - 1 >= 0
				 &&  Get_Row(x - 1, y + 1) ) { Dir = 5; bRow =  true; x--; y++; return( true ); }	// Nord-West
		default:
			Dir = 0;
		}
	}

	return( false );
}


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

//---------------------------------------------------------
#define EDGE_LINE		1
#define EDGE_NODE_ONE	2
#define EDGE_NODE_TWO	3

//---------------------------------------------------------
bool CGrid_To_Contour::Get_Polygons(CSG_Shape_Line *pContour_Lo, CSG_Shape_Line *pContour_Hi)
{
	double zMin = pContour_Lo ? pContour_Lo->asDouble(1) : m_pGrid->Get_Min();
	double zMax = pContour_Hi ? pContour_Hi->asDouble(1) : m_pGrid->Get_Max();

	#pragma omp parallel for
	for(int y=0; y<m_pGrid->Get_NY(); y++)
	{
		for(int x=0; x<m_pGrid->Get_NX(); x++)
		{
			m_Edge.Set_Value(x, y, is_Edge(x, y) && zMin <= m_pGrid->asDouble(x, y) && m_pGrid->asDouble(x, y) <= zMax ? EDGE_LINE : 0);
		}
	}

	//-----------------------------------------------------
	CSG_Shape_Polygon *pPolygon = (CSG_Shape_Polygon *)m_pPolygons->Add_Shape();

	pPolygon->Set_Value(0, m_pPolygons->Get_Count());
	pPolygon->Set_Value(1, zMin);
	pPolygon->Set_Value(2, zMax);
	pPolygon->Set_Value(3, SG_Get_String(zMin) + " - " + SG_Get_String(zMax));

	//-----------------------------------------------------
	CSG_Shapes	Segments(SHAPE_TYPE_Line);

	Segments.Add_Field("x0", SG_DATATYPE_Int);
	Segments.Add_Field("y0", SG_DATATYPE_Int);
	Segments.Add_Field("x1", SG_DATATYPE_Int);
	Segments.Add_Field("y1", SG_DATATYPE_Int);
	
	if( pContour_Lo ) { Add_Contour(Segments, pPolygon, pContour_Lo); }
	if( pContour_Hi ) { Add_Contour(Segments, pPolygon, pContour_Hi); }

	//-----------------------------------------------------
	if( Segments.Get_Count() == 0 )	// no edge crossing line
	{
		for(int y=0; y<m_pGrid->Get_NY(); y++)
		{
			for(int x=0; x<m_pGrid->Get_NX(); x++)
			{
				if( m_Edge.asInt(x, y) == EDGE_LINE )	// add outer ring
				{
					Add_Edge(Segments, x, y);

					if( Segments.Get_Count() )
					{
						pPolygon->Add_Part(((CSG_Shape_Polygon *)Segments.Get_Shape(0))->Get_Part(0));

						Segments.Del_Shapes();
					}
				}
			}
		}

		return( true );
	}

	//-----------------------------------------------------
//	{	CSG_Grid *pGrid	= SG_Create_Grid(m_Edge); pGrid->Set_NoData_Value(0); DataObject_Add(pGrid);	}
	for(int y=0; y<m_pGrid->Get_NY(); y++)
	{
		for(int x=0; x<m_pGrid->Get_NX(); x++)
		{
			if( m_Edge.asInt(x, y) == EDGE_NODE_ONE )
			{
				Add_Edge(Segments, x, y);
			}
		}
	}
//	DataObject_Add(SG_Create_Shapes(Segments));

	//-----------------------------------------------------
	while( Segments.Get_Count() > 0 )
	{
		int iPart = pPolygon->Get_Part_Count();

		CSG_Shape *pSegment = Segments.Get_Shape(0);

		int x = pSegment->asInt(2);
		int y = pSegment->asInt(3);

		bool bAscending = true;

		do
		{
			Add_Segment(pPolygon, iPart, pSegment, bAscending);
		}
		while( (pSegment = Get_Segment(Segments, x, y, bAscending)) != NULL );

		if( pPolygon->Get_Point_Count(iPart) < 3 )
		{
			pPolygon->Del_Part(iPart);
		}
	}

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

//---------------------------------------------------------
bool CGrid_To_Contour::Add_Contour(CSG_Shapes &Segments, CSG_Shape_Polygon *pPolygon, CSG_Shape_Line *pContour)
{
	for(int iPart=0, x, y; iPart<pContour->Get_Part_Count(); iPart++)
	{
		CSG_Shape_Part *pPart = pContour->Get_Part(iPart);

		if( !CSG_Point(pPart->Get_Point(0, true)).is_Equal(pPart->Get_Point(0, false)) )	// island
		{
			CSG_Shape *pSegment = Segments.Add_Shape();	pSegment->Add_Part(pPart);

			m_Edge.Get_System().Get_World_to_Grid(x, y, pPart->Get_Point(0,  true));
			m_Edge.Set_Value(x, y, m_Edge.asInt(x, y) == EDGE_NODE_ONE ? EDGE_NODE_TWO : EDGE_NODE_ONE);
			pSegment->Set_Value(0, x);
			pSegment->Set_Value(1, y);

			m_Edge.Get_System().Get_World_to_Grid(x, y, pPart->Get_Point(0, false));
			m_Edge.Set_Value(x, y, m_Edge.asInt(x, y) == EDGE_NODE_ONE ? EDGE_NODE_TWO : EDGE_NODE_ONE);
			pSegment->Set_Value(2, x);
			pSegment->Set_Value(3, y);
		}
		else if( pPart->Get_Count() >= 3 )
		{
			pPolygon->Add_Part(pPart);
		}
	}

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

//---------------------------------------------------------
bool CGrid_To_Contour::Add_Edge(CSG_Shapes &Segments, int x, int y)
{
	CSG_Shape *pSegment = NULL;

	for(int i=0; i<8 && !pSegment; i+=2)
	{
		int ix = CSG_Grid_System::Get_xTo(i, x);
		int iy = CSG_Grid_System::Get_yTo(i, y);

		if( m_Edge.is_InGrid(ix, iy) && m_Edge.asInt(ix, iy) == EDGE_LINE )
		{
			pSegment = Segments.Add_Shape();

			pSegment->Set_Value(0, x);
			pSegment->Set_Value(1, y);

			x = ix; y = iy;
		}
	}

	if( !pSegment )
	{
		for(int i=0; i<8 && !pSegment; i+=2)
		{
			int ix = CSG_Grid_System::Get_xTo(i, x);
			int iy = CSG_Grid_System::Get_yTo(i, y);

			if( m_Edge.is_InGrid(ix, iy) && m_Edge.asInt(ix, iy) == EDGE_NODE_ONE )
			{
				pSegment = Segments.Add_Shape();

				pSegment->Set_Value(0,  x);
				pSegment->Set_Value(1,  y);
				pSegment->Set_Value(2, ix);
				pSegment->Set_Value(3, iy);
			}
		}

		return( pSegment != NULL );
	}

	//-----------------------------------------------------
	int iLast = -1;

	for(bool bNext=true; bNext; )
	{
		bNext = false;

		m_Edge.Set_Value(x, y, 0);

		for(int i=0; i<8 && !bNext; i+=2)
		{
			int ix = CSG_Grid_System::Get_xTo(i, x);
			int iy = CSG_Grid_System::Get_yTo(i, y);

			if( m_Edge.is_InGrid(ix, iy) && m_Edge.asInt(ix, iy) == EDGE_LINE )
			{
				bNext = true;

				if( i != iLast )
				{
					iLast = i;

					pSegment->Add_Point(m_Edge.Get_System().Get_Grid_to_World(x, y));
				}

				x = ix; y = iy;
			}
		}
	}

	//-----------------------------------------------------
	pSegment->Add_Point(m_Edge.Get_System().Get_Grid_to_World(x, y));

	for(int i=0; i<8; i+=2)
	{
		int ix = CSG_Grid_System::Get_xTo(i, x);
		int iy = CSG_Grid_System::Get_yTo(i, y);

		if( m_Edge.is_InGrid(ix, iy) && m_Edge.asInt(ix, iy) != 0 )
		{
			pSegment->Set_Value(2, ix);
			pSegment->Set_Value(3, iy);

			return( true );
		}
	}

	pSegment->Set_Value(2, x);
	pSegment->Set_Value(3, y);

	return( false );
}

//---------------------------------------------------------
CSG_Shape * CGrid_To_Contour::Get_Segment(const CSG_Shapes &Segments, int &x, int &y, bool &bAscending)
{
	for(int i=0; i<Segments.Get_Count(); i++)
	{
		if( Segments[i].asInt(0) == x && Segments[i].asInt(1) == y )
		{
			bAscending	= true;

			x	= Segments[i].asInt(2);
			y	= Segments[i].asInt(3);

			return( Segments.Get_Shape(i) );
		}

		if( Segments[i].asInt(2) == x && Segments[i].asInt(3) == y )
		{
			bAscending	= false;

			x	= Segments[i].asInt(0);
			y	= Segments[i].asInt(1);

			return( Segments.Get_Shape(i) );
		}
	}

	return( NULL );
}

//---------------------------------------------------------
bool CGrid_To_Contour::Add_Segment(CSG_Shape *pPolygon, int iPart, CSG_Shape *pSegment, bool bAscending)
{
	for(int i=0; i<pSegment->Get_Point_Count(0); i++)
	{
		pPolygon->Add_Point(pSegment->Get_Point(i, 0, bAscending), iPart);
	}

	((CSG_Shapes *)pSegment->Get_Table())->Del_Shape(pSegment);

	return( true );
}


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

//---------------------------------------------------------
bool CGrid_To_Contour::Split_Polygon_Parts(CSG_Shapes *pPolygons)
{
	CSG_Shapes Polygons(*pPolygons);

	pPolygons->Del_Shapes();

	//-----------------------------------------------------
	for(int iPolygon=0; iPolygon<Polygons.Get_Count() && Set_Progress(iPolygon, Polygons.Get_Count()); iPolygon++)
	{
		CSG_Shape_Polygon *pPolygon = (CSG_Shape_Polygon *)Polygons.Get_Shape(iPolygon);

		for(int iPart=0; iPart<pPolygon->Get_Part_Count() && Process_Get_Okay(); iPart++)
		{
			if( !pPolygon->is_Lake(iPart) )
			{
				CSG_Shape_Polygon *pPart = (CSG_Shape_Polygon *)pPolygons->Add_Shape(pPolygon, SHAPE_COPY_ATTR);

				pPart->Add_Part(pPolygon->Get_Part(iPart));

				for(int jPart=0; jPart<pPolygon->Get_Part_Count(); jPart++)
				{
					if(	pPolygon->is_Lake(jPart) && pPart->Contains(pPolygon->Get_Point(0, jPart)) )
					{
						pPart->Add_Part(pPolygon->Get_Part(jPart));
					}
				}
			}
		}
	}

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


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

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