1 
2 ///////////////////////////////////////////////////////////
3 //                                                       //
4 //                         SAGA                          //
5 //                                                       //
6 //      System for Automated Geoscientific Analyses      //
7 //                                                       //
8 //                    Module Library:                    //
9 //                     image_tools                       //
10 //                                                       //
11 //-------------------------------------------------------//
12 //                                                       //
13 //               sentinel_2_scene_import.cpp             //
14 //                                                       //
15 //                 Olaf Conrad (C) 2019                  //
16 //                                                       //
17 //-------------------------------------------------------//
18 //                                                       //
19 // This file is part of 'SAGA - System for Automated     //
20 // Geoscientific Analyses'. SAGA is free software; you   //
21 // can redistribute it and/or modify it under the terms  //
22 // of the GNU General Public License as published by the //
23 // Free Software Foundation, either version 2 of the     //
24 // License, or (at your option) any later version.       //
25 //                                                       //
26 // SAGA is distributed in the hope that it will be       //
27 // useful, but WITHOUT ANY WARRANTY; without even the    //
28 // implied warranty of MERCHANTABILITY or FITNESS FOR A  //
29 // PARTICULAR PURPOSE. See the GNU General Public        //
30 // License for more details.                             //
31 //                                                       //
32 // You should have received a copy of the GNU General    //
33 // Public License along with this program; if not, see   //
34 // <http://www.gnu.org/licenses/>.                       //
35 //                                                       //
36 //-------------------------------------------------------//
37 //                                                       //
38 //    e-mail:     oconrad@saga-gis.org                   //
39 //                                                       //
40 //    contact:    Olaf Conrad                            //
41 //                Institute of Geography                 //
42 //                University of Hamburg                  //
43 //                Germany                                //
44 //                                                       //
45 ///////////////////////////////////////////////////////////
46 
47 //---------------------------------------------------------
48 #include "sentinel_2_scene_import.h"
49 
50 
51 ///////////////////////////////////////////////////////////
52 //														 //
53 //														 //
54 //														 //
55 ///////////////////////////////////////////////////////////
56 
57 //---------------------------------------------------------
58 enum EBand_Head
59 {
60 	INFO_FIELD_ID = 0,
61 	INFO_FIELD_BAND,
62 	INFO_FIELD_NAME,
63 	INFO_FIELD_RES,
64 	INFO_FIELD_WAVE_MIN,
65 	INFO_FIELD_WAVE,
66 	INFO_FIELD_WAVE_MAX,
67 	INFO_FIELD_COUNT
68 };
69 
70 //---------------------------------------------------------
Get_Info_Bands(void)71 CSG_Table CSentinel_2_Scene_Import::Get_Info_Bands(void)
72 {
73 	CSG_Table	Info_Bands;
74 
75 	Info_Bands.Add_Field("ID"        , SG_DATATYPE_Int   );
76 	Info_Bands.Add_Field("BAND"      , SG_DATATYPE_String);
77 	Info_Bands.Add_Field("NAME"      , SG_DATATYPE_String);
78 	Info_Bands.Add_Field("RESOLUTION", SG_DATATYPE_Int   );
79 	Info_Bands.Add_Field("WAVE_MIN"  , SG_DATATYPE_Double);
80 	Info_Bands.Add_Field("WAVE"      , SG_DATATYPE_Double);
81 	Info_Bands.Add_Field("WAVE_MAX"  , SG_DATATYPE_Double);
82 
83 	#define ADD_INFO_BAND(band, name, res, wmin, wave, wmax) { CSG_Table_Record &Info = *Info_Bands.Add_Record();\
84 		Info.Set_Value(INFO_FIELD_ID  , 1 + Info.Get_Index());\
85 		Info.Set_Value(INFO_FIELD_BAND, band);\
86 		Info.Set_Value(INFO_FIELD_NAME, CSG_String::Format("[%s] %s", SG_T(band), name));\
87 		Info.Set_Value(INFO_FIELD_RES , res);\
88 		Info.Set_Value(INFO_FIELD_WAVE_MIN, wave);\
89 		Info.Set_Value(INFO_FIELD_WAVE    , wave);\
90 		Info.Set_Value(INFO_FIELD_WAVE_MAX, wave);\
91 	}
92 
93 	ADD_INFO_BAND("B01", _TL("Aerosols"                 ), 60,  412,  442.7,  456);	//  1, 60m
94 	ADD_INFO_BAND("B02", _TL("Blue"                     ), 10,  456,  492.7,  533);	//  2, 10m
95 	ADD_INFO_BAND("B03", _TL("Green"                    ), 10,  538,  559.8,  583);	//  3, 10m
96 	ADD_INFO_BAND("B04", _TL("Red"                      ), 10,  646,  664.6,  684);	//  4, 10m
97 	ADD_INFO_BAND("B05", _TL("Red Edge"                 ), 20,  695,  704.1,  714);	//  5, 20m
98 	ADD_INFO_BAND("B06", _TL("Red Edge"                 ), 20,  731,  740.5,  749);	//  6, 20m
99 	ADD_INFO_BAND("B07", _TL("Red Edge"                 ), 20,  769,  782.8,  797);	//  7, 20m
100 	ADD_INFO_BAND("B08", _TL("NIR"                      ), 10,  760,  832.8,  907);	//  8, 10m
101 	ADD_INFO_BAND("B8A", _TL("NIR"                      ), 20,  837,  864.7,  881);	//  9, 20m
102 	ADD_INFO_BAND("B09", _TL("Water Vapour"             ), 60,  932,  945.1,  958);	// 10, 60m
103 	ADD_INFO_BAND("B10", _TL("Cirrus"                   ), 60, 1337, 1373.5, 1412);	// 11, 60m
104 	ADD_INFO_BAND("B11", _TL("SWIR"                     ), 20, 1539, 1613.7, 1682);	// 12, 20m
105 	ADD_INFO_BAND("B12", _TL("SWIR"                     ), 20, 2078, 2202.4, 2320);	// 13, 20m
106 	ADD_INFO_BAND("TCI", _TL("True Color Image"         ), 10,    0,    0.0,    0);	// 14, 10m
107 	ADD_INFO_BAND("AOT", _TL("Aerosol Optical Thickness"), 10,    0,    0.0,    0);	// 15, 10m
108 	ADD_INFO_BAND("WVP", _TL("Water Vapour"             ), 10,    0,    0.0,    0);	// 16, 10m
109 	ADD_INFO_BAND("SCL", _TL("Scene Classification"     ), 20,    0,    0.0,    0);	// 17, 20m
110 
111 	return( Info_Bands );
112 }
113 
114 //---------------------------------------------------------
115 #define BAND_IS_10m(b)	(b+1 ==  2 || b+1 ==  3 || b+1 ==  4 || b+1 ==  8)
116 #define BAND_IS_20m(b)	(b+1 ==  5 || b+1 ==  6 || b+1 ==  7 || b+1 ==  9 || b+1 == 12 || b+1 == 13)
117 #define BAND_IS_60m(b)	(b+1 ==  1 || b+1 == 10 || b+1 == 11)
118 #define BAND_IS_TCI(b)	(b+1 == 14)
119 #define BAND_IS_AOT(b)	(b+1 == 15)
120 #define BAND_IS_WVP(b)	(b+1 == 16)
121 #define BAND_IS_SCL(b)	(b+1 == 17)
122 
123 
124 ///////////////////////////////////////////////////////////
125 //														 //
126 //														 //
127 //														 //
128 ///////////////////////////////////////////////////////////
129 
130 //---------------------------------------------------------
CSentinel_2_Scene_Import(void)131 CSentinel_2_Scene_Import::CSentinel_2_Scene_Import(void)
132 {
133 	Set_Name		(_TL("Import Sentinel-2 Scene"));
134 
135 	Set_Author		("O.Conrad (c) 2019");
136 
137 	Set_Description	(_TW(
138 		"Import Sentinel-2 scenes from a folder structure as provided "
139 		"by the original ESA download. "
140 	));
141 
142 	Add_Reference("https://sentinel.esa.int/web/sentinel/missions/sentinel-2",
143 		SG_T("Sentinel-2 at ESA's Sentinel Online")
144 	);
145 
146 	Add_Reference("https://sentinel.esa.int/web/sentinel/document-library/content/-/article/sentinel-2-user-handbook",
147 		SG_T("Sentinel-2 User Handbook")
148 	);
149 
150 	//-----------------------------------------------------
151 	Parameters.Add_FilePath("",
152 		"METAFILE"		, _TL("Metadata File"),
153 		_TL(""),
154 		CSG_String::Format("%s|MTD*.xml|%s|*.*",
155 			_TL("Sentinel-2 Metadata Files"),
156 			_TL("All Files")
157 		)
158 	);
159 
160 	Parameters.Add_Grid_List("",
161 		"BANDS"			, _TL("Bands"),
162 		_TL(""),
163 		PARAMETER_OUTPUT
164 	);
165 
166 	Parameters.Add_Bool("",
167 		"MULTI2GRIDS"	, _TL("Multispectral Bands as Grid Collection"),
168 		_TL(""),
169 		true
170 	);
171 
172 	Parameters.Add_Bool("", "LOAD_60M", _TL("Aerosol, Vapour, Cirrus"  ), _TL(""), false);
173 //	Parameters.Add_Bool("", "LOAD_TCI", _TL("True Color Image"         ), _TL(""), false);
174 //	Parameters.Add_Bool("", "LOAD_AOT", _TL("Aerosol Optical Thickness"), _TL(""), false);
175 //	Parameters.Add_Bool("", "LOAD_WVP", _TL("Water Vapour"             ), _TL(""), false);
176 	Parameters.Add_Bool("", "LOAD_SCL", _TL("Scene Classification"     ), _TL(""),  true);
177 
178 	Parameters.Add_Choice("",
179 		"REFLECTANCE"	, _TL("Reflectance Values"),
180 		_TL(""),
181 		CSG_String::Format("%s|%s",
182 			_TL("original"),
183 			_TL("fraction")
184 		), 1
185 	);
186 
187 	Parameters.Add_Choice("",
188 		"PROJECTION"	, _TL("Coordinate System"),
189 		_TL(""),
190 		CSG_String::Format("%s|%s|%s",
191 			_TL("UTM North"),
192 			_TL("UTM South"),
193 			_TL("Geographic Coordinates")
194 		), 0
195 	);
196 
197 	Parameters.Add_Choice("PROJECTION",
198 		"RESAMPLING"	, _TL("Resampling"),
199 		_TL(""),
200 		CSG_String::Format("%s|%s|%s|%s",
201 			_TL("Nearest Neighbour"),
202 			_TL("Bilinear Interpolation"),
203 			_TL("Bicubic Spline Interpolation"),
204 			_TL("B-Spline Interpolation")
205 		), 3
206 	);
207 
208 	//-----------------------------------------------------
209 	Parameters.Add_Choice("",
210 		"EXTENT"		, _TL("Extent"),
211 		_TL(""),
212 		CSG_String::Format("%s|%s|%s|%s",
213 			_TL("original"),
214 			_TL("user defined"),
215 			_TL("grid system"),
216 			_TL("shapes extent")
217 		), 0
218 	);
219 
220 	Parameters.Add_Double("EXTENT", "EXTENT_XMIN", _TL("Left"  ), _TL(""));
221 	Parameters.Add_Double("EXTENT", "EXTENT_XMAX", _TL("Right" ), _TL(""));
222 	Parameters.Add_Double("EXTENT", "EXTENT_YMIN", _TL("Bottom"), _TL(""));
223 	Parameters.Add_Double("EXTENT", "EXTENT_YMAX", _TL("Top"   ), _TL(""));
224 
225 	Parameters.Add_Grid_System("EXTENT",
226 		"EXTENT_GRID"	, _TL("Grid System"),
227 		_TL("")
228 	);
229 
230 	Parameters.Add_Shapes("EXTENT",
231 		"EXTENT_SHAPES"	, _TL("Shapes Extent"),
232 		_TL(""),
233 		PARAMETER_INPUT
234 	);
235 
236 	Parameters.Add_Double("EXTENT",
237 		"EXTENT_BUFFER"	, _TL("Buffer"),
238 		_TL(""),
239 		0., 0., true
240 	);
241 }
242 
243 
244 ///////////////////////////////////////////////////////////
245 //														 //
246 ///////////////////////////////////////////////////////////
247 
248 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)249 int CSentinel_2_Scene_Import::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
250 {
251 	if( pParameter->Cmp_Identifier("METAFILE") )
252 	{
253 		bool bLevel2 = SG_File_Exists  (pParameter->asString())
254 					&& SG_File_Get_Name(pParameter->asString(), false).Find("MTD_MSIL2") == 0;
255 
256 		pParameters->Set_Enabled("LOAD_AOT", bLevel2);
257 		pParameters->Set_Enabled("LOAD_WVP", bLevel2);
258 		pParameters->Set_Enabled("LOAD_SCL", bLevel2);
259 	}
260 
261 	if( pParameter->Cmp_Identifier("PROJECTION") )
262 	{
263 		pParameters->Set_Enabled("RESAMPLING", pParameter->asInt() == 2);
264 	}
265 
266 	if(	pParameter->Cmp_Identifier("EXTENT") )
267 	{
268 		pParameters->Set_Enabled("EXTENT_XMIN"  , pParameter->asInt() == 1);
269 		pParameters->Set_Enabled("EXTENT_XMAX"  , pParameter->asInt() == 1);
270 		pParameters->Set_Enabled("EXTENT_YMIN"  , pParameter->asInt() == 1);
271 		pParameters->Set_Enabled("EXTENT_YMAX"  , pParameter->asInt() == 1);
272 		pParameters->Set_Enabled("EXTENT_GRID"  , pParameter->asInt() == 2);
273 		pParameters->Set_Enabled("EXTENT_SHAPES", pParameter->asInt() == 3);
274 		pParameters->Set_Enabled("EXTENT_BUFFER", pParameter->asInt() >= 2);
275 	}
276 
277 	return( CSG_Tool::On_Parameters_Enable(pParameters, pParameter) );
278 }
279 
280 
281 ///////////////////////////////////////////////////////////
282 //														 //
283 ///////////////////////////////////////////////////////////
284 
285 //---------------------------------------------------------
On_Execute(void)286 bool CSentinel_2_Scene_Import::On_Execute(void)
287 {
288 	CSG_MetaData	Info_General, Info_Granule;
289 
290 	if( !Load_Metadata(Parameters("METAFILE")->asString(), Info_General, Info_Granule) )
291 	{
292 		Error_Fmt("%s [%s]", _TL("failed to load metadata"), Parameters("METAFILE")->asString());
293 
294 		return( false );
295 	}
296 
297 	CSG_String	Date	= Info_General["PRODUCT_START_TIME"].Get_Content().BeforeFirst('T');
298 
299 	//-----------------------------------------------------
300 	CSG_String	Path	= SG_File_Get_Path(Parameters("METAFILE")->asString());
301 
302 	bool	bLoadTCI	= Parameters("LOAD_TCI") && Parameters("LOAD_TCI")->asBool();
303 	bool	bLoadAOT	= Parameters("LOAD_AOT") && Parameters("LOAD_AOT")->asBool();
304 	bool	bLoadWVP	= Parameters("LOAD_WVP") && Parameters("LOAD_WVP")->asBool();
305 	bool	bLoadSCL	= Parameters("LOAD_SCL") && Parameters("LOAD_SCL")->asBool();
306 	bool	bLoad60m	= Parameters("LOAD_60M") && Parameters("LOAD_60M")->asBool();
307 
308 	bool	bMultiGrids	= Parameters("MULTI2GRIDS")->asBool();
309 	double	Scaling		= Parameters("REFLECTANCE")->asInt () == 0 ? 1. : 1. / 10000.;
310 
311 	CSG_Table	Info_Bands(Get_Info_Bands());
312 
313 	//-----------------------------------------------------
314 	Parameters("BANDS")->asGridList()->Del_Items();
315 
316 	CSG_Grids *pBands[2]; pBands[0] = pBands[1] = NULL; CSG_Grid *pSCL = NULL;
317 
318 	for(int Band=0; Band<Info_Bands.Get_Count() && Process_Get_Okay(); Band++)
319 	{
320 		if( (!bLoadTCI && BAND_IS_TCI(Band))
321 		||  (!bLoadAOT && BAND_IS_AOT(Band))
322 		||  (!bLoadWVP && BAND_IS_WVP(Band))
323 		||  (!bLoadSCL && BAND_IS_SCL(Band))
324 		||  (!bLoad60m && BAND_IS_60m(Band)) )
325 		{
326 			continue;
327 		}
328 
329 		CSG_Grid	*pBand	= Load_Band(Path, Find_Band(Info_Bands[Band], Info_Granule));
330 
331 		if( !pBand )
332 		{
333 			continue;
334 		}
335 
336 		pBand->Get_MetaData().Add_Child(Info_General)->Set_Name("SENTINEL-2");
337 		pBand->Set_Description(Info_General.asText());
338 
339 		if( bMultiGrids && (BAND_IS_10m(Band) || BAND_IS_20m(Band)) )
340 		{
341 			int	b	= BAND_IS_10m(Band) ? 0 : 1;
342 
343 			if( pBands[b] == NULL )
344 			{
345 				if( (pBands[b] = SG_Create_Grids(pBand->Get_System(), Info_Bands)) == NULL )
346 				{
347 					Error_Set(_TL("memory allocation failed"));
348 
349 					return( false );
350 				}
351 
352 				Parameters("BANDS")->asGridList()->Add_Item(pBands[b]);
353 			}
354 
355 			pBands[b]->Add_Grid(Info_Bands[Band], pBand, true);
356 		}
357 		else
358 		{
359 			pBand->Fmt_Name("S2_%s_%s", Date.c_str(), Info_Bands[Band].asString(INFO_FIELD_BAND));
360 
361 			if( BAND_IS_10m(Band) || BAND_IS_20m(Band) || BAND_IS_60m(Band) )
362 			{
363 				pBand->Set_Scaling(Scaling);
364 			}
365 
366 			if( BAND_IS_SCL(Band) )
367 			{
368 				pSCL = pBand;
369 			}
370 
371 			Parameters("BANDS")->asGridList()->Add_Item(pBand);
372 		}
373 	}
374 
375 	//-----------------------------------------------------
376 	for(int i=0; i<2; i++)
377 	{
378 		if( pBands[i] )
379 		{
380 			pBands[i]->Fmt_Name("S2_%s_%dm", Date.c_str(), i == 0 ? 10 : 20);
381 			pBands[i]->Get_MetaData().Add_Child(Info_General)->Set_Name("SENTINEL-2");
382 			pBands[i]->Set_Description(Info_General.asText());
383 			pBands[i]->Set_Z_Attribute (INFO_FIELD_WAVE);
384 			pBands[i]->Set_Z_Name_Field(INFO_FIELD_NAME);
385 			pBands[i]->Set_Scaling(Scaling);
386 
387 			DataObject_Add(pBands[i], true);
388 		}
389 	}
390 
391 	if( pSCL )
392 	{
393 		Load_Classification(pSCL, Parameters("METAFILE")->asString());
394 	}
395 
396 	return( true );
397 }
398 
399 
400 ///////////////////////////////////////////////////////////
401 //														 //
402 ///////////////////////////////////////////////////////////
403 
404 //---------------------------------------------------------
Load_Metadata(const CSG_String & File,CSG_MetaData & General,CSG_MetaData & Granule)405 bool CSentinel_2_Scene_Import::Load_Metadata(const CSG_String &File, CSG_MetaData &General, CSG_MetaData &Granule)
406 {
407 	CSG_MetaData	Metadata;
408 
409 	if( !Metadata.Load(File)
410 	||  !Metadata("n1:General_Info")
411 	||  !Metadata["n1:General_Info"]("Product_Info")
412 	||  !Metadata["n1:General_Info"]["Product_Info"]("Product_Organisation")
413 	||  !Metadata["n1:General_Info"]["Product_Info"]["Product_Organisation"]("Granule_List")
414 	||  !Metadata["n1:General_Info"]["Product_Info"]["Product_Organisation"]["Granule_List"]("Granule") )
415 	{
416 		return( false );
417 	}
418 
419 	Granule	= Metadata["n1:General_Info"]["Product_Info"]["Product_Organisation"]["Granule_List"]["Granule"];
420 	General	= Metadata["n1:General_Info"]["Product_Info"];
421 
422 	General.Del_Child("Product_Organisation");
423 
424 	return( true );
425 }
426 
427 
428 ///////////////////////////////////////////////////////////
429 //														 //
430 ///////////////////////////////////////////////////////////
431 
432 //---------------------------------------------------------
Find_Band(const CSG_Table_Record & Band,const CSG_MetaData & Granule)433 CSG_String CSentinel_2_Scene_Import::Find_Band(const CSG_Table_Record &Band, const CSG_MetaData &Granule)
434 {
435 	CSG_String IDold(Band.asString(1)), IDnew(CSG_String::Format("%s_%dm", Band.asString(1), Band.asInt(3)));
436 
437 	for(int i=0; i<Granule.Get_Children_Count(); i++)
438 	{
439 		if(  Granule[i].Cmp_Name("IMAGE_FILE")
440 		&& (!Granule[i].Get_Content().Right(IDnew.Length()).Cmp(IDnew)
441 		||  !Granule[i].Get_Content().Right(IDold.Length()).Cmp(IDold)) )
442 		{
443 			return( Granule[i].Get_Content() );
444 		}
445 	}
446 
447 	return( "" );
448 }
449 
450 //---------------------------------------------------------
Load_Band(const CSG_String & Path,const CSG_String & File)451 CSG_Grid * CSentinel_2_Scene_Import::Load_Band(const CSG_String &Path, const CSG_String &File)
452 {
453 	Process_Set_Text("%s: %s", _TL("loading"), File.AfterLast('/').c_str());
454 
455 	//-----------------------------------------------------
456 	CSG_String	_File(Path + "/" + File + ".jp2");
457 
458 	#ifdef _SAGA_MSW
459 	_File.Replace("/", "\\");
460 	#endif
461 
462 	CSG_Grid	*pBand	= Load_Grid(_File);
463 
464 	if( !pBand )
465 	{
466 		return( NULL );
467 	}
468 
469 	pBand->Set_NoData_Value(0);	// landsat 8 pretends to use a value of 65535 (2^16 - 1)
470 
471 	if( !pBand->Get_Projection().is_Okay() )
472 	{
473 		// undefined coordinate system, nothing to do be further done...
474 	}
475 
476 	//-----------------------------------------------------
477 	else if( Parameters("PROJECTION")->asInt() != 2 ) // UTM
478 	{
479 		CSG_Grid	*pTmp	= pBand;
480 
481 		CSG_String	Projection	= pTmp->Get_Projection().Get_Proj4();
482 
483 		if( Projection.Find("+proj=utm") >= 0
484 		&&  (  (Projection.Find("+south") >= 0 && Parameters("PROJECTION")->asInt() == 0)
485 		    || (Projection.Find("+south") <  0 && Parameters("PROJECTION")->asInt() == 1))
486 		&&  (pBand = SG_Create_Grid(pTmp->Get_Type(), pTmp->Get_NX(), pTmp->Get_NY(), pTmp->Get_Cellsize(),
487 				pTmp->Get_XMin(), pTmp->Get_YMin() + (Parameters("PROJECTION")->asInt() == 1 ? 10000000 : -10000000)
488 			)) != NULL )
489 		{
490 			if( Parameters("PROJECTION")->asInt() == 1 )
491 				Projection.Append (" +south");
492 			else
493 				Projection.Replace(" +south", "");
494 
495 			pBand->Get_Projection().Create(Projection, SG_PROJ_FMT_Proj4);
496 
497 			pBand->Set_Name              (pTmp->Get_Name());
498 			pBand->Set_Description       (pTmp->Get_Description());
499 			pBand->Set_NoData_Value_Range(pTmp->Get_NoData_Value(), pTmp->Get_NoData_Value(true));
500 			pBand->Set_Scaling           (pTmp->Get_Scaling(), pTmp->Get_Offset());
501 
502 			#pragma omp parallel for
503 			for(int y=0; y<pBand->Get_NY(); y++)
504 			{
505 				for(int x=0; x<pBand->Get_NX(); x++)
506 				{
507 					pBand->Set_Value(x, y, pTmp->asDouble(x, y));
508 				}
509 			}
510 
511 			delete(pTmp);
512 		}
513 	}
514 
515 	//-----------------------------------------------------
516 	else if( Parameters("PROJECTION")->asInt() == 2 )	// Geographic Coordinates
517 	{
518 		CSG_Tool	*pTool	= SG_Get_Tool_Library_Manager().Create_Tool("pj_proj4", 4);	// Coordinate Transformation (Grid)
519 
520 		if(	pTool )
521 		{
522 			Message_Fmt("\n%s (%s: %s)\n", _TL("re-projection to geographic coordinates"), _TL("original"), pBand->Get_Projection().Get_Name().c_str());
523 
524 			pTool->Set_Manager(NULL);
525 
526 			if( pTool->Set_Parameter("CRS_PROJ4" , SG_T("+proj=longlat +ellps=WGS84 +datum=WGS84"))
527 			&&  pTool->Set_Parameter("SOURCE"    , pBand)
528 			&&  pTool->Set_Parameter("RESAMPLING", Parameters("RESAMPLING"))
529 			&&  pTool->Set_Parameter("KEEP_TYPE" , true)
530 			&&  pTool->Execute() )
531 			{
532 				delete(pBand);
533 
534 				pBand	= pTool->Get_Parameters()->Get_Parameter("GRID")->asGrid();
535 			}
536 
537 			SG_Get_Tool_Library_Manager().Delete_Tool(pTool);
538 		}
539 	}
540 
541 	//-----------------------------------------------------
542 	return( pBand );
543 }
544 
545 //---------------------------------------------------------
Load_Grid(const CSG_String & File)546 CSG_Grid * CSentinel_2_Scene_Import::Load_Grid(const CSG_String &File)
547 {
548 	CSG_Rect Extent;
549 
550 	switch( Parameters("EXTENT")->asInt() )
551 	{
552 	default: // original
553 		return( SG_Create_Grid(File) );
554 
555 	case  1: // user defined
556 		Extent.Assign(
557 			Parameters("EXTENT_XMIN")->asDouble(),
558 			Parameters("EXTENT_YMIN")->asDouble(),
559 			Parameters("EXTENT_XMAX")->asDouble(),
560 			Parameters("EXTENT_YMAX")->asDouble()
561 		);
562 		break;
563 
564 	case  2: // grid system
565 		Extent = Parameters("EXTENT_GRID"  )->asGrid_System()->Get_Extent();
566 		Extent.Inflate(Parameters("EXTENT_BUFFER")->asDouble(), false);
567 		break;
568 
569 	case  3: // shapes extent
570 		Extent = Parameters("EXTENT_SHAPES")->asShapes     ()->Get_Extent();
571 		Extent.Inflate(Parameters("EXTENT_BUFFER")->asDouble(), false);
572 		break;
573 	}
574 
575 	//-----------------------------------------------------
576 	CSG_Grid	*pGrid	= NULL;
577 	CSG_Tool	*pTool	= SG_Get_Tool_Library_Manager().Create_Tool("io_gdal", 0);	// Import Raster
578 
579 	if( pTool && pTool->Set_Manager(NULL)
580 		&&  pTool->Set_Parameter("FILES"      , File)
581 		&&	pTool->Set_Parameter("EXTENT"     , 1)
582 		&&	pTool->Set_Parameter("EXTENT_XMIN", Extent.Get_XMin())
583 		&&	pTool->Set_Parameter("EXTENT_XMAX", Extent.Get_XMax())
584 		&&	pTool->Set_Parameter("EXTENT_YMIN", Extent.Get_YMin())
585 		&&	pTool->Set_Parameter("EXTENT_YMAX", Extent.Get_YMax())
586 		&&  pTool->Execute() )
587 	{
588 		pGrid	= pTool->Get_Parameter("GRIDS")->asGridList()->Get_Grid(0);
589 	}
590 
591 	SG_Get_Tool_Library_Manager().Delete_Tool(pTool);
592 
593 	return( pGrid );
594 }
595 
596 
597 ///////////////////////////////////////////////////////////
598 //														 //
599 ///////////////////////////////////////////////////////////
600 
601 //---------------------------------------------------------
Load_Classification(CSG_Grid * pGrid,const CSG_String & File)602 bool CSentinel_2_Scene_Import::Load_Classification(CSG_Grid *pGrid, const CSG_String &File)
603 {
604 	CSG_MetaData	Metadata;
605 
606 	if( !Metadata.Load(File)
607 	||  !Metadata("n1:General_Info")
608 	||  !Metadata["n1:General_Info"]("Product_Image_Characteristics")
609 	||  !Metadata["n1:General_Info"]["Product_Image_Characteristics"]("Scene_Classification_List") )
610 	{
611 		return( false );
612 	}
613 
614 	CSG_MetaData ClassList = Metadata["n1:General_Info"]["Product_Image_Characteristics"]["Scene_Classification_List"];
615 
616 	//-----------------------------------------------------
617 	CSG_Table LUT;
618 
619 	LUT.Add_Field("Color"      , SG_DATATYPE_Color );
620 	LUT.Add_Field("Name"       , SG_DATATYPE_String);
621 	LUT.Add_Field("Description", SG_DATATYPE_String);
622 	LUT.Add_Field("Minimum"    , SG_DATATYPE_Double);
623 	LUT.Add_Field("Maximum"    , SG_DATATYPE_Double);
624 
625 	for(int i=0, Index; i<ClassList.Get_Children_Count(); i++)
626 	{
627 		if( ClassList[i].Cmp_Name("Scene_Classification_ID")
628 		&&  ClassList[i]("SCENE_CLASSIFICATION_TEXT" )
629 		&&  ClassList[i]["SCENE_CLASSIFICATION_INDEX"].Get_Content().asInt(Index) )
630 		{
631 			CSG_String Name(ClassList[i]["SCENE_CLASSIFICATION_TEXT"].Get_Content().AfterFirst('_')); Name.Replace("_", " ");
632 
633 			CSG_Table_Record &Class = *LUT.Add_Record();
634 
635 			Class.Set_Value(0, SG_Color_Get_Random());
636 			Class.Set_Value(1, Name);
637 			Class.Set_Value(3, Index);
638 			Class.Set_Value(4, Index);
639 		}
640 	}
641 
642 	if( LUT.Get_Count() > 0 )
643 	{
644 		DataObject_Add(pGrid);
645 
646 		CSG_Parameter *pLUT = DataObject_Get_Parameter(pGrid, "LUT");
647 
648 		if( pLUT && pLUT->asTable() && pLUT->asTable()->Assign_Values(&LUT) )
649 		{
650 			DataObject_Set_Parameter(pGrid, pLUT);
651 			DataObject_Set_Parameter(pGrid, "COLORS_TYPE", 1);	// Color Classification Type: Lookup Table
652 
653 			return( true );
654 		}
655 	}
656 
657 	return( false );
658 }
659 
660 
661 ///////////////////////////////////////////////////////////
662 //														 //
663 //														 //
664 //														 //
665 ///////////////////////////////////////////////////////////
666 
667 //---------------------------------------------------------
668