1 
2 ///////////////////////////////////////////////////////////
3 //                                                       //
4 //                         SAGA                          //
5 //                                                       //
6 //      System for Automated Geoscientific Analyses      //
7 //                                                       //
8 //                     Tool Library                      //
9 //                    ta_morphometry                     //
10 //                                                       //
11 //-------------------------------------------------------//
12 //                                                       //
13 //                fuzzy_landform_elements.h              //
14 //                                                       //
15 //                 Copyright (C) 2013 by                 //
16 //                      Olaf Conrad                      //
17 //                                                       //
18 //-------------------------------------------------------//
19 //                                                       //
20 // This file is part of 'SAGA - System for Automated     //
21 // Geoscientific Analyses'. SAGA is free software; you   //
22 // can redistribute it and/or modify it under the terms  //
23 // of the GNU General Public License as published by the //
24 // Free Software Foundation, either version 2 of the     //
25 // License, or (at your option) any later version.       //
26 //                                                       //
27 // SAGA is distributed in the hope that it will be       //
28 // useful, but WITHOUT ANY WARRANTY; without even the    //
29 // implied warranty of MERCHANTABILITY or FITNESS FOR A  //
30 // PARTICULAR PURPOSE. See the GNU General Public        //
31 // License for more details.                             //
32 //                                                       //
33 // You should have received a copy of the GNU General    //
34 // Public License along with this program; if not, see   //
35 // <http://www.gnu.org/licenses/>.                       //
36 //                                                       //
37 //-------------------------------------------------------//
38 //                                                       //
39 //    e-mail:     oconrad@saga-gis.org                   //
40 //                                                       //
41 //    contact:    Olaf Conrad                            //
42 //                Institute of Geography                 //
43 //                University of Hamburg                  //
44 //                Germany                                //
45 //                                                       //
46 ///////////////////////////////////////////////////////////
47 
48 //---------------------------------------------------------
49 #include "fuzzy_landform_elements.h"
50 
51 
52 ///////////////////////////////////////////////////////////
53 //														 //
54 //														 //
55 //														 //
56 ///////////////////////////////////////////////////////////
57 
58 //---------------------------------------------------------
59 enum
60 {
61 	SLOPE	= 0,
62 	MINCURV,
63 	MAXCURV,
64 	PCURV,
65 	TCURV,
66 	IN_COUNT
67 };
68 
69 //---------------------------------------------------------
70 const CSG_String	IN_Type[IN_COUNT][2]	=
71 {
72 	{	"SLOPE"     , _TL("Slope"               )	},
73 	{	"MINCURV"   , _TL("Minimum Curvature"   )	},
74 	{	"MAXCURV"   , _TL("Maximum Curvature"   )	},
75 	{	"PCURV"     , _TL("Profile Curvature"   )	},
76 	{	"TCURV"     , _TL("Tangential Curvature")	}
77 };
78 
79 
80 ///////////////////////////////////////////////////////////
81 //														 //
82 ///////////////////////////////////////////////////////////
83 
84 //---------------------------------------------------------
85 struct SForm_Def
86 {
87 	CSG_String	ID, Name;
88 
89 	long		Color;
90 
91 	int			Value;
92 };
93 
94 //---------------------------------------------------------
95 enum
96 {
97 	PLAIN	= 0,
98 	PIT,
99 	PEAK,
100 	RIDGE,
101 	CHANNEL,
102 	SADDLE,
103 	BSLOPE,
104 	FSLOPE,
105 	SSLOPE,
106 	HOLLOW,
107 	FHOLLOW,
108 	SHOLLOW,
109 	SPUR,
110 	FSPUR,
111 	SSPUR,
112 	FE_COUNT
113 };
114 
115 //---------------------------------------------------------
116 const struct SForm_Def Form_Def[FE_COUNT]	=
117 {
118 	{	"PLAIN"     , _TL("Plain"          ), SG_GET_RGB(220, 220, 220), 100	},
119 	{	"PIT"       , _TL("Pit"            ), SG_GET_RGB(  0,   0, 100), 111	},
120 	{	"PEAK"      , _TL("Peak"           ), SG_GET_RGB(100,   0,   0), 122	},
121 	{	"RIDGE"     , _TL("Ridge"          ), SG_GET_RGB(200,   0,   0), 120	},
122 	{	"CHANNEL"   , _TL("Channel"        ), SG_GET_RGB(  0,   0, 255), 101	},
123 	{	"SADDLE"    , _TL("Saddle"         ), SG_GET_RGB(  0, 200, 255), 121	},
124 	{	"BSLOPE"    , _TL("Back Slope"     ), SG_GET_RGB(255, 255,   0),   0	},
125 	{	"FSLOPE"    , _TL("Foot Slope"     ), SG_GET_RGB(240, 220,   0),  10	},
126 	{	"SSLOPE"    , _TL("Shoulder Slope" ), SG_GET_RGB(255, 255, 150),  20	},
127 	{	"HOLLOW"    , _TL("Hollow"         ), SG_GET_RGB(180, 230,   0),   1	},
128 	{	"FHOLLOW"   , _TL("Foot Hollow"    ), SG_GET_RGB(100, 230,   0),  11	},
129 	{	"SHOLLOW"   , _TL("Shoulder Hollow"), SG_GET_RGB(180, 255,   0),  21	},
130 	{	"SPUR"      , _TL("Spur"           ), SG_GET_RGB(250, 210,   0),   2	},
131 	{	"FSPUR"     , _TL("Foot Spur"      ), SG_GET_RGB(250, 170,   0),  12	},
132 	{	"SSPUR"     , _TL("Shoulder Spur"  ), SG_GET_RGB(255, 120,   0),  22	}
133 };
134 
135 
136 ///////////////////////////////////////////////////////////
137 //														 //
138 //														 //
139 //														 //
140 ///////////////////////////////////////////////////////////
141 
142 //---------------------------------------------------------
CFuzzy_Landform_Elements(void)143 CFuzzy_Landform_Elements::CFuzzy_Landform_Elements(void)
144 {
145 	Set_Name		(_TL("Fuzzy Landform Element Classification"));
146 
147 	Set_Author		("O.Conrad (c) 2013");
148 
149 	Set_Description	(_TW(
150 		"Algorithm for derivation of form elements according to slope, maximum curvature, "
151 		"minimum curvature, profile curvature, tangential curvature, "
152 		"based on a linear semantic import model for slope and curvature and a fuzzy classification "
153 		"Based on the AML script \'felementf\' by Jochen Schmidt, Landcare Research. "
154 	));
155 
156 	Add_Reference("Schmidt, J. & Hewitt, A.", "2004",
157 		"Fuzzy land element classification from DTMs based on geometry and terrain position",
158 		"Geoderma, 121(3-4), 243-256.",
159 		SG_T("http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.472.3485&rep=rep1&type=pdf"), SG_T("CiteSeerX")
160 	//	SG_T("https://www.sciencedirect.com/science/article/pii/S0016706103003720"), SG_T("ScienceDirect")
161 	);
162 
163 	//-----------------------------------------------------
164 	int		i;
165 
166 	Parameters.Add_Grid("", "ELEVATION", _TL("Elevation"         ), _TL(""), PARAMETER_INPUT);
167 
168 	for(i=0; i<IN_COUNT; i++)
169 	{
170 		Parameters.Add_Grid("", IN_Type [i][0], IN_Type[i][1]   , _TL(""), PARAMETER_INPUT);
171 	}
172 
173 	Parameters.Add_Grid("", "FORM"     , _TL("Landform"          ), _TL(""), PARAMETER_OUTPUT);
174 	Parameters.Add_Grid("", "MEM"      , _TL("Maximum Membership"), _TL(""), PARAMETER_OUTPUT_OPTIONAL);
175 	Parameters.Add_Grid("", "ENTROPY"  , _TL("Entropy"           ), _TL(""), PARAMETER_OUTPUT_OPTIONAL);
176 	Parameters.Add_Grid("", "CI"       , _TL("Confusion Index"   ), _TL(""), PARAMETER_OUTPUT_OPTIONAL);
177 
178 	for(i=0; i<FE_COUNT; i++)
179 	{
180 		Parameters.Add_Grid("", Form_Def[i].ID, Form_Def[i].Name, _TL(""), PARAMETER_OUTPUT_OPTIONAL);
181 	}
182 
183 	//-----------------------------------------------------
184 	Parameters.Add_Choice("",
185 		"INPUT"		, _TL("Input"),
186 		_TL("if elevation is selected, slopes and curvatures will be calculated internally"),
187 		CSG_String::Format("%s|%s",
188 			_TL("elevation"),
189 			_TL("slope and curvatures")
190 		), 0
191 	);
192 
193 	Parameters.Add_Bool("",
194 		"MEMBERSHIP", _TL("Memberships"),
195 		_TL(""),
196 		false
197 	);
198 
199 	Parameters.Add_Choice("SLOPE",
200 		"SLOPETODEG", _TL("Slope Grid Units"),
201 		_TL(""),
202 		CSG_String::Format("%s|%s",
203 			_TL("degree"),
204 			_TL("radians")
205 		), 0
206 	);
207 
208 	Parameters.Add_Range("",
209 		"T_SLOPE"	, _TL("Slope Thresholds [Degree]"),
210 		_TL("lower and upper thresholds for semantic import model, planar vs. sloped areas"),
211 		2., 7., 0., true, 90., true
212 	);
213 
214 	Parameters.Add_Range("",
215 		"T_CURVE"	, _TL("Curvature Thresholds [1000 / m]"),
216 		_TL("lower and upper thresholds for semantic import model, straight vs. curved areas"),
217 		0.02, 0.5
218 	);
219 }
220 
221 
222 ///////////////////////////////////////////////////////////
223 //														 //
224 ///////////////////////////////////////////////////////////
225 
226 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)227 int CFuzzy_Landform_Elements::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
228 {
229 	if( pParameter->Cmp_Identifier("INPUT") )
230 	{
231 		for(int i=0; i<IN_COUNT; i++)
232 		{
233 			pParameters->Set_Enabled(IN_Type[i][0], pParameter->asInt() == 1);
234 		}
235 
236 		pParameters->Set_Enabled("ELEVATION", pParameter->asInt() == 0);
237 	}
238 
239 	if( pParameter->Cmp_Identifier("MEMBERSHIP") )
240 	{
241 		for(int i=0; i<FE_COUNT; i++)
242 		{
243 			pParameters->Set_Enabled(Form_Def[i].ID, pParameter->asBool());
244 		}
245 	}
246 
247 	return( CSG_Tool_Grid::On_Parameters_Enable(pParameters, pParameter) );
248 }
249 
250 
251 ///////////////////////////////////////////////////////////
252 //														 //
253 ///////////////////////////////////////////////////////////
254 
255 //---------------------------------------------------------
On_Execute(void)256 bool CFuzzy_Landform_Elements::On_Execute(void)
257 {
258 	int			i;
259 	CSG_Grid	*pInput[IN_COUNT], _Input[IN_COUNT], *pMembership[FE_COUNT], *pForm, *pMem, *pEntropy, *pCI;
260 
261 	//-----------------------------------------------------
262 	if( Parameters("INPUT")->asInt() == 0 )	// elevation
263 	{
264 		CSG_Grid	Aspect;	Aspect.Create(Get_System());
265 
266 		for(i=0; i<IN_COUNT; i++)
267 		{
268 			_Input[i].Create(Get_System());
269 
270 			pInput[i]	= &_Input[i];
271 		}
272 
273 		CSG_Tool	*pTool	= SG_Get_Tool_Library_Manager().Create_Tool("ta_morphometry", 0);	// Slope, Aspect, Curvatures
274 
275 		pTool->Set_Manager(NULL);
276 
277 		if( !pTool
278 		||  !pTool->Set_Parameter("ELEVATION" , Parameters("ELEVATION"))
279 		||  !pTool->Set_Parameter("ASPECT"    , &Aspect)
280 		||  !pTool->Set_Parameter("SLOPE"     , pInput[SLOPE  ])
281 		||  !pTool->Set_Parameter("C_PROF"    , pInput[PCURV  ])
282 		||  !pTool->Set_Parameter("C_TANG"    , pInput[TCURV  ])
283 		||  !pTool->Set_Parameter("C_MINI"    , pInput[MINCURV])
284 		||  !pTool->Set_Parameter("C_MAXI"    , pInput[MAXCURV])
285 		||  !pTool->Set_Parameter("UNIT_SLOPE", 1)	// degree
286 		||  !pTool->Execute() )
287 		{
288 			SG_Get_Tool_Library_Manager().Delete_Tool(pTool);
289 
290 			return( false );
291 		}
292 
293 		SG_Get_Tool_Library_Manager().Delete_Tool(pTool);
294 
295 		m_bToDegree	= false;
296 	}
297 	else
298 	{
299 		for(i=0; i<IN_COUNT; i++)
300 		{
301 			pInput[i]	= Parameters(IN_Type[i][0])->asGrid();
302 		}
303 
304 		m_bToDegree	= Parameters("SLOPETODEG")->asInt() == 1;
305 	}
306 
307 	//-----------------------------------------------------
308 	m_loSlope	= Parameters("T_SLOPE.MIN")->asDouble();// * M_DEG_TO_RAD;
309 	m_hiSlope	= Parameters("T_SLOPE.MAX")->asDouble();// * M_DEG_TO_RAD;
310 
311 	m_loCurve	= Parameters("T_CURVE.MIN")->asDouble() / 1000.;
312 	m_hiCurve	= Parameters("T_CURVE.MAX")->asDouble() / 1000.;
313 
314 	//-----------------------------------------------------
315 	pForm		= Parameters("FORM"   )->asGrid();
316 	pMem		= Parameters("MEM"    )->asGrid();
317 	pEntropy	= Parameters("ENTROPY")->asGrid();
318 	pCI			= Parameters("CI"     )->asGrid();
319 
320 	for(i=0; i<FE_COUNT; i++)
321 	{
322 		pMembership[i]	= Parameters("MEMBERSHIP")->asBool() ? Parameters(Form_Def[i].ID)->asGrid() : NULL;
323 
324 		DataObject_Set_Colors(pMembership[i], 11, SG_COLORS_WHITE_RED);
325 	}
326 
327 	//-----------------------------------------------------
328 	CSG_Parameter	*pLUT	= DataObject_Get_Parameter(pForm, "LUT");
329 
330 	if( pLUT && pLUT->asTable() )
331 	{
332 		pLUT->asTable()->Del_Records();
333 
334 		for(i=0; i<FE_COUNT; i++)
335 		{
336 			CSG_Table_Record	*pRecord	= pLUT->asTable()->Add_Record();
337 
338 			pRecord->Set_Value(0, Form_Def[i].Color);
339 			pRecord->Set_Value(1, Form_Def[i].Name );
340 			pRecord->Set_Value(3, Form_Def[i].Value);
341 			pRecord->Set_Value(4, Form_Def[i].Value);
342 		}
343 
344 		DataObject_Set_Parameter(pForm, pLUT);
345 
346 		DataObject_Set_Parameter(pForm, "COLORS_TYPE", 1);	// Color Classification Type: Lookup Table
347 	}
348 
349 	//-----------------------------------------------------
350 	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
351 	{
352 		#pragma omp parallel for private(i)
353 		for(int x=0; x<Get_NX(); x++)
354 		{
355 			bool	bOkay;
356 			int		Element;
357 			double	MaxMem, Entropy, CI, Input[IN_COUNT], Membership[FE_COUNT];
358 
359 			for(i=0, bOkay=true; i<IN_COUNT && bOkay; i++)
360 			{
361 				if( pInput[i]->is_NoData(x, y) )
362 				{
363 					bOkay	= false;
364 				}
365 				else
366 				{
367 					Input[i]	= pInput[i]->asDouble(x, y);
368 				}
369 			}
370 
371 			if( bOkay && Get_Memberships(Input, Membership, Element, MaxMem, Entropy, CI) )
372 			{
373 				if( pForm    ) pForm   ->Set_Value(x, y, Element);	// hard classification according to highest membership value
374 				if( pMem     ) pMem    ->Set_Value(x, y, MaxMem );
375 				if( pEntropy ) pEntropy->Set_Value(x, y, Entropy);
376 				if( pCI      ) pCI     ->Set_Value(x, y, CI     );	// confusion index
377 
378 				for(i=0; i<FE_COUNT; i++)
379 				{
380 					if( pMembership[i] ) pMembership[i]->Set_Value(x, y, Membership[i]);
381 				}
382 			}
383 			else
384 			{
385 				if( pForm    ) pForm   ->Set_NoData(x, y);
386 				if( pMem     ) pMem    ->Set_NoData(x, y);
387 				if( pEntropy ) pEntropy->Set_NoData(x, y);
388 				if( pCI      ) pCI     ->Set_NoData(x, y);
389 
390 				for(i=0; i<FE_COUNT; i++)
391 				{
392 					if( pMembership[i] ) pMembership[i]->Set_NoData(x, y);
393 				}
394 			}
395 		}
396 	}
397 
398 	//-----------------------------------------------------
399 	return( true );
400 }
401 
402 
403 ///////////////////////////////////////////////////////////
404 //														 //
405 ///////////////////////////////////////////////////////////
406 
407 //---------------------------------------------------------
408 #define GET_MINIMUM(a, b, c)	M_GET_MIN(a, M_GET_MIN(b, c))
409 
410 //---------------------------------------------------------
Get_Memberships(double Input[],double M[],int & Element,double & MaxMem,double & Entropy,double & CI)411 bool CFuzzy_Landform_Elements::Get_Memberships(double Input[], double M[], int &Element, double &MaxMem, double &Entropy, double &CI)
412 {
413 	double	bFlat, bSlope, MaxMem2,
414 			bPCurve  , bPCurveX  , bPCurveV,
415 			bTCurve  , bTCurveX  , bTCurveV,
416 			bMaxCurve, bMaxCurveX, bMaxCurveV,
417 			bMinCurve, bMinCurveX, bMinCurveV;
418 
419 	//-----------------------------------------------------
420 	if( m_bToDegree )
421 	{
422 		Input[SLOPE]	*= M_RAD_TO_DEG;
423 	}
424 
425 	//-----------------------------------------------------
426 	// membership slope flat
427 	bFlat		= Input[SLOPE] <= m_loSlope ? 1.0 : (Input[SLOPE] >= m_hiSlope ? 0.0
428 				: (m_hiSlope - Input[SLOPE]) / (m_hiSlope - m_loSlope));			// linear membership fct
429 	bSlope		= 1 - bFlat;
430 
431 	// membership pcurv straight
432 	bPCurve		= fabs(Input[PCURV]) <= m_loCurve ? 1.0 : (fabs(Input[PCURV]) >= m_hiCurve ? 0.0
433 				: (m_hiCurve - fabs(Input[PCURV])) / (m_hiCurve - m_loCurve));		// linear membership fct
434 	bPCurveX	= Input[PCURV] >= 0.0 ? 1.0 - bPCurve : 0.0;
435 	bPCurveV	= Input[PCURV] <= 0.0 ? 1.0 - bPCurve : 0.0;
436 
437 	// membership tcurv straight
438 	bTCurve		= fabs(Input[TCURV]) <= m_loCurve ? 1.0 : (fabs(Input[TCURV]) >= m_hiCurve ? 0.0
439 				: (m_hiCurve - fabs(Input[TCURV])) / (m_hiCurve - m_loCurve));		// linear membership fct
440 	bTCurveX	= Input[TCURV] >= 0.0 ? 1.0 - bTCurve : 0.0;
441 	bTCurveV	= Input[TCURV] <= 0.0 ? 1.0 - bTCurve : 0.0;
442 
443 	// membership maxcurv straight
444 	bMaxCurve	= fabs(Input[MAXCURV]) <= m_loCurve ? 1.0 : (fabs(Input[MAXCURV]) >= m_hiCurve ? 0.0
445 				: (m_hiCurve - fabs(Input[MAXCURV])) / (m_hiCurve - m_loCurve));	// linear membership fct
446 	bMaxCurveX	= Input[MAXCURV] >= 0.0 ? 1.0 - bMaxCurve : 0.0;
447 	bMaxCurveV	= Input[MAXCURV] <= 0.0 ? 1.0 - bMaxCurve : 0.0;
448 
449 	// membership mincurv straight
450 	bMinCurve	= fabs(Input[MINCURV]) <= m_loCurve ? 1.0 : (fabs(Input[MINCURV]) >= m_hiCurve ? 0.0
451 				: (m_hiCurve - fabs(Input[MINCURV])) / (m_hiCurve - m_loCurve));	// linear membership fct
452 	bMinCurveX	= Input[MINCURV] >= 0.0 ? 1.0 - bMinCurve : 0.0;
453 	bMinCurveV	= Input[MINCURV] <= 0.0 ? 1.0 - bMinCurve : 0.0;
454 
455 	//-----------------------------------------------------
456 	M[PLAIN]	= GET_MINIMUM(bFlat, bMaxCurve, bMinCurve);
457 	Element		= Form_Def[PLAIN].Value;
458 	MaxMem		= M[PLAIN];
459 
460 	M[PIT]		= GET_MINIMUM(bFlat, bMaxCurveV, bMinCurveV);
461 	if( M[PIT] > MaxMem )
462 	{
463 		Element	= Form_Def[PIT].Value;
464 		MaxMem	= M[PIT];
465 		MaxMem2	= M[PLAIN];
466 	}
467 	else
468 	{
469 		MaxMem2	= M[PIT];
470 	}
471 
472 	M[PEAK]		= GET_MINIMUM(bFlat, bMaxCurveX, bMinCurveX);
473 	if( M[PEAK] > MaxMem )
474 	{
475 		Element	= Form_Def[PEAK].Value;
476 		MaxMem	= M[PEAK];
477 	}
478 	else if( M[PEAK] > MaxMem2 )
479 	{
480 		MaxMem2	= M[PEAK];
481 	}
482 
483 	M[RIDGE]	= GET_MINIMUM(bFlat, bMaxCurveX, bMinCurve);
484 	if( M[RIDGE] > MaxMem )
485 	{
486 		Element	= Form_Def[RIDGE].Value;
487 		MaxMem	= M[RIDGE];
488 	}
489 	else if( M[RIDGE] > MaxMem2 )
490 	{
491 		MaxMem2	= M[RIDGE];
492 	}
493 
494 	M[CHANNEL]   = GET_MINIMUM(bFlat, bMaxCurve, bMinCurveV);
495 	if( M[CHANNEL] > MaxMem )
496 	{
497 		Element	= Form_Def[CHANNEL].Value;
498 		MaxMem	= M[CHANNEL];
499 	}
500 	else if( M[CHANNEL] > MaxMem2 )
501 	{
502 		MaxMem2	= M[CHANNEL];
503 	}
504 
505 	M[SADDLE]   = GET_MINIMUM(bFlat, bMaxCurveX, bMinCurveV);
506 	if( M[SADDLE] > MaxMem )
507 	{
508 		Element	= Form_Def[SADDLE].Value;
509 		MaxMem	= M[SADDLE];
510 	}
511 	else if( M[SADDLE] > MaxMem2 )
512 	{
513 		MaxMem2	= M[SADDLE];
514 	}
515 
516 	M[BSLOPE]   = GET_MINIMUM(bSlope, bPCurve, bTCurve);
517 	if( M[BSLOPE] > MaxMem )
518 	{
519 		Element	= Form_Def[BSLOPE].Value;
520 		MaxMem	= M[BSLOPE];
521 	}
522 	else if( M[BSLOPE] > MaxMem2 )
523 	{
524 		MaxMem2	= M[BSLOPE];
525 	}
526 
527 	M[FSLOPE]   = GET_MINIMUM(bSlope, bPCurveV, bTCurve);
528 	if( M[FSLOPE] > MaxMem )
529 	{
530 		Element	= Form_Def[FSLOPE].Value;
531 		MaxMem	= M[FSLOPE];
532 	}
533 	else if( M[FSLOPE] > MaxMem2 )
534 	{
535 		MaxMem2	= M[FSLOPE];
536 	}
537 
538 	M[SSLOPE]   = GET_MINIMUM(bSlope, bPCurveX, bTCurve);
539 	if( M[SSLOPE] > MaxMem )
540 	{
541 		Element	= Form_Def[SSLOPE].Value;
542 		MaxMem	= M[SSLOPE];
543 	}
544 	else if( M[SSLOPE] > MaxMem2 )
545 	{
546 		MaxMem2	= M[SSLOPE];
547 	}
548 
549 	M[HOLLOW]   = GET_MINIMUM(bSlope, bPCurve, bTCurveV);
550 	if( M[HOLLOW] > MaxMem )
551 	{
552 		Element	= Form_Def[HOLLOW].Value;
553 		MaxMem	= M[HOLLOW];
554 	}
555 	else if( M[HOLLOW] > MaxMem2 )
556 	{
557 		MaxMem2	= M[HOLLOW];
558 	}
559 
560 	M[FHOLLOW]   = GET_MINIMUM(bSlope, bPCurveV, bTCurveV);
561 	if( M[FHOLLOW] > MaxMem )
562 	{
563 		Element	= Form_Def[FHOLLOW].Value;
564 		MaxMem	= M[FHOLLOW];
565 	}
566 	else if( M[FHOLLOW] > MaxMem2 )
567 	{
568 		MaxMem2	= M[FHOLLOW];
569 	}
570 
571 	M[SHOLLOW]   = GET_MINIMUM(bSlope, bPCurveX, bTCurveV);
572 	if( M[SHOLLOW] > MaxMem )
573 	{
574 		Element	= Form_Def[SHOLLOW].Value;
575 		MaxMem	= M[SHOLLOW];
576 	}
577 	else if( M[SHOLLOW] > MaxMem2 )
578 	{
579 		MaxMem2	= M[SHOLLOW];
580 	}
581 
582 	M[SPUR]   = GET_MINIMUM(bSlope, bPCurve, bTCurveX);
583 	if( M[SPUR] > MaxMem )
584 	{
585 		Element	= Form_Def[SPUR].Value;
586 		MaxMem	= M[SPUR];
587 	}
588 	else if( M[SPUR] > MaxMem2 )
589 	{
590 		MaxMem2	= M[SPUR];
591 	}
592 
593 	M[FSPUR] = GET_MINIMUM(bSlope, bPCurveV, bTCurveX);
594 	if( M[FSPUR] > MaxMem )
595 	{
596 		Element	= Form_Def[FSPUR].Value;
597 		MaxMem	= M[FSPUR];
598 	}
599 	else if( M[FSPUR] > MaxMem2 )
600 	{
601 		MaxMem2	= M[FSPUR];
602 	}
603 
604 	M[SSPUR] = GET_MINIMUM(bSlope, bPCurveX, bTCurveX);
605 	if( M[SSPUR] > MaxMem )
606 	{
607 		Element	= Form_Def[SSPUR].Value;
608 		MaxMem	= M[SSPUR];
609 	}
610 	else if( M[SSPUR] > MaxMem2 )
611 	{
612 		MaxMem2	= M[SSPUR];
613 	}
614 
615 	//-----------------------------------------------------
616 	double	summem	= 0.0;	// = %PLAIN% + %PIT% + %PEAK% + %RIDGE% + %CHANNEL% + %SADDLE% + %BSLOPE% + %FSLOPE% + %SSLOPE% + %HOLLOW% + %FHOLLOW% + %SHOLLOW% + %SPUR% + %FSPUR% + %SSPUR%
617 
618 	for(int i=0; i<FE_COUNT; i++)
619 	{
620 		summem	+= M[i];
621 	}
622 
623 	// entropy: / ln (15 elements)
624 	Entropy	= -0.3692693 *
625 		( (M[PLAIN]   <= 0 || summem <= 0 ? 0 : M[PLAIN]   * log(M[PLAIN]  ) / summem)
626 		+ (M[PIT]     <= 0 || summem <= 0 ? 0 : M[PIT]     * log(M[PIT]    ) / summem)
627 		+ (M[PEAK]    <= 0 || summem <= 0 ? 0 : M[PEAK]    * log(M[PEAK]   ) / summem)
628 		+ (M[RIDGE]   <= 0 || summem <= 0 ? 0 : M[RIDGE]   * log(M[RIDGE]  ) / summem)
629 		+ (M[CHANNEL] <= 0 || summem <= 0 ? 0 : M[CHANNEL] * log(M[CHANNEL]) / summem)
630 		+ (M[SADDLE]  <= 0 || summem <= 0 ? 0 : M[SADDLE]  * log(M[SADDLE] ) / summem)
631 		+ (M[BSLOPE]  <= 0 || summem <= 0 ? 0 : M[BSLOPE]  * log(M[BSLOPE] ) / summem)
632 		+ (M[FSLOPE]  <= 0 || summem <= 0 ? 0 : M[FSLOPE]  * log(M[FSLOPE] ) / summem)
633 		+ (M[SSLOPE]  <= 0 || summem <= 0 ? 0 : M[SSLOPE]  * log(M[SSLOPE] ) / summem)
634 		+ (M[HOLLOW]  <= 0 || summem <= 0 ? 0 : M[HOLLOW]  * log(M[HOLLOW] ) / summem)
635 		+ (M[FHOLLOW] <= 0 || summem <= 0 ? 0 : M[FHOLLOW] * log(M[FHOLLOW]) / summem)
636 		+ (M[SHOLLOW] <= 0 || summem <= 0 ? 0 : M[SHOLLOW] * log(M[SHOLLOW]) / summem)
637 		+ (M[SPUR]    <= 0 || summem <= 0 ? 0 : M[SPUR]    * log(M[SPUR]   ) / summem)
638 		+ (M[FSPUR]   <= 0 || summem <= 0 ? 0 : M[FSPUR]   * log(M[FSPUR]  ) / summem)
639 		+ (M[SSPUR]   <= 0 || summem <= 0 ? 0 : M[SSPUR]   * log(M[SSPUR]  ) / summem)
640 		- (summem <= 0 ? 0 : log(summem))
641 	);
642 
643 	CI	= MaxMem2 / MaxMem;	// confusion index
644 
645 	//-----------------------------------------------------
646 	return( true );
647 }
648 
649 
650 ///////////////////////////////////////////////////////////
651 //														 //
652 //														 //
653 //														 //
654 ///////////////////////////////////////////////////////////
655 
656 //---------------------------------------------------------
657