1 
2 ///////////////////////////////////////////////////////////
3 //                                                       //
4 //                         SAGA                          //
5 //                                                       //
6 //      System for Automated Geoscientific Analyses      //
7 //                                                       //
8 //           Application Programming Interface           //
9 //                                                       //
10 //                  Library: SAGA_API                    //
11 //                                                       //
12 //-------------------------------------------------------//
13 //                                                       //
14 //                    shapes_io.cpp                      //
15 //                                                       //
16 //          Copyright (C) 2005 by Olaf Conrad            //
17 //                                                       //
18 //-------------------------------------------------------//
19 //                                                       //
20 // This file is part of 'SAGA - System for Automated     //
21 // Geoscientific Analyses'.                              //
22 //                                                       //
23 // This library is free software; you can redistribute   //
24 // it and/or modify it under the terms of the GNU Lesser //
25 // General Public License as published by the Free       //
26 // Software Foundation, either version 2.1 of the        //
27 // License, or (at your option) any later version.       //
28 //                                                       //
29 // This library is distributed in the hope that it will  //
30 // be useful, but WITHOUT ANY WARRANTY; without even the //
31 // implied warranty of MERCHANTABILITY or FITNESS FOR A  //
32 // PARTICULAR PURPOSE. See the GNU Lesser General Public //
33 // License for more details.                             //
34 //                                                       //
35 // You should have received a copy of the GNU Lesser     //
36 // General Public License along with this program; if    //
37 // not, see <http://www.gnu.org/licenses/>.              //
38 //                                                       //
39 //-------------------------------------------------------//
40 //                                                       //
41 //    contact:    Olaf Conrad                            //
42 //                Institute of Geography                 //
43 //                University of Goettingen               //
44 //                Goldschmidtstr. 5                      //
45 //                37077 Goettingen                       //
46 //                Germany                                //
47 //                                                       //
48 //    e-mail:     oconrad@saga-gis.org                   //
49 //                                                       //
50 ///////////////////////////////////////////////////////////
51 
52 //---------------------------------------------------------
53 #include "shapes.h"
54 #include "table_dbase.h"
55 #include "tool_library.h"
56 #include "data_manager.h"
57 
58 
59 ///////////////////////////////////////////////////////////
60 //														 //
61 //														 //
62 //														 //
63 ///////////////////////////////////////////////////////////
64 
65 //---------------------------------------------------------
On_Reload(void)66 bool CSG_Shapes::On_Reload(void)
67 {
68 	return( Create(Get_File_Name(false)) );
69 }
70 
71 //---------------------------------------------------------
On_Delete(void)72 bool CSG_Shapes::On_Delete(void)
73 {
74 	CSG_String	File_Name	= Get_File_Name(true);
75 
76 	SG_File_Delete(File_Name);
77 
78 	SG_File_Set_Extension(File_Name, "shp"); SG_File_Delete(File_Name);	// shapes
79 	SG_File_Set_Extension(File_Name, "shx"); SG_File_Delete(File_Name);	// shape index
80 	SG_File_Set_Extension(File_Name, "dbf"); SG_File_Delete(File_Name);	// attributes
81 	SG_File_Set_Extension(File_Name, "prj"); SG_File_Delete(File_Name);	// projection
82 	SG_File_Set_Extension(File_Name, "sbn"); SG_File_Delete(File_Name);	// spatial index
83 	SG_File_Set_Extension(File_Name, "sbx"); SG_File_Delete(File_Name);	// spatial index
84 	SG_File_Set_Extension(File_Name, "atx"); SG_File_Delete(File_Name);	// attribute index
85 	SG_File_Set_Extension(File_Name, "xml"); SG_File_Delete(File_Name);	// metadata
86 	SG_File_Set_Extension(File_Name, "cpg"); SG_File_Delete(File_Name);	// code page
87 	SG_File_Set_Extension(File_Name, "qix"); SG_File_Delete(File_Name);	// quadtree spatial index
88 
89 	return( true );
90 }
91 
92 
93 ///////////////////////////////////////////////////////////
94 //														 //
95 //														 //
96 //														 //
97 ///////////////////////////////////////////////////////////
98 
99 //---------------------------------------------------------
_Load_GDAL(const CSG_String & File_Name)100 bool CSG_Shapes::_Load_GDAL(const CSG_String &File_Name)
101 {
102 	CSG_Data_Manager	Data;
103 
104 	CSG_Tool	*pTool	= SG_Get_Tool_Library_Manager().Create_Tool("io_gdal", 3);	// Import Shapes
105 
106 	if( pTool )
107 	{
108 		if( pTool->Settings_Push(&Data) && pTool->Set_Parameter("FILES", File_Name, PARAMETER_TYPE_FilePath) )
109 		{
110 			SG_UI_Msg_Lock(true);
111 			pTool->Execute();
112 			SG_UI_Msg_Lock(false);
113 		}
114 
115 		SG_Get_Tool_Library_Manager().Delete_Tool(pTool);
116 	}
117 
118 	//-----------------------------------------------------
119 	CSG_Shapes	*pShapes	= Data.Get_Shapes()->Count() ? Data.Get_Shapes()->Get(0)->asShapes() : NULL;
120 
121 	if( !pShapes || !Create(*pShapes) )
122 	{
123 		return( false );
124 	}
125 
126 	Get_MetaData  ()	= pShapes->Get_MetaData  ();
127 	Get_Projection()	= pShapes->Get_Projection();
128 
129 	//-----------------------------------------------------
130 	if( SG_File_Cmp_Extension(File_Name, "gpkg"   )
131 	||  SG_File_Cmp_Extension(File_Name, "GeoJSON")	)
132 	{
133 		Set_File_Name(File_Name, true);
134 	}
135 	else
136 	{
137 		Set_File_Name(File_Name, false);
138 	}
139 
140 	return( true );
141 }
142 
143 //---------------------------------------------------------
_Save_GDAL(const CSG_String & File_Name,const CSG_String & Driver)144 bool CSG_Shapes::_Save_GDAL(const CSG_String &File_Name, const CSG_String &Driver)
145 {
146 	bool	bResult;
147 
148 	SG_RUN_TOOL(bResult, "io_gdal", 4,	// Export Shapes
149 		    SG_TOOL_PARAMETER_SET("SHAPES", this)
150 		&&	SG_TOOL_PARAMETER_SET("FORMAT", Driver)
151 		&&	SG_TOOL_PARAMETER_SET("FILE"  , File_Name)
152 	);
153 
154 	return( bResult );
155 }
156 
157 
158 ///////////////////////////////////////////////////////////
159 //														 //
160 //														 //
161 //														 //
162 ///////////////////////////////////////////////////////////
163 
164 //---------------------------------------------------------
_Load_ESRI(const CSG_String & File_Name)165 bool CSG_Shapes::_Load_ESRI(const CSG_String &File_Name)
166 {
167 	int				Type, iField, iPart, nParts, *Parts, iPoint, nPoints, iOffset;
168 	double			*pZ	= NULL, *pM	= NULL;
169 	TSG_Point		*pPoint;
170 	CSG_Buffer		File_Header(100), Record_Header(8), Content;
171 	CSG_File		fSHP;
172 
173 	//-----------------------------------------------------
174 	// Open DBase File...
175 
176 	m_Encoding	= SG_FILE_ENCODING_ANSI;
177 
178 	if( fSHP.Open(SG_File_Make_Path("", File_Name, "cpg")) )
179 	{
180 		CSG_String	sLine;
181 
182 		if( fSHP.Read_Line(sLine) && sLine.Find("UTF-8") >= 0 )
183 		{
184 			m_Encoding	= SG_FILE_ENCODING_UTF8;
185 		}
186 
187 		fSHP.Close();
188 	}
189 
190 	CSG_Table_DBase	fDBF(m_Encoding);
191 
192 	if( !fDBF.Open_Read(SG_File_Make_Path("", File_Name, "dbf"), this, false) )
193 	{
194 		SG_UI_Msg_Add_Error(_TL("DBase file could not be opened."));
195 
196 		return( false );
197 	}
198 
199     fDBF.Move_First();
200 
201     if( fDBF.Get_Field_Count() < 1 )
202 	{
203 		SG_UI_Msg_Add_Error(_TL("DBase file does not contain any attribute fields."));
204 
205 		return( false );
206 	}
207 
208 	//-----------------------------------------------------
209 	// Open Shapes File...
210 
211 	if( !fSHP.Open(SG_File_Make_Path("", File_Name, "shp"), SG_FILE_R, true) )
212 	{
213 		SG_UI_Msg_Add_Error(_TL("Shape file could not be opened."));
214 
215 		return( false );
216 	}
217 
218 	//-----------------------------------------------------
219 	// Read File Header (100 Bytes)...
220 
221 	if( fSHP.Read(File_Header.Get_Data(), sizeof(char), 100) != 100 )
222 	{
223 		SG_UI_Msg_Add_Error(_TL("corrupted file header"));
224 
225 		return( false );
226 	}
227 
228 	if( File_Header.asInt( 0,  true) != 9994 )	// Byte 00 -> File Code 9994 (Integer Big)...
229 	{
230 		SG_UI_Msg_Add_Error(_TL("invalid file code"));
231 
232 		return( false );
233 	}
234 
235 	if( File_Header.asInt(28, false) != 1000 )	// Byte 28 -> Version 1000 (Integer Little)...
236 	{
237 		SG_UI_Msg_Add_Error(_TL("unsupported file version"));
238 
239 		return( false );
240 	}
241 
242 	switch( Type = File_Header.asInt(32) )	// Byte 32 -> Shape Type (Integer Little)...
243 	{
244 	case 1:		m_Type	= SHAPE_TYPE_Point;		m_Vertex_Type	= SG_VERTEX_TYPE_XY;	break;	// Point
245 	case 8:		m_Type	= SHAPE_TYPE_Points;	m_Vertex_Type	= SG_VERTEX_TYPE_XY;	break;	// MultiPoint
246 	case 3:		m_Type	= SHAPE_TYPE_Line;		m_Vertex_Type	= SG_VERTEX_TYPE_XY;	break;	// PolyLine
247 	case 5:		m_Type	= SHAPE_TYPE_Polygon;	m_Vertex_Type	= SG_VERTEX_TYPE_XY;	break;	// Polygon
248 
249 	case 11:	m_Type	= SHAPE_TYPE_Point;		m_Vertex_Type	= SG_VERTEX_TYPE_XYZM;	break;	// PointZ
250 	case 18:	m_Type	= SHAPE_TYPE_Points;	m_Vertex_Type	= SG_VERTEX_TYPE_XYZM;	break;	// MultiPointZ
251 	case 13:	m_Type	= SHAPE_TYPE_Line;		m_Vertex_Type	= SG_VERTEX_TYPE_XYZM;	break;	// PolyLineZ
252 	case 15:	m_Type	= SHAPE_TYPE_Polygon;	m_Vertex_Type	= SG_VERTEX_TYPE_XYZM;	break;	// PolygonZ
253 
254 	case 21:	m_Type	= SHAPE_TYPE_Point;		m_Vertex_Type	= SG_VERTEX_TYPE_XYZ;	break;	// PointM
255 	case 28:	m_Type	= SHAPE_TYPE_Points;	m_Vertex_Type	= SG_VERTEX_TYPE_XYZ;	break;	// MultiPointM
256 	case 23:	m_Type	= SHAPE_TYPE_Line;		m_Vertex_Type	= SG_VERTEX_TYPE_XYZ;	break;	// PolyLineM
257 	case 25:	m_Type	= SHAPE_TYPE_Polygon;	m_Vertex_Type	= SG_VERTEX_TYPE_XYZ;	break;	// PolygonM
258 
259 	default:	// unsupported...
260 	case 31:	// unsupported: MultiPatch...
261 		SG_UI_Msg_Add_Error(_TL("unsupported shape type."));
262 
263 		return( false );
264 	}
265 
266 	//-----------------------------------------------------
267 	// Load Shapes...
268 
269 	for(int iShape=0; iShape<fDBF.Get_Record_Count() && SG_UI_Process_Set_Progress(iShape, fDBF.Get_Record_Count()); iShape++)
270 	{
271 		if( fSHP.Read(Record_Header.Get_Data(0), sizeof(int), 2) != 2 )		// read record header
272 		{
273 			SG_UI_Msg_Add_Error(_TL("corrupted record header"));
274 
275 			return( false );
276 		}
277 
278 		if( Record_Header.asInt(0, true) != iShape + 1 )					// record number
279 		{
280 			SG_UI_Msg_Add_Error(CSG_String::Format("%s (%d != %d)", _TL("corrupted shapefile."), Record_Header.asInt(0, true), iShape + 1));
281 
282 			return( false );
283 		}
284 
285 		size_t	Length	= 2 * Record_Header.asInt(4, true);	// content length as 16-bit words !!!
286 
287 		if( !Content.Set_Size(Length, false) )
288 		{
289 			SG_UI_Msg_Add_Error(_TL("memory allocation error."));
290 
291 			return( false );
292 		}
293 
294 		if( fSHP.Read(Content.Get_Data(), sizeof(char), Length) != Length )
295 		{
296 			SG_UI_Msg_Add_Error(_TL("corrupted shapefile."));
297 
298 			return( false );
299 		}
300 
301 		if( fDBF.isDeleted() )
302 		{
303 			continue;	// nop
304 		}
305 
306 		if( Content.asInt(0) != Type )
307 		{
308 			if( Content.asInt(0) == 0 )
309 			{
310 				// null shape is allowed !!!
311 			}
312 			else
313 			{
314 				SG_UI_Msg_Add_Error(_TL("corrupted shapefile."));
315 
316 				return( false );
317 			}
318 		}
319 
320 		//-------------------------------------------------
321 		else
322 		{
323 			CSG_Shape	*pShape	= Add_Shape();
324 
325 			switch( m_Type )
326 			{
327 			default:	break;
328 
329 			//---------------------------------------------
330 			case SHAPE_TYPE_Point: ////////////////////////
331 
332 				pPoint	= (TSG_Point *)Content.Get_Data(4);
333 
334 				pShape->Add_Point(pPoint->x, pPoint->y);
335 
336 				switch( m_Vertex_Type )	// read Z + M
337 				{
338 				default:	break;
339 				case SG_VERTEX_TYPE_XYZM: pShape->Set_M(Content.asDouble(28), 0);
340 				case SG_VERTEX_TYPE_XYZ : pShape->Set_Z(Content.asDouble(20), 0);
341 				}
342 
343 				break;
344 
345 			//---------------------------------------------
346 			case SHAPE_TYPE_Points: ///////////////////////
347 
348 				nPoints	= Content.asInt(36);
349 				pPoint	= (TSG_Point *)Content.Get_Data(40);
350 
351 				switch( m_Vertex_Type )	// read Z + M
352 				{
353 				default:
354 					break;
355 
356 				case SG_VERTEX_TYPE_XYZ:
357 					pZ	= 56 + nPoints * 24 <= (int)Length ? (double *)Content.Get_Data(56 + nPoints * 16) : NULL;	// [40 + nPoints * 16 + 2 * 8] + [nPoints * 8]
358 					break;
359 
360 				case SG_VERTEX_TYPE_XYZM:
361 					pZ	= 56 + nPoints * 24 <= (int)Length ? (double *)Content.Get_Data(56 + nPoints * 16) : NULL;	// [40 + nPoints * 16 + 2 * 8] + [nPoints * 8]
362 					pM	= 72 + nPoints * 32 <= (int)Length ? (double *)Content.Get_Data(72 + nPoints * 24) : NULL;	// [40 + nPoints * 16 + 2 * 8] + [nPoints * 8 + 2 * 8] + [nPoints * 8]
363 					break;
364 				}
365 
366 				//-----------------------------------------
367 				for(iPoint=0; iPoint<nPoints; iPoint++, pPoint++)
368 				{
369 					pShape->Add_Point(pPoint->x, pPoint->y);
370 
371 					if( pZ )	{	pShape->Set_Z(*(pZ++), iPoint);	}
372 					if( pM )	{	pShape->Set_M(*(pM++), iPoint);	}
373 				}
374 
375 				break;
376 
377 			//---------------------------------------------
378 			case SHAPE_TYPE_Line   : //////////////////////
379 			case SHAPE_TYPE_Polygon: //////////////////////
380 
381 				nParts	= Content.asInt(36);
382 				nPoints	= Content.asInt(40);
383 				Parts	= (int       *)Content.Get_Data(44);
384 				pPoint	= (TSG_Point *)Content.Get_Data(44 + 4 * nParts);
385 
386 				switch( m_Vertex_Type )	// read Z + M
387 				{
388 				default:
389 					break;
390 
391 				case SG_VERTEX_TYPE_XYZ:
392 					pZ	= 60 + nParts * 4 + nPoints * 24 <= (int)Length ? (double *)Content.Get_Data(60 + nParts * 4 + nPoints * 16) : NULL;	// [44 + nParts * 4 + nPoints * 16 + 2 * 8] + [nPoints * 8]
393 					break;
394 
395 				case SG_VERTEX_TYPE_XYZM:
396 					pZ	= 60 + nParts * 4 + nPoints * 24 <= (int)Length ? (double *)Content.Get_Data(60 + nParts * 4 + nPoints * 16) : NULL;	// [44 + nParts * 4 + nPoints * 16 + 2 * 8] + [nPoints * 8]
397 					pM	= 76 + nParts * 4 + nPoints * 32 <= (int)Length ? (double *)Content.Get_Data(76 + nParts * 4 + nPoints * 24) : NULL;	// [44 + nParts * 4 + nPoints * 16 + 2 * 8] + [nPoints * 8 + 2 * 8] +  [nPoints * 8]
398 					break;
399 				}
400 
401 				//-----------------------------------------
402 				iOffset = 0;
403 
404 				for(iPoint=0, iPart=0; iPoint<nPoints; iPoint++, pPoint++)
405 				{
406 					if( iPart < nParts - 1 && iPoint >= Parts[iPart + 1] )
407 					{
408 						iPart++;
409 						iOffset = 0;
410 					}
411 
412 					pShape->Add_Point(pPoint->x, pPoint->y, iPart);
413 
414 					if( pZ )	{	pShape->Set_Z(*(pZ++), iOffset, iPart);	}
415 					if( pM )	{	pShape->Set_M(*(pM++), iOffset, iPart);	}
416 
417 					iOffset++;
418 				}
419 
420 				break;
421 			}
422 
423 			//---------------------------------------------
424 			for(iField=0; iField<Get_Field_Count(); iField++)
425 			{
426 				switch( fDBF.Get_Field_Type(iField) )
427 				{
428 				default:
429 					pShape->Set_Value(iField, fDBF.asString(iField));
430 					break;
431 
432 				case DBF_FT_FLOAT:
433 				case DBF_FT_NUMERIC:
434 					{
435 						double	Value;
436 
437 						if( fDBF.asDouble(iField, Value) )
438 						{
439 							pShape->Set_Value(iField, Value);
440 						}
441 						else
442 						{
443 							pShape->Set_NoData(iField);
444 						}
445 					}
446 					break;
447 				}
448 			}
449 		}
450 
451 		fDBF.Move_Next();
452 	}
453 
454 	//-----------------------------------------------------
455 	Get_Projection().Load(SG_File_Make_Path("", File_Name, "prj"), SG_PROJ_FMT_WKT);
456 
457 	//-----------------------------------------------------
458 	Load_MetaData(File_Name);
459 
460 	CSG_MetaData	*pFields	= Get_MetaData_DB().Get_Child("FIELDS");
461 
462 	if( pFields && pFields->Get_Children_Count() == Get_Field_Count() )
463 	{
464 		for(iField=0; iField<Get_Field_Count(); iField++)
465 		{
466 			Set_Field_Name(iField, pFields->Get_Content(iField));
467 		}
468 	}
469 
470 	Set_File_Name(File_Name, true);
471 
472 	//-----------------------------------------------------
473 	return( true );
474 }
475 
476 
477 ///////////////////////////////////////////////////////////
478 //														 //
479 //														 //
480 //														 //
481 ///////////////////////////////////////////////////////////
482 
483 //---------------------------------------------------------
484 #define Set_Content_Length(n)	Record_Header.Set_Value(4, (int)(n), true);\
485 	fSHP.Write(Record_Header.Get_Data(), sizeof(int), 2);\
486 	fSHX.Write_Int(fSHP_Size, true);\
487 	fSHX.Write_Int((n)      , true);\
488 	fSHP_Size	+= 4 + (n);\
489 	fSHX_Size	+= 4;
490 
491 //---------------------------------------------------------
_Save_ESRI(const CSG_String & File_Name)492 bool CSG_Shapes::_Save_ESRI(const CSG_String &File_Name)
493 {
494 	int			Type, fSHP_Size, fSHX_Size, iField, iPart, iPoint, nPoints;
495 	TSG_Point	Point;
496 	CSG_Buffer	File_Header(100), Record_Header(8), Content;
497 	CSG_File	fSHP, fSHX;
498 
499 	//-----------------------------------------------------
500 	// Determine Shape Type...
501 
502 	switch( m_Type )
503 	{
504 	case SHAPE_TYPE_Point   : Type  =  1; break;
505 	case SHAPE_TYPE_Points  : Type  =  8; break;
506 	case SHAPE_TYPE_Line    : Type  =  3; break;
507 	case SHAPE_TYPE_Polygon : Type  =  5; break;
508 	default:	return( false );
509 	}
510 
511 	TSG_Vertex_Type	Vertex_Type	= m_Vertex_Type == 0 ? SG_VERTEX_TYPE_XY : SG_VERTEX_TYPE_XYZM;
512 
513 	switch( Vertex_Type )
514 	{
515 	case SG_VERTEX_TYPE_XY  :             break;
516 	case SG_VERTEX_TYPE_XYZ : Type += 20; break;	// M
517 	case SG_VERTEX_TYPE_XYZM: Type += 10; break;	// Z (+M)
518 	default:	return( false );
519 	}
520 
521 	//-----------------------------------------------------
522 	// DBase File Access...
523 
524 	SG_File_Delete(SG_File_Make_Path("", File_Name, "cpg"));
525 
526 	if( m_Encoding == SG_FILE_ENCODING_UTF8 )
527 	{
528 		if( fSHP.Open(SG_File_Make_Path("", File_Name, "cpg"), SG_FILE_W, false) )
529 		{
530 			fSHP.Printf("UTF-8\n");
531 
532 			fSHP.Close();
533 		}
534 	}
535 
536 	CSG_Table_DBase	fDBF(m_Encoding);
537 
538 	if( !fDBF.Open_Write(SG_File_Make_Path("", File_Name, "dbf"), this, false) )
539 	{
540 		return( false );
541 	}
542 
543 	//-----------------------------------------------------
544 	// Shape File Access...
545 
546 	if( !fSHX.Open(SG_File_Make_Path("", File_Name, "shx"), SG_FILE_W, true) )
547 	{
548 		SG_UI_Msg_Add_Error(_TL("index file could not be opened"));
549 
550 		return( false );
551 	}
552 
553 	if( !fSHP.Open(SG_File_Make_Path("", File_Name, "shp"), SG_FILE_W, true) )
554 	{
555 		SG_UI_Msg_Add_Error(_TL("shape file could not be opened."));
556 
557 		return( false );
558 	}
559 
560 	//-----------------------------------------------------
561 	// Save Header...
562 
563 	Make_Clean();	// polygons: first == last point, inner rings > anti-clockwise...
564 
565 	Update();
566 
567 	File_Header.Set_Value( 0, 9994					, true );	// Byte  0  Integer Big     File Code = 9994
568 	File_Header.Set_Value( 4, 0						, true );	// Byte  4  Integer Big     unused
569 	File_Header.Set_Value( 8, 0						, true );	// Byte  8  Integer Big     unused
570 	File_Header.Set_Value(12, 0						, true );	// Byte 12  Integer Big     unused
571 	File_Header.Set_Value(16, 0						, true );	// Byte 16  Integer Big     unused
572 	File_Header.Set_Value(20, 0						, true );	// Byte 20  Integer Big     unused
573 	File_Header.Set_Value(24, 0						, true );	// Byte 24  Integer Big     File Length
574 	File_Header.Set_Value(28, 1000					, false);	// Byte 28  Integer Little  Version   = 1000
575 	File_Header.Set_Value(32, Type					, false);	// Byte 32  Integer Little  Shape Type
576 	File_Header.Set_Value(36, m_Extent.Get_XMin()	, false);	// Byte 36  Double  Little  Bounding Box Xmin
577 	File_Header.Set_Value(44, m_Extent.Get_YMin()	, false);	// Byte 44  Double  Little  Bounding Box Ymin
578 	File_Header.Set_Value(52, m_Extent.Get_XMax()	, false);	// Byte 52  Double  Little  Bounding Box Xmax
579 	File_Header.Set_Value(60, m_Extent.Get_YMax()	, false);	// Byte 60  Double  Little  Bounding Box Ymax
580 	File_Header.Set_Value(68,          Get_ZMin()	, false);	// Byte 68  Double  Little  Bounding Box Zmin
581 	File_Header.Set_Value(76,          Get_ZMax()	, false);	// Byte 76  Double  Little  Bounding Box Zmax
582 	File_Header.Set_Value(84,          Get_MMin()	, false);	// Byte 84  Double  Little  Bounding Box Mmin
583 	File_Header.Set_Value(92,          Get_MMax()	, false);	// Byte 92  Double  Little  Bounding Box Mmax
584 
585 	fSHP.Write(File_Header.Get_Data(), sizeof(char), 100);
586 	fSHX.Write(File_Header.Get_Data(), sizeof(char), 100);
587 
588 	fSHP_Size	= 50;	// file size measured in 16-bit words...
589 	fSHX_Size	= 50;	// file size measured in 16-bit words...
590 
591 	//-----------------------------------------------------
592 	// Save Shapes...
593 
594 	for(int iShape=0; iShape<Get_Count() && SG_UI_Process_Set_Progress(iShape, Get_Count()); iShape++)
595 	{
596 		CSG_Shape	*pShape	= Get_Shape(iShape);
597 
598 		//-------------------------------------------------
599 		// geometries...
600 
601 		Record_Header.Set_Value(0, iShape + 1, true);	// record number
602 
603 		for(iPart=0, nPoints=0; iPart<pShape->Get_Part_Count(); iPart++)
604 		{
605 			nPoints	+= pShape->Get_Point_Count(iPart);	// total number of points in shape
606 		}
607 
608 		//-------------------------------------------------
609 		switch( m_Type )			// write content header
610 		{
611 		default:	break;
612 
613 		//-------------------------------------------------
614 		case SHAPE_TYPE_Point:		///////////////////////
615 
616 			switch( Vertex_Type )
617 			{
618 			case SG_VERTEX_TYPE_XY:		Set_Content_Length(10);	break;
619 			case SG_VERTEX_TYPE_XYZ:	Set_Content_Length(14);	break;
620 			case SG_VERTEX_TYPE_XYZM:	Set_Content_Length(18);	break;
621 			}
622 
623 			fSHP.Write_Int		(Type);
624 
625 			break;
626 
627 		//-------------------------------------------------
628 		case SHAPE_TYPE_Points:		///////////////////////
629 
630 			switch( Vertex_Type )
631 			{
632 			case SG_VERTEX_TYPE_XY:		Set_Content_Length(20 +  8 * nPoints);	break;
633 			case SG_VERTEX_TYPE_XYZ:	Set_Content_Length(28 + 12 * nPoints);	break;
634 			case SG_VERTEX_TYPE_XYZM:	Set_Content_Length(36 + 16 * nPoints);	break;
635 			}
636 
637 			fSHP.Write_Int		(Type);
638 			fSHP.Write_Double	(pShape->Get_Extent().Get_XMin());
639 			fSHP.Write_Double	(pShape->Get_Extent().Get_YMin());
640 			fSHP.Write_Double	(pShape->Get_Extent().Get_XMax());
641 			fSHP.Write_Double	(pShape->Get_Extent().Get_YMax());
642 			fSHP.Write_Int		(nPoints);
643 
644 			break;
645 
646 		//-------------------------------------------------
647 		case SHAPE_TYPE_Line:		///////////////////////
648 		case SHAPE_TYPE_Polygon:	///////////////////////
649 
650 			switch( Vertex_Type )
651 			{
652 			case SG_VERTEX_TYPE_XY:		Set_Content_Length(22 + 2 * pShape->Get_Part_Count() +  8 * nPoints);	break;
653 			case SG_VERTEX_TYPE_XYZ:	Set_Content_Length(30 + 2 * pShape->Get_Part_Count() + 12 * nPoints);	break;
654 			case SG_VERTEX_TYPE_XYZM:	Set_Content_Length(38 + 2 * pShape->Get_Part_Count() + 16 * nPoints);	break;
655 			}
656 
657 			fSHP.Write_Int		(Type);
658 			fSHP.Write_Double	(pShape->Get_Extent().Get_XMin());
659 			fSHP.Write_Double	(pShape->Get_Extent().Get_YMin());
660 			fSHP.Write_Double	(pShape->Get_Extent().Get_XMax());
661 			fSHP.Write_Double	(pShape->Get_Extent().Get_YMax());
662 			fSHP.Write_Int		(pShape->Get_Part_Count());
663 			fSHP.Write_Int		(nPoints);
664 
665 			for(iPart=0, iPoint=0; iPart<pShape->Get_Part_Count(); iPoint+=pShape->Get_Point_Count(iPart++))
666 			{
667 				fSHP.Write_Int(iPoint);
668 			}
669 
670 			break;
671 		}
672 
673 		//-------------------------------------------------
674 		switch( m_Type )			// write point data
675 		{
676 		default:	break;
677 
678 		//-------------------------------------------------
679 		case SHAPE_TYPE_Point:		///////////////////////
680 
681 			fSHP.Write(&(Point = pShape->Get_Point(0)), sizeof(TSG_Point));
682 
683 			//---------------------------------------------
684 			if( Vertex_Type != SG_VERTEX_TYPE_XY )
685 			{
686 				fSHP.Write_Double(pShape->Get_Z(0));
687 
688 				if( Vertex_Type == SG_VERTEX_TYPE_XYZM )
689 				{
690 					fSHP.Write_Double(pShape->Get_M(0));
691 				}
692 			}
693 
694 			break;
695 
696 		//-------------------------------------------------
697 		case SHAPE_TYPE_Points:		///////////////////////
698 		case SHAPE_TYPE_Line:		///////////////////////
699 		case SHAPE_TYPE_Polygon:	///////////////////////
700 
701 			for(iPart=0; iPart<pShape->Get_Part_Count(); iPart++)
702 			{
703 				for(iPoint=0; iPoint<pShape->Get_Point_Count(iPart); iPoint++)
704 				{
705 					fSHP.Write(&(Point = pShape->Get_Point(iPoint, iPart)), sizeof(TSG_Point));
706 				}
707 			}
708 
709 			//---------------------------------------------
710 			if( Vertex_Type != SG_VERTEX_TYPE_XY )
711 			{
712 				fSHP.Write_Double(pShape->Get_ZMin());
713 				fSHP.Write_Double(pShape->Get_ZMax());
714 
715 				for(iPart=0; iPart<pShape->Get_Part_Count(); iPart++)
716 				{
717 					for(iPoint=0; iPoint<pShape->Get_Point_Count(iPart); iPoint++)
718 					{
719 						fSHP.Write_Double(pShape->Get_Z(iPoint, iPart));
720 					}
721 				}
722 
723 				if( Vertex_Type == SG_VERTEX_TYPE_XYZM )
724 				{
725 					fSHP.Write_Double(pShape->Get_MMin());
726 					fSHP.Write_Double(pShape->Get_MMax());
727 
728 					for(iPart=0; iPart<pShape->Get_Part_Count(); iPart++)
729 					{
730 						for(iPoint=0; iPoint<pShape->Get_Point_Count(iPart); iPoint++)
731 						{
732 							fSHP.Write_Double(pShape->Get_M(iPoint, iPart));
733 						}
734 					}
735 				}
736 			}
737 		}
738 
739 		//-------------------------------------------------
740 		// attributes...
741 
742 		fDBF.Add_Record();
743 
744 		for(iField=0; iField<Get_Field_Count(); iField++)
745 		{
746 			if( pShape->is_NoData(iField) )
747 			{
748 				fDBF.Set_NoData(iField);
749 			}
750 			else switch( fDBF.Get_Field_Type(iField) )
751 			{
752 			default:
753 				fDBF.Set_Value(iField, pShape->asString(iField));
754 				break;
755 
756 			case DBF_FT_FLOAT:
757 			case DBF_FT_NUMERIC:
758 				fDBF.Set_Value(iField, pShape->asDouble(iField));
759 				break;
760 			}
761 		}
762 
763 		fDBF.Flush_Record();
764 	}
765 
766 	//-----------------------------------------------------
767 	// File Sizes...
768 
769 	fSHP.Seek(24);
770 	fSHP.Write_Int(fSHP_Size, true);
771 
772 	fSHX.Seek(24);
773 	fSHX.Write_Int(fSHX_Size, true);
774 
775 	//-----------------------------------------------------
776 	Get_Projection().Save(SG_File_Make_Path("", File_Name, "prj"), SG_PROJ_FMT_WKT);
777 
778 	//-----------------------------------------------------
779 	CSG_MetaData	*pFields	= Get_MetaData_DB().Get_Child("FIELDS");
780 
781 	if( !pFields )
782 	{
783 		pFields	= Get_MetaData_DB().Add_Child("FIELDS");
784 	}
785 
786 	pFields->Del_Children();
787 
788 	for(iField=0; iField<Get_Field_Count(); iField++)
789 	{
790 		pFields->Add_Child("FIELD", Get_Field_Name(iField))->Add_Property("TYPE", gSG_Data_Type_Identifier[Get_Field_Type(iField)]);
791 	}
792 
793 	Get_MetaData().Del_Child("GDAL_DRIVER");
794 
795 	Save_MetaData(File_Name);
796 
797 	//-----------------------------------------------------
798 	return( true );
799 }
800 
801 
802 ///////////////////////////////////////////////////////////
803 //														 //
804 //														 //
805 //														 //
806 ///////////////////////////////////////////////////////////
807 
808 //---------------------------------------------------------
809