├── GIS.HPU.ZYZ.SHP
├── DBF
│ ├── DbfColumn.cs
│ ├── DbfDataTruncateException.cs
│ ├── DbfFile.cs
│ ├── DbfHeader.cs
│ └── DbfRecord.cs
├── GIS.HPU.ZYZ.SHP.csproj
├── Properties
│ └── AssemblyInfo.cs
├── SHP
│ ├── ShpData.cs
│ ├── ShpDataType.cs
│ ├── ShpFile.cs
│ ├── ShpHeader.cs
│ └── ShpRecord.cs
├── SHX
│ ├── ShxData.cs
│ └── ShxRecord.cs
├── Util
│ ├── BorderUtil.cs
│ ├── ByteTransUtil.cs
│ └── CoordTransform.cs
├── bin
│ └── Debug
│ │ ├── GIS.HPU.ZYZ.SHP.dll
│ │ ├── GIS.HPU.ZYZ.SHP.pdb
│ │ ├── GIS.HPU.ZYZ.SHP.vshost.exe
│ │ ├── GIS.HPU.ZYZ.SHP.vshost.exe.manifest
│ │ ├── ProjNet.dll
│ │ └── project
│ │ ├── abers.prj
│ │ └── geopro.prj
└── obj
│ └── x86
│ └── Debug
│ ├── DesignTimeResolveAssemblyReferencesInput.cache
│ ├── GIS.HPU.ZYZ.SHP.csproj.FileListAbsolute.txt
│ ├── GIS.HPU.ZYZ.SHP.csprojResolveAssemblyReference.cache
│ ├── GIS.HPU.ZYZ.SHP.dll
│ └── GIS.HPU.ZYZ.SHP.pdb
├── README.md
├── SHPReaderCSharp.sln
├── SHPReaderCSharp.suo
└── SHPTest
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── SHPTest.csproj
├── app.config
├── bin
└── Debug
│ ├── GIS.HPU.ZYZ.SHP.dll
│ ├── GIS.HPU.ZYZ.SHP.pdb
│ ├── ProjNet.dll
│ ├── SHPTest.exe
│ ├── SHPTest.exe.config
│ ├── SHPTest.pdb
│ ├── SHPTest.vshost.exe
│ ├── SHPTest.vshost.exe.config
│ ├── SHPTest.vshost.exe.manifest
│ └── project
│ ├── abers.prj
│ └── geopro.prj
└── obj
└── x86
└── Debug
├── DesignTimeResolveAssemblyReferences.cache
├── DesignTimeResolveAssemblyReferencesInput.cache
├── SHPTest.csproj.FileListAbsolute.txt
├── SHPTest.csprojResolveAssemblyReference.cache
├── SHPTest.exe
└── SHPTest.pdb
/GIS.HPU.ZYZ.SHP/DBF/DbfColumn.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:Add type 'F' for DbfColumnType. After ARCGIS10.0 the dbf file add.
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:Ahmed Lacevic
9 | /// Date:12/1/2007
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 |
16 | namespace GIS.HPU.ZYZ.SHP.DBF
17 | {
18 | ///
19 | /// This class represents a DBF Column.
20 | ///
21 | ///
22 | ///
23 | /// Note that certain properties can not be modified after creation of the object.
24 | /// This is because we are locking the header object after creation of a data row,
25 | /// and columns are part of the header so either we have to have a lock field for each column,
26 | /// or make it so that certain properties such as length can only be set during creation of a column.
27 | /// Otherwise a user of this object could modify a column that belongs to a locked header and thus corrupt the DBF file.
28 | ///
29 | public class DbfColumn : ICloneable
30 | {
31 | /*
32 | (FoxPro/FoxBase) Double integer *NOT* a memo field
33 | G General (dBASE V: like Memo) OLE Objects in MS Windows versions
34 | P Picture (FoxPro) Like Memo fields, but not for text processing.
35 | Y Currency (FoxPro)
36 | T DateTime (FoxPro)
37 | I Integer Length: 4 byte little endian integer (FoxPro)
38 | */
39 | ///
40 | /// Great information on DBF located here:
41 | /// http://www.clicketyclick.dk/databases/xbase/format/data_types.html
42 | /// http://www.clicketyclick.dk/databases/xbase/format/dbf.html
43 | ///
44 | public enum DbfColumnType
45 | {
46 |
47 | ///
48 | /// Character less than 254 length
49 | /// ASCII text less than 254 characters long in dBASE.
50 | ///
51 | /// Character fields can be up to 32 KB long (in Clipper and FoxPro) using decimal
52 | /// count as high byte in field length. It's possible to use up to 64KB long fields
53 | /// by reading length as unsigned.
54 | ///
55 | ///
56 | Character = 0,
57 |
58 | ///
59 | /// Number Length: less than 18
60 | /// ASCII text up till 18 characters long (include sign and decimal point).
61 | ///
62 | /// Valid characters:
63 | /// "0" - "9" and "-". Number fields can be up to 20 characters long in FoxPro and Clipper.
64 | ///
65 | ///
66 | /// We are not enforcing this 18 char limit.
67 | ///
68 | Number = 1,
69 |
70 | ///
71 | /// L Logical Length: 1 Boolean/byte (8 bit)
72 | ///
73 | /// Legal values:
74 | /// ? Not initialised (default)
75 | /// Y,y Yes
76 | /// N,n No
77 | /// F,f False
78 | /// T,t True
79 | /// Logical fields are always displayed using T/F/?. Some sources claims
80 | /// that space (ASCII 20h) is valid for not initialised. Space may occur, but is not defined.
81 | ///
82 | Boolean = 2,
83 |
84 | ///
85 | /// D Date Length: 8 Date in format YYYYMMDD. A date like 0000-00- 00 is *NOT* valid.
86 | ///
87 | Date = 3,
88 |
89 | ///
90 | /// M Memo Length: 10 Pointer to ASCII text field in memo file 10 digits representing a pointer to a DBT block (default is blanks).
91 | ///
92 | Memo = 4,
93 |
94 | ///
95 | /// B Binary (dBASE V) Like Memo fields, but not for text processing.
96 | ///
97 | Binary = 5,
98 |
99 | ///
100 | /// I Integer Length: 4 byte little endian integer (FoxPro)
101 | ///
102 | Integer = 6,
103 |
104 | }
105 | ///
106 | /// Column (field) name
107 | ///
108 | private string mName;
109 | ///
110 | /// Field Type (Char, number, boolean, date, memo, binary)
111 | ///
112 | private DbfColumnType mType;
113 | ///
114 | /// Offset from the start of the record
115 | ///
116 | internal int mDataAddress;
117 | ///
118 | /// Length of the data in bytes; some rules apply which are in the spec (read more above).
119 | ///
120 | private int mLength;
121 | ///
122 | /// Decimal precision count, or number of digits afer decimal point. This applies to Number types only.
123 | ///
124 | private int mDecimalCount;
125 |
126 | #region
127 | ///
128 | /// Field Name.
129 | ///
130 | public string Name
131 | {
132 | get
133 | {
134 | return mName;
135 | }
136 | set
137 | {
138 | //name:
139 | if (string.IsNullOrEmpty(value))
140 | throw new Exception("Field names must be at least one char long and can not be null.");
141 | if (value.Length > 11)
142 | throw new Exception("Field names can not be longer than 11 chars.");
143 | mName = value;
144 | }
145 | }
146 | ///
147 | /// Field Type (C N L D or M).
148 | /// 新加的F按N算
149 | ///
150 | public DbfColumnType ColumnType
151 | {
152 | get
153 | {
154 | return mType;
155 | }
156 | }
157 | ///
158 | /// Returns column type as a char, (as written in the DBF column header)
159 | /// N=number, C=char, B=binary, L=boolean, D=date, I=integer, M=memo
160 | ///
161 | public char ColumnTypeChar
162 | {
163 | get
164 | {
165 | switch (mType)
166 | {
167 | case DbfColumnType.Number:
168 | return 'N';
169 |
170 | case DbfColumnType.Character:
171 | return 'C';
172 |
173 | case DbfColumnType.Binary:
174 | return 'B';
175 |
176 | case DbfColumnType.Boolean:
177 | return 'L';
178 |
179 | case DbfColumnType.Date:
180 | return 'D';
181 |
182 | case DbfColumnType.Integer:
183 | return 'I';
184 |
185 | case DbfColumnType.Memo:
186 | return 'M';
187 | }
188 | throw new Exception("Unrecognized field type!");
189 | }
190 | }
191 | ///
192 | /// Field Data Address offset from the start of the record.
193 | ///
194 | public int DataAddress
195 | {
196 | get
197 | {
198 | return mDataAddress;
199 | }
200 | }
201 | ///
202 | /// Length of the data in bytes.
203 | ///
204 | public int Length
205 | {
206 | get
207 | {
208 | return mLength;
209 | }
210 | }
211 | ///
212 | /// Field decimal count in Binary, indicating where the decimal is.
213 | ///
214 | public int DecimalCount
215 | {
216 | get
217 | {
218 | return mDecimalCount;
219 | }
220 | }
221 | #endregion
222 |
223 | ///
224 | /// Full spec constructor sets all relevant fields.
225 | ///
226 | ///
227 | ///
228 | ///
229 | ///
230 | public DbfColumn(string sName, DbfColumnType type, int nLength, int nDecimals)
231 | {
232 | Name = sName;
233 | mType = type;
234 | mLength = nLength;
235 |
236 | if (type == DbfColumnType.Number)
237 | mDecimalCount = nDecimals;
238 | else
239 | mDecimalCount = 0;
240 |
241 | //perform some simple integrity checks...
242 | //-------------------------------------------
243 |
244 | //decimal precision:
245 | //we could also fix the length property with a statement like this: mLength = mDecimalCount + 2;
246 | if (mDecimalCount > 0 && mLength - mDecimalCount <= 1)
247 | throw new Exception("Decimal precision can not be larger than the length of the field.");
248 |
249 | if (mType == DbfColumnType.Integer)
250 | mLength = 4;
251 |
252 | if (mType == DbfColumnType.Binary)
253 | mLength = 1;
254 |
255 | if (mType == DbfColumnType.Date)
256 | mLength = 8; //Dates are exactly yyyyMMdd
257 |
258 | if (mType == DbfColumnType.Memo)
259 | mLength = 10; //Length: 10 Pointer to ASCII text field in memo file. pointer to a DBT block.
260 |
261 | if (mType == DbfColumnType.Boolean)
262 | mLength = 1;
263 |
264 | //field length:
265 | if (mLength <= 0)
266 | throw new Exception("Invalid field length specified. Field length can not be zero or less than zero.");
267 | else if (type != DbfColumnType.Character && type != DbfColumnType.Binary && mLength > 255)
268 | throw new Exception("Invalid field length specified. For numbers it should be within 20 digits, but we allow up to 255. For Char and binary types, length up to 65,535 is allowed. For maximum compatibility use up to 255.");
269 | else if ((type == DbfColumnType.Character || type == DbfColumnType.Binary) && mLength > 65535)
270 | throw new Exception("Invalid field length specified. For Char and binary types, length up to 65535 is supported. For maximum compatibility use up to 255.");
271 | }
272 | ///
273 | /// Create a new column fully specifying all properties.
274 | ///
275 | /// column name
276 | /// type of field
277 | /// field length including decimal places and decimal point if any
278 | /// decimal places
279 | /// offset from start of record
280 | internal DbfColumn(string sName, DbfColumnType type, int nLength, int nDecimals, int nDataAddress)
281 | : this(sName, type, nLength, nDecimals)
282 | {
283 | mDataAddress = nDataAddress;
284 | }
285 | public DbfColumn(string sName, DbfColumnType type)
286 | : this(sName, type, 0, 0)
287 | {
288 | if (type == DbfColumnType.Number || type == DbfColumnType.Character)
289 | throw new Exception("For number and character field types you must specify Length and Decimal Precision.");
290 |
291 | }
292 |
293 | ///
294 | /// Returns corresponding dbf field type given a .net Type.
295 | ///
296 | ///
297 | ///
298 | public static DbfColumnType GetDbaseType(Type type)
299 | {
300 | if (type == typeof(string))
301 | return DbfColumnType.Character;
302 | else if (type == typeof(double) || type == typeof(float))
303 | return DbfColumnType.Number;
304 | else if (type == typeof(bool))
305 | return DbfColumnType.Boolean;
306 | else if (type == typeof(DateTime))
307 | return DbfColumnType.Date;
308 |
309 | throw new NotSupportedException(String.Format("{0} does not have a corresponding dbase type.", type.Name));
310 |
311 | }
312 |
313 | ///
314 | /// 通过字节编码值获取类型
315 | /// 新加的F按N算
316 | ///
317 | ///
318 | ///
319 | public static DbfColumnType GetDbaseType(char c)
320 | {
321 | switch (c.ToString().ToUpper())
322 | {
323 | case "C": return DbfColumnType.Character;
324 | case "N": return DbfColumnType.Number;
325 | case "F": return DbfColumnType.Number;
326 | case "B": return DbfColumnType.Binary;
327 | case "L": return DbfColumnType.Boolean;
328 | case "D": return DbfColumnType.Date;
329 | case "I": return DbfColumnType.Integer;
330 | case "M": return DbfColumnType.Memo;
331 | }
332 |
333 | throw new NotSupportedException(String.Format("{0} does not have a corresponding dbase type.", c));
334 |
335 | }
336 |
337 | ///
338 | /// Returns shp file Shape Field.
339 | ///
340 | ///
341 | public static DbfColumn ShapeField()
342 | {
343 | return new DbfColumn("Geometry", DbfColumnType.Binary);
344 | }
345 |
346 | ///
347 | /// Returns Shp file ID field.
348 | ///
349 | ///
350 | public static DbfColumn IdField()
351 | {
352 | return new DbfColumn("Row", DbfColumnType.Integer);
353 | }
354 |
355 | public object Clone()
356 | {
357 | return this.MemberwiseClone();
358 | }
359 | }
360 | }
361 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/DBF/DbfDataTruncateException.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:Add type 'F' for DbfColumnType. After ARCGIS10.0 the dbf file add.
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:Ahmed Lacevic
9 | /// Date:12/1/2007
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.Runtime.Serialization;
16 |
17 | namespace GIS.HPU.ZYZ.SHP.DBF
18 | {
19 | public class DbfDataTruncateException : Exception
20 | {
21 |
22 | public DbfDataTruncateException(string smessage)
23 | : base(smessage)
24 | {
25 | }
26 |
27 | public DbfDataTruncateException(string smessage, Exception innerException)
28 | : base(smessage, innerException)
29 | {
30 | }
31 |
32 | public DbfDataTruncateException(SerializationInfo info, StreamingContext context)
33 | : base(info, context)
34 | {
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/DBF/DbfFile.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:Add type 'F' for DbfColumnType. After ARCGIS10.0 the dbf file add.
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:Ahmed Lacevic
9 | /// Date:12/1/2007
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.IO;
16 |
17 | namespace GIS.HPU.ZYZ.SHP.DBF
18 | {
19 | ///
20 | /// This class represents a DBF file. You can create new, open, update and save DBF files using this class and supporting classes.
21 | /// Also, this class supports reading/writing from/to an internet forward only type of stream!
22 | ///
23 | ///
24 | /// TODO: add end of file byte '0x1A' !!!
25 | /// We don't relly on that byte at all, and everything works with or without that byte, but it should be there by spec.
26 | ///
27 | public class DbfFile
28 | {
29 | ///
30 | /// Helps read/write dbf file header information.
31 | ///
32 | protected DbfHeader mHeader;
33 | ///
34 | /// flag that indicates whether the header was written or not...
35 | ///
36 | protected bool mHeaderWritten = false;
37 | ///
38 | /// Streams to read and write to the DBF file.
39 | ///
40 | protected Stream mDbfFile = null;
41 | ///
42 | /// 读取流
43 | ///
44 | protected BinaryReader mDbfFileReader = null;
45 | ///
46 | /// 写入流
47 | ///
48 | protected BinaryWriter mDbfFileWriter = null;
49 | ///
50 | /// 编码
51 | ///
52 | private Encoding encoding = Encoding.ASCII;
53 | ///
54 | /// File that was opened, if one was opened at all.
55 | ///
56 | protected string mFileName = "";
57 | ///
58 | /// Number of records read using ReadNext() methods only. This applies only when we are using a forward-only stream.
59 | /// mRecordsReadCount is used to keep track of record index. With a seek enabled stream,
60 | /// we can always calculate index using stream position.
61 | ///
62 | protected int mRecordsReadCount = 0;
63 | ///
64 | /// keep these values handy so we don't call functions on every read.
65 | ///
66 | protected bool mIsForwardOnly = false;
67 | protected bool mIsReadOnly = false;
68 |
69 | #region
70 | ///
71 | /// Access DBF header with information on columns. Use this object for faster access to header.
72 | /// Remove one layer of function calls by saving header reference and using it directly to access columns.
73 | ///
74 | public DbfHeader Header
75 | {
76 | get
77 | {
78 | return mHeader;
79 | }
80 | }
81 | ///
82 | /// Returns true if we can not write to the DBF file stream.
83 | ///
84 | public bool IsReadOnly
85 | {
86 | get
87 | {
88 | return mIsReadOnly;
89 | }
90 | }
91 | ///
92 | /// Returns true if we can not seek to different locations within the file, such as internet connections.
93 | ///
94 | public bool IsForwardOnly
95 | {
96 | get
97 | {
98 | return mIsForwardOnly;
99 | }
100 | }
101 | ///
102 | /// Returns the name of the filestream.
103 | ///
104 | public string FileName
105 | {
106 | get
107 | {
108 | return mFileName;
109 | }
110 | }
111 | #endregion
112 |
113 | [Obsolete]
114 | public DbfFile()
115 | : this(Encoding.ASCII)
116 | {
117 | }
118 |
119 | public DbfFile(Encoding encoding)
120 | {
121 | this.encoding = encoding;
122 | mHeader = new DbfHeader(encoding);
123 | }
124 |
125 | ///
126 | /// Open a DBF from a FileStream. This can be a file or an internet connection stream. Make sure that it is positioned at start of DBF file.
127 | /// Reading a DBF over the internet we can not determine size of the file, so we support HasMore(), ReadNext() interface.
128 | /// RecordCount information in header can not be trusted always, since some packages store 0 there.
129 | ///
130 | ///
131 | public void Open(Stream ofs)
132 | {
133 | if (mDbfFile != null)
134 | Close();
135 |
136 | mDbfFile = ofs;
137 | mDbfFileReader = null;
138 | mDbfFileWriter = null;
139 |
140 | if (mDbfFile.CanRead)
141 | mDbfFileReader = new BinaryReader(mDbfFile, encoding);
142 |
143 | if (mDbfFile.CanWrite)
144 | mDbfFileWriter = new BinaryWriter(mDbfFile, encoding);
145 |
146 | //reset position
147 | mRecordsReadCount = 0;
148 |
149 | //assume header is not written
150 | mHeaderWritten = false;
151 |
152 | //read the header
153 | if (ofs.CanRead)
154 | {
155 | //try to read the header...
156 | try
157 | {
158 | mHeader.Read(mDbfFileReader);
159 | mHeaderWritten = true;
160 |
161 | }
162 | catch (EndOfStreamException)
163 | {
164 | //could not read header, file is empty
165 | mHeader = new DbfHeader(encoding);
166 | mHeaderWritten = false;
167 | }
168 | }
169 | if (mDbfFile != null)
170 | {
171 | mIsReadOnly = !mDbfFile.CanWrite;
172 | mIsForwardOnly = !mDbfFile.CanSeek;
173 | }
174 | }
175 | ///
176 | /// Open a DBF file or create a new one.
177 | ///
178 | /// Full path to the file.
179 | ///
180 | public void Open(string sPath, FileMode mode, FileAccess access, FileShare share)
181 | {
182 | mFileName = sPath;
183 | Open(File.Open(sPath, mode, access, share));
184 | }
185 | ///
186 | /// Open a DBF file or create a new one.
187 | ///
188 | /// Full path to the file.
189 | ///
190 | public void Open(string sPath, FileMode mode, FileAccess access)
191 | {
192 | mFileName = sPath;
193 | Open(File.Open(sPath, mode, access));
194 | }
195 | ///
196 | /// Open a DBF file or create a new one.
197 | ///
198 | /// Full path to the file.
199 | ///
200 | public void Open(string sPath, FileMode mode)
201 | {
202 | mFileName = sPath;
203 | Open(File.Open(sPath, mode));
204 | }
205 |
206 | ///
207 | /// Creates a new DBF 4 file. Overwrites if file exists! Use Open() function for more options.
208 | ///
209 | ///
210 | public void Create(string sPath)
211 | {
212 | Open(sPath, FileMode.Create, FileAccess.ReadWrite);
213 | mHeaderWritten = false;
214 | }
215 | ///
216 | /// Update header info, flush buffers and close streams. You should always call this method when you are done with a DBF file.
217 | ///
218 | public void Close()
219 | {
220 | //try to update the header if it has changed
221 | //------------------------------------------
222 | if (mHeader.IsDirty)
223 | WriteHeader();
224 | //Empty header...
225 | //--------------------------------
226 | mHeader = new DbfHeader(encoding);
227 | mHeaderWritten = false;
228 |
229 | //reset current record index
230 | //--------------------------------
231 | mRecordsReadCount = 0;
232 |
233 | //Close streams...
234 | //--------------------------------
235 | if (mDbfFileWriter != null)
236 | {
237 | mDbfFileWriter.Flush();
238 | mDbfFileWriter.Close();
239 | }
240 |
241 | if (mDbfFileReader != null)
242 | mDbfFileReader.Close();
243 |
244 | if (mDbfFile != null)
245 | mDbfFile.Close();
246 | //set streams to null
247 | //--------------------------------
248 | mDbfFileReader = null;
249 | mDbfFileWriter = null;
250 | mDbfFile = null;
251 |
252 | mFileName = "";
253 | }
254 |
255 | ///
256 | /// Read next record and fill data into parameter oFillRecord. Returns true if a record was read, otherwise false.
257 | ///
258 | ///
259 | ///
260 | public bool ReadNext(DbfRecord oFillRecord)
261 | {
262 | //check if we can fill this record with data. it must match record size specified by header and number of columns.
263 | //we are not checking whether it comes from another DBF file or not, we just need the same structure. Allow flexibility but be safe.
264 | if (oFillRecord.Header != mHeader && (oFillRecord.Header.ColumnCount != mHeader.ColumnCount || oFillRecord.Header.RecordLength != mHeader.RecordLength))
265 | throw new Exception("Record parameter does not have the same size and number of columns as the " +
266 | "header specifies, so we are unable to read a record into oFillRecord. " +
267 | "This is a programming error, have you mixed up DBF file objects?");
268 |
269 | //DBF file reader can be null if stream is not readable...
270 | if (mDbfFileReader == null)
271 | throw new Exception("Read stream is null, either you have opened a stream that can not be " +
272 | "read from (a write-only stream) or you have not opened a stream at all.");
273 |
274 | //read next record...
275 | bool bRead = oFillRecord.Read(mDbfFile);
276 |
277 | if (bRead)
278 | {
279 | if (mIsForwardOnly)
280 | {
281 | //zero based index! set before incrementing count.
282 | oFillRecord.RecordIndex = mRecordsReadCount;
283 | mRecordsReadCount++;
284 | }
285 | else
286 | oFillRecord.RecordIndex = ((int)((mDbfFile.Position - mHeader.HeaderLength) / mHeader.RecordLength)) - 1;
287 |
288 | }
289 | return bRead;
290 | }
291 | ///
292 | /// Tries to read a record and returns a new record object or null if nothing was read.
293 | ///
294 | ///
295 | public DbfRecord ReadNext()
296 | {
297 | //create a new record and fill it.
298 | DbfRecord orec = new DbfRecord(mHeader);
299 |
300 | return ReadNext(orec) ? orec : null;
301 | }
302 | ///
303 | /// Reads a record specified by index into oFillRecord object. You can use this method
304 | /// to read in and process records without creating and discarding record objects.
305 | /// Note that you should check that your stream is not forward-only! If you have a forward only stream, use ReadNext() functions.
306 | ///
307 | /// Zero based record index.
308 | /// Record object to fill, must have same size and number of fields as thid DBF file header!
309 | ///
310 | /// True if read a record was read, otherwise false. If you read end of file false will be returned and oFillRecord will NOT be modified!
311 | /// The parameter record (oFillRecord) must match record size specified by the header and number of columns as well.
312 | /// It does not have to come from the same header, but it must match the structure. We are not going as far as to check size of each field.
313 | /// The idea is to be flexible but safe. It's a fine balance, these two are almost always at odds.
314 | ///
315 | public bool Read(int index, DbfRecord oFillRecord)
316 | {
317 | //check if we can fill this record with data. it must match record size specified by header and number of columns.
318 | //we are not checking whether it comes from another DBF file or not, we just need the same structure. Allow flexibility but be safe.
319 | if (oFillRecord.Header != mHeader && (oFillRecord.Header.ColumnCount != mHeader.ColumnCount || oFillRecord.Header.RecordLength != mHeader.RecordLength))
320 | throw new Exception("Record parameter does not have the same size and number of columns as the " +
321 | "header specifies, so we are unable to read a record into oFillRecord. " +
322 | "This is a programming error, have you mixed up DBF file objects?");
323 |
324 | //DBF file reader can be null if stream is not readable...
325 | if (mDbfFileReader == null)
326 | throw new Exception("ReadStream is null, either you have opened a stream that can not be " +
327 | "read from (a write-only stream) or you have not opened a stream at all.");
328 |
329 | //move to the specified record, note that an exception will be thrown is stream is not seekable!
330 | //This is ok, since we provide a function to check whether the stream is seekable.
331 | long nSeekToPosition = mHeader.HeaderLength + (index * mHeader.RecordLength);
332 |
333 | //check whether requested record exists. Subtract 1 from file length (there is a terminating character 1A at the end of the file)
334 | //so if we hit end of file, there are no more records, so return false;
335 | if (index < 0 || mDbfFile.Length - 1 <= nSeekToPosition)
336 | return false;
337 |
338 | //move to record and read
339 | mDbfFile.Seek(nSeekToPosition, SeekOrigin.Begin);
340 |
341 | //read the record
342 | bool bRead = oFillRecord.Read(mDbfFile);
343 | if (bRead)
344 | oFillRecord.RecordIndex = index;
345 |
346 | return bRead;
347 | }
348 |
349 | ///
350 | /// 读取指定行列的值
351 | ///
352 | ///
353 | ///
354 | ///
355 | ///
356 | public bool ReadValue(int rowIndex, int columnIndex, out string result)
357 | {
358 | result = String.Empty;
359 |
360 | DbfColumn ocol = mHeader[columnIndex];
361 |
362 | //move to the specified record, note that an exception will be thrown is stream is not seekable!
363 | //This is ok, since we provide a function to check whether the stream is seekable.
364 | long nSeekToPosition = mHeader.HeaderLength + (rowIndex * mHeader.RecordLength) + ocol.DataAddress;
365 |
366 | //check whether requested record exists. Subtract 1 from file length (there is a terminating character 1A at the end of the file)
367 | //so if we hit end of file, there are no more records, so return false;
368 | if (rowIndex < 0 || mDbfFile.Length - 1 <= nSeekToPosition)
369 | return false;
370 |
371 | //move to position and read
372 | mDbfFile.Seek(nSeekToPosition, SeekOrigin.Begin);
373 |
374 | //read the value
375 | byte[] data = new byte[ocol.Length];
376 | mDbfFile.Read(data, 0, ocol.Length);
377 | result = new string(encoding.GetChars(data, 0, ocol.Length));
378 |
379 | return true;
380 | }
381 |
382 | ///
383 | /// Reads a record specified by index. This method requires the stream to be able to seek to position.
384 | /// If you are using a http stream, or a stream that can not stream, use ReadNext() methods to read in all records.
385 | ///
386 | /// Zero based index.
387 | /// Null if record can not be read, otherwise returns a new record.
388 | public DbfRecord Read(int index)
389 | {
390 | //create a new record and fill it.
391 | DbfRecord orec = new DbfRecord(mHeader);
392 |
393 | return Read(index, orec) ? orec : null;
394 | }
395 |
396 | ///
397 | /// Write a record to file. If RecordIndex is present, record will be updated, otherwise a new record will be written.
398 | /// Header will be output first if this is the first record being writen to file.
399 | /// This method does not require stream seek capability to add a new record.
400 | ///
401 | ///
402 | public void Write(DbfRecord orec)
403 | {
404 | //if header was never written, write it first, then output the record
405 | if (!mHeaderWritten)
406 | WriteHeader();
407 |
408 | //if this is a new record (RecordIndex should be -1 in that case)
409 | if (orec.RecordIndex < 0)
410 | {
411 | if (mDbfFileWriter.BaseStream.CanSeek)
412 | {
413 | //calculate number of records in file. do not rely on header's RecordCount property since client can change that value.
414 | //also note that some DBF files do not have ending 0x1A byte, so we subtract 1 and round off
415 | //instead of just cast since cast would just drop decimals.
416 | int nNumRecords = (int)Math.Round(((double)(mDbfFile.Length - mHeader.HeaderLength - 1) / mHeader.RecordLength));
417 | if (nNumRecords < 0)
418 | nNumRecords = 0;
419 |
420 | orec.RecordIndex = nNumRecords;
421 | Update(orec);
422 | mHeader.RecordCount++;
423 |
424 | }
425 | else
426 | {
427 | //we can not position this stream, just write out the new record.
428 | orec.Write(mDbfFile);
429 | mHeader.RecordCount++;
430 | }
431 | }
432 | else
433 | Update(orec);
434 |
435 | }
436 |
437 | public void Write(DbfRecord orec, bool bClearRecordAfterWrite)
438 | {
439 | Write(orec);
440 |
441 | if (bClearRecordAfterWrite)
442 | orec.Clear();
443 | }
444 | ///
445 | /// Update a record. RecordIndex (zero based index) must be more than -1, otherwise an exception is thrown.
446 | /// You can also use Write method which updates a record if it has RecordIndex or adds a new one if RecordIndex == -1.
447 | /// RecordIndex is set automatically when you call any Read() methods on this class.
448 | ///
449 | ///
450 | public void Update(DbfRecord orec)
451 | {
452 | //if header was never written, write it first, then output the record
453 | if (!mHeaderWritten)
454 | WriteHeader();
455 |
456 | //Check if record has an index
457 | if (orec.RecordIndex < 0)
458 | throw new Exception("RecordIndex is not set, unable to update record. Set RecordIndex or call Write() method to add a new record to file.");
459 |
460 | //Check if this record matches record size specified by header and number of columns.
461 | //Client can pass a record from another DBF that is incompatible with this one and that would corrupt the file.
462 | if (orec.Header != mHeader && (orec.Header.ColumnCount != mHeader.ColumnCount || orec.Header.RecordLength != mHeader.RecordLength))
463 | throw new Exception("Record parameter does not have the same size and number of columns as the " +
464 | "header specifies. Writing this record would corrupt the DBF file. " +
465 | "This is a programming error, have you mixed up DBF file objects?");
466 |
467 | //DBF file writer can be null if stream is not writable to...
468 | if (mDbfFileWriter == null)
469 | throw new Exception("Write stream is null. Either you have opened a stream that can not be " +
470 | "writen to (a read-only stream) or you have not opened a stream at all.");
471 |
472 |
473 | //move to the specified record, note that an exception will be thrown if stream is not seekable!
474 | //This is ok, since we provide a function to check whether the stream is seekable.
475 | long nSeekToPosition = (long)mHeader.HeaderLength + (long)((long)orec.RecordIndex * (long)mHeader.RecordLength);
476 |
477 | //check whether we can seek to this position. Subtract 1 from file length (there is a terminating character 1A at the end of the file)
478 | //so if we hit end of file, there are no more records, so return false;
479 | if (mDbfFile.Length < nSeekToPosition)
480 | throw new Exception("Invalid record position. Unable to save record.");
481 |
482 | //move to record start
483 | mDbfFile.Seek(nSeekToPosition, SeekOrigin.Begin);
484 |
485 | //write
486 | orec.Write(mDbfFile);
487 | }
488 |
489 | ///
490 | /// Save header to file. Normally, you do not have to call this method, header is saved
491 | /// automatically and updated when you close the file (if it changed).
492 | ///
493 | public bool WriteHeader()
494 | {
495 | //update header if possible
496 | //--------------------------------
497 | if (mDbfFileWriter != null)
498 | {
499 | if (mDbfFileWriter.BaseStream.CanSeek)
500 | {
501 | mDbfFileWriter.Seek(0, SeekOrigin.Begin);
502 | mHeader.Write(mDbfFileWriter);
503 | mHeaderWritten = true;
504 | return true;
505 | }
506 | else
507 | {
508 | //if stream can not seek, then just write it out and that's it.
509 | if (!mHeaderWritten)
510 | mHeader.Write(mDbfFileWriter);
511 |
512 | mHeaderWritten = true;
513 | }
514 | }
515 |
516 | return false;
517 | }
518 |
519 | }
520 | }
521 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/DBF/DbfHeader.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:Add type 'F' for DbfColumnType. After ARCGIS10.0 the dbf file add.
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:Ahmed Lacevic
9 | /// Date:12/1/2007
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.IO;
16 |
17 | namespace GIS.HPU.ZYZ.SHP.DBF
18 | {
19 | ///
20 | /// This class represents a DBF IV file header.
21 | ///
22 | ///
23 | ///
24 | /// DBF files are really wasteful on space but this legacy format lives on because it's really really simple.
25 | /// It lacks much in features though.
26 | ///
27 | ///
28 | /// Thanks to Erik Bachmann for providing the DBF file structure information!!
29 | /// http://www.clicketyclick.dk/databases/xbase/format/dbf.html
30 | ///
31 | /// _______________________ _______
32 | /// 00h / 0| Version number *1| ^
33 | /// |-----------------------| |
34 | /// 01h / 1| Date of last update | |
35 | /// 02h / 2| YYMMDD *21| |
36 | /// 03h / 3| *14| |
37 | /// |-----------------------| |
38 | /// 04h / 4| Number of records | Record
39 | /// 05h / 5| in data file | header
40 | /// 06h / 6| ( 32 bits ) *14| |
41 | /// 07h / 7| | |
42 | /// |-----------------------| |
43 | /// 08h / 8| Length of header *14| |
44 | /// 09h / 9| structure ( 16 bits ) | |
45 | /// |-----------------------| |
46 | /// 0Ah / 10| Length of each record | |
47 | /// 0Bh / 11| ( 16 bits ) *2 *14| |
48 | /// |-----------------------| |
49 | /// 0Ch / 12| ( Reserved ) *3| |
50 | /// 0Dh / 13| | |
51 | /// |-----------------------| |
52 | /// 0Eh / 14| Incomplete transac.*12| |
53 | /// |-----------------------| |
54 | /// 0Fh / 15| Encryption flag *13| |
55 | /// |-----------------------| |
56 | /// 10h / 16| Free record thread | |
57 | /// 11h / 17| (reserved for LAN | |
58 | /// 12h / 18| only ) | |
59 | /// 13h / 19| | |
60 | /// |-----------------------| |
61 | /// 14h / 20| ( Reserved for | | _ |=======================| ______
62 | /// | multi-user dBASE ) | | / 00h / 0| Field name in ASCII | ^
63 | /// : ( dBASE III+ - ) : | / : (terminated by 00h) : |
64 | /// : : | | | | |
65 | /// 1Bh / 27| | | | 0Ah / 10| | |
66 | /// |-----------------------| | | |-----------------------| For
67 | /// 1Ch / 28| MDX flag (dBASE IV)*14| | | 0Bh / 11| Field type (ASCII) *20| each
68 | /// |-----------------------| | | |-----------------------| field
69 | /// 1Dh / 29| Language driver *5| | / 0Ch / 12| Field data address | |
70 | /// |-----------------------| | / | *6| |
71 | /// 1Eh / 30| ( Reserved ) | | / | (in memory !!!) | |
72 | /// 1Fh / 31| *3| | / 0Fh / 15| (dBASE III+) | |
73 | /// |=======================|__|____/ |-----------------------| | -
74 | /// 20h / 32| | | ^ 10h / 16| Field length *22| | |
75 | /// |- - - - - - - - - - - -| | | |-----------------------| | | *7
76 | /// | *19| | | 11h / 17| Decimal count *23| | |
77 | /// |- - - - - - - - - - - -| | Field |-----------------------| | -
78 | /// | | | Descriptor 12h / 18| ( Reserved for | |
79 | /// :. . . . . . . . . . . .: | |array 13h / 19| multi-user dBASE)*18| |
80 | /// : : | | |-----------------------| |
81 | /// n | |__|__v_ 14h / 20| Work area ID *16| |
82 | /// |-----------------------| | \ |-----------------------| |
83 | /// n+1| Terminator (0Dh) | | \ 15h / 21| ( Reserved for | |
84 | /// |=======================| | \ 16h / 22| multi-user dBASE ) | |
85 | /// m | Database Container | | \ |-----------------------| |
86 | /// : *15: | \ 17h / 23| Flag for SET FIELDS | |
87 | /// : : | | |-----------------------| |
88 | /// / m+263 | | | 18h / 24| ( Reserved ) | |
89 | /// |=======================|__v_ ___ | : : |
90 | /// : : ^ | : : |
91 | /// : : | | : : |
92 | /// : : | | 1Eh / 30| | |
93 | /// | Record structure | | | |-----------------------| |
94 | /// | | | \ 1Fh / 31| Index field flag *8| |
95 | /// | | | \_ |=======================| _v_____
96 | /// | | Records
97 | /// |-----------------------| |
98 | /// | | | _ |=======================| _______
99 | /// | | | / 00h / 0| Record deleted flag *9| ^
100 | /// | | | / |-----------------------| |
101 | /// | | | / | Data *10| One
102 | /// | | | / : (ASCII) *17: record
103 | /// | |____|_____/ | | |
104 | /// : : | | | _v_____
105 | /// : :____|_____ |=======================|
106 | /// : : |
107 | /// | | |
108 | /// | | |
109 | /// | | |
110 | /// | | |
111 | /// | | |
112 | /// |=======================| |
113 | /// |__End_of_File__________| ___v____ End of file ( 1Ah ) *11
114 | ///
115 | ///
116 | public class DbfHeader : ICloneable
117 | {
118 | ///
119 | /// 头文件描述信息长度
120 | /// Header file descriptor size is 33 bytes (32 bytes + 1 terminator byte), followed by column metadata which is 32 bytes each.
121 | ///
122 | public const int FileDescriptorSize = 33;
123 | ///
124 | /// 头文件每个记录描述信息长度
125 | /// Field or DBF Column descriptor is 32 bytes long.
126 | ///
127 | public const int ColumnDescriptorSize = 32;
128 |
129 | //type of the file, must be 03h
130 | private const int mFileType = 0x03;
131 |
132 | //Date the file was last updated.
133 | private DateTime mUpdateDate;
134 |
135 | //Number of records in the datafile, 32bit little-endian, unsigned
136 | private uint mNumRecords = 0;
137 |
138 | //Length of the header structure
139 | private ushort mHeaderLength = FileDescriptorSize; //empty header is 33 bytes long. Each column adds 32 bytes.
140 |
141 | //Length of the records, ushort - unsigned 16 bit integer
142 | private int mRecordLength = 1; //start with 1 because the first byte is a delete flag
143 |
144 | //DBF fields/columns
145 | internal List mFields = new List();
146 | //indicates whether header columns can be modified!
147 | bool mLocked = false;
148 | //keeps column name index for the header, must clear when header columns change.
149 | private Dictionary mColumnNameIndex = null;
150 | ///
151 | /// When object is modified dirty flag is set.
152 | ///
153 | bool mIsDirty = false;
154 | ///
155 | /// mEmptyRecord is an array used to clear record data in CDbf4Record.
156 | /// This is shared by all record objects, used to speed up clearing fields or entire record.
157 | ///
158 | ///
159 | private byte[] mEmptyRecord = null;
160 | ///
161 | /// 编码方式
162 | ///
163 | public Encoding encoding = Encoding.ASCII;
164 |
165 | #region
166 | ///
167 | /// Gets header length.
168 | ///
169 | public ushort HeaderLength
170 | {
171 | get
172 | {
173 | return mHeaderLength;
174 | }
175 | }
176 | ///
177 | /// Returns Number of columns in this dbf header.
178 | ///
179 | public int ColumnCount
180 | {
181 | get { return mFields.Count; }
182 |
183 | }
184 | ///
185 | /// Size of one record in bytes. All fields + 1 byte delete flag.
186 | ///
187 | public int RecordLength
188 | {
189 | get
190 | {
191 | return mRecordLength;
192 | }
193 | }
194 | ///
195 | /// Get/Set number of records in the DBF.
196 | ///
197 | ///
198 | /// The reason we allow client to set RecordCount is beause in certain streams
199 | /// like internet streams we can not update record count as we write out records, we have to set it in advance,
200 | /// so client has to be able to modify this property.
201 | ///
202 | public uint RecordCount
203 | {
204 | get
205 | {
206 | return mNumRecords;
207 | }
208 | set
209 | {
210 | mNumRecords = value;
211 | //set the dirty bit
212 | mIsDirty = true;
213 | }
214 | }
215 | ///
216 | /// Get/set whether this header is read only or can be modified. When you create a CDbfRecord
217 | /// object and pass a header to it, CDbfRecord locks the header so that it can not be modified any longer.
218 | /// in order to preserve DBF integrity.
219 | ///
220 | internal bool Locked
221 | {
222 | get
223 | {
224 | return mLocked;
225 | }
226 | set
227 | {
228 | mLocked = value;
229 | }
230 |
231 | }
232 | ///
233 | /// Returns true when this object is modified after read or write.
234 | ///
235 | public bool IsDirty
236 | {
237 | get
238 | {
239 | return mIsDirty;
240 | }
241 | set
242 | {
243 | mIsDirty = value;
244 | }
245 | }
246 | ///
247 | /// Look up a column index by name. Note that this is case sensitive, internally it does a lookup using a dictionary.
248 | ///
249 | ///
250 | public DbfColumn this[string sName]
251 | {
252 | get
253 | {
254 | int colIndex = FindColumn(sName);
255 | if (colIndex > -1)
256 | return mFields[colIndex];
257 | return null;
258 | }
259 | }
260 | ///
261 | /// Returns column at specified index. Index is 0 based.
262 | ///
263 | /// Zero based index.
264 | ///
265 | public DbfColumn this[int nIndex]
266 | {
267 | get
268 | {
269 | return mFields[nIndex];
270 | }
271 | }
272 | #endregion
273 |
274 | [Obsolete]
275 | public DbfHeader()
276 | {
277 | }
278 | public DbfHeader(Encoding encoding)
279 | {
280 | this.encoding = encoding;
281 | }
282 | ///
283 | /// Specify initial column capacity.
284 | ///
285 | ///
286 | public DbfHeader(int nFieldCapacity)
287 | {
288 | mFields = new List(nFieldCapacity);
289 | }
290 |
291 | ///
292 | /// Add a new column to the DBF header.
293 | ///
294 | ///
295 | public void AddColumn(DbfColumn oNewCol)
296 | {
297 | //throw exception if the header is locked
298 | if (mLocked)
299 | throw new InvalidOperationException("This header is locked and can not be modified. Modifying the header would result in a corrupt DBF file. You can unlock the header by calling UnLock() method.");
300 |
301 | //since we are breaking the spec rules about max number of fields, we should at least
302 | //check that the record length stays within a number that can be recorded in the header!
303 | //we have 2 unsigned bytes for record length for a maximum of 65535.
304 | if (mRecordLength + oNewCol.Length > 65535)
305 | throw new ArgumentOutOfRangeException("oNewCol", "Unable to add new column. Adding this column puts the record length over the maximum (which is 65535 bytes).");
306 |
307 | //add the column
308 | mFields.Add(oNewCol);
309 |
310 | //update offset bits, record and header lengths
311 | oNewCol.mDataAddress = mRecordLength;
312 | mRecordLength += oNewCol.Length;
313 | mHeaderLength += ColumnDescriptorSize;
314 |
315 | //clear empty record
316 | mEmptyRecord = null;
317 |
318 | //set dirty bit
319 | mIsDirty = true;
320 | mColumnNameIndex = null;
321 | }
322 | ///
323 | /// Create and add a new column with specified name and type.
324 | ///
325 | ///
326 | ///
327 | public void AddColumn(string sName, DbfColumn.DbfColumnType type)
328 | {
329 | AddColumn(new DbfColumn(sName, type));
330 | }
331 | ///
332 | /// Create and add a new column with specified name, type, length, and decimal precision.
333 | ///
334 | /// Field name. Uniqueness is not enforced.
335 | ///
336 | /// Length of the field including decimal point and decimal numbers
337 | /// Number of decimal places to keep.
338 | public void AddColumn(string sName, DbfColumn.DbfColumnType type, int nLength, int nDecimals)
339 | {
340 | AddColumn(new DbfColumn(sName, type, nLength, nDecimals));
341 | }
342 |
343 | ///
344 | /// Remove column from header definition.
345 | ///
346 | ///
347 | public void RemoveColumn(int nIndex)
348 | {
349 | //throw exception if the header is locked
350 | if (mLocked)
351 | throw new InvalidOperationException("This header is locked and can not be modified. Modifying the header would result in a corrupt DBF file. You can unlock the header by calling UnLock() method.");
352 |
353 | DbfColumn oColRemove = mFields[nIndex];
354 | mFields.RemoveAt(nIndex);
355 |
356 |
357 | oColRemove.mDataAddress = 0;
358 | mRecordLength -= oColRemove.Length;
359 | mHeaderLength -= ColumnDescriptorSize;
360 |
361 | //if you remove a column offset shift for each of the columns
362 | //following the one removed, we need to update those offsets.
363 | int nRemovedColLen = oColRemove.Length;
364 | for (int i = nIndex; i < mFields.Count; i++)
365 | mFields[i].mDataAddress -= nRemovedColLen;
366 |
367 | //clear the empty record
368 | mEmptyRecord = null;
369 |
370 | //set dirty bit
371 | mIsDirty = true;
372 | mColumnNameIndex = null;
373 | }
374 |
375 | ///
376 | /// Finds a column index by using a fast dictionary lookup-- creates column dictionary on first use. Returns -1 if not found. Note this is case sensitive!
377 | ///
378 | /// Column name
379 | /// column index (0 based) or -1 if not found.
380 | public int FindColumn(string sName)
381 | {
382 | if (mColumnNameIndex == null)
383 | {
384 | mColumnNameIndex = new Dictionary(mFields.Count);
385 |
386 | //create a new index
387 | for (int i = 0; i < mFields.Count; i++)
388 | {
389 | mColumnNameIndex.Add(mFields[i].Name, i);
390 | }
391 | }
392 |
393 | int columnIndex;
394 | if (mColumnNameIndex.TryGetValue(sName, out columnIndex))
395 | return columnIndex;
396 |
397 | return -1;
398 | }
399 | ///
400 | /// 查找列索引
401 | /// 针对最后一个字符乱码的问题
402 | ///
403 | ///
404 | ///
405 | public int FindColumnEx(string sName)
406 | {
407 | if (mColumnNameIndex == null)
408 | {
409 | mColumnNameIndex = new Dictionary(mFields.Count);
410 |
411 | //create a new index
412 | for (int i = 0; i < mFields.Count; i++)
413 | {
414 | mColumnNameIndex.Add(mFields[i].Name, i);
415 | }
416 | }
417 |
418 | int columnIndex=-1;
419 | //if (mColumnNameIndex.TryGetValue(sName, out columnIndex))
420 | // return columnIndex;
421 | foreach (var item in mColumnNameIndex) {
422 | if (item.Key.Contains(sName.Substring(0, sName.Length - 1))) {
423 | columnIndex= item.Value;
424 | break;
425 | }
426 | }
427 | return columnIndex;
428 | }
429 | ///
430 | /// Returns an empty data record. This is used to clear columns
431 | ///
432 | ///
433 | /// The reason we put this in the header class is because it allows us to use the CDbf4Record class in two ways.
434 | /// 1. we can create one instance of the record and reuse it to write many records quickly clearing the data array by bitblting to it.
435 | /// 2. we can create many instances of the record (a collection of records) and have only one copy of this empty dataset for all of them.
436 | /// If we had put it in the Record class then we would be taking up twice as much space unnecessarily. The empty record also fits the model
437 | /// and everything is neatly encapsulated and safe.
438 | ///
439 | ///
440 | protected internal byte[] EmptyDataRecord
441 | {
442 | get { return mEmptyRecord ?? (mEmptyRecord = encoding.GetBytes("".PadLeft(mRecordLength, ' ').ToCharArray())); }
443 | }
444 | ///
445 | /// Use this method with caution. Headers are locked for a reason, to prevent DBF from becoming corrupt.
446 | ///
447 | public void Unlock()
448 | {
449 | mLocked = false;
450 | }
451 |
452 | ///
453 | /// Encoding must be ASCII for this binary writer.
454 | ///
455 | ///
456 | ///
457 | /// See class remarks for DBF file structure.
458 | ///
459 | public void Write(BinaryWriter writer)
460 | {
461 | //write the header
462 | // write the output file type.
463 | writer.Write((byte)mFileType);
464 |
465 | mUpdateDate = DateTime.Now;
466 | //Update date format is YYMMDD, which is different from the column Date type (YYYYDDMM) 从1990算起
467 | writer.Write((byte)(mUpdateDate.Year - 1900));
468 | writer.Write((byte)mUpdateDate.Month);
469 | writer.Write((byte)mUpdateDate.Day);
470 |
471 | // write the number of records in the datafile. (32 bit number, little-endian unsigned)
472 | writer.Write(mNumRecords);
473 |
474 | // write the length of the header structure.
475 | writer.Write(mHeaderLength);
476 |
477 | // write the length of a record
478 | writer.Write((ushort)mRecordLength);
479 |
480 | for (int i = 0; i < 17; i++)
481 | writer.Write((byte)0);
482 | byte driverID = 77;
483 | writer.Write(driverID);
484 | writer.Write((byte)0);
485 | writer.Write((byte)0);
486 |
487 | // write all of the header records
488 | byte[] byteReserved = new byte[14]; //后面的14字节 (2:保留字节 1:工作区ID 10:保留字节1:MDX标识) these are initialized to 0 by default.
489 | foreach (DbfColumn field in mFields)
490 | {
491 | //char[] cname = field.Name.PadRight(11, (char)0).ToCharArray();
492 | byte[] bName = encoding.GetBytes(field.Name);
493 | byte[] cname = new byte[11];
494 | Array.ConstrainedCopy(bName, 0, cname, 0, bName.Length > 11 ? 11 : bName.Length);
495 |
496 | writer.Write(cname);
497 |
498 | // write the field type
499 | char typeChar = (char)field.ColumnTypeChar;
500 | byte typeByte = Convert.ToByte(typeChar);
501 | writer.Write(typeChar);
502 |
503 | // write the field data address, offset from the start of the record.
504 | writer.Write(field.DataAddress);
505 |
506 | // write the length of the field.
507 | // if char field is longer than 255 bytes, then we use the decimal field as part of the field length.
508 | if (field.ColumnType == DbfColumn.DbfColumnType.Character && field.Length > 255)
509 | {
510 | //treat decimal count as high byte of field length, this extends char field max to 65535
511 | writer.Write((ushort)field.Length);
512 | }
513 | else
514 | {
515 | // write the length of the field.
516 | writer.Write((byte)field.Length);
517 |
518 | // write the decimal count.
519 | writer.Write((byte)field.DecimalCount);
520 | }
521 |
522 | // write the reserved bytes.
523 | writer.Write(byteReserved);
524 |
525 | }
526 | // write the end of the field definitions marker
527 | writer.Write((byte)0x0D);
528 | writer.Flush();
529 |
530 | //clear dirty bit
531 | mIsDirty = false;
532 | //lock the header so it can not be modified any longer,
533 | //we could actually postpond this until first record is written!
534 | mLocked = true;
535 | }
536 |
537 | ///
538 | /// Read header data, make sure the stream is positioned at the start of the file to read the header otherwise you will get an exception.
539 | /// When this function is done the position will be the first record.
540 | ///
541 | ///
542 | public void Read(BinaryReader reader)
543 | {
544 | // type of reader.
545 | int nFileType = reader.ReadByte();
546 |
547 | if (nFileType != 0x03)//默认的是3 国家系统导出的是3
548 | throw new NotSupportedException("Unsupported DBF reader Type " + nFileType);
549 |
550 | // parse the update date information.
551 | int year = (int)reader.ReadByte();
552 | int month = (int)reader.ReadByte();
553 | int day = (int)reader.ReadByte();
554 | mUpdateDate = new DateTime(year + 1900, month, day);
555 |
556 | // 文件中的记录条数.
557 | mNumRecords = reader.ReadUInt32();
558 | // 文件头中的字节数
559 | mHeaderLength = reader.ReadUInt16();
560 | // 一条记录中的字节长度
561 | mRecordLength = reader.ReadInt16();
562 |
563 | // skip the reserved bytes in the header.
564 | //1表示未完成的操作 1dBASE IV编密码标记 12保留字节,用于多用户处理时使用1DBF文件的MDX标识1Language driver ID2保留字节
565 | //reader.ReadBytes(20);
566 | byte[] re1 = reader.ReadBytes(2);//保留字节,用于以后添加新的说明性信息时使用,这里用0来填写
567 | byte[] re2 = reader.ReadBytes(1);//表示未完成的操作
568 | byte[] re3 = reader.ReadBytes(1);//dBASE IV编密码标记。
569 | byte[] re4 = reader.ReadBytes(12);//保留字节,用于多用户处理时使用。
570 | /*
571 | * DBF文件的MDX标识。
572 | * 在创建一个DBF 表时 ,
573 | * 如果使用了MDX 格式的索引文件,
574 | * 那么 DBF 表的表头中的这个字节就自动被设置了一个标志,
575 | * 当你下次试图重新打开这个DBF表的时候,
576 | * 数据引擎会自动识别这个标志,
577 | * 如果此标志为真,则数据引擎将试图打开相应的MDX 文件。
578 | */
579 | byte re5 = reader.ReadByte();
580 | byte re6 = reader.ReadByte();//Language driver ID. 国家导出:0(无法导入) 国家标准:77 arcgis导出:77
581 | byte driverID = 77;
582 | if (re6 == driverID)
583 | {
584 | this.encoding = Encoding.GetEncoding(936);
585 | }
586 | else
587 | {
588 | this.encoding = Encoding.UTF8;
589 | }
590 | byte[] re7 = reader.ReadBytes(2);//保留字节,用于以后添加新的说明性信息时使用,这里用0来填写。
591 |
592 | // calculate the number of Fields in the header
593 | int nNumFields = (mHeaderLength - FileDescriptorSize) / ColumnDescriptorSize;
594 |
595 | //offset from start of record, start at 1 because that's the delete flag.
596 | int nDataOffset = 1;
597 |
598 | // read all of the header records
599 | mFields = new List(nNumFields);
600 | for (int i = 0; i < nNumFields; i++)
601 | {
602 | // read the field name
603 | //char[] buffer = new char[11];//有中文字符 不能这样写 一个中文字符占2char
604 | //buffer = reader.ReadChars(11);
605 | byte[] nbytes = reader.ReadBytes(11);
606 | string sFieldName = encoding.GetString(nbytes); //new string(nbytes);//不指定编码?
607 | int nullPoint = sFieldName.IndexOf((char)0);
608 | if (nullPoint != -1)
609 | sFieldName = sFieldName.Substring(0, nullPoint);
610 |
611 | //read the field type
612 | char cDbaseType = (char)reader.ReadByte();
613 |
614 | // read the field data address, offset from the start of the record.
615 | int nFieldDataAddress = reader.ReadInt32();
616 |
617 | //read the field length in bytes
618 | //if field type is char, then read FieldLength and Decimal count as one number to allow char fields to be
619 | //longer than 256 bytes (ASCII char). This is the way Clipper and FoxPro do it, and there is really no downside
620 | //since for char fields decimal count should be zero for other versions that do not support this extended functionality.
621 | //-----------------------------------------------------------------------------------------------------------------------
622 | int nFieldLength = 0;
623 | int nDecimals = 0;
624 | if (cDbaseType == 'C' || cDbaseType == 'c')
625 | {
626 | //treat decimal count as high byte
627 | nFieldLength = (int)reader.ReadUInt16();
628 | }
629 | else
630 | {
631 | //read field length as an unsigned byte.
632 | nFieldLength = (int)reader.ReadByte();
633 |
634 | //read decimal count as one byte
635 | nDecimals = (int)reader.ReadByte();
636 | }
637 |
638 | //read the reserved bytes.
639 | reader.ReadBytes(14);
640 |
641 | //Create and add field to collection
642 | mFields.Add(new DbfColumn(sFieldName, DbfColumn.GetDbaseType(cDbaseType), nFieldLength, nDecimals, nDataOffset));
643 |
644 | // add up address information, you can not trust the address recorded in the DBF file...
645 | nDataOffset += nFieldLength;
646 |
647 | }
648 |
649 | // Last byte is a marker for the end of the field definitions.
650 | reader.ReadBytes(1);
651 |
652 | //read any extra header bytes...move to first record
653 | //equivalent to reader.BaseStream.Seek(mHeaderLength, SeekOrigin.Begin) except that we are not using the seek function since
654 | //we need to support streams that can not seek like web connections.
655 | int nExtraReadBytes = mHeaderLength - (FileDescriptorSize + (ColumnDescriptorSize * mFields.Count));
656 | if (nExtraReadBytes > 0)
657 | reader.ReadBytes(nExtraReadBytes);
658 |
659 | //if the stream is not forward-only, calculate number of records using file size,
660 | //sometimes the header does not contain the correct record count
661 | //if we are reading the file from the web, we have to use ReadNext() functions anyway so
662 | //Number of records is not so important and we can trust the DBF to have it stored correctly.
663 | if (reader.BaseStream.CanSeek && mNumRecords == 0)
664 | {
665 | //notice here that we subtract file end byte which is supposed to be 0x1A,
666 | //but some DBF files are incorrectly written without this byte, so we round off to nearest integer.
667 | //that gives a correct result with or without ending byte.
668 | if (mRecordLength > 0)
669 | mNumRecords = (uint)Math.Round(((double)(reader.BaseStream.Length - mHeaderLength - 1) / mRecordLength));
670 |
671 | }
672 | //lock header since it was read from a file. we don't want it modified because that would corrupt the file.
673 | //user can override this lock if really necessary by calling UnLock() method.
674 | mLocked = true;
675 | //clear dirty bit
676 | mIsDirty = false;
677 | }
678 |
679 | public object Clone()
680 | {
681 | return this.MemberwiseClone();
682 | }
683 | }
684 | }
685 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/DBF/DbfRecord.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:Add type 'F' for DbfColumnType. After ARCGIS10.0 the dbf file add.
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:Ahmed Lacevic
9 | /// Date:12/1/2007
10 | /// Desc:
11 | ///
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Linq;
15 | using System.Text;
16 | using System.IO;
17 | using System.Globalization;
18 |
19 | namespace GIS.HPU.ZYZ.SHP.DBF
20 | {
21 | ///
22 | /// Use this class to create a record and write it to a dbf file. You can use one record object to write all records!!
23 | /// It was designed for this kind of use. You can do this by clearing the record of all data
24 | /// (call Clear() method) or setting values to all fields again, then write to dbf file.
25 | /// This eliminates creating and destroying objects and optimizes memory use.
26 | ///
27 | /// Once you create a record the header can no longer be modified, since modifying the header would make a corrupt DBF file.
28 | ///
29 | public class DbfRecord
30 | {
31 | ///
32 | /// Header provides information on all field types, sizes, precision and other useful information about the DBF.
33 | ///
34 | private DbfHeader mHeader = null;
35 | ///
36 | /// Dbf data are a mix of ASCII characters and binary, which neatly fit in a byte array.
37 | /// BinaryWriter would esentially perform the same conversion using the same Encoding class.
38 | ///
39 | private byte[] mData = null;
40 | ///
41 | /// Zero based record index. -1 when not set, new records for example.
42 | ///
43 | private int mRecordIndex = -1;
44 | ///
45 | /// Empty Record array reference used to clear fields quickly (or entire record).
46 | ///
47 | private readonly byte[] mEmptyRecord = null;
48 | ///
49 | /// Specifies whether we allow strings to be truncated. If false and string is longer than we can fit in the field, an exception is thrown.
50 | ///
51 | private bool mAllowStringTruncate = true;
52 | ///
53 | /// Specifies whether we allow the decimal portion of numbers to be truncated.
54 | /// If false and decimal digits overflow the field, an exception is thrown.
55 | ///
56 | private bool mAllowDecimalTruncate = false;
57 | ///
58 | /// Specifies whether we allow the integer portion of numbers to be truncated.
59 | /// If false and integer digits overflow the field, an exception is thrown.
60 | ///
61 | private bool mAllowIntegerTruncate = false;
62 | //array used to clear decimals, we can clear up to 40 decimals which is much more than is allowed under DBF spec anyway.
63 | //Note: 48 is ASCII code for 0.
64 | private static readonly byte[] mDecimalClear = new byte[] {48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
65 | 48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
66 | 48,48,48,48,48,48,48,48,48,48,48,48,48,48,48};
67 |
68 |
69 | //Warning: do not make this one static because that would not be thread safe!! The reason I have
70 | //placed this here is to skip small memory allocation/deallocation which fragments memory in .net.
71 | private int[] mTempIntVal = { 0 };
72 | //Ascii Encoder
73 | private readonly Encoding encoding = Encoding.ASCII;
74 | ///
75 | /// Column Name to Column Index map
76 | ///
77 | private readonly Dictionary mColNameToConIdx = new Dictionary(StringComparer.InvariantCulture);
78 |
79 | #region
80 | ///
81 | /// Gets/sets a zero based record index. This information is not directly stored in DBF.
82 | /// It is the location of this record within the DBF.
83 | ///
84 | ///
85 | /// This property is managed from outside this object,
86 | /// CDbfFile object updates it when records are read. The reason we don't set it in the Read()
87 | /// function within this object is that the stream can be forward-only so the Position property
88 | /// is not available and there is no way to figure out what index the record was unless you
89 | /// count how many records were read, and that's exactly what CDbfFile does.
90 | ///
91 | public int RecordIndex
92 | {
93 | get
94 | {
95 | return mRecordIndex;
96 | }
97 | set
98 | {
99 | mRecordIndex = value;
100 | }
101 | }
102 | ///
103 | /// Gets column count from header.
104 | ///
105 | public int ColumnCount
106 | {
107 | get
108 | {
109 | return mHeader.ColumnCount;
110 | }
111 | }
112 | ///
113 | /// Returns/sets flag indicating whether this record was tagged deleted.
114 | ///
115 | /// Use CDbf4File.Compress() function to rewrite dbf removing records flagged as deleted.
116 | ///
117 | public bool IsDeleted
118 | {
119 | get { return mData[0] == '*'; }
120 | set { mData[0] = value ? (byte)'*' : (byte)' '; }
121 | }
122 | ///
123 | /// Specifies whether strings can be truncated. If false and string is longer than can fit in the field, an exception is thrown.
124 | /// Default is True.
125 | ///
126 | public bool AllowStringTurncate
127 | {
128 | get { return mAllowStringTruncate; }
129 | set { mAllowStringTruncate = value; }
130 | }
131 | ///
132 | /// Specifies whether to allow the decimal portion of numbers to be truncated.
133 | /// If false and decimal digits overflow the field, an exception is thrown. Default is false.
134 | ///
135 | public bool AllowDecimalTruncate
136 | {
137 | get { return mAllowDecimalTruncate; }
138 | set { mAllowDecimalTruncate = value; }
139 | }
140 | ///
141 | /// Specifies whether integer portion of numbers can be truncated.
142 | /// If false and integer digits overflow the field, an exception is thrown.
143 | /// Default is False.
144 | ///
145 | public bool AllowIntegerTruncate
146 | {
147 | get { return mAllowIntegerTruncate; }
148 | set { mAllowIntegerTruncate = value; }
149 | }
150 | ///
151 | /// Returns header object associated with this record.
152 | ///
153 | public DbfHeader Header
154 | {
155 | get
156 | {
157 | return mHeader;
158 | }
159 | }
160 | ///
161 | /// Set string data to a column, if the string is longer than specified column length it will be truncated!
162 | /// If dbf column type is not a string, input will be treated as dbf column
163 | /// type and if longer than length an exception will be thrown.
164 | ///
165 | ///
166 | ///
167 | public string this[int nColIndex]
168 | {
169 | set
170 | {
171 | DbfColumn ocol = mHeader[nColIndex];
172 | DbfColumn.DbfColumnType ocolType = ocol.ColumnType;
173 |
174 | //
175 | //if an empty value is passed, we just clear the data, and leave it blank.
176 | //note: test have shown that testing for null and checking length is faster than comparing to "" empty str :)
177 | //------------------------------------------------------------------------------------------------------------
178 | if (string.IsNullOrEmpty(value))
179 | {
180 | //this is like NULL data, set it to empty. i looked at SAS DBF output when a null value exists
181 | //and empty data are output. we get the same result, so this looks good.
182 | Buffer.BlockCopy(mEmptyRecord, ocol.DataAddress, mData, ocol.DataAddress, ocol.Length);
183 |
184 | }
185 | else
186 | {
187 |
188 | //set values according to data type:
189 | //-------------------------------------------------------------
190 | if (ocolType == DbfColumn.DbfColumnType.Character)
191 | {
192 | if (!mAllowStringTruncate && value.Length > ocol.Length)
193 | throw new DbfDataTruncateException("Value not set. String truncation would occur and AllowStringTruncate flag is set to false. To supress this exception change AllowStringTruncate to true.");
194 |
195 | //BlockCopy copies bytes. First clear the previous value, then set the new one.
196 | Buffer.BlockCopy(mEmptyRecord, ocol.DataAddress, mData, ocol.DataAddress, ocol.Length);
197 | encoding.GetBytes(value, 0, value.Length > ocol.Length ? ocol.Length : value.Length, mData, ocol.DataAddress);
198 |
199 | }
200 | else if (ocolType == DbfColumn.DbfColumnType.Number)
201 | {
202 | if (ocol.DecimalCount == 0)
203 | {
204 | //integers
205 | //----------------------------------
206 |
207 | //throw an exception if integer overflow would occur
208 | if (!mAllowIntegerTruncate && value.Length > ocol.Length)
209 | throw new DbfDataTruncateException("Value not set. Integer does not fit and would be truncated. AllowIntegerTruncate is set to false. To supress this exception set AllowIntegerTruncate to true, although that is not recomended.");
210 | //clear all numbers, set to [space].
211 | //-----------------------------------------------------
212 | Buffer.BlockCopy(mEmptyRecord, 0, mData, ocol.DataAddress, ocol.Length);
213 |
214 | //set integer part, CAREFUL not to overflow buffer! (truncate instead)
215 | //-----------------------------------------------------------------------
216 | int nNumLen = value.Length > ocol.Length ? ocol.Length : value.Length;
217 | encoding.GetBytes(value, 0, nNumLen, mData, (ocol.DataAddress + ocol.Length - nNumLen));
218 | }
219 | else
220 | {
221 | ///TODO: we can improve perfomance here by not using temp char arrays cDec and cNum,
222 | ///simply direcly copy from source string using encoding!
223 |
224 | //break value down into integer and decimal portions
225 | //--------------------------------------------------------------------------
226 | int nidxDecimal = value.IndexOf('.'); //index where the decimal point occurs
227 | char[] cDec = null; //decimal portion of the number
228 | char[] cNum = null; //integer portion
229 |
230 | if (nidxDecimal > -1)
231 | {
232 | cDec = value.Substring(nidxDecimal + 1).Trim().ToCharArray();
233 | cNum = value.Substring(0, nidxDecimal).ToCharArray();
234 |
235 | //throw an exception if decimal overflow would occur
236 | if (!mAllowDecimalTruncate && cDec.Length > ocol.DecimalCount)
237 | throw new DbfDataTruncateException("Value not set. Decimal does not fit and would be truncated. AllowDecimalTruncate is set to false. To supress this exception set AllowDecimalTruncate to true.");
238 | }
239 | else
240 | cNum = value.ToCharArray();
241 |
242 | //throw an exception if integer overflow would occur
243 | if (!mAllowIntegerTruncate && cNum.Length > ocol.Length - ocol.DecimalCount - 1)
244 | throw new DbfDataTruncateException("Value not set. Integer does not fit and would be truncated. AllowIntegerTruncate is set to false. To supress this exception set AllowIntegerTruncate to true, although that is not recomended.");
245 |
246 | //clear all decimals, set to 0.
247 | //-----------------------------------------------------
248 | Buffer.BlockCopy(mDecimalClear, 0, mData, (ocol.DataAddress + ocol.Length - ocol.DecimalCount), ocol.DecimalCount);
249 |
250 | //clear all numbers, set to [space].
251 | Buffer.BlockCopy(mEmptyRecord, 0, mData, ocol.DataAddress, (ocol.Length - ocol.DecimalCount));
252 |
253 |
254 |
255 | //set decimal numbers, CAREFUL not to overflow buffer! (truncate instead)
256 | //-----------------------------------------------------------------------
257 | if (nidxDecimal > -1)
258 | {
259 | int nLen = cDec.Length > ocol.DecimalCount ? ocol.DecimalCount : cDec.Length;
260 | encoding.GetBytes(cDec, 0, nLen, mData, (ocol.DataAddress + ocol.Length - ocol.DecimalCount));
261 | }
262 |
263 | //set integer part, CAREFUL not to overflow buffer! (truncate instead)
264 | //-----------------------------------------------------------------------
265 | int nNumLen = cNum.Length > ocol.Length - ocol.DecimalCount - 1 ? (ocol.Length - ocol.DecimalCount - 1) : cNum.Length;
266 | encoding.GetBytes(cNum, 0, nNumLen, mData, ocol.DataAddress + ocol.Length - ocol.DecimalCount - nNumLen - 1);
267 |
268 |
269 | //set decimal point
270 | //-----------------------------------------------------------------------
271 | mData[ocol.DataAddress + ocol.Length - ocol.DecimalCount - 1] = (byte)'.';
272 |
273 | }
274 |
275 | }
276 | else if (ocolType == DbfColumn.DbfColumnType.Integer)
277 | {
278 | //note this is a binary Integer type!
279 | //----------------------------------------------
280 |
281 | ///TODO: maybe there is a better way to copy 4 bytes from int to byte array. Some memory function or something.
282 | mTempIntVal[0] = Convert.ToInt32(value);
283 | Buffer.BlockCopy(mTempIntVal, 0, mData, ocol.DataAddress, 4);
284 |
285 | }
286 | else if (ocolType == DbfColumn.DbfColumnType.Memo)
287 | {
288 | //copy 10 digits...
289 | ///TODO: implement MEMO
290 |
291 | throw new NotImplementedException("Memo data type functionality not implemented yet!");
292 |
293 | }
294 | else if (ocolType == DbfColumn.DbfColumnType.Boolean)
295 | {
296 | if (String.Compare(value, "true", true) == 0 || String.Compare(value, "1", true) == 0 ||
297 | String.Compare(value, "T", true) == 0 || String.Compare(value, "yes", true) == 0 ||
298 | String.Compare(value, "Y", true) == 0)
299 | mData[ocol.DataAddress] = (byte)'T';
300 | else if (value == " " || value == "?")
301 | mData[ocol.DataAddress] = (byte)'?';
302 | else
303 | mData[ocol.DataAddress] = (byte)'F';
304 |
305 | }
306 | else if (ocolType == DbfColumn.DbfColumnType.Date)
307 | {
308 | //try to parse out date value using Date.Parse() function, then set the value
309 | DateTime dateval;
310 | if (DateTime.TryParse(value, out dateval))
311 | {
312 | SetDateValue(nColIndex, dateval);
313 | }
314 | else
315 | throw new InvalidOperationException("Date could not be parsed from source string! Please parse the Date and set the value (you can try using DateTime.Parse() or DateTime.TryParse() functions).");
316 |
317 | }
318 | else if (ocolType == DbfColumn.DbfColumnType.Binary)
319 | throw new InvalidOperationException("Can not use string source to set binary data. Use SetBinaryValue() and GetBinaryValue() functions instead.");
320 |
321 | else
322 | throw new InvalidDataException("Unrecognized data type: " + ocolType.ToString());
323 |
324 | }
325 | }
326 |
327 | get
328 | {
329 | DbfColumn ocol = mHeader[nColIndex];
330 | return new string(encoding.GetChars(mData, ocol.DataAddress, ocol.Length));//考虑中文
331 | }
332 | }
333 | ///
334 | /// Set string data to a column, if the string is longer than specified column length it will be truncated!
335 | /// If dbf column type is not a string, input will be treated as dbf column
336 | /// type and if longer than length an exception will be thrown.
337 | ///
338 | ///
339 | ///
340 | public string this[string nColName]
341 | {
342 | get
343 | {
344 | if (mColNameToConIdx.ContainsKey(nColName))
345 | return this[mColNameToConIdx[nColName]];
346 | throw new InvalidOperationException(string.Format("There's no column with name '{0}'", nColName));
347 | }
348 | set
349 | {
350 | if (mColNameToConIdx.ContainsKey(nColName))
351 | this[mColNameToConIdx[nColName]] = value;
352 | else
353 | throw new InvalidOperationException(string.Format("There's no column with name '{0}'", nColName));
354 | }
355 | }
356 | #endregion
357 |
358 | ///
359 | ///
360 | ///
361 | /// Dbf Header will be locked once a record is created
362 | /// since the record size is fixed and if the header was modified it would corrupt the DBF file.
363 | public DbfRecord(DbfHeader oHeader)
364 | {
365 | mHeader = oHeader;
366 | mHeader.Locked = true;
367 |
368 | //create a buffer to hold all record data. We will reuse this buffer to write all data to the file.
369 | mData = new byte[mHeader.RecordLength];
370 | mEmptyRecord = mHeader.EmptyDataRecord;
371 | encoding = oHeader.encoding;
372 |
373 | for (int i = 0; i < oHeader.mFields.Count; i++)
374 | mColNameToConIdx[oHeader.mFields[i].Name] = i;
375 | }
376 |
377 | ///
378 | /// Get date value.
379 | ///
380 | ///
381 | ///
382 | public DateTime GetDateValue(int nColIndex)
383 | {
384 | DbfColumn ocol = mHeader[nColIndex];
385 |
386 | if (ocol.ColumnType == DbfColumn.DbfColumnType.Date)
387 | {
388 | string sDateVal = encoding.GetString(mData, ocol.DataAddress, ocol.Length);
389 | return DateTime.ParseExact(sDateVal, "yyyyMMdd", CultureInfo.InvariantCulture);
390 |
391 | }
392 | else
393 | throw new Exception("Invalid data type. Column '" + ocol.Name + "' is not a date column.");
394 | }
395 | ///
396 | /// Get date value.
397 | ///
398 | ///
399 | ///
400 | public void SetDateValue(int nColIndex, DateTime value)
401 | {
402 | DbfColumn ocol = mHeader[nColIndex];
403 | DbfColumn.DbfColumnType ocolType = ocol.ColumnType;
404 |
405 | if (ocolType == DbfColumn.DbfColumnType.Date)
406 | {
407 |
408 | //Format date and set value, date format is like this: yyyyMMdd
409 | //-------------------------------------------------------------
410 | encoding.GetBytes(value.ToString("yyyyMMdd"), 0, ocol.Length, mData, ocol.DataAddress);
411 |
412 | }
413 | else
414 | throw new Exception("Invalid data type. Column is of '" + ocol.ColumnType.ToString() + "' type, not date.");
415 |
416 | }
417 | ///
418 | /// Clears all data in the record.
419 | ///
420 | public void Clear()
421 | {
422 | Buffer.BlockCopy(mEmptyRecord, 0, mData, 0, mEmptyRecord.Length);
423 | mRecordIndex = -1;
424 |
425 | }
426 | ///
427 | /// returns a string representation of this record.
428 | ///
429 | ///
430 | public override string ToString()
431 | {
432 | return new string(encoding.GetChars(mData));
433 | }
434 |
435 | ///
436 | /// Get column by index.
437 | ///
438 | ///
439 | ///
440 | public DbfColumn Column(int index)
441 | {
442 | return mHeader[index];
443 | }
444 | ///
445 | /// Get column by name.
446 | ///
447 | ///
448 | ///
449 | public DbfColumn Column(string sName)
450 | {
451 | return mHeader[sName];
452 | }
453 |
454 | ///
455 | /// Finds a column index by searching sequentially through the list. Case is ignored. Returns -1 if not found.
456 | ///
457 | /// Column name.
458 | /// Column index (0 based) or -1 if not found.
459 | public int FindColumn(string sName)
460 | {
461 | return mHeader.FindColumn(sName);
462 | }
463 |
464 | public int FindColumEx(string sName) {
465 | return mHeader.FindColumnEx(sName);
466 | }
467 | ///
468 | /// Writes data to stream. Make sure stream is positioned correctly because we simply write out the data to it.
469 | ///
470 | ///
471 | protected internal void Write(Stream osw)
472 | {
473 | osw.Write(mData, 0, mData.Length);
474 | }
475 | ///
476 | /// Writes data to stream. Make sure stream is positioned correctly because we simply write out data to it, and clear the record.
477 | ///
478 | ///
479 | protected internal void Write(Stream obw, bool bClearRecordAfterWrite)
480 | {
481 | obw.Write(mData, 0, mData.Length);
482 | if (bClearRecordAfterWrite)
483 | Clear();
484 | }
485 |
486 | ///
487 | /// Read record from stream. Returns true if record read completely, otherwise returns false.
488 | ///
489 | ///
490 | ///
491 | protected internal bool Read(Stream obr)
492 | {
493 | return obr.Read(mData, 0, mData.Length) >= mData.Length;
494 | }
495 |
496 | protected internal string ReadValue(Stream obr, int colIndex)
497 | {
498 | DbfColumn ocol = mHeader[colIndex];
499 | return new string(encoding.GetChars(mData, ocol.DataAddress, ocol.Length));
500 | }
501 | }
502 | }
503 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/GIS.HPU.ZYZ.SHP.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}
9 | Library
10 | Properties
11 | GIS.HPU.ZYZ.SHP
12 | GIS.HPU.ZYZ.SHP
13 | v4.0
14 |
15 |
16 | 512
17 |
18 |
19 | x86
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | x86
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 | False
43 | bin\Debug\ProjNet.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
80 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过以下
6 | // 特性集控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("GIS.HPU.ZYZ.SHP")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("GIS.HPU.ZYZ.SHP")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("0e168bd8-30fa-4c51-8b6f-1ece11f20a03")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 内部版本号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHP/ShpData.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 |
16 | namespace GIS.HPU.ZYZ.SHP.SHP
17 | {
18 | ///
19 | /// shp文件一条记录的数据结构
20 | ///
21 | ///
22 | public class ShpData
23 | {
24 | ///
25 | /// shape对象
26 | /// 目前支持处理EVPoint、EVPolyLine、EVPolygon三类
27 | ///
28 | private BaseShape mGeoShape;
29 | ///
30 | /// 几何对象的WKT字符串
31 | ///
32 | private string mWKTStr;
33 | ///
34 | /// 标记geo是否转换为wkt
35 | ///
36 | private bool mGeo2WKT = false;
37 | ///
38 | /// 标记wkt是否转换为geo
39 | ///
40 | private bool mWKT2Geo = false;
41 |
42 | ///
43 | /// shape对象
44 | /// 目前支持处理EVPoint、EVPolyLine、EVPolygon三类
45 | ///
46 | public BaseShape GeoShape
47 | {
48 | get { return mGeoShape; }
49 | set { mGeoShape = value; }
50 | }
51 | ///
52 | /// 几何对象的WKT字符串
53 | ///
54 | public string WKTStr
55 | {
56 | get {
57 | if (!mGeo2WKT) {
58 | mWKTStr=mGeoShape.ExportWKT();
59 | mGeo2WKT = true;
60 | }
61 | return mWKTStr;
62 | }
63 | set {
64 | mWKTStr = value;
65 | if (!mWKT2Geo) {
66 | //mGeoShape.TransFormWKT(mWKTStr);
67 | if (mWKTStr.Contains("POINT"))
68 | {
69 | mGeoShape = new EVPoint();
70 | mGeoShape.TransFormWKT(mWKTStr);
71 | }
72 | else if (mWKTStr.Contains("LINESTRING"))
73 | {
74 | mGeoShape = new EVPolyLine();
75 | mGeoShape.TransFormWKT(mWKTStr);
76 | }
77 | else if (mWKTStr.Contains("POLYGON"))
78 | {
79 | mGeoShape = new EVPolygon();
80 | mGeoShape.TransFormWKT(mWKTStr);
81 | }
82 | mWKT2Geo = true;
83 | }
84 | }
85 | }
86 |
87 | ///
88 | /// 投影坐标系转地理坐标系
89 | ///
90 | public void CoordTransPro2Geo()
91 | {
92 | mGeoShape.CoordTransPro2Geo();
93 | }
94 | ///
95 | /// 地理坐标系转投影坐标系
96 | ///
97 | public void CoordTransGeo2Pro()
98 | {
99 | mGeoShape.CoordTransGeo2Pro();
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHP/ShpDataType.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.Collections;
16 | using GIS.HPU.ZYZ.SHP.Util;
17 | using System.Text.RegularExpressions;
18 |
19 | namespace GIS.HPU.ZYZ.SHP.SHP
20 | {
21 | ///
22 | /// 坐标文件的坐标系
23 | /// 目前支持GCS_WGS_1984、WGS_1984_Albers两种
24 | ///
25 | public enum ShapeProject
26 | {
27 | ///
28 | /// 未知坐标系
29 | ///
30 | NULL=-1,
31 | ///
32 | /// wgs84地理坐标系
33 | ///
34 | GCS_WGS_1984=0,
35 | ///
36 | /// wgs84 albers投影坐标系
37 | ///
38 | WGS_1984_Albers=1
39 | }
40 |
41 | ///
42 | /// 记录几何类型
43 | ///
44 | public enum ShapeType
45 | {
46 | ///
47 | /// 表示这个Shapefile文件不含坐标
48 | ///
49 | NullShape =0,
50 | ///
51 | /// 表示Shapefile文件记录的是点状目标,但不是多点
52 | ///
53 | Point =1,
54 | ///
55 | /// 表示Shapefile文件记录的是线状目标
56 | ///
57 | PolyLine =3,
58 | ///
59 | /// 表示Shapefile文件记录的是面状目标
60 | ///
61 | Polygon =5,
62 | ///
63 | /// 表示Shapefile文件记录的是多点,即点集合
64 | ///
65 | MultiPoint =8,
66 | ///
67 | /// 表示Shapefile文件记录的是三维点状目标
68 | ///
69 | PointZ =11,
70 | ///
71 | /// 表示Shapefile文件记录的是三维线状目标
72 | ///
73 | PolyLineZ =13,
74 | ///
75 | /// 表示Shapefile文件记录的是三维面状目标
76 | ///
77 | PolygonZ =15,
78 | ///
79 | /// 表示Shapefile文件记录的是三维点集合目标
80 | ///
81 | MultiPointZ=18,
82 | ///
83 | /// 表示含有Measure值的点状目标
84 | ///
85 | PointM =21,
86 | ///
87 | /// 表示含有Measure值的线状目标
88 | ///
89 | PolyLineM =23,
90 | ///
91 | /// 表示含有Measure值的面状目标
92 | ///
93 | PolygonM =25,
94 | ///
95 | /// 表示含有Measure值的多点目标
96 | ///
97 | MultiPointM=28,
98 | ///
99 | /// 表示复合目标
100 | ///
101 | MultiPatch =31
102 | }
103 |
104 | ///
105 | /// 记录几何类型基类
106 | /// 每条记录有相应的文件头RecordNum DataLength ShapeType
107 | ///
108 | public class BaseShape
109 | {
110 | ///
111 | /// 记录在文件中的编号 索引从1开始
112 | /// 相当于featureid 第几个记录
113 | ///
114 | public ulong RecordNum;
115 | ///
116 | /// 记录长度
117 | /// shp文件标准:表示坐标文件中的对应记录的长度。
118 | /// 以字为单位,一字等于2字节,等于16位
119 | /// 长度不包括每条记录的RecordNum和DataLength共8字节4位
120 | ///
121 | public int DataLength;
122 | ///
123 | /// 记录的几何类型
124 | ///
125 | public ShapeType GeoType;
126 |
127 | ///
128 | /// 将GeoShape转为wkt字符串
129 | ///
130 | ///
131 | public virtual string ExportWKT()
132 | {
133 | return null;
134 | }
135 | ///
136 | /// 将wkt转为字符串GeoShape
137 | ///
138 | public virtual void TransFormWKT(string wkt)
139 | {
140 | }
141 | ///
142 | /// 投影坐标系转地理坐标系
143 | ///
144 | public virtual void CoordTransPro2Geo()
145 | {
146 | }
147 | ///
148 | /// 地理坐标系转投影坐标系
149 | ///
150 | public virtual void CoordTransGeo2Pro()
151 | {
152 | }
153 | }
154 |
155 | ///
156 | /// 点
157 | ///
158 | public class EVPoint:BaseShape
159 | {
160 | public double X;
161 | public double Y;
162 |
163 | public EVPoint() { }
164 | public EVPoint(double _x,double _y)
165 | {
166 | this.X = _x;
167 | this.Y = _y;
168 | }
169 | ///
170 | /// 导出wkt
171 | ///
172 | ///
173 | public override string ExportWKT()
174 | {
175 | return "POINT("+X+" "+Y+")";
176 | }
177 | ///
178 | /// 导入wkt
179 | ///
180 | ///
181 | public override void TransFormWKT(string wkt)
182 | {
183 | if (wkt.Contains("POINT")) {
184 | Regex coordinateGroupPattern = new Regex("[0-9.]+ [0-9., ]+");
185 | MatchCollection coordinateGroupMatch = coordinateGroupPattern.Matches(wkt);
186 | List value = new List();
187 | //如果大于0说明正常
188 | if (coordinateGroupMatch.Count != 0)
189 | {
190 | char[] scpoint = { ' ' };
191 | string[] xy = coordinateGroupMatch[0].Value.Split(scpoint, StringSplitOptions.RemoveEmptyEntries);//coordinateGroupMatch[0].Value.Split(',');
192 | if (xy.Length >= 2)//是2或者三
193 | {
194 | X = Convert.ToDouble(xy[0]);
195 | Y = Convert.ToDouble(xy[1]);
196 | }
197 |
198 | }
199 | }
200 | this.GeoType = ShapeType.Point;
201 | //this.RecordNum //放在外围控制
202 | //记录长度 每条长度不算RecorderNum和ContentLength的4字 点固定为10字
203 | this.DataLength = 10;//字数 1字=2byte=16位
204 | }
205 | ///
206 | /// 投影坐标系转地理坐标系
207 | ///
208 | public override void CoordTransPro2Geo()
209 | {
210 | EVPoint point = CoordTransform.ProToGeo(new EVPoint(X, Y));
211 | X = point.X;
212 | Y = point.Y;
213 | }
214 | ///
215 | /// 地理坐标系转投影坐标系
216 | ///
217 | public override void CoordTransGeo2Pro()
218 | {
219 | EVPoint point = CoordTransform.GeoToPro(new EVPoint(X, Y));
220 | X = point.X;
221 | Y = point.Y;
222 | }
223 | }
224 | ///
225 | /// 线
226 | ///
227 | public class EVPolyLine : BaseShape
228 | {
229 | public double Xmin; //边界盒
230 | public double Ymin; //边界盒
231 | public double Xmax; //边界盒
232 | public double Ymax; //边界盒
233 | public int NumParts; //部分的数目:包含子线段个数
234 | public int NumPoints; //点的总数目
235 | public ArrayList Parts; //在部分中第一个点的索引
236 | public ArrayList Points; //所有部分的点
237 |
238 | ///
239 | /// 导出wkt
240 | ///
241 | ///
242 | public override string ExportWKT()
243 | {
244 | string wkt = "";
245 | //只考虑外环 其它舍去!
246 | int index = 0;
247 | if (Parts.Count > 1)
248 | {
249 | index = (int)Parts[1] - 1;//是否需要减1 没有验证!!!
250 | }
251 | else
252 | {
253 | index = Points.Count;
254 | }
255 | if (index >= 2)
256 | {
257 | wkt += "LINESTRING ((";
258 | wkt += ((EVPoint)Points[0]).X + " " + ((EVPoint)Points[0]).Y;
259 | for (int i = 1; i < index; i++)
260 | {
261 | wkt += "," + ((EVPoint)Points[i]).X + " " + ((EVPoint)Points[i]).Y;
262 | }
263 | wkt += "))";
264 | }
265 | return wkt;
266 | }
267 | ///
268 | /// 导入wkt
269 | ///
270 | ///
271 | public override void TransFormWKT(string wkt)
272 | {
273 | if (wkt.Contains("LINESTRING"))
274 | {
275 | //清除当前
276 | if (Parts != null)
277 | {
278 | Parts.Clear();
279 | }
280 | else
281 | {
282 | Parts = new ArrayList();
283 | }
284 | if (Points != null)
285 | {
286 | Points.Clear();
287 | }
288 | else
289 | {
290 | Points = new ArrayList();
291 | }
292 | Regex coordinateGroupPattern = new Regex("[0-9.]+ [0-9., ]+");
293 | MatchCollection coordinateGroupMatch = coordinateGroupPattern.Matches(wkt);
294 | List value = new List();
295 | //如果大于0说明正常 大于1说明有内环 第一个为多边形外环
296 | if (coordinateGroupMatch.Count != 0)
297 | {
298 | //只考虑外环,内含舍去
299 | //foreach (Match m in coordinateGroupMatch)
300 | //{
301 | // value.Add(m.Value);
302 | //}
303 | char[] sc = { ',' };
304 | char[] scpoint = { ' ' };
305 | string[] cors = coordinateGroupMatch[0].Value.Split(sc, StringSplitOptions.RemoveEmptyEntries);//coordinateGroupMatch[0].Value.Split(',');
306 | if (cors.Length > 0)
307 | {
308 | for (int i = 0; i < cors.Length; i++)
309 | {
310 | EVPoint point = new EVPoint();
311 | string[] xy = cors[i].Split(scpoint, StringSplitOptions.RemoveEmptyEntries);//cors[i].Split(' ');
312 | if (xy.Length < 2)//是2或者三
313 | {
314 | continue;
315 | }
316 | else
317 | {
318 | point.X = Convert.ToDouble(xy[0]);
319 | point.Y = Convert.ToDouble(xy[1]);
320 | Points.Add(point);
321 | }
322 | }
323 | }
324 | }
325 | }
326 | if (Points.Count > 0)
327 | {
328 | Parts.Add(0);
329 | NumParts = 1;
330 | NumPoints = Points.Count;
331 | //求边界盒
332 | double[] border = BorderUtil.GetBorder(Points);
333 | Xmin = border[0];
334 | Ymin = border[1];
335 | Xmax = border[2];
336 | Ymax = border[3];
337 |
338 | this.GeoType = ShapeType.PolyLine;
339 | //this.RecordNum //放在外围控制
340 | //记录长度 每条长度不算RecorderNum和ContentLength的4字 NumParts固定为1
341 | this.DataLength = 24 + 8 * NumPoints;//字数 1字=2byte=16位
342 | }
343 | else
344 | {
345 | Parts.Add(0);
346 | NumParts = 0;
347 | NumPoints = 0;
348 | Xmin = 0;
349 | Ymin = 0;
350 | Xmax = 0;
351 | Ymax = 0;
352 | this.GeoType = ShapeType.PolyLine;
353 | this.DataLength = 24 + 8 * NumPoints;//字数 1字=2byte=16位
354 | }
355 | }
356 |
357 | ///
358 | /// 投影坐标系转地理坐标系
359 | ///
360 | public override void CoordTransPro2Geo()
361 | {
362 | for (int i = 0; i < Points.Count; i++)
363 | {
364 | EVPoint point = CoordTransform.ProToGeo((EVPoint)Points[i]);
365 | Points.RemoveAt(i);
366 | Points.Insert(i, point);
367 | }
368 | }
369 | ///
370 | /// 地理坐标系转投影坐标系
371 | ///
372 | public override void CoordTransGeo2Pro()
373 | {
374 | for (int i = 0; i < Points.Count; i++) {
375 | EVPoint point = CoordTransform.GeoToPro((EVPoint)Points[i]);
376 | Points.RemoveAt(i);
377 | Points.Insert(i,point);
378 | }
379 | //更新边界盒
380 | double[] border = BorderUtil.GetBorder(Points);
381 | Xmin = border[0];
382 | Ymin = border[1];
383 | Xmax = border[2];
384 | Ymax = border[3];
385 | }
386 | }
387 | ///
388 | /// 面
389 | ///
390 | public class EVPolygon : BaseShape
391 | {
392 | public double Xmin; //边界盒
393 | public double Ymin; //边界盒
394 | public double Xmax; //边界盒
395 | public double Ymax; //边界盒
396 | public int NumParts; //部分的数目:包含子环的个数
397 | public int NumPoints; //点的总数目
398 | public ArrayList Parts; //在部分中第一个点的索引
399 | public ArrayList Points; //所有部分的点
400 |
401 | ///
402 | /// 导出wkt
403 | ///
404 | ///
405 | public override string ExportWKT()
406 | {
407 | string wkt = "";
408 | //只考虑外环 其它舍去!
409 | int index = 0;
410 | if (Parts.Count > 1)
411 | {
412 | index = (int)Parts[1] - 1;//是否需要减1 没有验证!!!
413 | }
414 | else {
415 | index = Points.Count;
416 | }
417 | if (index > 2)
418 | {
419 | wkt += "POLYGON ((";
420 | wkt += ((EVPoint)Points[0]).X + " " + ((EVPoint)Points[0]).Y;
421 | for (int i = 1; i < index; i++)
422 | {
423 | wkt += "," + ((EVPoint)Points[i]).X + " " + ((EVPoint)Points[i]).Y;
424 | }
425 | wkt += "))";
426 | }
427 | return wkt;
428 | }
429 | ///
430 | /// 导入wkt
431 | ///
432 | ///
433 | public override void TransFormWKT(string wkt)
434 | {
435 | if (wkt.Contains("POLYGON"))
436 | {
437 | //清除当前
438 | if (Parts != null)
439 | {
440 | Parts.Clear();
441 | }
442 | else
443 | {
444 | Parts = new ArrayList();
445 | }
446 | if (Points != null)
447 | {
448 | Points.Clear();
449 | }
450 | else
451 | {
452 | Points = new ArrayList();
453 | }
454 | Regex coordinateGroupPattern = new Regex("[0-9.]+ [0-9., ]+");
455 | MatchCollection coordinateGroupMatch = coordinateGroupPattern.Matches(wkt);
456 | List value = new List();
457 | //如果大于0说明正常 大于1说明有内环 第一个为多边形外环
458 | if (coordinateGroupMatch.Count != 0)
459 | {
460 | //只考虑外环,内含舍去
461 | //foreach (Match m in coordinateGroupMatch)
462 | //{
463 | // value.Add(m.Value);
464 | //}
465 | char[] sc = { ',' };
466 | char[] scpoint = { ' ' };
467 | string[] cors = coordinateGroupMatch[0].Value.Split(sc, StringSplitOptions.RemoveEmptyEntries);//coordinateGroupMatch[0].Value.Split(',');
468 | if (cors.Length > 0)
469 | {
470 | for (int i = 0; i < cors.Length; i++)
471 | {
472 | EVPoint point = new EVPoint();
473 | string[] xy = cors[i].Split(scpoint, StringSplitOptions.RemoveEmptyEntries);//cors[i].Split(' ');
474 | if (xy.Length < 2)//是2或者三
475 | {
476 | continue;
477 | }
478 | else
479 | {
480 | point.X = Convert.ToDouble(xy[0]);
481 | point.Y = Convert.ToDouble(xy[1]);
482 | Points.Add(point);
483 | }
484 | }
485 | }
486 | }
487 | }
488 | if (Points.Count > 0)
489 | {
490 | Parts.Add(0);
491 | NumParts = 1;
492 | NumPoints = Points.Count;
493 | //求边界盒
494 | double[] border = BorderUtil.GetBorder(Points);
495 | Xmin = border[0];
496 | Ymin = border[1];
497 | Xmax = border[2];
498 | Ymax = border[3];
499 |
500 | this.GeoType = ShapeType.Polygon;
501 | //this.RecordNum //放在外围控制
502 | //记录长度 每条长度不算RecorderNum和ContentLength的4字 NumParts固定为1
503 | this.DataLength = 24 + 8 * NumPoints;//字数 1字=2byte=16位
504 | }
505 | else
506 | {
507 | Parts.Add(0);
508 | NumParts = 0;
509 | NumPoints = 0;
510 | Xmin = 0;
511 | Ymin = 0;
512 | Xmax = 0;
513 | Ymax = 0;
514 | this.GeoType = ShapeType.Polygon;
515 | this.DataLength = 24 + 8 * NumPoints;//字数 1字=2byte=16位
516 | }
517 | }
518 |
519 | ///
520 | /// 投影坐标系转地理坐标系
521 | ///
522 | public override void CoordTransPro2Geo()
523 | {
524 | for (int i = 0; i < Points.Count; i++)
525 | {
526 | EVPoint point = CoordTransform.ProToGeo((EVPoint)Points[i]);
527 | Points.RemoveAt(i);
528 | Points.Insert(i, point);
529 | }
530 | }
531 | ///
532 | /// 地理坐标系转投影坐标系
533 | ///
534 | public override void CoordTransGeo2Pro()
535 | {
536 | for (int i = 0; i < Points.Count; i++)
537 | {
538 | EVPoint point = CoordTransform.GeoToPro((EVPoint)Points[i]);
539 | Points.RemoveAt(i);
540 | Points.Insert(i, point);
541 | }
542 | //更新边界盒
543 | double[] border = BorderUtil.GetBorder(Points);
544 | Xmin = border[0];
545 | Ymin = border[1];
546 | Xmax = border[2];
547 | Ymax = border[3];
548 | }
549 | }
550 | }
551 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHP/ShpFile.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.IO;
16 | using GIS.HPU.ZYZ.SHP.SHX;
17 |
18 | namespace GIS.HPU.ZYZ.SHP.SHP
19 | {
20 | ///
21 | /// 负责shp文件的读写
22 | /// 文件的读取和写入同一实例对象不能同时使用!
23 | /// 可以从shp文件和数据库的数据类中读取Header和Record信息
24 | /// 如果是写入信息,则需要先从数据库的数据类中读取Record信息,然后计算出Header信息
25 | ///
26 | ///
27 | /// 读取shp文件流程:
28 | /// var oshp = new ShpFile();
29 | /// oshp.Open(filepath, FileMode.Open);//open的同时执行了头文件的读取
30 | /// oshp.ReadShxRecord();//读取索引文件 此处需要注意shp头文件和shx头文件的FileLength不同其他都一样,这个在读取文件没影响,shx头文件直接使用shp的头文件
31 | /// oshp.ReadShpRecord();//读取记录
32 | /// oshp.ShpFileProject = ShapeProject.WGS_1984_Albers;//设置坐标系
33 | /// oshp.CoordTransPro2Geo();//投影转换
34 | /// string wkt = oshp.ShpRecord.RecordDic[1].WKTStr;//获取每个记录的wkt
35 | ///
36 | public class ShpFile
37 | {
38 | ///
39 | /// 读写shp文件头
40 | /// 读写操作放在ShpHeader
41 | ///
42 | protected ShpHeader mHeader;
43 | ///
44 | /// 读写shp记录
45 | /// 读写操作放在ShpRecord
46 | ///
47 | protected ShpRecord mShpRecord;
48 | ///
49 | /// 读写shx
50 | /// 读写操作放在ShxRecord
51 | ///
52 | protected ShxRecord mShxRecord;
53 | ///
54 | /// 当前数据坐标系
55 | ///
56 | private ShapeProject mShpFileProject;
57 | ///
58 | /// 标记是否已经读取文件头
59 | ///
60 | protected bool mHeaderWritten = false;
61 | ///
62 | /// 标记是否已经设置wkt
63 | ///
64 | protected bool mFileCreat = false;
65 | ///
66 | /// 读写shp文件流.
67 | ///
68 | protected Stream mShpFile = null;
69 | ///
70 | /// 读写shx文件流.
71 | ///
72 | protected Stream mShxFile = null;
73 | ///
74 | /// shp文件读取流
75 | ///
76 | protected BinaryReader mShpFileReader = null;
77 | ///
78 | /// shx文件读取流
79 | ///
80 | protected BinaryReader mShxFileReader = null;
81 | ///
82 | /// shp文件写入流
83 | ///
84 | protected BinaryWriter mShpFileWriter = null;
85 | ///
86 | /// shx文件写入流
87 | ///
88 | protected BinaryWriter mShxFileWriter = null;
89 | ///
90 | /// 当前打开文件的文件名.
91 | ///
92 | protected string mFileName = "";
93 | ///
94 | /// 记录读取的记录个数
95 | ///
96 | protected int mRecordsReadCount = 0;
97 | ///
98 | /// keep these values handy so we don't call functions on every read.
99 | ///
100 | protected bool mIsForwardOnly = false;
101 | protected bool mIsReadOnly = false;
102 |
103 | #region 属性
104 | ///
105 | /// 返回文件流的文件名
106 | ///
107 | public string FileName
108 | {
109 | get
110 | {
111 | return mFileName;
112 | }
113 | }
114 | ///
115 | /// 如果不能写入shp文件流返回true
116 | ///
117 | public bool IsReadOnly
118 | {
119 | get
120 | {
121 | return mIsReadOnly;
122 | }
123 | }
124 | ///
125 | /// 如果可以指针可以移动到文件流的任意位置返回true
126 | ///
127 | public bool IsForwardOnly
128 | {
129 | get
130 | {
131 | return mIsForwardOnly;
132 | }
133 | }
134 | ///
135 | /// 当前数据坐标系
136 | ///
137 | public ShapeProject ShpFileProject
138 | {
139 | get { return mShpFileProject; }
140 | set { mShpFileProject = value; }
141 | }
142 | ///
143 | /// 头文件信息操作类
144 | ///
145 | public ShpHeader Header
146 | {
147 | get
148 | {
149 | return mHeader;
150 | }
151 | }
152 | ///
153 | /// shp文件记录信息操作类
154 | ///
155 | public ShpRecord ShpRecord
156 | {
157 | get
158 | {
159 | return mShpRecord;
160 | }
161 | }
162 | ///
163 | /// shx文件记录信息操作类
164 | ///
165 | public ShxRecord ShxRecord
166 | {
167 | get
168 | {
169 | return mShxRecord;
170 | }
171 | }
172 | #endregion
173 |
174 | ///
175 | ///
176 | ///
177 | public ShpFile()
178 | {
179 | mHeader = new ShpHeader();
180 | }
181 |
182 | ///
183 | /// 打开shp、shx文件流
184 | /// 读取shp、shx信息时需要首先执行此操作
185 | /// 同时操作头文件读取和ShxRecord、ShpRecord示例化
186 | ///
187 | ///
188 | public void Open(Stream shpStream,Stream shxStream)
189 | {
190 | if (mShpFile != null)
191 | Close();
192 |
193 | mShpFile = shpStream;
194 | mShxFile = shxStream;
195 | mShpFileReader = null;
196 | mShpFileWriter = null;
197 | mShxFileReader = null;
198 | mShxFileWriter = null;
199 | if (mShpFile.CanRead)
200 | mShpFileReader = new BinaryReader(mShpFile);
201 | if (mShpFile.CanWrite)
202 | mShpFileWriter = new BinaryWriter(mShpFile);
203 |
204 | if (mShxFile.CanRead)
205 | mShxFileReader = new BinaryReader(mShxFile);
206 | if (mShxFile.CanWrite)
207 | mShxFileWriter = new BinaryWriter(mShxFile);
208 | //reset position
209 | mRecordsReadCount = 0;
210 | //assume header is not written
211 | mHeaderWritten = false;
212 |
213 | //read the header
214 | if (shpStream.CanRead)
215 | {
216 | //try to read the header...
217 | try
218 | {
219 | mHeader.Read(mShpFileReader);
220 | mHeaderWritten = true;
221 |
222 | }
223 | catch (EndOfStreamException)
224 | {
225 | //could not read header, file is empty
226 | mHeader = new ShpHeader();
227 | mHeaderWritten = false;
228 | }
229 | }
230 | if (mShpFile != null)
231 | {
232 | mIsReadOnly = !mShpFile.CanWrite;
233 | mIsForwardOnly = !mShpFile.CanSeek;
234 | }
235 | mShxRecord = new ShxRecord(mHeader);
236 | mShpRecord = new ShpRecord(mHeader);
237 | }
238 | ///
239 | /// 打开shp、shx文件流
240 | /// 读取shp、shx信息时需要首先执行此操作
241 | ///
242 | /// 文件全路径
243 | ///
244 | public void Open(string sPath, FileMode mode)
245 | {
246 | mFileName = sPath;
247 | string shxpath = mFileName.Remove(mFileName.Length - 1, 1) + "x";
248 | Open(File.Open(sPath, mode), File.Open(shxpath, mode));
249 | }
250 |
251 | ///
252 | /// 读取shx文件的记录信息
253 | /// 先执行ReadShxRecord 再执行ReadShpRecord
254 | /// 先执行Open此函数才有效
255 | ///
256 | public void ReadShxRecord()
257 | {
258 | if (mHeaderWritten && mShxFile.CanRead)
259 | {
260 | mShxRecord.Read(mShxFileReader);
261 | }
262 | }
263 |
264 | ///
265 | /// 读取shp文件的记录信息
266 | /// 先执行ReadShxRecord 再执行ReadShpRecord
267 | /// 先执行Open此函数才有效
268 | ///
269 | public void ReadShpRecord()
270 | {
271 | if (mHeaderWritten && mShpFile.CanRead) {
272 | mShpRecord.Read(mShpFileReader);
273 | }
274 | }
275 | ///
276 | /// 写入文件
277 | ///
278 | ///
279 | ///
280 | public void Creat(Stream shpStream, Stream shxStream)
281 | {
282 | if (mShpFile != null)
283 | Close();
284 |
285 | mShpFile = shpStream;
286 | mShxFile = shxStream;
287 | mShpFileWriter = null;
288 | mShxFileWriter = null;
289 | //if (mShpFile.CanRead)
290 | // mShpFileReader = new BinaryReader(mShpFile);
291 | if (mShpFile.CanWrite)
292 | mShpFileWriter = new BinaryWriter(mShpFile);
293 |
294 | //if (mShxFile.CanRead)
295 | // mShxFileReader = new BinaryReader(mShxFile);
296 | if (mShxFile.CanWrite)
297 | mShxFileWriter = new BinaryWriter(mShxFile);
298 | //reset position
299 | //mRecordsReadCount = 0;
300 | //assume header is not written
301 | mFileCreat = true;
302 |
303 | if (mShpFile != null)
304 | {
305 | mIsReadOnly = !mShpFile.CanWrite;
306 | mIsForwardOnly = !mShpFile.CanSeek;
307 | }
308 | //mShxRecord = new ShxRecord(mHeader);
309 | mShpRecord = new ShpRecord(mHeader);
310 | }
311 | ///
312 | /// 写入文件
313 | ///
314 | ///
315 | ///
316 | public void Creat(string sPath, FileMode mode)
317 | {
318 | mFileName = sPath;
319 | string shxpath = mFileName.Remove(mFileName.Length - 1, 1) + "x";
320 | Creat(File.Open(sPath, mode), File.Open(shxpath, mode));
321 | }
322 | ///
323 | /// 写入shx
324 | /// 此时需要注意头文件不能和shp头文件关联,FileLength不同
325 | ///
326 | public void WriteShx()
327 | {
328 | if (mFileCreat) {
329 | mShxRecord.Write(mShxFileWriter);
330 | }
331 | }
332 | ///
333 | /// 写入shp
334 | ///
335 | ///
336 | public void WriteShp(List wktList)
337 | {
338 | if (mFileCreat) {
339 | mShpRecord.GetWKTInfo(wktList);
340 | mShxRecord = new ShxRecord(mShpRecord.Header);
341 | mHeader.Write(mShpFileWriter);
342 | mShxRecord.RecordDic= mShpRecord.Write(mShpFileWriter);
343 | }
344 | }
345 | ///
346 | /// 将WGS_1984_Albers坐标系转换为GCS_WGS_1984
347 | ///
348 | public void CoordTransPro2Geo()
349 | {
350 | if (mShpFileProject == ShapeProject.GCS_WGS_1984){}
351 | else {
352 | mShpRecord.CoordTransPro2Geo();
353 | mShpFileProject = ShapeProject.GCS_WGS_1984;
354 | }
355 | }
356 | ///
357 | /// 将GCS_WGS_1984坐标系转换为WGS_1984_Albers
358 | ///
359 | public void CoordTransGeo2Pro()
360 | {
361 | if (mShpFileProject == ShapeProject.WGS_1984_Albers) { }
362 | else
363 | {
364 | mShpRecord.CoordTransGeo2Pro();
365 | mShpFileProject = ShapeProject.WGS_1984_Albers;
366 | }
367 | }
368 | ///
369 | /// 更新header信息 清除(flush)buffers 关闭streams
370 | /// 在操作完shp文件后必须调用此方法
371 | ///
372 | public void Close()
373 | {
374 | //Empty header...
375 | //--------------------------------
376 | mHeader = new ShpHeader();
377 | mHeaderWritten = false;
378 |
379 | //reset current record index
380 | //--------------------------------
381 | mRecordsReadCount = 0;
382 |
383 | //Close streams...
384 | //--------------------------------
385 | if (mShpFileWriter != null)
386 | {
387 | mShpFileWriter.Flush();
388 | mShpFileWriter.Close();
389 | }
390 | if (mShpFileReader != null)
391 | mShpFileReader.Close();
392 | if (mShpFile != null)
393 | mShpFile.Close();
394 |
395 | if (mShxFileWriter != null)
396 | {
397 | mShxFileWriter.Flush();
398 | mShxFileWriter.Close();
399 | }
400 | if (mShxFileReader != null)
401 | mShxFileReader.Close();
402 | if (mShxFile != null)
403 | mShxFile.Close();
404 | //set streams to null
405 | //--------------------------------
406 | mShpFileReader = null;
407 | mShpFileWriter = null;
408 | mShpFile = null;
409 |
410 | mShxFileReader = null;
411 | mShxFileWriter = null;
412 | mShxFile = null;
413 |
414 | mFileName = "";
415 | }
416 |
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHP/ShpHeader.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.IO;
16 | using GIS.HPU.ZYZ.SHP.Util;
17 |
18 | namespace GIS.HPU.ZYZ.SHP.SHP
19 | {
20 | ///
21 | /// 坐标文件(.shp)用于记录空间坐标信息。
22 | /// 它由头文件和实体信息两部分构成。
23 | /// 坐标文件的文件头是一个长度固定(100 bytes)的记录段,一共有9个int型和7个double型数据。
24 | ///
25 | ///
26 | /// Position Field Value Type Order
27 | /// Byte 0 File Code 9994 Integer Big |-----
28 | /// Byte 4 Unused 0 Integer Big |Value Shape Type
29 | /// Byte 8 Unused 0 Integer Big |0 Null Shape
30 | /// Byte 12 Unused 0 Integer Big |1 Point
31 | /// Byte 16 Unused 0 Integer Big |3 PolyLine
32 | /// Byte 20 Unused 0 Integer Big |5 Polygon
33 | /// Byte 24 File Length File Length Integer Big |8 MultiPoint
34 | /// Byte 28 Version 1000 Integer Little |11 PointZ
35 | /// Byte 32 Shape Type Shape Type Integer Little----|13 PolyLineZ
36 | /// Byte 36 Bounding Box Xmin Double Little |15 PolygonZ
37 | /// Byte 44 Bounding Box Ymin Double Little |18 MultiPointZ
38 | /// Byte 52 Bounding Box Xmax Double Little |21 PointM
39 | /// Byte 60 Bounding Box Ymax Double Little |23 PolyLineM
40 | /// Byte 68* Bounding Box Zmin Double Little |25 PolygonM
41 | /// Byte 76* Bounding Box Zmax Double Little |28 MultiPointM
42 | /// Byte 84* Bounding Box Mmin Double Little |31 MultiPatch
43 | /// Byte 92* Bounding Box Mmax Double Little |----
44 | ///
45 | ///
46 | public class ShpHeader
47 | {
48 | private int mFileCode;
49 | private int mFileLength;
50 | private int mVersion;
51 | private ShapeType mGeoType;
52 | private double mXmin;
53 | private double mYmin;
54 | private double mXmax;
55 | private double mYmax;
56 | private double mZmin;
57 | private double mZmax;
58 | private double mMmin;
59 | private double mMmax;
60 | #region 属性
61 | ///
62 | /// 文件code
63 | /// 9994 big位序
64 | ///
65 | public int FileCode
66 | {
67 | get { return mFileCode; }
68 | set { mFileCode = value; }
69 | }
70 | ///
71 | /// 文件长度
72 | /// 文件的实际长度
73 | /// big位序
74 | ///
75 | public int FileLength
76 | {
77 | get { return mFileLength; }
78 | set { mFileLength = value; }
79 | }
80 | ///
81 | /// 版本号
82 | /// 1000
83 | /// Little位序
84 | ///
85 | public int Version
86 | {
87 | get { return mVersion; }
88 | set { mVersion = value; }
89 | }
90 | ///
91 | /// 几何类型
92 | /// 表示这个Shapefile文件所记录的空间数据的几何类型
93 | /// Little位序
94 | ///
95 | public ShapeType GeoType
96 | {
97 | get { return mGeoType; }
98 | set { mGeoType = value; }
99 | }
100 | ///
101 | /// 空间数据所占空间范围的X方向最小值
102 | ///
103 | public double Xmin
104 | {
105 | get { return mXmin; }
106 | set { mXmin = value; }
107 | }
108 | ///
109 | /// 空间数据所占空间范围的Y方向最小值
110 | ///
111 | public double Ymin
112 | {
113 | get { return mYmin; }
114 | set { mYmin = value; }
115 | }
116 | ///
117 | /// 空间数据所占空间范围的X方向最大值
118 | ///
119 | public double Xmax
120 | {
121 | get { return mXmax; }
122 | set { mXmax = value; }
123 | }
124 | ///
125 | /// 空间数据所占空间范围的Y方向最大值
126 | ///
127 | public double Ymax
128 | {
129 | get { return mYmax; }
130 | set { mYmax = value; }
131 | }
132 | ///
133 | /// 空间数据所占空间范围的Z方向最小值
134 | ///
135 | public double Zmin
136 | {
137 | get { return mZmin; }
138 | set { mZmin = value; }
139 | }
140 | ///
141 | /// 空间数据所占空间范围的Z方向最大值
142 | ///
143 | public double Zmax
144 | {
145 | get { return mZmax; }
146 | set { mZmax = value; }
147 | }
148 | ///
149 | /// 最小Measure值
150 | ///
151 | public double Mmin
152 | {
153 | get { return mMmin; }
154 | set { mMmin = value; }
155 | }
156 | ///
157 | /// 最大Measure值
158 | ///
159 | public double Mmax
160 | {
161 | get { return mMmax; }
162 | set { mMmax = value; }
163 | }
164 | #endregion
165 |
166 | ///
167 | /// 读取header数据
168 | /// 确定读取时文件流位置在开始处
169 | /// 当读取完成文件流位置在第一个记录处
170 | ///
171 | ///
172 | public void Read(BinaryReader reader)
173 | {
174 | // type of reader.
175 |
176 | mFileCode = reader.ReadInt32();
177 | mFileCode = (int)ByteTransUtil.big2little(mFileCode);
178 | reader.ReadBytes(20);//预留5个inter
179 | mFileLength = reader.ReadInt32();
180 | mFileLength = (int)ByteTransUtil.big2little(mFileLength);//以字为单位,一字等于2字节,等于16位
181 |
182 | mVersion = reader.ReadInt32();
183 | int typeTemp = reader.ReadInt32();
184 | mGeoType = (ShapeType)typeTemp;//目前考虑点 线 面三种
185 | mXmin = reader.ReadDouble();
186 | mYmin = reader.ReadDouble();
187 | mXmax = reader.ReadDouble();
188 | mYmax = reader.ReadDouble();
189 | mZmin = reader.ReadDouble();
190 | mZmax = reader.ReadDouble();
191 | mMmin = reader.ReadDouble();
192 | mMmax = reader.ReadDouble();
193 | }
194 |
195 | ///
196 | /// Encoding must be ASCII for this binary writer.
197 | ///
198 | ///
199 | ///
200 | /// 参考shp文件头文件结构.
201 | ///
202 | public void Write(BinaryWriter writer)
203 | {
204 | writer.Flush();//清除
205 | byte[] lbtFileCode = BitConverter.GetBytes(mFileCode); //将int转变为byte
206 | byte[] bbtFileCode = ByteTransUtil.little2big(lbtFileCode);
207 | writer.Write(bbtFileCode);
208 | int Unused = 0; //未使用 一共5个 big
209 | byte[] lbtUnused = BitConverter.GetBytes(Unused); //将int转变为byte
210 | byte[] bbtUnused = ByteTransUtil.little2big(lbtUnused);
211 | for (int i = 0; i < 5; i++)
212 | {
213 | writer.Write(bbtUnused);
214 | }
215 | byte[] lbtFileLength = BitConverter.GetBytes(mFileLength); //将int转变为byte
216 | byte[] bbtFileLength = ByteTransUtil.little2big(lbtFileLength);
217 | writer.Write(bbtFileLength);
218 | writer.Write(mVersion);
219 | int tyeptemp = (int)mGeoType;
220 | writer.Write(tyeptemp);//---------------------
221 | writer.Write(mXmin);
222 | writer.Write(mYmin);
223 | writer.Write(mXmax);
224 | writer.Write(mYmax);
225 | writer.Write(mZmin);
226 | writer.Write(mZmax);
227 | writer.Write(mMmin);
228 | writer.Write(mMmax);
229 | }
230 |
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHP/ShpRecord.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using System.IO;
16 | using System.Collections;
17 | using GIS.HPU.ZYZ.SHP.Util;
18 | using GIS.HPU.ZYZ.SHP.SHX;
19 |
20 | namespace GIS.HPU.ZYZ.SHP.SHP
21 | {
22 | ///
23 | /// shp文件对应的记录内容
24 | /// 提供文件记录的读写操作
25 | ///
26 | public class ShpRecord
27 | {
28 | ///
29 | /// Header提供shp文件的详细信息
30 | /// 类型、大小、包围盒等
31 | ///
32 | private ShpHeader mHeader = null;
33 | ///
34 | /// shp文件记录字典
35 | /// key:shp的记录索引--从1开始(featureid)
36 | /// value:该条记录的具体内容:ShpGeoData类型
37 | ///
38 | private Dictionary mRecordDic = null;
39 | ///
40 | /// 是否获取wkt信息,标记是否可以写入record
41 | ///
42 | private bool isGetWKTInfo = false;
43 | #region
44 | ///
45 | /// Header提供shp文件的详细信息
46 | /// 类型、大小、包围盒等
47 | ///
48 | public ShpHeader Header
49 | {
50 | get { return mHeader; }
51 | set { mHeader = value; }
52 | }
53 | ///
54 | /// shp文件记录字典
55 | /// key:shp的记录索引--从1开始(featureid)
56 | /// value:该条记录的具体内容:ShpData类型
57 | ///
58 | public Dictionary RecordDic
59 | {
60 | get { return mRecordDic; }
61 | //set { mRecordDic = value; }
62 | }
63 | ///
64 | /// 是否获取wkt信息,标记是否可以写入record
65 | /// 在调用Write时先判断此标记
66 | ///
67 | public bool IsGetWKTInfo
68 | {
69 | get { return isGetWKTInfo; }
70 | //set { isGetWKTInfo = value; }
71 | }
72 | #endregion
73 |
74 | ///
75 | /// 构造时传入已经实例化的ShpHeader对象
76 | ///
77 | ///
78 | public ShpRecord(ShpHeader oHeader)
79 | {
80 | mHeader = oHeader;
81 | }
82 |
83 | ///
84 | /// 读取record数据
85 | /// 当读取时文件流位置在第一个记录处
86 | ///
87 | ///
88 | public void Read(BinaryReader reader)
89 | {
90 | //如果header为空先读取header
91 | if (mHeader == null) {
92 | mHeader.Read(reader);
93 | }
94 | if (mRecordDic != null)
95 | {
96 | mRecordDic.Clear();
97 | }
98 | else {
99 | mRecordDic = new Dictionary();
100 | }
101 | //目前只考虑点线面三种类型
102 | switch (mHeader.GeoType) {
103 | case ShapeType.Point:
104 | while (reader.PeekChar() != -1)
105 | {
106 | EVPoint point = new EVPoint();
107 | uint RecordNum = reader.ReadUInt32();
108 | int DataLength = reader.ReadInt32();
109 | ulong uRecordNum = ByteTransUtil.big2little((int)RecordNum);//读取第i个记录
110 | //每一条的记录长度没有考虑RecordNum、DataLength 所以比实际长度少4字
111 | int uDataLength = (int)ByteTransUtil.big2little(DataLength);//以字为单位,一字等于2字节,等于16位 长度不包括uRecordNum和uDataLength
112 | int geoType = reader.ReadInt32();//每一个对象的类型 没有用!
113 | point.RecordNum = uRecordNum;
114 | point.DataLength = uDataLength;//固定10字 没有考虑RecordNum、DataLength 比实际长度少4字
115 | point.GeoType = ShapeType.Point;
116 |
117 | point.X = reader.ReadDouble();
118 | point.Y = reader.ReadDouble();//
119 | ShpData item = new ShpData();
120 | item.GeoShape = point;
121 | mRecordDic.Add(uRecordNum, item);
122 | }
123 | break;
124 | case ShapeType.PolyLine:
125 | while (reader.PeekChar() != -1)
126 | {
127 | EVPolyLine polyline = new EVPolyLine();
128 | polyline.Parts = new ArrayList();
129 | polyline.Points = new ArrayList();
130 |
131 | uint RecordNum = reader.ReadUInt32();//读取第i个记录 从1开始算
132 | ulong uRecordNum = ByteTransUtil.big2little((int)RecordNum);
133 | int DataLength = reader.ReadInt32();//以字为单位,一字等于2字节,等于16位
134 | //每一条的记录长度没有考虑RecordNum、DataLength 所以比实际长度少4字
135 | int uDataLength = (int)ByteTransUtil.big2little(DataLength);
136 | int geoType = reader.ReadInt32();//每一个对象的类型 没有用
137 |
138 | polyline.RecordNum = uRecordNum;
139 | polyline.DataLength = uDataLength;//没有考虑RecordNum、DataLength 比实际长度少4字
140 | polyline.GeoType = ShapeType.PolyLine;
141 |
142 | polyline.Xmin = reader.ReadDouble();
143 | polyline.Ymin = reader.ReadDouble();
144 | polyline.Xmax = reader.ReadDouble();
145 | polyline.Ymax = reader.ReadDouble();
146 | polyline.NumParts = reader.ReadInt32();
147 | polyline.NumPoints = reader.ReadInt32();
148 | for (int i = 0; i < polyline.NumParts; i++)
149 | {
150 | int parts = new int();
151 | parts = reader.ReadInt32();
152 | polyline.Parts.Add(parts);
153 | }
154 | for (int j = 0; j < polyline.NumPoints; j++)
155 | {
156 |
157 | EVPoint pointtemp = new EVPoint();
158 | pointtemp.X = reader.ReadDouble();
159 | pointtemp.Y = reader.ReadDouble();
160 | polyline.Points.Add(pointtemp);
161 | }
162 | ShpData item = new ShpData();
163 | item.GeoShape = polyline;
164 | mRecordDic.Add(uRecordNum, item);
165 | }
166 | break;
167 | case ShapeType.Polygon:
168 | while (reader.PeekChar() != -1)
169 | {
170 | EVPolygon polygon = new EVPolygon();
171 | polygon.Parts = new ArrayList();
172 | polygon.Points = new ArrayList();
173 |
174 | uint RecordNum = reader.ReadUInt32();
175 | int DataLength = reader.ReadInt32();
176 | ulong uRecordNum = ByteTransUtil.big2little((int)RecordNum);//读取第i个记录
177 | //每一条的记录长度没有考虑RecordNum、DataLength 所以比实际长度少4字
178 | //以字为单位,一字等于2字节,等于16位
179 | int uDataLength = (int)ByteTransUtil.big2little(DataLength);
180 | int geoType = reader.ReadInt32();
181 |
182 | polygon.RecordNum = uRecordNum;
183 | polygon.DataLength = uDataLength;
184 | polygon.GeoType = ShapeType.Polygon;
185 | polygon.Xmin = reader.ReadDouble();
186 | polygon.Ymin = reader.ReadDouble();
187 | polygon.Xmax = reader.ReadDouble();
188 | polygon.Ymax = reader.ReadDouble();
189 | polygon.NumParts = reader.ReadInt32();
190 | polygon.NumPoints = reader.ReadInt32();
191 | for (int j = 0; j < polygon.NumParts; j++)
192 | {
193 | int parts = new int();
194 | parts = reader.ReadInt32();
195 | polygon.Parts.Add(parts);
196 | }
197 | for (int j = 0; j < polygon.NumPoints; j++)
198 | {
199 | EVPoint pointtemp = new EVPoint();
200 | pointtemp.X = reader.ReadDouble();
201 | pointtemp.Y = reader.ReadDouble();
202 | polygon.Points.Add(pointtemp);
203 | }
204 | ShpData item = new ShpData();
205 | item.GeoShape = polygon;
206 | mRecordDic.Add(uRecordNum, item);
207 | }
208 | break;
209 | }
210 | }
211 |
212 | ///
213 | /// 从wkt获取shp文件信息,包括头文件和记录
214 | ///
215 | ///
216 | public void GetWKTInfo(List wktList)
217 | {
218 | if (mRecordDic != null)
219 | {
220 | mRecordDic.Clear();
221 | }
222 | else {
223 | mRecordDic = new Dictionary();
224 | }
225 |
226 | if (wktList!=null && wktList.Count > 0) {
227 | for(int i=0;i
240 | /// 写入record记录
241 | /// 先判断是否获取wkt信息以及写入header
242 | /// 写入的同时 计算shx的记录内容
243 | ///
244 | ///
245 | /// 返回shx的文件内容
246 | public Dictionary Write(BinaryWriter writer)
247 | {
248 | Dictionary shxDic = new Dictionary();
249 | if (isGetWKTInfo) {
250 | if (writer.BaseStream.Position == 0)
251 | {
252 | mHeader.Write(writer);
253 | }
254 | int offset = 50;//跳过头文件
255 | switch (mHeader.GeoType)
256 | {
257 | case ShapeType.Point:
258 | for (int i = 0; i < mRecordDic.Count; i++)
259 | {
260 | EVPoint point = mRecordDic[(ulong)(i + 1)].GeoShape as EVPoint;
261 | ShxData shx = new ShxData();
262 | shx.ContentLength = point.DataLength;
263 | shx.Offset = offset;
264 | offset += shx.ContentLength + 4;
265 | shxDic.Add(point.RecordNum, shx);
266 | //写入shp
267 | byte[] lbtRecorderNum = BitConverter.GetBytes(point.RecordNum);
268 | byte[] bbtRecorderNum = ByteTransUtil.little2big(lbtRecorderNum);
269 | writer.Write(bbtRecorderNum);
270 |
271 | byte[] lbtDataLength = BitConverter.GetBytes(point.DataLength);
272 | byte[] bbtDataLength = ByteTransUtil.little2big(lbtDataLength);
273 | writer.Write(bbtDataLength);
274 | writer.Write((int)ShapeType.Point);
275 | writer.Write(point.X);
276 | writer.Write(point.Y);
277 | }
278 |
279 | break;
280 | case ShapeType.PolyLine:
281 | for (int i = 0; i < mRecordDic.Count; i++)
282 | {
283 | EVPolyLine line = mRecordDic[(ulong)(i + 1)].GeoShape as EVPolyLine;
284 | ShxData shx = new ShxData();
285 | shx.ContentLength = line.DataLength;
286 | shx.Offset = offset;
287 | offset += shx.ContentLength + 4;
288 | shxDic.Add(line.RecordNum,shx);
289 | //写入shp
290 | byte[] lbtRecorderNum = BitConverter.GetBytes(line.RecordNum);
291 | byte[] bbtRecorderNum = ByteTransUtil.little2big(lbtRecorderNum);
292 | writer.Write(bbtRecorderNum);
293 |
294 | byte[] lbtDataLength = BitConverter.GetBytes(line.DataLength);
295 | byte[] bbtDataLength = ByteTransUtil.little2big(lbtDataLength);
296 | writer.Write(bbtDataLength);
297 |
298 | writer.Write((int)line.GeoType);
299 | writer.Write(line.Xmin);
300 | writer.Write(line.Ymin);
301 | writer.Write(line.Xmax);
302 | writer.Write(line.Ymax);
303 |
304 | writer.Write(line.NumParts);
305 | writer.Write(line.NumPoints);
306 |
307 | for (int j = 0; j < line.NumParts; j++)
308 | {
309 | writer.Write((int)line.Parts[j]);
310 | }
311 |
312 | for (int j = 0; j < line.NumPoints; j++)
313 | {
314 | EVPoint point = line.Points[j] as EVPoint;
315 | writer.Write(point.X);
316 | writer.Write(point.Y);
317 | }
318 | }
319 |
320 | break;
321 | case ShapeType.Polygon:
322 | for (int i = 0; i < mRecordDic.Count; i++)
323 | {
324 | EVPolygon gon = mRecordDic[(ulong)(i + 1)].GeoShape as EVPolygon;
325 | ShxData shx = new ShxData();
326 | shx.ContentLength = gon.DataLength;
327 | shx.Offset = offset;
328 | offset += shx.ContentLength + 4;
329 | shxDic.Add(gon.RecordNum, shx);
330 | //写入shp
331 | byte[] lbtRecorderNum = BitConverter.GetBytes(gon.RecordNum);
332 | byte[] bbtRecorderNum = ByteTransUtil.little2big(lbtRecorderNum);
333 | writer.Write(bbtRecorderNum);
334 |
335 | byte[] lbtDataLength = BitConverter.GetBytes(gon.DataLength);
336 | byte[] bbtDataLength = ByteTransUtil.little2big(lbtDataLength);
337 | writer.Write(bbtDataLength);
338 |
339 | writer.Write((int)gon.GeoType);
340 | writer.Write(gon.Xmin);
341 | writer.Write(gon.Ymin);
342 | writer.Write(gon.Xmax);
343 | writer.Write(gon.Ymax);
344 |
345 | writer.Write(gon.NumParts);
346 | writer.Write(gon.NumPoints);
347 |
348 | for (int j = 0; j < gon.NumParts; j++)
349 | {
350 | writer.Write((int)gon.Parts[j]);
351 | }
352 |
353 | for (int j = 0; j < gon.NumPoints; j++)
354 | {
355 | EVPoint point = gon.Points[j] as EVPoint;
356 | writer.Write(point.X);
357 | writer.Write(point.Y);
358 | }
359 | }
360 |
361 | break;
362 | }
363 |
364 | }
365 | return shxDic;
366 | }
367 | ///
368 | /// 根据RecordDic求header
369 | ///
370 | private void TransHeader() {
371 | if (mHeader == null) {
372 | mHeader = new ShpHeader();
373 | }
374 |
375 | mHeader.FileCode = 9994;//固定
376 | mHeader.FileLength = 50;//文件头
377 | foreach (var item in mRecordDic) {
378 | mHeader.FileLength += item.Value.GeoShape.DataLength+4;//每条记录多出4字
379 | }
380 | mHeader.Version = 1000;//固定
381 | mHeader.GeoType = mRecordDic[1].GeoShape.GeoType;
382 | GetBorder();//求包围盒
383 | }
384 | ///
385 | /// 求包围盒
386 | ///
387 | private void GetBorder() {
388 | mHeader.Zmin = 0;
389 | mHeader.Zmax = 0;
390 | mHeader.Mmin = 0;
391 | mHeader.Mmax = 0;
392 |
393 | double Xmin = 0;
394 | double Ymin = 0;
395 | double Xmax = 0;
396 | double Ymax = 0;
397 | switch (mHeader.GeoType) {
398 | case ShapeType.Point:
399 | EVPoint pP = mRecordDic[1].GeoShape as EVPoint;
400 | Xmin = pP.X;
401 | Ymin = pP.Y;
402 | Xmax = pP.X;
403 | Ymax = pP.Y;
404 | for (ulong i = 0; i <(ulong)mRecordDic.Count;i++ )
405 | {
406 | EVPoint pont = mRecordDic[i+1].GeoShape as EVPoint;
407 | if (Xmin > pont.X)
408 | {
409 | Xmin = pont.X;
410 | }
411 | if (Xmax < pont.X)
412 | {
413 | Xmax = pont.X;
414 | }
415 | if (Ymin > pont.Y)
416 | {
417 | Ymin = pont.Y;
418 | }
419 | if (Ymax < pont.Y)
420 | {
421 | Ymax = pont.Y;
422 | }
423 | }
424 | mHeader.Xmin = Xmin;
425 | mHeader.Ymin = Ymin;
426 | mHeader.Xmax = Xmax;
427 | mHeader.Ymax = Ymax;
428 | break;
429 | case ShapeType.PolyLine:
430 | EVPolyLine pL = mRecordDic[1].GeoShape as EVPolyLine;
431 | Xmin = pL.Xmin;
432 | Ymin = pL.Ymin;
433 | Xmax = pL.Xmax;
434 | Ymax = pL.Ymax;
435 | for (ulong i = 0; i <(ulong)mRecordDic.Count;i++ )
436 | {
437 | EVPolyLine line = mRecordDic[i + 1].GeoShape as EVPolyLine;
438 | if (Xmin > line.Xmin)
439 | {
440 | Xmin = line.Xmin;
441 | }
442 | if (Xmax < line.Xmax)
443 | {
444 | Xmax = line.Xmax;
445 | }
446 | if (Ymin > line.Ymin)
447 | {
448 | Ymin = line.Ymin;
449 | }
450 | if (Ymax < line.Ymax)
451 | {
452 | Ymax = line.Ymax;
453 | }
454 | }
455 | mHeader.Xmin = Xmin;
456 | mHeader.Ymin = Ymin;
457 | mHeader.Xmax = Xmax;
458 | mHeader.Ymax = Ymax;
459 | break;
460 | case ShapeType.Polygon:
461 | EVPolygon pG = mRecordDic[1].GeoShape as EVPolygon;
462 | Xmin = pG.Xmin;
463 | Ymin = pG.Ymin;
464 | Xmax = pG.Xmax;
465 | Ymax = pG.Ymax;
466 | for (ulong i = 0; i <(ulong)mRecordDic.Count;i++ )
467 | {
468 | EVPolygon gon = mRecordDic[i + 1].GeoShape as EVPolygon;
469 | if (Xmin > gon.Xmin)
470 | {
471 | Xmin = gon.Xmin;
472 | }
473 | if (Xmax < gon.Xmax)
474 | {
475 | Xmax = gon.Xmax;
476 | }
477 | if (Ymin > gon.Ymin)
478 | {
479 | Ymin = gon.Ymin;
480 | }
481 | if (Ymax < gon.Ymax)
482 | {
483 | Ymax = gon.Ymax;
484 | }
485 | }
486 | mHeader.Xmin = Xmin;
487 | mHeader.Ymin = Ymin;
488 | mHeader.Xmax = Xmax;
489 | mHeader.Ymax = Ymax;
490 | break;
491 | }
492 | }
493 | ///
494 | /// 获取记录数量
495 | ///
496 | ///
497 | public int GetRecordCount() {
498 | return mRecordDic!=null ? mRecordDic.Count : 0;
499 | }
500 | ///
501 | /// 投影坐标系转地理坐标系
502 | ///
503 | public void CoordTransPro2Geo()
504 | {
505 | foreach (var item in mRecordDic)
506 | {
507 | item.Value.CoordTransPro2Geo();
508 | }
509 | }
510 | ///
511 | /// 地理坐标系转投影坐标系
512 | ///
513 | public void CoordTransGeo2Pro()
514 | {
515 | foreach (var item in mRecordDic)
516 | {
517 | item.Value.CoordTransGeo2Pro();
518 | }
519 | }
520 |
521 | }
522 | }
523 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHX/ShxData.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 |
16 | namespace GIS.HPU.ZYZ.SHP.SHX
17 | {
18 | ///
19 | /// shx文件记录内容的数据结构
20 | ///
21 | ///
22 | /// 每一条记录包括偏移量(offset)和记录段长度(Content Length)两个记录项,它们的位序都是big,两个记录项都是int型。
23 | ///
24 | /// Position Field Value Type Byte Order
25 | /// Byte 0 Offset Offset Integer Big
26 | /// Byte 4 Content Length Content Length Integer Big
27 | ///
28 | /// 位移量(Offset)表示坐标文件中的对应记录的起始位置相对于坐标文件起始位置的位移量。
29 | /// 以字为单位,一字等于2字节,等于16位 长度包括每条记录的RecordNum和DataLength共8字节4位
30 | /// 记录长度(Content Length) 表示坐标文件中的对应记录的长度。
31 | /// 以字为单位,一字等于2字节,等于16位 长度不包括每条记录的RecordNum和DataLength共8字节4位
32 | ///
33 | public class ShxData
34 | {
35 | ///
36 | /// 位移量(Offset)
37 | /// 表示坐标文件中的对应记录的起始位置相对于坐标文件起始位置的位移量。
38 | /// 以字为单位,一字等于2字节,等于16位
39 | /// 长度包括每条记录的RecordNum和DataLength共8字节4位
40 | ///
41 | public int Offset;
42 | ///
43 | /// 记录长度(Content Length)
44 | /// 表示坐标文件中的对应记录的长度
45 | /// 以字为单位,一字等于2字节,等于16位
46 | /// 长度不包括每条记录的RecordNum和DataLength共8字节4位。
47 | ///
48 | public int ContentLength;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/SHX/ShxRecord.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:
9 | /// Date:
10 | /// Desc:
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using GIS.HPU.ZYZ.SHP.SHP;
16 | using System.IO;
17 | using GIS.HPU.ZYZ.SHP.Util;
18 |
19 | namespace GIS.HPU.ZYZ.SHP.SHX
20 | {
21 | ///
22 | /// shx文件详细信息
23 | /// 包括头文件和记录内容
24 | ///
25 | ///
26 | /// 索引文件(.shx)主要包含坐标文件的索引信息
27 | /// 文件中每个记录包含对应的坐标文件记录距离坐标文件的文件头的偏移量。
28 | /// 通过索引文件可以很方便地在坐标文件中定位到指定目标的坐标信息。
29 | /// 其中文件头部分是一个长度固定(100 bytes)的记录段,其内容与坐标文件的文件头基本一致。
30 | /// 它的实体信息以记录为基本单位,每一条记录包括偏移量(offset)和记录段长度(Content Length)两个记录项,它们的位序都是big,两个记录项都是int型。
31 | ///
32 | /// Position Field Value Type Byte Order
33 | /// Byte 0 Offset Offset Integer Big
34 | /// Byte 4 Content Length Content Length Integer Big
35 | ///
36 | /// 位移量(Offset)表示坐标文件中的对应记录的起始位置相对于坐标文件起始位置的位移量。
37 | /// 以字为单位,一字等于2字节,等于16位 长度包括每条记录的RecordNum和DataLength共8字节4位
38 | /// 记录长度(Content Length) 表示坐标文件中的对应记录的长度。
39 | /// 以字为单位,一字等于2字节,等于16位 长度不包括每条记录的RecordNum和DataLength共8字节4位
40 | ///
41 | public class ShxRecord
42 | {
43 | ///
44 | /// Header提供shp文件的详细信息
45 | /// 类型、大小、包围盒等
46 | ///
47 | private ShpHeader mHeader = null;
48 | ///
49 | /// shp文件记录字典
50 | /// key:shp的记录索引--从1开始(featureid)
51 | /// value:该条记录的具体内容:ShpGeoData类型
52 | ///
53 | private Dictionary mRecordDic = null;
54 | #region
55 | ///
56 | /// Header提供shp文件的详细信息
57 | /// 类型、大小、包围盒等
58 | ///
59 | public ShpHeader Header
60 | {
61 | get { return mHeader; }
62 | set { mHeader = value; }
63 | }
64 | ///
65 | /// shp文件记录字典
66 | /// key:shp的记录索引--从1开始(featureid)
67 | /// value:该条记录的具体内容:ShxData类型
68 | ///
69 | public Dictionary RecordDic
70 | {
71 | get { return mRecordDic; }
72 | set { mRecordDic = value; }
73 | }
74 | #endregion
75 |
76 | ///
77 | /// 构造时传入已经实例化的ShpHeader对象
78 | ///
79 | ///
80 | public ShxRecord(ShpHeader oHeader)
81 | {
82 | mHeader = oHeader;
83 | }
84 | ///
85 | /// 读取record数据
86 | /// 当读取时文件流位置在第一个记录处
87 | ///
88 | /// shx文件流
89 | public void Read(BinaryReader reader)
90 | {
91 | //如果header为空先读取header shp和shx的文件头一样
92 | if (mHeader == null)
93 | {
94 | mHeader.Read(reader);
95 | }
96 | if (mRecordDic != null)
97 | {
98 | mRecordDic.Clear();
99 | }
100 | else
101 | {
102 | mRecordDic = new Dictionary();
103 | }
104 | if (reader.BaseStream.Position == 0) {
105 | reader.BaseStream.Seek(100,SeekOrigin.Begin);//跳过文件头
106 | }
107 | while (reader.PeekChar() != -1)
108 | {
109 | ShxData item = new ShxData();
110 | int Offset = reader.ReadInt32();
111 | int ContentLength = reader.ReadInt32();
112 | ulong uOffset = ByteTransUtil.big2little(Offset);//
113 | ulong uContentLength = ByteTransUtil.big2little(ContentLength);//
114 | item.Offset = (int)uOffset;//
115 | item.ContentLength = (int)uContentLength;
116 | mRecordDic.Add((ulong)(mRecordDic.Count+1), item);//key值从1开始
117 | }
118 | }
119 | ///
120 | /// 写入record
121 | ///
122 | ///
123 | public void Write(BinaryWriter writer)
124 | {
125 | if (mRecordDic == null||mHeader==null) {
126 | return;
127 | }
128 | writer.Flush();//清除
129 | byte[] lbtFileCode = BitConverter.GetBytes(mHeader.FileCode); //将int转变为byte
130 | byte[] bbtFileCode = ByteTransUtil.little2big(lbtFileCode);
131 | writer.Write(bbtFileCode);
132 | int Unused = 0; //未使用 一共5个 big
133 | byte[] lbtUnused = BitConverter.GetBytes(Unused); //将int转变为byte
134 | byte[] bbtUnused = ByteTransUtil.little2big(lbtUnused);
135 | for (int i = 0; i < 5; i++)
136 | {
137 | writer.Write(bbtUnused);
138 | }
139 | int mFileLength = 50 + mRecordDic.Count * 4;//两个inter 4字
140 | byte[] lbtFileLength = BitConverter.GetBytes(mFileLength); //将int转变为byte
141 | byte[] bbtFileLength = ByteTransUtil.little2big(lbtFileLength);
142 | writer.Write(bbtFileLength);
143 | writer.Write(mHeader.Version);
144 | int tyeptemp = (int)mHeader.GeoType;
145 | writer.Write(tyeptemp);
146 | writer.Write(mHeader.Xmin);
147 | writer.Write(mHeader.Ymin);
148 | writer.Write(mHeader.Xmax);
149 | writer.Write(mHeader.Ymax);
150 | writer.Write(mHeader.Zmin);
151 | writer.Write(mHeader.Zmax);
152 | writer.Write(mHeader.Mmin);
153 | writer.Write(mHeader.Mmax);
154 | //---------------------写入记录---------------------
155 | for (ulong i = 0; i < (ulong)mRecordDic.Count; i++)
156 | {
157 |
158 | //写入shp
159 | byte[] lbtOffset = BitConverter.GetBytes(mRecordDic[i+1].Offset);
160 | byte[] bbtOffset = ByteTransUtil.little2big(lbtOffset);
161 | writer.Write(bbtOffset);
162 |
163 | byte[] lbtContentLength = BitConverter.GetBytes(mRecordDic[i + 1].ContentLength);
164 | byte[] bbtContentLength = ByteTransUtil.little2big(lbtContentLength);
165 | writer.Write(bbtContentLength);
166 |
167 | }
168 |
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/Util/BorderUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Collections;
6 | using GIS.HPU.ZYZ.SHP.SHP;
7 |
8 | namespace GIS.HPU.ZYZ.SHP.Util
9 | {
10 | ///
11 | /// 边界盒操作类
12 | ///
13 | public class BorderUtil
14 | {
15 | ///
16 | /// 获取坐标串的边界盒
17 | ///
18 | /// evpoint类型
19 | ///
20 | public static double[] GetBorder(ArrayList points)
21 | {
22 | double[] border = new double[4];
23 | double Xmin = ((EVPoint)points[0]).X;
24 | double Ymin = ((EVPoint)points[0]).Y;
25 | double Xmax = ((EVPoint)points[0]).X;
26 | double Ymax = ((EVPoint)points[0]).Y;
27 | foreach (EVPoint pont in points)
28 | {
29 | if (Xmin > pont.X)
30 | {
31 | Xmin = pont.X;
32 | }
33 | if (Xmax < pont.X)
34 | {
35 | Xmax = pont.X;
36 | }
37 | if (Ymin > pont.Y)
38 | {
39 | Ymin = pont.Y;
40 | }
41 | if (Ymax < pont.Y)
42 | {
43 | Ymax = pont.Y;
44 | }
45 | }
46 | border[0] = Xmin;
47 | border[1] = Ymin;
48 | border[2] = Xmax;
49 | border[3] = Ymax;
50 | return border;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/Util/ByteTransUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace GIS.HPU.ZYZ.SHP.Util
7 | {
8 | ///
9 | /// 位操作工具类
10 | /// 提供big little转换
11 | ///
12 | public static class ByteTransUtil
13 | {
14 | ///
15 | /// 将big位序转换为little位序
16 | ///
17 | ///
18 | ///
19 | public static ulong big2little(int code)
20 | {
21 | ulong value;
22 | value = ((((ulong)(code) & (ulong)0x000000ffUL) << 24) | (((ulong)(code) & (ulong)0x0000ff00UL) << 8) | (((ulong)(code) & (ulong)0x00ff0000UL) >> 8) | (((ulong)(code) & (ulong)0xff000000UL) >> 24));
23 | return value;
24 | }
25 | ///
26 | /// 将little位序转换为big位序
27 | ///
28 | ///
29 | ///
30 | public static byte[] little2big(byte[] lbt)
31 | {
32 | byte[] bbt = new byte[4];
33 | bbt[0] = lbt[3];
34 | bbt[1] = lbt[2];
35 | bbt[2] = lbt[1];
36 | bbt[3] = lbt[0];
37 | return bbt;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/Util/CoordTransform.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Author: YanzheZhang
3 | /// Date: 26/1/2018
4 | /// Desc:
5 | ///
6 | /// Revision History:
7 | /// -----------------------------------
8 | /// Author:LiuJiao
9 | /// Date:---
10 | /// Desc:---
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Text;
15 | using ProjNet.CoordinateSystems;
16 | using ProjNet.Converters.WellKnownText;
17 | using GIS.HPU.ZYZ.SHP.SHP;
18 | using ProjNet.CoordinateSystems.Transformations;
19 |
20 | namespace GIS.HPU.ZYZ.SHP.Util
21 | {
22 | ///
23 | /// 该类为平面坐标与地理坐标相互转换类
24 | ///
25 | public class CoordTransform
26 | {
27 | ///
28 | /// 该值指示地理坐标系统
29 | ///
30 | private static IGeographicCoordinateSystem GeoCS;
31 | ///
32 | /// 该值指示投影坐标系统
33 | ///
34 | private static IProjectedCoordinateSystem ProCS;
35 | ///
36 | /// 是否初始化路径
37 | ///
38 | private static bool isInitPath = false;
39 |
40 | ///
41 | /// 创建坐标转换工具
42 | ///
43 | public static void InitPath()
44 | {
45 | string basePath = "";
46 | System.Reflection.Assembly asm = System.Reflection.Assembly.GetEntryAssembly();
47 | if (asm == null)
48 | basePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
49 | else
50 | basePath = System.AppDomain.CurrentDomain.BaseDirectory;// System.Windows.Forms.Application.StartupPath;
51 | string geoFile = System.IO.Path.Combine(basePath, "Project\\geopro.prj");
52 | string proFile = System.IO.Path.Combine(basePath, "Project\\abers.prj");
53 | using (System.IO.StreamReader sr = new System.IO.StreamReader(geoFile))
54 | {
55 | string geowkt = sr.ReadLine();
56 | GeoCS = CoordinateSystemWktReader.Parse(geowkt) as IGeographicCoordinateSystem;
57 | }
58 | using (System.IO.StreamReader sr = new System.IO.StreamReader(proFile))
59 | {
60 | string prowkt = sr.ReadLine();
61 | ProCS = CoordinateSystemWktReader.Parse(prowkt) as IProjectedCoordinateSystem;
62 | }
63 | isInitPath = true;
64 | }
65 | ///
66 | /// 创建坐标转换工具
67 | ///
68 | /// 该值指示地理-投影的坐标文件
69 | /// 该值指示投影-地理的坐标文件
70 | public static void ChangePath(string geoToPrj, string prjToGeo)
71 | {
72 | using (System.IO.StreamReader sr = new System.IO.StreamReader(geoToPrj))
73 | {
74 | string geowkt = sr.ReadLine();
75 | GeoCS = CoordinateSystemWktReader.Parse(geowkt) as IGeographicCoordinateSystem;
76 | }
77 | using (System.IO.StreamReader sr = new System.IO.StreamReader(prjToGeo))
78 | {
79 | string prowkt = sr.ReadLine();
80 | ProCS = CoordinateSystemWktReader.Parse(prowkt) as IProjectedCoordinateSystem;
81 | }
82 | isInitPath = true;
83 | }
84 | ///
85 | /// 该方法用于将投影坐标转换为地理坐标
86 | ///
87 | /// 该值指示投影坐标
88 | /// 该值指示地理坐标
89 | public static EVPoint ProToGeo(EVPoint position)
90 | {
91 | if (!isInitPath) {
92 | InitPath();
93 | }
94 | double[] d = new double[3] { position.X, position.Y, 0 };
95 | EVPoint v = CoordianteConvert(new EVPoint(d[0], d[1]), ProCS, GeoCS);
96 | return v;
97 | }
98 | ///
99 | /// 该方法用于将地理坐标转换成投影坐标
100 | ///
101 | /// 地理坐标
102 | /// 投影坐标
103 | public static EVPoint GeoToPro(EVPoint geoPosition)
104 | {
105 | if (!isInitPath)
106 | {
107 | InitPath();
108 | }
109 | double[] d = new double[3] { geoPosition.X, geoPosition.Y, 0 };
110 | EVPoint v = CoordianteConvert(new EVPoint(d[0], d[1]), GeoCS, ProCS);
111 | return v;
112 | }
113 | ///
114 | /// 该方法用于将某一坐标从一个坐标系统转换成另一个坐标系统的坐标
115 | ///
116 | /// 该值指示待转换坐标
117 | /// 该值指示待转换坐标的坐标系统
118 | /// 该值指示目标坐标系统
119 | /// 该值指示转换后的结果
120 | private static EVPoint CoordianteConvert(EVPoint input, ICoordinateSystem fromCS, ICoordinateSystem toCS)
121 | {
122 | try
123 | {
124 | List linput = new List();
125 | linput.Add(new double[] { input.X, input.Y, 0 });
126 | CoordinateTransformationFactory ctfac = new CoordinateTransformationFactory();
127 | ICoordinateTransformation trans = ctfac.CreateFromCoordinateSystems(fromCS, toCS);
128 | List loutput = trans.MathTransform.TransformList(linput);
129 | EVPoint output = new EVPoint();
130 | double[] dout = loutput[0];
131 | output.X = dout[0];
132 | output.Y = dout[1];
133 | //output.Z = dout[2];
134 | return output;
135 | }
136 | catch (Exception ex)
137 | {
138 | Console.WriteLine("坐标转换错误:CoordinateConvert(...)", ex.ToString());
139 | return null;
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.dll
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.pdb
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.vshost.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.vshost.exe
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/GIS.HPU.ZYZ.SHP.vshost.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/ProjNet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/bin/Debug/ProjNet.dll
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/project/abers.prj:
--------------------------------------------------------------------------------
1 | PROJCS["WGS_1984_Albers",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Albers"],PARAMETER["false_easting",0.0],PARAMETER["false_northing",0.0],PARAMETER["central_meridian",105.0],PARAMETER["standard_parallel_1",25.0],PARAMETER["standard_parallel_2",47.0],PARAMETER["latitude_of_origin",0.0],UNIT["Meter",1.0]]
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/bin/Debug/project/geopro.prj:
--------------------------------------------------------------------------------
1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.csproj.FileListAbsolute.txt:
--------------------------------------------------------------------------------
1 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\GIS.HPU.ZYZ.SHP\bin\Debug\GIS.HPU.ZYZ.SHP.dll
2 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\GIS.HPU.ZYZ.SHP\bin\Debug\GIS.HPU.ZYZ.SHP.pdb
3 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\GIS.HPU.ZYZ.SHP\obj\x86\Debug\GIS.HPU.ZYZ.SHP.dll
4 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\GIS.HPU.ZYZ.SHP\obj\x86\Debug\GIS.HPU.ZYZ.SHP.pdb
5 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\GIS.HPU.ZYZ.SHP\obj\x86\Debug\GIS.HPU.ZYZ.SHP.csprojResolveAssemblyReference.cache
6 | F:\GitHub\SHPReader\SHPReaderCSharp\GIS.HPU.ZYZ.SHP\bin\Debug\GIS.HPU.ZYZ.SHP.dll
7 | F:\GitHub\SHPReader\SHPReaderCSharp\GIS.HPU.ZYZ.SHP\bin\Debug\GIS.HPU.ZYZ.SHP.pdb
8 | F:\GitHub\SHPReader\SHPReaderCSharp\GIS.HPU.ZYZ.SHP\obj\x86\Debug\GIS.HPU.ZYZ.SHP.csprojResolveAssemblyReference.cache
9 | F:\GitHub\SHPReader\SHPReaderCSharp\GIS.HPU.ZYZ.SHP\obj\x86\Debug\GIS.HPU.ZYZ.SHP.dll
10 | F:\GitHub\SHPReader\SHPReaderCSharp\GIS.HPU.ZYZ.SHP\obj\x86\Debug\GIS.HPU.ZYZ.SHP.pdb
11 |
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.csprojResolveAssemblyReference.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.csprojResolveAssemblyReference.cache
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.dll
--------------------------------------------------------------------------------
/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/GIS.HPU.ZYZ.SHP/obj/x86/Debug/GIS.HPU.ZYZ.SHP.pdb
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SHPReaderCSharp
2 | This project is the operation description of shp file(include .shp .dbf ,shx). Some code was copied from other blog(http://blog.csdn.net/gis0911178/article/details/52162072).
3 |
--------------------------------------------------------------------------------
/SHPReaderCSharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GIS.HPU.ZYZ.SHP", "GIS.HPU.ZYZ.SHP\GIS.HPU.ZYZ.SHP.csproj", "{A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SHPTest", "SHPTest\SHPTest.csproj", "{D39F07CD-BDCF-4F0D-9D78-4729B1CE504C}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x86 = Debug|x86
11 | Release|x86 = Release|x86
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}.Debug|x86.ActiveCfg = Debug|x86
15 | {A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}.Debug|x86.Build.0 = Debug|x86
16 | {A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}.Release|x86.ActiveCfg = Release|x86
17 | {A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}.Release|x86.Build.0 = Release|x86
18 | {D39F07CD-BDCF-4F0D-9D78-4729B1CE504C}.Debug|x86.ActiveCfg = Debug|x86
19 | {D39F07CD-BDCF-4F0D-9D78-4729B1CE504C}.Debug|x86.Build.0 = Debug|x86
20 | {D39F07CD-BDCF-4F0D-9D78-4729B1CE504C}.Release|x86.ActiveCfg = Release|x86
21 | {D39F07CD-BDCF-4F0D-9D78-4729B1CE504C}.Release|x86.Build.0 = Release|x86
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------
/SHPReaderCSharp.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPReaderCSharp.suo
--------------------------------------------------------------------------------
/SHPTest/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using GIS.HPU.ZYZ.SHP.DBF;
6 | using System.IO;
7 | using GIS.HPU.ZYZ.SHP.SHP;
8 |
9 | namespace SHPTest
10 | {
11 | class Program
12 | {
13 | static void Main(string[] args)
14 | {
15 | string testPath = AppDomain.CurrentDomain.BaseDirectory;
16 | //dbf写
17 | //DbfTestWrite(Path.Combine(testPath, "testdata\\导出t.dbf"));
18 | //dbf读
19 | DbfTestRead(Path.Combine(testPath, "testdata\\国家导出\\面.dbf"));// result\\936\\图斑_面 result\\936\\导出t 国家导出\\面 国家标准\\面状措施
20 |
21 | //shp写入
22 | //ShpTestWrite(Path.Combine(testPath, "testdata\\导出t.shp"));
23 | //shp读取
24 | //ShpTestRead(Path.Combine(testPath, "testdata\\91导出\\面.shp"));//
25 | //ShpTestRead(Path.Combine(testPath, "testdata\\点.shp"));
26 | //ShpTestRead(Path.Combine(testPath, "testdata\\线.shp"));
27 | //ShpTestRead(Path.Combine(testPath, "testdata\\面.shp"));
28 | Console.ReadKey();
29 | }
30 |
31 | private static void DbfTestWrite(string filepath)
32 | {
33 | //编码Encoding.UTF8 中文字符占三字节 Encoding.GetEncoding(936) Encoding.Default中文字符占二字节
34 | var odbf = new DbfFile(Encoding.GetEncoding(936));
35 | odbf.Open(filepath, FileMode.Create);//FileMode.Create OpenOrCreate
36 | //创建列头
37 | //odbf.Header.AddColumn(new DbfColumn("编号", DbfColumn.DbfColumnType.Character, 20, 0));
38 | //odbf.Header.AddColumn(new DbfColumn("名称", DbfColumn.DbfColumnType.Character, 20, 0));
39 | //odbf.Header.AddColumn(new DbfColumn("地址", DbfColumn.DbfColumnType.Character, 20, 0));
40 | //odbf.Header.AddColumn(new DbfColumn("时间", DbfColumn.DbfColumnType.Date));
41 | //odbf.Header.AddColumn(new DbfColumn("余额", DbfColumn.DbfColumnType.Number, 15, 3));
42 |
43 | //var orec = new DbfRecord(odbf.Header) { AllowDecimalTruncate = true };
44 | //List list = User.GetList();
45 | ////foreach (var item in list)
46 | ////{
47 | //User item=list[0];
48 | // orec[0] = item.UserCode;
49 | // orec[1] = item.UserName;
50 | // orec[2] = item.Address;
51 | // orec[3] = item.date.ToString("yyyy-MM-dd HH:mm:ss");
52 | // orec[4] = item.money.ToString();
53 | // odbf.Write(orec, true);
54 | ////}
55 |
56 | //写入边界
57 | //odbf.Header.AddColumn(new DbfColumn("id", DbfColumn.DbfColumnType.Number, 19, 0));
58 | //var orec = new DbfRecord(odbf.Header) { AllowDecimalTruncate = true };
59 | //orec[0] = 1.ToString();
60 | //odbf.Write(orec, true);
61 |
62 | //写入图斑
63 | odbf.Header.AddColumn(new DbfColumn("图斑编码", DbfColumn.DbfColumnType.Character, 80, 0));
64 | odbf.Header.AddColumn(new DbfColumn("措施代码", DbfColumn.DbfColumnType.Character, 80, 0));
65 | odbf.Header.AddColumn(new DbfColumn("措施名称", DbfColumn.DbfColumnType.Character, 80, 0));
66 | odbf.Header.AddColumn(new DbfColumn("利用现状", DbfColumn.DbfColumnType.Character, 80, 0));
67 | odbf.Header.AddColumn(new DbfColumn("措施数量", DbfColumn.DbfColumnType.Number, 18, 15));
68 | odbf.Header.AddColumn(new DbfColumn("坡度", DbfColumn.DbfColumnType.Number, 18, 15));
69 |
70 | var orec = new DbfRecord(odbf.Header) { AllowDecimalTruncate = true };
71 | List list = MapPolygon.GetList();
72 | foreach (var item in list)
73 | {
74 | //MapPolygon item = list[0];
75 | orec[0] = item.Code;//顺序要与header顺序保持一致
76 | orec[1] = item.Mark;
77 | orec[2] = item.Name;
78 | orec[3] = item.State;
79 | orec[4] = item.Number.ToString();
80 | orec[5] = item.Slope.ToString();
81 | odbf.Write(orec, true);
82 | }
83 | odbf.Close();
84 | }
85 |
86 | private static void DbfTestRead(string filepath)
87 | {
88 | //编码Encoding.UTF8 中文字符占三字节 Encoding.GetEncoding(936)中文字符占二字节
89 | var odbf = new DbfFile(Encoding.GetEncoding(936));//编码
90 | odbf.Open(filepath,FileMode.Open);
91 | DbfHeader header= odbf.Header;
92 | Console.WriteLine(header.ColumnCount);
93 | //List list = new List();
94 | //for (int i = 0; i < header.RecordCount; i++)
95 | //{
96 | // DbfRecord record = odbf.Read(i);
97 | // if (record.ColumnCount >= 5)
98 | // {
99 | // User item = new User();
100 | // item.Address = record["地址"].Trim();//record.Column(record.FindColumn("编号"));
101 | // item.date = record.GetDateValue(record.FindColumn("时间"));
102 | // item.money = Convert.ToDecimal(record["余额"]);
103 | // item.UserCode = record["编号"].Trim();
104 | // item.UserName = record["名称"].Trim();
105 | // list.Add(item);
106 | // }
107 | //}
108 |
109 | List list = new List();
110 | for (int i = 0; i < header.RecordCount; i++)
111 | {
112 | DbfRecord record = odbf.Read(i);
113 | if (record.ColumnCount >= 5)
114 | {
115 | MapPolygon item = new MapPolygon();
116 | item.Code = record[record.FindColumEx("图斑编码")].Trim();// record["图斑编码"].Trim();//record.Column(record.FindColumn("编号"));
117 | item.Mark = record[record.FindColumEx("措施代码")].Trim();//record["措施代码"].Trim();
118 | item.Name = record[record.FindColumEx("措施名称")].Trim();//record["措施名称"].Trim();
119 | item.State = record[record.FindColumEx("利用现状")].Trim();//record["利用现状"].Trim();
120 | //item.Number =Convert.ToSingle( record["措施数量"]);
121 | //item.Slope = Convert.ToSingle(record["坡度"]);
122 | list.Add(item);
123 | }
124 | }
125 | Console.Read();
126 | }
127 |
128 | private static void ShpTestWrite(string filepath) {
129 | //string wkt = "POLYGON ((125.96146242562871 42.286033095153826,125.96045391503911 42.288221777709978,125.961333679596 42.289101542266863,125.96285717431645 42.289337576660174,125.96596853677373 42.288951338562029,125.96738474313359 42.287942827972429,125.96753494683843 42.286698282989519,125.96611874047856 42.285453738006609,125.96395151559453 42.285861433776873,125.96285717431645 42.286590994628924,125.96225635949712 42.28618329885866,125.962234901825 42.28618329885866,125.96146242562871 42.286033095153826),(125.96146242562871 42.286033095153826,125.96045391503911 42.288221777709978,125.961333679596 42.289101542266863,125.96285717431645 42.289337576660174,125.96596853677373 42.288951338562029,125.96738474313359 42.287942827972429,125.96753494683843 42.286698282989519,125.96611874047856 42.285453738006609,125.96395151559453 42.285861433776873,125.96285717431645 42.286590994628924,125.96225635949712 42.28618329885866,125.962234901825 42.28618329885866,125.96146242562871 42.286033095153826))";
130 | //string wkt = "LINESTRING ((125.96146242562871 42.286033095153826,125.96045391503911 42.288221777709978,125.961333679596 42.289101542266863,125.96285717431645 42.289337576660174,125.96596853677373 42.288951338562029,125.96738474313359 42.287942827972429,125.96753494683843 42.286698282989519,125.96611874047856 42.285453738006609,125.96395151559453 42.285861433776873,125.96285717431645 42.286590994628924,125.96225635949712 42.28618329885866,125.962234901825 42.28618329885866,125.96146242562871 42.286033095153826),(125.96146242562871 42.286033095153826,125.96045391503911 42.288221777709978,125.961333679596 42.289101542266863,125.96285717431645 42.289337576660174,125.96596853677373 42.288951338562029,125.96738474313359 42.287942827972429,125.96753494683843 42.286698282989519,125.96611874047856 42.285453738006609,125.96395151559453 42.285861433776873,125.96285717431645 42.286590994628924,125.96225635949712 42.28618329885866,125.962234901825 42.28618329885866,125.96146242562871 42.286033095153826))";
131 | //string wkt = "POINT (125.96146242562871 42.286033095153826)";
132 | //边界
133 | //string border = "POLYGON ((124.797090749881 43.4559641037949,124.833482961795 43.4679804001816,124.848589162967 43.4528741990098,124.855455618045 43.4346780930527,124.85854552283 43.4171686326035,124.861978750369 43.3986292038926,124.851679067752 43.3872995530137,124.840349416873 43.3728799973496,124.82421324744 43.3701334153184,124.804987173221 43.3704767380723,124.784387807987 43.3708200608262,124.777178030155 43.378716484166,124.773744802615 43.3996591721543,124.771684866092 43.4236917649277,124.797090749881 43.4559641037949))";
134 | //图斑
135 | string p1 = "POLYGON ((124.799150686405 43.4515009079941,124.808077078006 43.4490976487168,124.807390432498 43.4415445481309,124.801210622928 43.4401712571152,124.795374136112 43.4449777756699,124.799150686405 43.4515009079941))";
136 | string p2 = "POLYGON ((124.792970876834 43.4370813523301,124.797777395389 43.4288416062363,124.801553945682 43.4168253098496,124.795717458865 43.4151086960801,124.787821035526 43.422661796666,124.792284231326 43.4284982834824,124.792970876834 43.4370813523301))";
137 | string p3 = "POLYGON ((124.82249663367 43.441201225377,124.830736379764 43.4532175217637,124.835199575565 43.4442911301621,124.83657286658 43.4339914475449,124.832452993533 43.4309015427598,124.826273183963 43.4315881882676,124.82249663367 43.441201225377))";
138 | string p4 = "POLYGON ((124.807733755252 43.4130487595566,124.8163168241 43.4212885056504,124.821466665408 43.4096155320176,124.821466665408 43.4024057541855,124.815630178592 43.397255912877,124.80842040076 43.400689140416,124.807733755252 43.4130487595566))";
139 |
140 | List lwkt = new List();
141 | //lwkt.Add(border);
142 | lwkt.Add(p1);
143 | lwkt.Add(p2);
144 | lwkt.Add(p3);
145 | lwkt.Add(p4);
146 | var oshp = new ShpFile();
147 | oshp.Creat(filepath, FileMode.Create);
148 | oshp.WriteShp(lwkt);
149 | oshp.WriteShx();
150 | oshp.Close();
151 | }
152 |
153 | private static void ShpTestRead(string filepath)
154 | {
155 | var oshp = new ShpFile();
156 | oshp.Open(filepath, FileMode.Open);
157 | oshp.ReadShxRecord();
158 | oshp.ReadShpRecord();
159 | //根据实际情况进行投影
160 | //oshp.ShpFileProject = ShapeProject.WGS_1984_Albers;
161 | //oshp.CoordTransPro2Geo();//投影转换
162 | foreach (var item in oshp.ShpRecord.RecordDic)
163 | {
164 | string wkt = item.Value.WKTStr;//获取wkt
165 | Console.WriteLine(wkt);
166 | }
167 | }
168 |
169 | }
170 | ///
171 | /// 面图斑数据结构
172 | ///
173 | public class MapPolygon {
174 | public string Code { get; set; }
175 | public string Mark { get; set; }
176 | public string Name { get; set; }
177 | public string State { get; set; }
178 | public float Number { get; set; }
179 | public float Slope { get; set; }
180 |
181 | public static List GetList()
182 | {
183 | List list = new List();
184 | list.Add(new MapPolygon() { Code = "01", Mark = "sbl3", Name = "水保林", State = "耕地", Number = 1.1f, Slope=12f });
185 | list.Add(new MapPolygon() { Code = "02", Mark = "sbl3", Name = "水保林", State = "耕地", Number = 1.6f, Slope = 2f });
186 | list.Add(new MapPolygon() { Code = "03", Mark = "sbl3", Name = "水保林", State = "耕地", Number = 5.1f, Slope = 7f });
187 | list.Add(new MapPolygon() { Code = "04", Mark = "sbl3", Name = "水保林", State = "耕地", Number = 4.1f, Slope = 5f });
188 | return list;
189 | }
190 | }
191 | ///
192 | /// 测试数据结构
193 | ///
194 | public class User
195 | {
196 | public string UserCode { get; set; }
197 | public string UserName { get; set; }
198 | public string Address { get; set; }
199 | public DateTime date { get; set; }
200 | public decimal money { get; set; }
201 |
202 | public static List GetList()
203 | {
204 | List list = new List();
205 | list.Add(new User() { UserCode = "A1", UserName = "张三", Address = "上海杨浦", date = DateTime.Now, money = 1000.12m });
206 | list.Add(new User() { UserCode = "A2", UserName = "李四", Address = "湖北武汉", date = DateTime.Now, money = 31000.008m });
207 | list.Add(new User() { UserCode = "A3", UserName = "王子龙", Address = "陕西西安", date = DateTime.Now, money = 2000.12m });
208 | list.Add(new User() { UserCode = "A4", UserName = "李三", Address = "北京", date = DateTime.Now, money = 3000.12m });
209 | return list;
210 | }
211 | }
212 |
213 | }
214 |
--------------------------------------------------------------------------------
/SHPTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过以下
6 | // 特性集控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("SHPTest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("SHPTest")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("3e581fd8-571b-4e4a-9770-4207d2031b09")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 内部版本号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SHPTest/SHPTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {D39F07CD-BDCF-4F0D-9D78-4729B1CE504C}
9 | Exe
10 | Properties
11 | SHPTest
12 | SHPTest
13 | v4.0
14 |
15 |
16 | 512
17 |
18 |
19 | x86
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | x86
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {A819E4CD-29F2-4B5F-ABAA-BC2ED8B1A579}
53 | GIS.HPU.ZYZ.SHP
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/SHPTest/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/GIS.HPU.ZYZ.SHP.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/bin/Debug/GIS.HPU.ZYZ.SHP.dll
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/GIS.HPU.ZYZ.SHP.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/bin/Debug/GIS.HPU.ZYZ.SHP.pdb
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/ProjNet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/bin/Debug/ProjNet.dll
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/SHPTest.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/bin/Debug/SHPTest.exe
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/SHPTest.exe.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/SHPTest.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/bin/Debug/SHPTest.pdb
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/SHPTest.vshost.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/bin/Debug/SHPTest.vshost.exe
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/SHPTest.vshost.exe.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/SHPTest.vshost.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/project/abers.prj:
--------------------------------------------------------------------------------
1 | PROJCS["WGS_1984_Albers",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Albers"],PARAMETER["false_easting",0.0],PARAMETER["false_northing",0.0],PARAMETER["central_meridian",105.0],PARAMETER["standard_parallel_1",25.0],PARAMETER["standard_parallel_2",47.0],PARAMETER["latitude_of_origin",0.0],UNIT["Meter",1.0]]
--------------------------------------------------------------------------------
/SHPTest/bin/Debug/project/geopro.prj:
--------------------------------------------------------------------------------
1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
--------------------------------------------------------------------------------
/SHPTest/obj/x86/Debug/DesignTimeResolveAssemblyReferences.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/obj/x86/Debug/DesignTimeResolveAssemblyReferences.cache
--------------------------------------------------------------------------------
/SHPTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache
--------------------------------------------------------------------------------
/SHPTest/obj/x86/Debug/SHPTest.csproj.FileListAbsolute.txt:
--------------------------------------------------------------------------------
1 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\bin\Debug\SHPTest.exe.config
2 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\bin\Debug\SHPTest.exe
3 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\bin\Debug\SHPTest.pdb
4 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\bin\Debug\GIS.HPU.ZYZ.SHP.dll
5 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\bin\Debug\GIS.HPU.ZYZ.SHP.pdb
6 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\obj\x86\Debug\SHPTest.csprojResolveAssemblyReference.cache
7 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\obj\x86\Debug\SHPTest.exe
8 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\obj\x86\Debug\SHPTest.pdb
9 | F:\国遥\工具\shp c#读取\GIS.HPU.ZYZ.SHP\SHPTest\bin\Debug\ProjNet.dll
10 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\bin\Debug\SHPTest.exe.config
11 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\obj\x86\Debug\SHPTest.exe
12 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\obj\x86\Debug\SHPTest.pdb
13 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\bin\Debug\SHPTest.exe
14 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\bin\Debug\SHPTest.pdb
15 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\bin\Debug\GIS.HPU.ZYZ.SHP.dll
16 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\bin\Debug\ProjNet.dll
17 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\bin\Debug\GIS.HPU.ZYZ.SHP.pdb
18 | F:\GitHub\SHPReader\SHPReaderCSharp\SHPTest\obj\x86\Debug\SHPTest.csprojResolveAssemblyReference.cache
19 |
--------------------------------------------------------------------------------
/SHPTest/obj/x86/Debug/SHPTest.csprojResolveAssemblyReference.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/obj/x86/Debug/SHPTest.csprojResolveAssemblyReference.cache
--------------------------------------------------------------------------------
/SHPTest/obj/x86/Debug/SHPTest.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/obj/x86/Debug/SHPTest.exe
--------------------------------------------------------------------------------
/SHPTest/obj/x86/Debug/SHPTest.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YanzheZhang/SHPReaderCSharp/71ff1eaa97b6bed34d7b68cd2dddde49573dc082/SHPTest/obj/x86/Debug/SHPTest.pdb
--------------------------------------------------------------------------------