1 
2 ///////////////////////////////////////////////////////////
3 //                                                       //
4 //                         SAGA                          //
5 //                                                       //
6 //    System for an Automated Geo-Scientific Analysis    //
7 //                                                       //
8 //                     Tool Library                      //
9 //                    grid_analysis                      //
10 //                                                       //
11 //-------------------------------------------------------//
12 //                                                       //
13 //                    Soil_Texture.cpp                   //
14 //                                                       //
15 //                 Copyright (C) 2007 by                 //
16 //                    Gianluca Massei                    //
17 //                                                       //
18 //-------------------------------------------------------//
19 //                                                       //
20 // This file is part of 'SAGA - System for an Automated  //
21 // Geo-Scientific Analysis'. 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:     g_massa@libero.it				     	 //
40 //                                                       //
41 //    contact:    Gianluca Massei                        //
42 //                Department of Economics and Appraisal  //
43 //                University of Perugia - Italy			 //
44 //                www.unipg.it                           //
45 //                                                       //
46 ///////////////////////////////////////////////////////////
47 
48 //---------------------------------------------------------
49 #include "Soil_Texture.h"
50 
51 
52 ///////////////////////////////////////////////////////////
53 //														 //
54 //														 //
55 //														 //
56 ///////////////////////////////////////////////////////////
57 
58 //---------------------------------------------------------
59 const CSG_String	Definition_USDA[][4]	=
60 { {	"000, 000, 255", "C"   , _TL("Clay"           ), "  0 100,   0   60,  20  40,  45  40,  45  55,   0 100"
61 },{	"000, 200, 255", "SiC" , _TL("Silty Clay"     ), "  0 100,   0   60,  20  40,   0  40,   0 100"
62 },{	"000, 200, 200", "SiCL", _TL("Silty Clay Loam"), "  0  40,   0   27,  20  27,  20  40,   0  40"
63 },{	"200, 000, 255", "SC"  , _TL("Sandy Clay"     ), " 45  55,  45   35,  65  35,  45  55"
64 },{	"200, 200, 200", "SCL" , _TL("Sandy Clay Loam"), " 45  35,  45   27,  52  20,  80  20,  65  35,  45  35"
65 },{	"127, 127, 200", "CL"  , _TL("Clay Loam"      ), " 20  40,  20   27,  45  27,  45  40,  20  40"
66 },{	"000, 255, 000", "Si"  , _TL("Silt"           ), "  0  12,   0    0,  20   0,   8  12,   0  12"
67 },{	"127, 255, 127", "SiL" , _TL("Silt Loam"      ), "  8  12,  20    0,  50   0,  23  27,   0  27,   0  12,   8  12"
68 },{	"127, 127, 127", "L"   , _TL("Loam"           ), " 23  27,  43    7,  52   7,  52  20,  45  27,  23  27"
69 },{	"255, 000, 000", "S"   , _TL("Sand"           ), " 85   0, 100    0,  90  10,  85   0"
70 },{	"255, 000, 127", "LS"  , _TL("Loamy Sand"     ), " 70   0,  85    0,  90  10,  85  15,  70   0"
71 },{	"200, 127, 127", "SL"  , _TL("Sandy Loam"     ), " 43   7,  50    0,  70   0,  85  15,  80  20,  52  20,  52   7,  43   7"
72 },{	"", "", "", ""	}	};
73 
74 //---------------------------------------------------------
75 const CSG_String	Definition_Belgium[][4]	=
76 { {	"", "U", _TL("Heavy Clay"      ), " 65.000  35.000, 10.000 35.000,  0.000 45.000,  0.000 100.000,  65.000 35.000"
77 },{	"", "E", _TL("Clay"            ), " 82.500  17.500, 67.500 17.500, 30.000 17.500, 25.000  18.125,  20.000 20.000, 15.000 22.500,  0.000 30.000,  0.000 45.000, 10.000 35.000, 65.000 35.000, 82.500 17.500"
78 },{	"", "A", _TL("Silt Loam"       ), " 15.000   0.000,  0.000  0.000,  0.000 30.000, 15.000  22.500,  15.000  0.000"
79 },{	"", "L", _TL("Sandy Loam"      ), " 50.000   0.000, 15.000  0.000, 15.000 22.500, 20.000  20.000,  25.000 18.125, 30.000 17.500, 67.500 17.500, 67.500 11.250, 50.000 11.250, 50.000  0.000"
80 },{	"", "P", _TL("Light Sandy Loam"), " 67.500   0.000, 50.000  0.000, 50.000 11.250, 67.500  11.250,  67.500  0.000"
81 },{	"", "S", _TL("Loamy Sand"      ), " 82.500   0.000, 67.500  0.000, 67.500 11.250, 67.500  17.500,  82.500 17.500, 91.250  8.750, 82.500  8.750, 82.500  0.000"
82 },{	"", "Z", _TL("Sand"            ), "100.000   0.000, 82.500  0.000, 82.500  8.750, 91.250   8.750, 100.000  0.000"
83 },{	"", "", "", ""	}	};
84 
85 //---------------------------------------------------------
86 const CSG_String	Definition_KA5[][4]	=
87 { {	"", "Ss" , _TL("Reiner Sand"             ), "100  0, 95   5, 85  5, 90  0, 100  0"
88 
89 },{	"", "Su2", _TL("Schwach schluffiger Sand"), " 90  0, 85   5, 70  5, 75  0,  90  0"
90 },{	"", "Sl2", _TL("Schwach lehmiger Sand"   ), " 85  5, 82   8, 67  8, 70  5,  85  5"
91 },{	"", "Sl3", _TL("Mittel lehmiger Sand"    ), " 82  8, 78  12, 48 12, 52  8,  82  8"
92 },{	"", "St2", _TL("Schwach toniger Sand"    ), " 95  5, 83  17, 73 17, 85  5,  95  5"
93 
94 },{	"", "Su3", _TL("Mittel schluffiger Sand" ), " 75  0, 67   8, 52  8, 60  0,  75  0"
95 },{	"", "Su4", _TL("Stark schluffiger Sand"  ), " 60  0, 52   8, 42  8, 50  0,  60  0"
96 
97 },{	"", "Slu", _TL("Schluffig-lemiger Sand"  ), " 52  8, 43  17, 33 17, 42  8,  52  8"
98 },{	"", "Sl4", _TL("Stark lehmiger Sand"     ), " 78 12, 73  17, 43 17, 48 12,  78 12"
99 },{	"", "St3", _TL("Mittel toniger Sand"     ), " 83 17, 75  25, 60 25, 68 17,  83 17"
100 
101 },{	"", "Ls2", _TL("Schwach sandiger Lehm"   ), " 43 17, 35  25, 25 25, 33 17,  43 17"
102 },{	"", "Ls3", _TL("Mittel sandiger Lehm"    ), " 53 17, 45  25, 35 25, 43 17,  53 17"
103 },{	"", "Ls4", _TL("Stark sandiger Lehm"     ), " 68 17, 60  25, 45 25, 53 17,  68 17"
104 },{	"", "Lt2", _TL("Schwach toniger Lehm"    ), " 45 25, 35  35, 15 35, 25 25,  45 25"
105 
106 },{	"", "Lts", _TL("Sandig-toniger Lehm"     ), " 60 25, 40  45, 25 45, 45 25,  60 25"
107 },{	"", "Ts4", _TL("Stark sandiger Ton"      ), " 75 25, 65  35, 50 35, 60 25,  75 25"
108 },{	"", "Ts3", _TL("Mittel sandiger Ton"     ), " 65 35, 55  45, 40 45, 50 35,  65 35"
109 
110 },{	"", "Uu" , _TL("Reiner Schluff"          ), " 20  0, 12   8,  0  8,  0  0,  20  0"
111 },{	"", "Us" , _TL("Sandiger Schluff"        ), " 50  0, 42   8, 12  8, 20  0,  50  0"
112 
113 },{	"", "Ut2", _TL("Schwach toniger Schluff" ), " 27  8, 23  12,  0 12,  0  8,  27  8"
114 },{	"", "Ut3", _TL("Mittel toniger Schluff"  ), " 23 12, 18  17,  0 17,  0 12,  23 12"
115 },{	"", "Uls", _TL("Sandig-lehmiger Schluff" ), " 42  8, 33  17, 18 17, 27  8,  42  8"
116 
117 },{	"", "Ut4", _TL("Stark toniger Schluff"   ), " 18 17, 10  25,  0 25,  0 17,  18 17"
118 },{	"", "Lu" , _TL("Schluffiger Lehm"        ), " 33 17, 20  30,  5 30, 18 17,  33 17"
119 
120 },{	"", "Lt3", _TL("Mittel toniger Lehm"     ), " 35 35, 25  45,  5 45, 15 35,  35 35"
121 },{	"", "Tu3", _TL("Mittel schluffiger Ton"  ), " 20 30,  5  45,  0 45,  0 35,   5 30, 20 30"
122 },{	"", "Tu4", _TL("Stark schluffiger Ton"   ), " 10 25,  0  35,  0 25, 10 25"
123 
124 },{	"", "Ts2", _TL("Schwach sandiger Ton"    ), " 55 45, 35  65, 20 65, 40 45,  55 45"
125 },{	"", "Tl" , _TL("Lehmiger Ton"            ), " 40 45, 20  65,  5 65, 25 45,  40 45"
126 },{	"", "Tu2", _TL("Schwach schluffiger Ton" ), " 25 45,  5  65,  0 65,  0 45,  25 45"
127 },{	"", "Tt" , _TL("Reiner Ton"              ), " 35 65,  0 100,  0 65, 35 65"
128 
129 },{	"", "", "", ""	}	};
130 
131 
132 ///////////////////////////////////////////////////////////
133 //														 //
134 ///////////////////////////////////////////////////////////
135 
136 //---------------------------------------------------------
137 class CSoil_Texture_Classifier
138 {
139 public:
CSoil_Texture_Classifier(void)140 	CSoil_Texture_Classifier(void)	{}
141 
CSoil_Texture_Classifier(int Scheme,int Colors_Default)142 	CSoil_Texture_Classifier(int Scheme, int Colors_Default)	{	Initialize(Scheme, Colors_Default);	}
143 
144 	//-----------------------------------------------------
Get_Description(void)145 	static CSG_String		Get_Description	(void)
146 	{
147 		return( _TW(
148 			"Derive soil texture classes from sand, silt and clay contents. "
149 			"Currently supported schemes are USDA and German Kartieranleitung 5. "
150 		));
151 	}
152 
153 	//-----------------------------------------------------
Get_Count(void) const154 	int						Get_Count		(void)	const	{	return( m_Classes.Get_Count() );	}
155 
Get_ID(int i) const156 	int						Get_ID			(int i)	const	{	return( i >= 0 && i < Get_Count() ? m_Classes[i].asInt   (0) :        0 );	}
Get_Color(int i) const157 	int						Get_Color		(int i)	const	{	return( i >= 0 && i < Get_Count() ? m_Classes[i].asInt   (1) :        0 );	}
Get_Key(int i) const158 	CSG_String				Get_Key			(int i)	const	{	return( i >= 0 && i < Get_Count() ? m_Classes[i].asString(2) : SG_T("") );	}
Get_Name(int i) const159 	CSG_String				Get_Name		(int i)	const	{	return( i >= 0 && i < Get_Count() ? m_Classes[i].asString(3) : SG_T("") );	}
160 
Get_Polygons(CSG_Shapes * pPolygons,int Axes,bool bIsosceles) const161 	bool					Get_Polygons	(CSG_Shapes *pPolygons, int Axes, bool bIsosceles)	const
162 	{
163 		if( !pPolygons || !m_Classes.is_Valid() )
164 		{
165 			return( false );
166 		}
167 
168 		pPolygons->Create(m_Classes);
169 
170 		for(int i=0; (Axes!=0 || bIsosceles) && i<pPolygons->Get_Count(); i++)
171 		{
172 			CSG_Shape	*pPolygon	= pPolygons->Get_Shape(i);
173 
174 			for(int iPoint=0; iPoint<pPolygon->Get_Point_Count(0); iPoint++)
175 			{
176 				TSG_Point	p	= pPolygon->Get_Point(iPoint);
177 
178 				double	Sand	= p.x;
179 				double	Silt	= 100 - (p.x + p.y);
180 				double	Clay	= p.y;
181 
182 				switch( Axes )
183 				{
184 				case 0: p.x = Sand; p.y = Clay; break;
185 				case 1: p.x = Sand; p.y = Silt; break;
186 				case 2: p.x = Silt; p.y = Sand; break;
187 				case 3: p.x = Silt; p.y = Clay; break;
188 				case 4: p.x = Clay; p.y = Sand; break;
189 				case 5: p.x = Clay; p.y = Silt; break;
190 				}
191 
192 				if( bIsosceles )
193 				{
194 					p.x	+= 0.5 * p.y;
195 					p.y	*= 0.8660254038;	// sqrt(100*100 - 50*50) / 100.
196 				}
197 
198 				pPolygon->Set_Point(p, iPoint, 0);
199 			}
200 		}
201 
202 		return( true );
203 	}
204 
205 	//-----------------------------------------------------
Get_Table(CSG_Table & Classes,int Definition)206 	static bool	Get_Table	(CSG_Table &Classes, int Definition)
207 	{
208 		if( Definition < 0 || Definition > 2 )
209 		{
210 			return( false );
211 		}
212 
213 		Classes.Destroy();
214 
215 		Classes.Add_Field("COLOR"  , SG_DATATYPE_String);
216 		Classes.Add_Field("KEY"    , SG_DATATYPE_String);
217 		Classes.Add_Field("NAME"   , SG_DATATYPE_String);
218 		Classes.Add_Field("POLYGON", SG_DATATYPE_String);
219 
220 		for(int i=0; ; i++)
221 		{
222 			const CSG_String	*Class;
223 
224 			switch( Definition )
225 			{
226 			case  0: Class = Definition_USDA   [i]; break;
227 			case  1: Class = Definition_KA5    [i]; break;
228 			case  2: Class = Definition_Belgium[i]; break;
229 			}
230 
231 			if( Class[1].is_Empty() )	{	break;	}	else
232 			{
233 				CSG_Table_Record	*pClass	= Classes.Add_Record();
234 
235 				pClass->Set_Value(0, Class[0]);
236 				pClass->Set_Value(1, Class[1]);
237 				pClass->Set_Value(2, Class[2]);
238 				pClass->Set_Value(3, Class[3]);
239 			}
240 		}
241 
242 		return( Classes.Get_Count() > 0 );
243 	}
244 
245 	//-----------------------------------------------------
Initialize(int Definition,int Colors_Default)246 	bool	Initialize	(int Definition, int Colors_Default)
247 	{
248 		CSG_Table	Classes;	return( Get_Table(Classes, Definition) && Initialize(Classes, Colors_Default) );
249 	}
250 
251 	//-----------------------------------------------------
Initialize(const CSG_Table & Classes,int Colors_Default)252 	bool	Initialize	(const CSG_Table &Classes, int Colors_Default)
253 	{
254 		m_Classes.Create(SHAPE_TYPE_Polygon);
255 
256 		m_Classes.Add_Field("ID"   , SG_DATATYPE_Int   );
257 		m_Classes.Add_Field("COLOR", SG_DATATYPE_Color );
258 		m_Classes.Add_Field("KEY"  , SG_DATATYPE_String);
259 		m_Classes.Add_Field("NAME" , SG_DATATYPE_String);
260 
261 		//-------------------------------------------------
262 		for(int i=0; i<Classes.Get_Count(); i++)
263 		{
264 			CSG_String_Tokenizer	Items(Classes[i].asString(3), ",");
265 
266 			if( Items.Get_Tokens_Count() >= 3 )
267 			{
268 				CSG_Shape_Polygon	*pClass	= (CSG_Shape_Polygon *)m_Classes.Add_Shape();
269 
270 				pClass->Set_Value(0, i + 1);
271 				pClass->Set_Value(2, Classes[i].asString(1));
272 				pClass->Set_Value(3, Classes[i].asString(2));
273 
274 				do
275 				{
276 					CSG_String	Point(Items.Get_Next_Token());	Point.Trim();
277 
278 					double	Sand	= Point.BeforeFirst(' ').asDouble();
279 					double	Clay	= Point.AfterFirst (' ').asDouble();
280 
281 					pClass->Add_Point(Sand, Clay);
282 				}
283 				while( Items.Has_More_Tokens() );
284 
285 				Items.Set_String(Classes[i].asString(0), ",");
286 
287 				if( Items.Get_Tokens_Count() == 3 )
288 				{
289 					int	r	= Items.Get_Next_Token().asInt();
290 					int	g	= Items.Get_Next_Token().asInt();
291 					int	b	= Items.Get_Next_Token().asInt();
292 
293 					pClass->Set_Value(1, SG_GET_RGB(r, g, b));
294 				}
295 				else
296 				{
297 					TSG_Point	c	= pClass->Get_Centroid();
298 
299 					double	Sand, Silt, Clay, r, g, b;
300 
301 					Sand	= 0.01 * c.x;
302 					Silt	= 0.01 * (100 - (c.x + c.y));
303 					Clay	= 0.01 * c.y;
304 
305 					switch( Colors_Default )
306 					{
307 					default: r = Sand + Silt; g = Silt + Silt; b = Clay + Silt; break;
308 					case  1: r = Sand + Silt; g = Silt       ; b = Clay       ; break;
309 					case  2: r = Sand       ; g = Silt       ; b = Clay       ; break;
310 					}
311 
312 					pClass->Set_Value(1, SG_GET_RGB(
313 						(int)(255 * (r < 1 ? r : 1.)),
314 						(int)(255 * (g < 1 ? g : 1.)),
315 						(int)(255 * (b < 1 ? b : 1.))
316 					));
317 				}
318 			}
319 		}
320 
321 		return( Get_Count() > 0 );
322 	}
323 
324 	//-----------------------------------------------------
Get_Texture(double Sand,double Silt,double Clay,double & Sum) const325 	int						Get_Texture		(double Sand, double Silt, double Clay, double &Sum)	const
326 	{
327 		if( ((Sand < 0 ? 1 : 0) + (Silt < 0 ? 1 : 0) + (Clay < 0 ? 1 : 0)) > 1 )
328 		{
329 			return( -1 );
330 		}
331 
332 		if( Sand < 0 ) Sand = 100 - (       Silt + Clay);
333 		if( Silt < 0 ) Silt = 100 - (Sand        + Clay);
334 		if( Clay < 0 ) Clay = 100 - (Sand + Silt       );
335 
336 		Sum	= Sand + Silt + Clay;
337 
338 		if( Sum > 0.0 && Sum != 100.0 )
339 		{
340 			Sand	*= 100.0 / Sum;
341 			Clay	*= 100.0 / Sum;
342 		}
343 
344 		return( Get_Texture_From_Sand_and_Clay(Sand, Clay) );
345 	}
346 
347 	//-----------------------------------------------------
Set_LUT(CSG_Table * pClasses,bool bID) const348 	bool					Set_LUT			(CSG_Table *pClasses, bool bID)	const
349 	{
350 		pClasses->Set_Record_Count(Get_Count());
351 
352 		for(int i=0; i<Get_Count(); i++)
353 		{
354 			CSG_Table_Record	*pClass	= pClasses->Get_Record(i);
355 
356 			pClasses->Set_Value(i, 0, Get_Color(i));
357 			pClasses->Set_Value(i, 1, Get_Key  (i));
358 			pClasses->Set_Value(i, 2, Get_Name (i));
359 
360 			if( bID )
361 			{
362 				pClasses->Set_Value(i, 3, Get_ID(i));
363 				pClasses->Set_Value(i, 4, Get_ID(i));
364 			}
365 			else
366 			{
367 				pClasses->Set_Value(i, 3, Get_Key(i));
368 				pClasses->Set_Value(i, 4, Get_Key(i));
369 			}
370 		}
371 
372 		return( true );
373 	}
374 
375 
376 private: //////////////////////////////////////////////////
377 
378 	CSG_Shapes		m_Classes;
379 
380 
Get_Texture_From_Sand_and_Clay(double Sand,double Clay) const381 	int				Get_Texture_From_Sand_and_Clay	(double Sand, double Clay)	const
382 	{
383 		if( Sand < 0.001 ) Sand = 0.001; else if( Sand > 99.99 ) Sand = 99.99;
384 		if( Clay < 0.001 ) Clay = 0.001; else if( Clay > 99.99 ) Clay = 99.99;
385 
386 		if( Sand + Clay >= 99.99 )
387 		{
388 			double	Sum	= 99.99 / (Sand + Clay);
389 
390 			Sand	*= Sum;
391 			Clay	*= Sum;
392 		}
393 
394 		for(int i=0; i<Get_Count(); i++)
395 		{
396 			if( ((CSG_Shape_Polygon *)m_Classes.Get_Shape(i))->Contains(Sand, Clay) )
397 			{
398 				return( i );
399 			}
400 		}
401 
402 		return( -1 );
403 	}
404 
405 };
406 
407 
408 ///////////////////////////////////////////////////////////
409 //														 //
410 //														 //
411 //														 //
412 ///////////////////////////////////////////////////////////
413 
414 //---------------------------------------------------------
CSoil_Texture(void)415 CSoil_Texture::CSoil_Texture(void)
416 {
417 	Set_Name		(_TL("Soil Texture Classification"));
418 
419 	Set_Author		("Gianluca Massei (c) 2007 (g_massa@libero.it), L.Landschreiber, O.Conrad (c) 2017");
420 
421 	Set_Description	(CSoil_Texture_Classifier::Get_Description());
422 
423 	Add_Reference(
424 		"http://soils.usda.gov/technical/aids/investigations/texture/",
425 		SG_T("USDA NRCS Soils Website")
426 	);
427 
428 	//-----------------------------------------------------
429 	Parameters.Add_Grid("",
430 		"SAND"		, _TL("Sand"),
431 		_TL("sand content given as percentage"),
432 		PARAMETER_INPUT_OPTIONAL
433 	);
434 
435 	Parameters.Add_Grid("",
436 		"SILT"		, _TL("Silt"),
437 		_TL("silt content given as percentage"),
438 		PARAMETER_INPUT_OPTIONAL
439 	);
440 
441 	Parameters.Add_Grid("",
442 		"CLAY"		, _TL("Clay"),
443 		_TL("clay content given as percentage"),
444 		PARAMETER_INPUT_OPTIONAL
445 	);
446 
447 	Parameters.Add_Grid("",
448 		"TEXTURE"	, _TL("Soil Texture"),
449 		_TL("soil texture"),
450 		PARAMETER_OUTPUT, true, SG_DATATYPE_Char
451 	);
452 
453 	Parameters.Add_Grid("",
454 		"SUM"		, _TL("Sum"),
455 		_TL("Sum of percentages"),
456 		PARAMETER_OUTPUT_OPTIONAL
457 	);
458 
459 	Parameters.Add_Choice("",
460 		"SCHEME"	, _TL("Classification"),
461 		_TL(""),
462 		CSG_String::Format("%s|%s|%s|%s|",
463 			_TL("USDA"),
464 			_TL("Germany KA5"),
465 			_TL("Belgium/France"),
466 			_TL("user defined")
467 		)
468 	);
469 
470 	Parameters.Add_Choice("",
471 		"COLORS"	, _TL("Default Colour Scheme"),
472 		_TL(""),
473 		CSG_String::Format("%s 1|%s 2|%s 3|",
474 			_TL("Scheme"),
475 			_TL("Scheme"),
476 			_TL("Scheme")
477 		)
478 	);
479 
480 	Parameters.Add_FixedTable("SCHEME",
481 		"USER"		, _TL("User Definition"),
482 		_TW("The colour is defined as comma separated red, green and blue values (in the range 0 to 255). "
483 			"If the colour field is empty it will be generated from the chosen default colour scheme. "
484 			"Key and name are simple text labels specifying each class. The polygon is defined as pairs of "
485 			"sand (=x) and clay (=y) separated by a blank and separated from the next pair by a comma. "
486 		)
487 	);
488 
489 	CSoil_Texture_Classifier::Get_Table(*Parameters("USER")->asTable(), 0);
490 
491 	Parameters.Add_Shapes("",
492 		"POLYGONS"	, _TL("Scheme as Polygons"),
493 		_TL(""),
494 		PARAMETER_OUTPUT_OPTIONAL, SHAPE_TYPE_Polygon
495 	);
496 
497 	Parameters.Add_Choice("POLYGONS",
498 		"XY_AXES"	, _TL("X/Y Axes"),
499 		_TL(""),
500 		CSG_String::Format("%s|%s|%s|%s|%s|%s|",
501 			_TL("Sand and Clay"),
502 			_TL("Sand and Silt"),
503 			_TL("Silt and Sand"),
504 			_TL("Silt and Clay"),
505 			_TL("Clay and Sand"),
506 			_TL("Clay and Silt")
507 		), 3
508 	);
509 
510 	Parameters.Add_Choice("POLYGONS",
511 		"TRIANGLE"	, _TL("Triangle"),
512 		_TL(""),
513 		CSG_String::Format("%s|%s|",
514 			_TL("right-angled"),
515 			_TL("isosceles")
516 		), 1
517 	);
518 }
519 
520 
521 ///////////////////////////////////////////////////////////
522 //														 //
523 ///////////////////////////////////////////////////////////
524 
525 //---------------------------------------------------------
On_Parameter_Changed(CSG_Parameters * pParameters,CSG_Parameter * pParameter)526 int CSoil_Texture::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
527 {
528 	if( pParameter->Cmp_Identifier("SCHEME") )
529 	{
530 		CSoil_Texture_Classifier::Get_Table(*pParameters->Get_Parameter("USER")->asTable(), pParameter->asInt());
531 	}
532 
533 	return( CSG_Tool_Grid::On_Parameter_Changed(pParameters, pParameter) );
534 }
535 
536 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)537 int CSoil_Texture::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
538 {
539 	if( pParameter->Cmp_Identifier("POLYGONS") )
540 	{
541 		pParameters->Set_Enabled("XY_AXES" , pParameter->asPointer() != NULL);
542 		pParameters->Set_Enabled("TRIANGLE", pParameter->asPointer() != NULL);
543 	}
544 
545 	if( pParameter->Cmp_Identifier("SCHEME") )
546 	{
547 		pParameters->Set_Enabled("COLORS"  , pParameter->asInt() != 0);
548 		pParameters->Set_Enabled("USER"    , pParameter->asInt() == 3);
549 	}
550 
551 	return( CSG_Tool_Grid::On_Parameters_Enable(pParameters, pParameter) );
552 }
553 
554 
555 ///////////////////////////////////////////////////////////
556 //														 //
557 ///////////////////////////////////////////////////////////
558 
559 //---------------------------------------------------------
On_Execute(void)560 bool CSoil_Texture::On_Execute(void)
561 {
562 	//-----------------------------------------------------
563 	CSG_Grid	*pSand	= Parameters("SAND"   )->asGrid();
564 	CSG_Grid	*pSilt	= Parameters("SILT"   )->asGrid();
565 	CSG_Grid	*pClay	= Parameters("CLAY"   )->asGrid();
566 	CSG_Grid	*pClass	= Parameters("TEXTURE")->asGrid();
567 	CSG_Grid	*pSum	= Parameters("SUM"    )->asGrid();
568 
569 	//-----------------------------------------------------
570 	if( (pSand ? 1 : 0) + (pSilt ? 1 : 0) + (pClay ? 1 : 0) < 2 )
571 	{
572 		Error_Set(_TL("at least two contents (sand, silt, clay) have to be given"));
573 
574 		return( false );
575 	}
576 
577 	//-----------------------------------------------------
578 	CSoil_Texture_Classifier	Classifier(Parameters("SCHEME")->asInt(), Parameters("COLORS")->asInt());
579 
580 	if( Parameters("SCHEME")->asInt() == 3 && !Classifier.Initialize(*Parameters("USER")->asTable(), Parameters("COLORS")->asInt()) )	// user defined
581 	{
582 		return( false );
583 	}
584 
585 	//-----------------------------------------------------
586 	pClass->Set_NoData_Value(0.0);
587 
588 	CSG_Parameter	*pLUT	= DataObject_Get_Parameter(pClass, "LUT");
589 
590 	if( pLUT && pLUT->asTable() )
591 	{
592 		Classifier.Set_LUT(pLUT->asTable(), true);
593 
594 		DataObject_Set_Parameter(pClass, pLUT            );	// Lookup Table
595 		DataObject_Set_Parameter(pClass, "COLORS_TYPE", 1);	// Color Classification Type: Lookup Table
596 	}
597 
598 	if( Classifier.Get_Polygons(Parameters("POLYGONS")->asShapes(), Parameters("XY_AXES")->asInt(), Parameters("TRIANGLE")->asInt() == 1)
599 	&& (pLUT = DataObject_Get_Parameter(pClass, "LUT")) != NULL && pLUT->asTable() )
600 	{
601 		Classifier.Set_LUT(pLUT->asTable(), true);
602 
603 		DataObject_Set_Parameter(Parameters("POLYGONS")->asShapes(), pLUT            );	// Lookup Table
604 		DataObject_Set_Parameter(Parameters("POLYGONS")->asShapes(), "COLORS_TYPE", 1);	// Color Classification Type: Lookup Table
605 		DataObject_Set_Parameter(Parameters("POLYGONS")->asShapes(), "LUT_ATTRIB" , 0);	// Lookup Table Attribute
606 	}
607 
608 	//-----------------------------------------------------
609 	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
610 	{
611 		#pragma omp parallel for
612 		for(int x=0; x<Get_NX(); x++)
613 		{
614 			if(	(pSand && pSand->is_NoData(x, y))
615 			||	(pSilt && pSilt->is_NoData(x, y))
616 			||	(pClay && pClay->is_NoData(x, y)) )
617 			{
618 				SG_GRID_PTR_SAFE_SET_NODATA(pClass, x, y);
619 				SG_GRID_PTR_SAFE_SET_NODATA(pSum  , x, y);
620 			}
621 			else
622 			{
623 				double	Sum;
624 
625 				int		Class	= Classifier.Get_Texture(
626 					pSand ? pSand->asDouble(x, y) : -1.0,
627 					pSilt ? pSilt->asDouble(x, y) : -1.0,
628 					pClay ? pClay->asDouble(x, y) : -1.0, Sum
629 				);
630 
631 				SG_GRID_PTR_SAFE_SET_VALUE(pClass, x, y, Classifier.Get_ID(Class));
632 				SG_GRID_PTR_SAFE_SET_VALUE(pSum  , x, y, Sum);
633 			}
634 		}
635 	}
636 
637 	//-----------------------------------------------------
638 	return( true );
639 }
640 
641 
642 ///////////////////////////////////////////////////////////
643 //														 //
644 //														 //
645 //														 //
646 ///////////////////////////////////////////////////////////
647 
648 //---------------------------------------------------------
CSoil_Texture_Table(void)649 CSoil_Texture_Table::CSoil_Texture_Table(void)
650 {
651 	Set_Name		(_TL("Soil Texture Classification for Tables"));
652 
653 	Set_Author		("Gianluca Massei (c) 2007 (g_massa@libero.it), L.Landschreiber, O.Conrad (c) 2017");
654 
655 	Set_Description	(CSoil_Texture_Classifier::Get_Description());
656 
657 	Add_Reference(
658 		"http://soils.usda.gov/technical/aids/investigations/texture/",
659 		SG_T("USDA NRCS Soils Website")
660 	);
661 
662 	//-----------------------------------------------------
663 	Parameters.Add_Table("",
664 		"TABLE"	, _TL("Table"),
665 		_TL(""),
666 		PARAMETER_INPUT
667 	);
668 
669 	Parameters.Add_Table_Field("TABLE",
670 		"SAND"	, _TL("Sand"),
671 		_TL("sand content given as percentage"),
672 		true
673 	);
674 
675 	Parameters.Add_Table_Field("TABLE",
676 		"SILT"	, _TL("Silt"),
677 		_TL("silt content given as percentage"),
678 		true
679 	);
680 
681 	Parameters.Add_Table_Field("TABLE",
682 		"CLAY"	, _TL("Clay"),
683 		_TL("clay content given as percentage"),
684 		true
685 	);
686 
687 	Parameters.Add_Table_Field("TABLE",
688 		"TEXTURE"	, _TL("Texture"),
689 		_TL("soil texture"),
690 		true
691 	);
692 
693 	Parameters.Add_Choice("",
694 		"SCHEME"	, _TL("Classification"),
695 		_TL(""),
696 		CSG_String::Format("%s|%s|%s|%s|",
697 			_TL("USDA"),
698 			_TL("Germany KA5"),
699 			_TL("Belgium/France"),
700 			_TL("user defined")
701 		)
702 	);
703 
704 	Parameters.Add_Choice("",
705 		"COLORS"	, _TL("Default Colour Scheme"),
706 		_TL(""),
707 		CSG_String::Format("%s 1|%s 2|%s 3|",
708 			_TL("Scheme"),
709 			_TL("Scheme"),
710 			_TL("Scheme")
711 		)
712 	);
713 
714 	Parameters.Add_FixedTable("SCHEME",
715 		"USER"		, _TL("User Definition"),
716 		_TW("The colour is defined as comma separated red, green and blue values (in the range 0 to 255). "
717 			"If the colour field is empty it will be generated from the chosen default colour scheme. "
718 			"Key and name are simple text labels specifying each class. The polygon is defined as pairs of "
719 			"sand (=x) and clay (=y) separated by a blank and separated from the next pair by a comma. "
720 		)
721 	);
722 
723 	CSoil_Texture_Classifier::Get_Table(*Parameters("USER")->asTable(), 0);
724 
725 	Parameters.Add_Shapes("",
726 		"POLYGONS"	, _TL("Scheme as Polygons"),
727 		_TL(""),
728 		PARAMETER_OUTPUT_OPTIONAL, SHAPE_TYPE_Polygon
729 	);
730 
731 	Parameters.Add_Choice("POLYGONS",
732 		"XY_AXES"	, _TL("X/Y Axes"),
733 		_TL(""),
734 		CSG_String::Format("%s|%s|%s|%s|%s|%s|",
735 			_TL("Sand and Clay"),
736 			_TL("Sand and Silt"),
737 			_TL("Silt and Sand"),
738 			_TL("Silt and Clay"),
739 			_TL("Clay and Sand"),
740 			_TL("Clay and Silt")
741 		), 3
742 	);
743 
744 	Parameters.Add_Choice("POLYGONS",
745 		"TRIANGLE"	, _TL("Triangle"),
746 		_TL(""),
747 		CSG_String::Format("%s|%s|",
748 			_TL("right-angled"),
749 			_TL("isosceles")
750 		), 1
751 	);
752 }
753 
754 
755 ///////////////////////////////////////////////////////////
756 //														 //
757 ///////////////////////////////////////////////////////////
758 
759 //---------------------------------------------------------
On_Parameter_Changed(CSG_Parameters * pParameters,CSG_Parameter * pParameter)760 int CSoil_Texture_Table::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
761 {
762 	if( pParameter->Cmp_Identifier("SCHEME") )
763 	{
764 		CSoil_Texture_Classifier::Get_Table(*pParameters->Get_Parameter("USER")->asTable(), pParameter->asInt());
765 	}
766 
767 	return( CSG_Tool::On_Parameter_Changed(pParameters, pParameter) );
768 }
769 
770 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)771 int CSoil_Texture_Table::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
772 {
773 	if( pParameter->Cmp_Identifier("POLYGONS") )
774 	{
775 		pParameters->Set_Enabled("XY_AXES" , pParameter->asPointer() != NULL);
776 		pParameters->Set_Enabled("TRIANGLE", pParameter->asPointer() != NULL);
777 	}
778 
779 	if( pParameter->Cmp_Identifier("SCHEME") )
780 	{
781 		pParameters->Set_Enabled("COLORS"  , pParameter->asInt() != 0);
782 		pParameters->Set_Enabled("USER"    , pParameter->asInt() == 3);
783 	}
784 
785 	return( CSG_Tool::On_Parameters_Enable(pParameters, pParameter) );
786 }
787 
788 
789 ///////////////////////////////////////////////////////////
790 //														 //
791 ///////////////////////////////////////////////////////////
792 
793 //---------------------------------------------------------
On_Execute(void)794 bool CSoil_Texture_Table::On_Execute(void)
795 {
796 	//-----------------------------------------------------
797 	CSG_Table	*pTable		= Parameters("TABLE")->asTable();
798 
799 	int		iSand		= Parameters("SAND"   )->asInt();
800 	int		iSilt		= Parameters("SILT"   )->asInt();
801 	int		iClay		= Parameters("CLAY"   )->asInt();
802 	int		iTexture	= Parameters("TEXTURE")->asInt();
803 
804 	//-----------------------------------------------------
805 	if( (iSand >= 0 ? 1 : 0) + (iSilt >= 0 ? 1 : 0) + (iClay >= 0 ? 1 : 0) < 2 )
806 	{
807 		Error_Set(_TL("at least two contents (sand, silt, clay) have to be given"));
808 
809 		return( false );
810 	}
811 
812 	//-----------------------------------------------------
813 	CSoil_Texture_Classifier	Classifier(Parameters("SCHEME")->asInt(), Parameters("COLORS")->asInt());
814 
815 	if( Parameters("SCHEME")->asInt() == 3 && !Classifier.Initialize(*Parameters("USER")->asTable(), Parameters("COLORS")->asInt()) )	// user defined
816 	{
817 		return( false );
818 	}
819 
820 	//-----------------------------------------------------
821 	if( iTexture < 0 )
822 	{
823 		iTexture	= pTable->Get_Field_Count();
824 
825 		pTable->Add_Field("TEXTURE", SG_DATATYPE_String);
826 	}
827 
828 	//-----------------------------------------------------
829 	for(int i=0; i<pTable->Get_Count() && Set_Progress(i, pTable->Get_Count()); i++)
830 	{
831 		CSG_Table_Record	*pRecord	= pTable->Get_Record(i);
832 
833 		if(	(iSand >= 0 && pRecord->is_NoData(iSand))
834 		||	(iSilt >= 0 && pRecord->is_NoData(iSilt))
835 		||	(iClay >= 0 && pRecord->is_NoData(iClay)) )
836 		{
837 			pRecord->Set_NoData(iTexture);
838 		}
839 		else
840 		{
841 			double	Sum;
842 
843 			int		Class	= Classifier.Get_Texture(
844 				iSand >= 0 ? pRecord->asDouble(iSand) : -1.0,
845 				iSilt >= 0 ? pRecord->asDouble(iSilt) : -1.0,
846 				iClay >= 0 ? pRecord->asDouble(iClay) : -1.0, Sum
847 			);
848 
849 			pRecord->Set_Value (iTexture, Classifier.Get_Key(Class));
850 		}
851 	}
852 
853 	DataObject_Update(pTable);
854 
855 	//-----------------------------------------------------
856 	CSG_Parameter	*pLUT	= DataObject_Get_Parameter(pTable, "LUT");
857 
858 	if( pLUT && pLUT->asTable() )
859 	{
860 		Classifier.Set_LUT(pLUT->asTable(), false);
861 
862 		DataObject_Set_Parameter(pTable, pLUT                   );	// Lookup Table
863 		DataObject_Set_Parameter(pTable, "COLORS_TYPE", 1       );	// Color Classification Type: Lookup Table
864 		DataObject_Set_Parameter(pTable, "LUT_ATTRIB" , iTexture);	// Lookup Table Attribute
865 	}
866 
867 	if( Classifier.Get_Polygons(Parameters("POLYGONS")->asShapes(), Parameters("XY_AXES")->asInt(), Parameters("TRIANGLE")->asInt() == 1)
868 	&& (pLUT = DataObject_Get_Parameter(pTable, "LUT")) != NULL && pLUT->asTable() )
869 	{
870 		Classifier.Set_LUT(pLUT->asTable(), true);
871 
872 		DataObject_Set_Parameter(Parameters("POLYGONS")->asShapes(), pLUT            );	// Lookup Table
873 		DataObject_Set_Parameter(Parameters("POLYGONS")->asShapes(), "COLORS_TYPE", 1);	// Color Classification Type: Lookup Table
874 		DataObject_Set_Parameter(Parameters("POLYGONS")->asShapes(), "LUT_ATTRIB" , 0);	// Lookup Table Attribute
875 	}
876 
877 	//-----------------------------------------------------
878 	return( true );
879 }
880 
881 
882 ///////////////////////////////////////////////////////////
883 //														 //
884 //														 //
885 //														 //
886 ///////////////////////////////////////////////////////////
887 
888 //---------------------------------------------------------
889