1 
2 ///////////////////////////////////////////////////////////
3 //                                                       //
4 //                         SAGA                          //
5 //                                                       //
6 //      System for Automated Geoscientific Analyses      //
7 //                                                       //
8 //                     Tool Library                      //
9 //                     grid_analysis                     //
10 //                                                       //
11 //-------------------------------------------------------//
12 //                                                       //
13 //               coverage_of_categories.cpp              //
14 //                                                       //
15 //                 Copyright (C) 2019 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 "coverage_of_categories.h"
50 
51 
52 ///////////////////////////////////////////////////////////
53 //														 //
54 //														 //
55 //														 //
56 ///////////////////////////////////////////////////////////
57 
58 //---------------------------------------------------------
CCoverage_of_Categories(void)59 CCoverage_of_Categories::CCoverage_of_Categories(void)
60 {
61 	Set_Name		(_TL("Coverage of Categories"));
62 
63 	Set_Author		("O.Conrad (c) 2019");
64 
65 	Set_Description	(_TW(
66 		"The Coverage of Categories tool calculates for each category of "
67 		"the categories input grid the percentage it covers in each cell "
68 		"of the target grid system. "
69 	));
70 
71 	//-----------------------------------------------------
72 	Parameters.Add_Grid("",
73 		"CLASSES"	, _TL("Categories"),
74 		_TL(""),
75 		PARAMETER_INPUT
76 	);
77 
78 	Parameters.Add_Grid_List("",
79 		"COVERAGES"	, _TL("Coverages"),
80 		_TL(""),
81 		PARAMETER_OUTPUT
82 	);
83 
84 	Parameters.Add_Table("",
85 		"LUT"		, _TL("Classification"),
86 		_TL(""),
87 		PARAMETER_INPUT_OPTIONAL
88 	);
89 
90 	Parameters.Add_Table_Field("LUT",
91 		"LUT_VAL"	, _TL("Value"),
92 		_TL("The class value or - in combination with value 2 - the minimum/maximum value specifying a value range."),
93 		false
94 	);
95 
96 	Parameters.Add_Table_Field("LUT",
97 		"LUT_MAX"	, _TL("Maximum Value"),
98 		_TL("Use this option to specify a value range equal or greater than previous value and less than this (maximum) value."),
99 		true
100 	);
101 
102 	Parameters.Add_Table_Field("LUT",
103 		"LUT_NAME"	, _TL("Class name"),
104 		_TL("Optional, a class name used for the naming of the target coverage rasters."),
105 		true
106 	);
107 
108 	Parameters.Add_Bool("",
109 		"NO_DATA"	, _TL("Mark No Coverage as No-Data"),
110 		_TL(""),
111 		true
112 	);
113 
114 	Parameters.Add_Choice("",
115 		"DATADEPTH"	, _TL("Data Depth"),
116 		_TL(""),
117 		CSG_String::Format("%s|%s|%s|%s",
118 			_TL("1-byte"),
119 			_TL("2-byte"),
120 			_TL("4-byte"),
121 			_TL("8-byte")
122 		), 1
123 	);
124 
125 	//-----------------------------------------------------
126 	m_Grid_Target.Create(&Parameters, false, "", "TARGET_");
127 }
128 
129 
130 ///////////////////////////////////////////////////////////
131 //														 //
132 ///////////////////////////////////////////////////////////
133 
134 //---------------------------------------------------------
On_Parameter_Changed(CSG_Parameters * pParameters,CSG_Parameter * pParameter)135 int CCoverage_of_Categories::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
136 {
137 	if( pParameter == pParameters->Get_Grid_System_Parameter() && pParameter->asGrid_System() )
138 	{
139 		m_Grid_Target.Set_User_Defined(pParameters, *pParameter->asGrid_System());
140 	}
141 
142 	m_Grid_Target.On_Parameter_Changed(pParameters, pParameter);
143 
144 	return( CSG_Tool_Grid::On_Parameter_Changed(pParameters, pParameter) );
145 }
146 
147 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)148 int CCoverage_of_Categories::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
149 {
150 	if( pParameter->Cmp_Identifier("LUT") )
151 	{
152 		pParameter->Set_Children_Enabled(pParameter->asTable() != NULL);
153 	}
154 
155 	m_Grid_Target.On_Parameters_Enable(pParameters, pParameter);
156 
157 	return( CSG_Tool_Grid::On_Parameters_Enable(pParameters, pParameter) );
158 }
159 
160 
161 ///////////////////////////////////////////////////////////
162 //														 //
163 ///////////////////////////////////////////////////////////
164 
165 //---------------------------------------------------------
On_Execute(void)166 bool CCoverage_of_Categories::On_Execute(void)
167 {
168 	m_pClasses	= Parameters("CLASSES")->asGrid();
169 
170 	CSG_Grid_System	System	= m_Grid_Target.Get_System();
171 
172 	if( !System.Get_Extent().Intersects(m_pClasses->Get_Extent()) )
173 	{
174 		Error_Set(_TL("no overlap of grid extents"));
175 
176 		return( false );
177 	}
178 
179 	//-----------------------------------------------------
180 	Process_Set_Text(_TL("initializing"));
181 
182 	if( !Initialize(System) )
183 	{
184 		m_Classes.Destroy();
185 
186 		return( false );
187 	}
188 
189 	//---------------------------------------------------------
190 	CSG_Parameter_Grid_List	*pCoverages	= Parameters("COVERAGES")->asGridList();
191 
192 	double	dSize	= 0.5 * System.Get_Cellsize() / Get_Cellsize();
193 
194 	//-----------------------------------------------------
195 	Process_Set_Text(_TL("processing"));
196 
197 	for(int y=0; y<System.Get_NY() && Set_Progress(y, System.Get_NY()); y++)
198 	{
199 		double	yy	= (System.Get_yGrid_to_World(y) - Get_YMin()) / Get_Cellsize();
200 
201 		#ifndef _DEBUG
202 		#pragma omp parallel for
203 		#endif
204 		for(int x=0; x<System.Get_NX(); x++)
205 		{
206 			double	xx	= (System.Get_xGrid_to_World(x) - Get_XMin()) / Get_Cellsize();
207 
208 			CSG_Rect	Cell(xx - dSize, yy - dSize, xx + dSize, yy + dSize);
209 
210 			for(int iClass=0; iClass<pCoverages->Get_Grid_Count(); iClass++)
211 			{
212 				pCoverages->Get_Grid(iClass)->Set_Value(x, y, Get_Coverage(Cell, iClass));
213 			}
214 		}
215 	}
216 
217 	//-------------------------------------------------
218 	m_Classes.Destroy();
219 
220 	return( true );
221 }
222 
223 
224 ///////////////////////////////////////////////////////////
225 //														 //
226 ///////////////////////////////////////////////////////////
227 
228 //---------------------------------------------------------
Initialize(const CSG_Grid_System & System)229 bool CCoverage_of_Categories::Initialize(const CSG_Grid_System &System)
230 {
231 	m_Classes.Destroy();
232 
233 	m_Classes.Add_Field("NAM", SG_DATATYPE_String);
234 	m_Classes.Add_Field("VAL", m_pClasses->Get_Type());
235 
236 	//-----------------------------------------------------
237 	if( Parameters("LUT")->asTable() )
238 	{
239 		CSG_Table	&Classes	= *Parameters("LUT")->asTable();
240 
241 		int	fVal	= Parameters("LUT_VAL" )->asInt();
242 		int	fMax	= Parameters("LUT_MAX" )->asInt();
243 		int	fNam	= Parameters("LUT_NAME")->asInt();
244 
245 		if( fMax >= 0 )
246 		{
247 			m_Classes.Add_Field("MAX", m_pClasses->Get_Type());
248 		}
249 
250 		m_Classes.Set_Count(Classes.Get_Count());
251 
252 		for(int iClass=0; iClass<Classes.Get_Count(); iClass++)
253 		{
254 			m_Classes[iClass].Set_Value(1, Classes[iClass].asDouble(fVal));
255 
256 			if( fMax >= 0 )
257 			{
258 				m_Classes[iClass].Set_Value(2, Classes[iClass].asDouble(fMax));
259 			}
260 
261 			if( fNam >= 0 )
262 			{
263 				m_Classes[iClass].Set_Value(0, Classes[iClass].asString(fNam));
264 			}
265 			else
266 			{
267 				m_Classes[iClass].Set_Value(0, SG_Get_String(Classes[iClass].asDouble(fVal), -6));
268 			}
269 		}
270 	}
271 
272 	//-----------------------------------------------------
273 	else
274 	{
275 		CSG_Unique_Number_Statistics	Classes;
276 
277 		for(sLong iCell=0; iCell<Get_NCells() && Set_Progress_NCells(iCell); iCell++)
278 		{
279 			if( !m_pClasses->is_NoData(iCell) )
280 			{
281 				Classes	+= m_pClasses->asDouble(iCell);
282 			}
283 		}
284 
285 		//-------------------------------------------------
286 		m_Classes.Set_Count(Classes.Get_Count());
287 
288 		for(int iClass=0; iClass<Classes.Get_Count(); iClass++)
289 		{
290 			m_Classes[iClass].Set_Value(0, SG_Get_String(Classes.Get_Value(iClass), -6));
291 			m_Classes[iClass].Set_Value(1,               Classes.Get_Value(iClass)     );
292 		}
293 
294 		m_Classes.Set_Index(1, TABLE_INDEX_Ascending);
295 	}
296 
297 	//-----------------------------------------------------
298 	Message_Fmt("\n%s: %d", _TL("Number of Classes"), m_Classes.Get_Count());
299 
300 	if( m_Classes.Get_Count() < 1 )
301 	{
302 		Error_Set(_TL("No valid cells found"));
303 
304 		return( false );
305 	}
306 
307 	if( m_Classes.Get_Count() > 32 && !Message_Dlg_Confirm(CSG_String::Format("%s: %s [%d]!", _TL("Warning"), _TL("There are many unique values"), m_Classes.Get_Count()), _TL("Do you really want to proceed?")) )
308 	{
309 		return( false );
310 	}
311 
312 	//-----------------------------------------------------
313 	TSG_Data_Type	Type;	double	Scaling;
314 
315 	switch( Parameters("DATADEPTH")->asInt() )
316 	{
317 	case  0: Type = SG_DATATYPE_Byte  ; Scaling = 1. /   250.; break; // 0.004
318 	default: Type = SG_DATATYPE_Word  ; Scaling = 1. / 62500.; break; // 0.000016
319 	case  2: Type = SG_DATATYPE_Float ; Scaling = 1.         ; break;
320 	case  3: Type = SG_DATATYPE_Double; Scaling = 1.         ; break;
321 	}
322 
323 	CSG_Parameter_Grid_List	*pCoverages	= Parameters("COVERAGES")->asGridList();
324 
325 	pCoverages->Del_Items();
326 
327 	for(int iClass=0; iClass<m_Classes.Get_Count(); iClass++)
328 	{
329 		CSG_Grid	*pGrid	= SG_Create_Grid(System, Type);
330 
331 		if( !pGrid )
332 		{
333 			Error_Set(_TL("Failed to allocate memory for coverage grid"));
334 
335 			return( false );
336 		}
337 
338 		pGrid->Fmt_Name("%s [%s]", m_pClasses->Get_Name(), m_Classes[iClass].asString(0));
339 		pGrid->Set_NoData_Value(Parameters("NO_DATA")->asBool() ? 0. : -1.);
340 		pGrid->Set_Scaling(Scaling);
341 
342 		pCoverages->Add_Item(pGrid);
343 	}
344 
345 	//-----------------------------------------------------
346 	return( true );
347 }
348 
349 
350 ///////////////////////////////////////////////////////////
351 //														 //
352 ///////////////////////////////////////////////////////////
353 
354 //---------------------------------------------------------
Cmp_Class(int x,int y,int iClass)355 inline bool CCoverage_of_Categories::Cmp_Class(int x, int y, int iClass)
356 {
357 	if( m_pClasses->is_InGrid(x, y) )
358 	{
359 		double	Value	= m_pClasses->asDouble(x, y);
360 
361 		if( m_Classes.Get_Field_Count() > 2 )
362 		{
363 			return( Value >= m_Classes[iClass].asDouble(1)
364 				&&  Value <  m_Classes[iClass].asDouble(2)
365 			);
366 		}
367 
368 		return( Value == m_Classes[iClass].asDouble(1) );
369 	}
370 
371 	return( false );
372 }
373 
374 //---------------------------------------------------------
Get_Coverage(const CSG_Rect & Cell,int x,int y)375 inline double CCoverage_of_Categories::Get_Coverage(const CSG_Rect &Cell, int x, int y)
376 {
377 	CSG_Rect	c(x - 0.5, y - 0.5, x + 0.5, y + 0.5);
378 
379 	return( c.Intersect(Cell) ? c.Get_Area() : 0. );
380 }
381 
382 //---------------------------------------------------------
Get_Coverage(const CSG_Rect & Cell,int iClass)383 double CCoverage_of_Categories::Get_Coverage(const CSG_Rect &Cell, int iClass)
384 {
385 	double	Coverage	= 0.;
386 
387 	for(int y=(int)Cell.Get_YMin()-1; y<=(int)Cell.Get_YMax()+1; y++)
388 	{
389 		for(int x=(int)Cell.Get_XMin()-1; x<=(int)Cell.Get_XMax()+1; x++)
390 		{
391 			if( Cmp_Class(x, y, iClass) )
392 			{
393 				Coverage	+= Get_Coverage(Cell, x, y);
394 			}
395 		}
396 	}
397 
398 	return( Coverage / Cell.Get_Area() );
399 }
400 
401 
402 ///////////////////////////////////////////////////////////
403 //														 //
404 //														 //
405 //														 //
406 ///////////////////////////////////////////////////////////
407 
408 //---------------------------------------------------------
409